Sqlla
一套資料庫的 ORM 微型庫,提供簡單高效的 API 來運算元據庫。
Sqlla 擁有極少的API,使用方式簡單。讓開發者不需要關心資料庫操作的具體細節,只需專注SQL和業務邏輯。同時簡單的事務模型讓開發過程增益很多。
簡單使用
-
建立實體類,用 @SqllaEntity 標識
@SqllaEntity
public class UserBean {
private String uid;
private String phone;
private String name;
// 使用 SqllaColumnAlias 標識後的屬性使用 alias 的值來對應表中的列
// 沒有標識時直接使用屬性的名字來對應
@SqllaColumnAlias("gender")
private int sex;
// setter getter here
}
-
DAO 介面類
public interface UserDao {
// 用 @Sql 標識
@Sql("select * from t_user where uid = ?")
UserBean getUserById(String uid);
@Sql("select * from t_user desc by lastActiveTS limit ?")
List<UserBean> topUsers(int limit);
@Sql("select (count(*) > 0) from t_user where name = ?")
boolean userExist(String name);
@Sql("insert into t_user (id, uid, name, phone) values (null, ?, ?, ?)")
boolean insertUser(String uid, String name, String phone);
@Sql("delete from t_user where uid = ?")
boolean deleteUserById(String uid);
}
-
建立 Sqlla 例項
Sqlla.ConnectionPool pool = your implimention;
Sqlla sqlla = new Sqlla.Builder().pool(pool).build();
-
資料庫操作 CRUD
UserDao dao = sqlla.createApi(UserDao.class);
// 獲取結果集中的第一個物件(結果集的第一行)
UserBean bean = dao.getUserById("uid_10000001");
// 查詢最新的10個使用者
List<UserBean> beanList = dao.topUsers(10);
// 查詢使用者是否存在:對於updateable sql, 返回值可以是int, boolean(>0 for true), void
boolean exists = dao.userExist("李多情");
// 插入使用者到資料庫(update count > 0 for true)
boolean inserted = dao.insertUser("uid_10000002", "李明洙", "1324113361*");
// 刪除指定使用者
boolean deleted = dao.deleteUserById("uid_10000003");
DAO介面不需要任何的實現類,是不是使用非常簡單??
概念
-
實體: 庫中預置了兩種實體模型
-
@SqllaEntity 標識的Pojo實體
-
ViewObject 結果集檢視實體,代表著結果集的一樣。類似於扁平的 JSONObject
-
-
轉換器: 將結果集轉換成實體的部件,可以自定義
-
DAO介面: CRUD操作集合,每個方法代表一條SQL操作
-
事務: Transaction<T> 代表一個多條DAO方法的事務
深入使用
Sqlla.Builder 提供 pool() 和 addConverterFactory() 方法。
pool()方法必須設定一個ConnectionPool例項(test 程式碼提供了一個基於c3p0資料來源的 pool)。
addConverterFactory() 方法設定 自定義的結果集轉換工廠(實現 ResultConverter.Factory 介面)。 內部預置了三種工廠,
-
PrimitiveTypeConverterFactory 轉換基礎資料型別
-
SqllaEntityConverterFactory 轉換 @SqllaEntity 表示的實體類和其列表(List)
-
ViewObjectConverterFactory 轉換 ViewObject 結果集檢視和其列表(List<ViewObject>)
外部可以自定義針對自己特定型別的轉換工廠 (ResultSet –> CustomType),自定義的轉換工廠 return !null 時會攔截系統預置的轉換工廠。
事物支援
當前版本對事物進行了簡單的支援,與Spring的配合暫時沒有做過測試。
例子
Boolean ret = sqlla.transact(new Transaction<Boolean>(Isolation.SERIALIZABLE) {
public Boolean transact() throw Exception {
UserDao dao = sqlla.createApi(UserDao.class);
dao.deleteUserById("uid_10000004");
// 也可以手動rollback() or commit(true)
dao.deleteUserById("uid_10000005");
return true;
}
}, false); // failed value
上面的程式碼執行了一個簡單的事務,事務中包含兩條刪除語句。如果都成功,則自動 commit;只要有一個失敗,則會rollback。當回滾之後,事務將返回給定的 failed value。在事務中,也可以手動 rollback() 或者 commit(val),這兩個操作只能呼叫一次,而且會中斷其後面的程式碼執行,要謹慎使用。
開啟一個事務有兩種方法:
<T> T sqlla.transact(Transaction<T> transaction);
void sqlla.transact(Transaction0 transaction);
注意: transact方法是一個同步方法,它會立馬呼叫transaction, 並返回。
Transaction<T> 是一個抽象類,抽象方法transact用於實現事務的具體邏輯。最多有三個引數:isolation,readOnly 和 timeout,分別代表隔離級別,是否只讀,超時秒數。Transaction0 是其範型為 Void 的子類。Transaction0 是其範型為 Void 的子類。
巢狀事務
Sqlla 對事務的巢狀提供了簡單的支援。相比於Spring事務間的多種傳播機制,Sqlla只提供了 REQUIRES_NEW 的傳播機制:內層和外層完全隔離開來,互不影響。
methodA 和 methodB 是兩個事務方法,他們之間完全隔離,提交和回滾互不影響。
public void methodA() {
sqlla.transact(new Transaction0() {
protected void transact0() throws Exception {
UserDao dao = sqlla.createApi(UserDao.class);
boolean inserted = dao.insertUser("uid_1000007", "王五", "13241133615");
methodB(); // 事務B方法
boolean deleted = dao.deleteUserByName("張三");
}
});
}
public void methodB() {
sqlla.transact(new Transaction0() {
protected void transact0() throws Exception {
UserDao dao = sqlla.createApi(UserDao.class);
boolean inserted = dao.insertUser("uid_1000008", "趙六", "13241133616");
}
}
}
如何驗證兩個事務之間互不影響?
我們先把 mehtodA 中的 deleteUserByName 方法的sql改成一個錯的sql
@Sql("delete from t_user where name = ????????")
boolean deleteUserByName(String name);
現在再呼叫 methodA,你會發現 “王五” 這個使用者並未插入到資料庫中, “張三” 也沒有被刪除,而 “趙六” 卻實實在在的插入到了資料庫中。
注意:假如 methodB 本身並不是一個單獨的事務方法 (未用 sqlla.transact 開啟),那麼他將使用外層的事務。所以要不要使用事務,一定要考慮好,不然可能會有一些意想不到的效果。
Sqlla雖然提供了註解的方式來操作SQL,但是事務並沒有使用註解的方式, 這和MyBatis一致。實際內部實現也和 MyBatis 相差無幾。