摘要:注意供應器只會在倉庫工廠第一次創建工廠時調用,而參數處理器和結果解析器將在每次倉庫方法被調用時調用。解析器接收一個語句表模型的類聲明觸發解析器的倉庫方法聲明。因此當您配置了一個結果解析器,語句的執行時機將推遲到這里。
Juice
這是我自己做的一個小項目,也可能會棄坑... 留作紀念吧。GitHub 地址
簡介Juice 是一個簡易的、尚不完善的基于 Java 的SQL數據庫工具,它提供了對SQL語句最大程度的控制,和一點簡單的擴展能力。
這些是開發時的一點筆記:
做個數據庫幫助庫雛形
做個數據庫幫助庫雛形2
RepositoryFactory factory = RepositoryFactory.configure(ConnectionConfiguration.builder() .driverClass("com.mysql.jdbc.Driver") .connectionURL("jdbc:mysql://localhost:3306/hsc") .username("gdpi") .password("gdpi") .build()); StudentRepository repository = factory.get(StudentRepository.class); List功能與使用studentList = repository.findAll(); // LinkedList size: 56 Student student = repository.getNameById("20152203300"); // {name: "krun", id: null, college: null, ...} int count = repository.updateGenderById("20152203300", "男"); // 1 Student student2 = repository.findById("20152203300"); // {name: "krun", id: "20152203300", gender: "男", major: "軟件技術", ...}
使用 Juice 只需要簡單的幾步:
注: 本示例使用 lombok 和 mysql-connector(5.1.44)
數據庫連接配置: ConnectionConfiguration當前版本的 Juice 只需要以下幾個參數用以連接數據庫:
driverClass:這個參數用于向驅動管理器注冊一個數據庫連接驅動。(本示例將使用 com.mysql.jdbc.Driver)
connectionURL: 這個參數用于向驅動管理器獲取一個數據庫連接,常用的如:jdbc:mysql://localhost:3306/juice,您可以附帶任何連接語句中允許附加的參數,如字符編碼設置等等。
username: 這個參數是獲取數據庫連接時所需要的數據庫賬戶名
password: 這個參數是獲取數據庫連接時所需要的數據庫密碼
不建議直接在 connectionURL中配置連接所需的數據庫賬戶及密碼。
在未來的版本中,Juice會嘗試加入對 *.properties文件的支持,如此一來,您可以直接在 *.properties文件中設置連接的詳細參數。對MySQL適用的 properties選項請參見這里。
在 Java SE 環境中,您可以通過 ConnectionConfiguration.builder()構造器來構造一個配置:
ConnecetionConfiguration conf = ConnectionConfiguration.builder() .driverClass("com.mysql.jdbc.Driver") .connectionURL("jdbc:mysql://localhost:3306/juice") .username("gdpi") .password("gdpi") .build();
如果是類似 Spring 這樣可以配置 Bean實例的環境中,您可以使用類似如下的方式來以Bean的方式創建一個配置:
倉庫工廠: RepositoryFactory
倉庫工廠是創建、管理倉庫的地方。Juice 允許在一個 Java Application 中存在多個倉庫工廠的實例,但由于每個倉庫工廠都會持有一個 數據庫連接供應器(ConnectionProvider) ,因此建議使用默認全局工廠。
每個工廠都由一個自己的名字,默認全局工廠的名字為: global, 這并不是一個常量值,為了避免某些情況下發生沖突,Juice 允許你在創建前修改 RepositoryFactory.FACTORY_GLOBAL 的值來更改默認全局工廠的名字。請注意,如果您在創建全局工廠后修改了該值,那么再次使用 不指定名稱的工廠獲取方法(RepositoryFactory.get())將導致重新創建一個以新值命名的全局工廠。
在使用倉庫工廠前,需要傳入一個 ConnectionConfiguration實例,使倉庫工廠得以初始化內部的數據庫連接供應器。
在 Java SE 環境中,您可以通過下面的方式來配置倉庫工廠:
//這里的 conf 即為前一節所創建的數據庫連接配置 // 配置全局倉庫工廠 RepositoryFactory globalFactory = RepositoryFactory.configure(conf); // 配置指定名稱的倉庫工廠 RepositoryFactory fooFactory = RepositoryFactory.configure("foo", conf); // 請注意,使用第二種方式配置工廠時,使用默認全局工廠名稱將拋出錯誤,因為這會破壞 API 所劃分的全局、特定工廠的界限 RepositoryFactory wrongFactory = RepositoyFactory.configure(RepositoryFactory.FACTORY_GLOBAL, conf); // > RuntimeException
如果是類似 Spring 這樣可以配置 Bean實例的環境中,您可以使用類似如下的方式來以Bean的方式創建倉庫工廠:
在配置倉庫工廠后,您可以通過 RepositoryFactory.get()和 RepositoyFactory.get(name)來獲取全局或給定名稱的倉庫工廠。
表模型Juice 可以將您給定的一個 Java 類視為一個表模型,就像下面這樣:
@Data @Entity("student") public class Student { private String id; @Column("class") private String clazz; private int code; private String college; private String gender; private int grade; private String major; private String name; }
@Data 注解來自 lombok
@Entity 注解是一個可選項,它只有一個必填屬性: value。當配置該注解時,Juice將使用該值作為表名;如果您指定了這個類是個表模型,Juice 卻找不到該注解時,將使用類名的全小寫形式作為表名。
@Column注解同樣是一個可選項,它只有一個必填屬性: value。當配置該注解時,Juice將使用該值作為數據庫中此表的字段名,否則使用 Java 類字段名作為數據庫中此表的字段名。
倉庫: RepositoryRepository 是一個注解,它實際上只是一個用于表明某個接口是一個倉庫的標記。就像下面這樣:
public interface StudentRepository extends Repository{ @Query (value = "SELECT * FROM %s") List findAll(); @Query (value = "SELECT * FROM %s WHERE id = ?") Student findById(String id); @Query (value = "UPDATE %s SET gender = ? WHERE id = ?", processor = StudentChain.class, processMethod = "replaceParameterLocation") Integer updateGenderById(String id, String gender); @Query ("SELECT name FROM %s WHERE id = ?") Student getNameById(String id); }
Repository需要填入兩個泛型信息,第一個是該倉庫所操作的表模型,第二個是該表模型的主鍵類型。
@Query 注解注: 事實上到目前為止,Juice 并不區分主鍵和其他字段,只是為了以后完善留下空間。
由于到目前為止,Juice 短期內不會實現 解析方法名并映射為一個SQL操作 這個 feature, 因此需要 @Query 注解來標記一個方法,并以此提供一些信息,Juice 提供的擴展能力也在這里體現:
@Query注解具有以下七個屬性:
String value: 這個屬性指定了方法所映射的 SQL操作,其中有著一些約定:%s占位符用于 Juice 填充表名,而 ? 占位符是 PreparedStatement 所使用的參數占位符。由于 Juice 提供簡單的默認實現,這些默認實現使用的就是 PreparedStatement,因此如果您使用了不一樣的Statement實現,您可以使用任何與之配合的占位符。注意:如果您選擇了使用 %*系列作為占位符,那么請記得第一個 %s將會被 Juice 用來填充表名。
Class extends RepositoryStatementProvider> provider: 這個屬性指定了語句供應器所處的類,您可以指定任何實現了RepositoryStatementProvider接口的類,默認值為 Juiec 提供的DefaultPreparedStatementProvider,詳細信息請參見下文。
String provideMethod: 這個屬性指定了注解所在方法所使用的語句供應器,當provider 屬性使用默認值時,此屬性無效;默認值為注解所在方法的名字或provide。
Class extends RepositoryParameterProcessor> processor: 這個屬性指定了參數處理器所處的類,您可以指定任何實現了 RepositoryParameterProcessor接口的類,默認值為 Juiec 提供的默認參數處理器 DefaultParameterProcessor,詳細信息請參見下文。
String processMethod: 這個屬性指定了注解所在方法所使用的參數處理器,當processor屬性使用默認值時,此屬性無效;默認值為注解所在方法的名字或 process。
Class extends RepositoryResultResolver> resolver: 這個屬性指定了結果解析器所處的類,您可以指定任何實現了 RepositoryResultResolver接口的類,默認值為 Juiec 提供的 DefaultResultResolver,詳細信息請參見下文。
String resolveMethod: 這個屬性指定了注解所在方法所使用的結果解析器,當resolver屬性使用默認值時,此屬性無效;默認值為注解所在方法的名字或 resolve。
注意:
您所指定的 provideMethod、processMethod、resolveMethod都必須是靜態方法,這并無太多考量,只是為了減輕 Juice 的對象管理成本。
一個語句供應器的方法簽名應該如下:
public static Statement provideMethodName(Connection connection, String sql)
供應器所在的類是 @Query.provider 的值,方法名是 @Query.provideMethod 的值。
供應器接收一個 java.sql.connection和@Query.value值,并返回一個 java.sql.statement。
這里的 sql 已經填充了表名
這里的Connection可以不關閉,它會由倉庫工廠進行復用。
注意:供應器只會在倉庫工廠第一次創建工廠時調用,而參數處理器和結果解析器將在每次倉庫方法被調用時調用。
如果您希望使用項目所特定的、實現了裝飾器模式的、特殊的Statement實例,可以為方法定義一個、或創建一個全局的語句供應器,并為所有方法指定。
也許后期會在 factory 中加入替換默認語句供應器、參數處理器、結果解析器的接口。
默認的語句供應器 DefaultPreparedStatementProvider.provide將根據給定 sql創建一個 com.mysql.jdbc.PreparedStatement實例。
一個參數處理器的方法簽名應該類似下面這樣(這里對應的是 StudentRepository.findById):
public static Statement findById (Statement statement, String id)
處理器所在的類是 @Query.processor的值,方法名是 @Query.processMethod 的值。
處理器接收一個java.sql.statement和具體的參數列表,并返回一個java.sql.statement。
如果您希望在每次方法調用時都有個地方可以記錄日志、進行參數檢查,可以為其配置一個參數處理器。
在當前版本的 Juice 中,如果您希望處理類似下面這種情況:
public StudentRepository extends Repository{ @Query("INSERT INTO %s (%s) VALUES (%s)") Integer insert(Student student); }
您需要為其配置一個語句供應器:
public static Statement insert(Connection connection, String sql) { return connection.prepareStatement( String.format(sql, StringUtils.convertObjectFields2StringList(Student.class))); }
和一個參數處理器:
public static Statement insert(Statement statement, Student student) { PreparedStatement ps = (PreparedStatement) statement; for (Field field : student.getClass().getDeclaringFields()) { field.setAccessable(true); ps.setObject(index, field.get(student)); } }
以上均為偽代碼
Juice 所提供的默認參數處理器 DefaultParameterProcessor,只是簡單得把參數按順序填充入SQL語句中并返回。因此,類似下面這種情況可能會發生錯誤:
public StudentRepository extends Repository{ @Query("UPDATE %s SET gender = ? WHERE id = ?") Integer setGenderById(String id, String gender); }
setGenderById的參數列表中,id在前,gender·在后,這會使得DefaultParameter.process輸出:
UPDATE student SET gender = {id} WHERE id = {gender}
顯然這是錯誤的。如果要避免這種情況,可以直接把方法的參數列表按 SQL語句中的參數順序排放;也可以為其指定一個參數處理器用以調整參數填充順序。
一個結果解析器的方法簽名應該類似下面這樣:
public static Object resolve(Statement statement, Class> entityClass, Method method)
解析器所在的類是 @Query.resolver 的值,方法名是@Query.resolveMethod的值。
解析器接收一個java.sql.statement語句、Class>表模型的類聲明、Method觸發解析器的倉庫方法聲明。
這里的 statement 尚未執行,因為java.sql.statement.execute系列接口需要一些額外參數,這導致 Juice無法確保一致的行為。因此當您配置了一個結果解析器,語句的執行時機將推遲到這里。
Juice 所提供的默認解析器 DefaultResultResolver有著很多限制:
只支持解析倉庫所聲明的表模型類型和其List形式
對于 INSERT/UPDATE/DELETE操作,只會返回Integer數值用以表示該SQL操作影響的行數
不支持表模型字段含有其他非SQL types類型的遞歸、嵌套解析
因此,如果您希望能解析復雜的結果,例如將前一節中的 insert操作返回插入后的結果并映射為一個Student:
public StudentRepository extends Repository{ @Query("UPDATE %s SET gender = ? WHERE id = ?") Student setGenderById(String id, String gender); }
那么還需要配置一個結果解析器:
public static Student insert(Statement statement, Class> entityClass, Method method) { // 解析邏輯... }結束
那么, Juice 的介紹、使用幫助就到此結束了,感謝您的觀看 : )
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70572.html
摘要:在線簡歷生成工具,可以導出。技巧目前寫簡歷的方式有兩種普遍被認可,一種是一種是。培養新人和帶團隊其他項目項目該項目是,使用技術,完成功能。閱讀原文點擊查看簡歷模板。 工欲善其事必先利其器,這是自古以來的道理,所以如果想找到一份好的工作,一定要先整理一份好的簡歷。 模板 寫簡歷首先要有一個好的模板,我們做技術的不同于 UX,UED,我們不需要那么花哨,但是也需要整潔干凈。好的模板能讓你的...
摘要:用聲明的常量無法在后面的代碼中改值。表達式里還有一個很方便的就是表達式,舉個例子運行結果為后聲明的里以數組的形式存放了函數的剩余參數,是不是很方便。 ES6入門筆記(一) 安裝babel 由于瀏覽器對ES6的支持還不是很好,編寫ES6代碼前我們要安裝一個babel工具將ES6代碼編譯成ES5代碼,用如下命令安裝babel: npm install -g babel-core ...
摘要:前言在前面的之介紹和使用和之進階中講了的使用方式和一些常用的方法,這篇文章就來演示一下的實際應用。實際應用先創建一個訂單類和商品類,每個訂單都有年份商品數量和商品對象屬性,而商品類里面則包含了名字和價格屬性。 前言: 在前面的 Java 8之stream介紹和使用 和 Java 8之stream進階 中講了stream的使用方式和一些常用的方法,這篇文章就來演示一下stream的實際應...
摘要:狀態模式對于對象內部的狀態,允許其在不同的狀態下,擁有不同的行為,對狀態單獨封裝成類。通過什么來舉例子呢設計到多狀態,不同狀態下各自具有不同行為的東西,而且理解起來相對容易的。下篇更新適配器模式 目錄 狀態模式 為什么使用狀態模式? 應用實例 狀態模式 狀態模式,顧名思義,肯定是和狀態有關,進一步思考,我們在討論的是設計模式,設計模式中的重要原則對變化的進行封裝,順著這個思路去想,...
閱讀 1404·2021-10-14 09:43
閱讀 998·2021-09-10 10:51
閱讀 1446·2021-09-01 10:42
閱讀 2196·2019-08-30 15:55
閱讀 588·2019-08-30 15:55
閱讀 2347·2019-08-30 14:21
閱讀 1720·2019-08-30 13:04
閱讀 3471·2019-08-29 13:09