Sqlla
一套數(shù)據(jù)庫(kù)的 ORM 微型庫(kù)彰亥,提供簡(jiǎn)單高效的 API 來(lái)操作數(shù)據(jù)庫(kù)。
Sqlla 擁有極少的API臂痕,使用方式簡(jiǎn)單匾南。讓開發(fā)者不需要關(guān)心數(shù)據(jù)庫(kù)操作的具體細(xì)節(jié),只需專注SQL和業(yè)務(wù)邏輯馏锡。同時(shí)簡(jiǎn)單的事務(wù)模型讓開發(fā)過(guò)程增益很多雷蹂。
簡(jiǎn)單使用
- 創(chuàng)建實(shí)體類,用
@SqllaEntity
標(biāo)識(shí)
@SqllaEntity
public class UserBean {
private String uid;
private String phone;
private String name;
// 使用 SqllaColumnAlias 標(biāo)識(shí)后的屬性使用 alias 的值來(lái)對(duì)應(yīng)表中的列
// 沒(méi)有標(biāo)識(shí)時(shí)直接使用屬性的名字來(lái)對(duì)應(yīng)
@SqllaColumnAlias("gender")
private int sex;
// setter getter here
}
- DAO 接口類
public interface UserDao {
// 用 @Sql 標(biāo)識(shí)
@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);
}
- 創(chuàng)建 Sqlla 實(shí)例
Sqlla.ConnectionPool pool = your implimention;
Sqlla sqlla = new Sqlla.Builder().pool(pool).build();
- 數(shù)據(jù)庫(kù)操作 CRUD
UserDao dao = sqlla.createApi(UserDao.class);
// 獲取結(jié)果集中的第一個(gè)對(duì)象(結(jié)果集的第一行)
UserBean bean = dao.getUserById("uid_10000001");
// 查詢最新的10個(gè)用戶
List<UserBean> beanList = dao.topUsers(10);
// 查詢用戶是否存在:對(duì)于updateable sql, 返回值可以是int, boolean(>0 for true), void
boolean exists = dao.userExist("李多情");
// 插入用戶到數(shù)據(jù)庫(kù)(update count > 0 for true)
boolean inserted = dao.insertUser("uid_10000002", "李明洙", "1324113361*");
// 刪除指定用戶
boolean deleted = dao.deleteUserById("uid_10000003");
DAO接口不需要任何的實(shí)現(xiàn)類杯道,是不是使用非常簡(jiǎn)單匪煌???
概念
- 實(shí)體: 庫(kù)中預(yù)置了兩種實(shí)體模型
-
@SqllaEntity
標(biāo)識(shí)的Pojo實(shí)體 -
ViewObject
結(jié)果集視圖實(shí)體,代表著結(jié)果集的一行蕉饼,類似于扁平的JSONObject
-
- 轉(zhuǎn)換器: 將結(jié)果集轉(zhuǎn)換成實(shí)體的部件虐杯,可以自定義
- DAO接口: CRUD 操作集合,每個(gè)方法代表一條SQL操作
- 事務(wù):
Transaction<T>
代表一個(gè)多條 DAO 方法的事務(wù)
深入使用
Sqlla.Builder
提供 pool()
和 addConverterFactory()
方法昧港。
pool()方法必須設(shè)置一個(gè)ConnectionPool
實(shí)例(test 代碼提供了一個(gè)基于c3p0數(shù)據(jù)源的 pool)擎椰。
addConverterFactory() 方法設(shè)置 自定義的結(jié)果集轉(zhuǎn)換工廠(實(shí)現(xiàn) ResultConverter.Factory
接口)。 內(nèi)部預(yù)置了三種工廠,
-
PrimitiveTypeConverterFactory
轉(zhuǎn)換基礎(chǔ)數(shù)據(jù)類型 -
SqllaEntityConverterFactory
轉(zhuǎn)換@SqllaEntity
表示的實(shí)體類和其列表(List
) -
ViewObjectConverterFactory
轉(zhuǎn)換ViewObject
結(jié)果集視圖和其列表(List<ViewObject>
)
外部可以自定義針對(duì)自己特定類型的轉(zhuǎn)換工廠 (
ResultSet --> CustomType
)创肥,自定義的轉(zhuǎn)換工廠return !null
時(shí)會(huì)攔截系統(tǒng)預(yù)置的轉(zhuǎn)換工廠达舒。
事物支持
當(dāng)前版本對(duì)事物進(jìn)行了簡(jiǎn)單的支持,與Spring
的配合暫時(shí)沒(méi)有做過(guò)測(cè)試叹侄。
例子
Boolean ret = sqlla.transact(new Transaction<Boolean>(Isolation.SERIALIZABLE) {
public Boolean transact() throw Exception {
UserDao dao = sqlla.createApi(UserDao.class);
dao.deleteUserById("uid_10000004");
// 也可以手動(dòng)rollback() or commit(true)
dao.deleteUserById("uid_10000005");
return true;
}
}, false); // failed value
上面的代碼執(zhí)行了一個(gè)簡(jiǎn)單的事務(wù)巩搏,事務(wù)中包含兩條刪除語(yǔ)句。如果都成功趾代,則自動(dòng) commit
贯底;只要有一個(gè)失敗,則會(huì)rollback
撒强。當(dāng)回滾之后禽捆,事務(wù)將返回給定的 failed value笙什。在事務(wù)中,也可以手動(dòng) rollback()
或者 commit(val)
胚想,這兩個(gè)操作只能調(diào)用一次琐凭,而且會(huì)中斷其后面的代碼執(zhí)行,要謹(jǐn)慎使用浊服。
開啟一個(gè)事務(wù)有兩種方法:
<T> T sqlla.transact(Transaction<T> transaction);
void sqlla.transact(Transaction0 transaction);
注意: transact
方法是一個(gè)同步方法统屈,它會(huì)立馬調(diào)用transaction
, 并返回。
Transaction<T>
是一個(gè)抽象類牙躺,抽象方法transact用于實(shí)現(xiàn)事務(wù)的具體邏輯愁憔。最多有三個(gè)參數(shù):isolation
,readOnly
和 timeout
述呐,分別代表 隔離級(jí)別惩淳,是否只讀,超時(shí)秒數(shù)乓搬。Transaction0
是其范型為 Void 的子類思犁。
嵌套事務(wù)
Sqlla 對(duì)事務(wù)的嵌套提供了簡(jiǎn)單的支持。相比于Spring
事務(wù)間的多種傳播機(jī)制进肯,Sqlla只提供了 REQUIRES_NEW
的傳播機(jī)制:內(nèi)層和外層完全隔離開來(lái)激蹲,互不影響。
methodA
和 methodB
是兩個(gè)事務(wù)方法江掩,他們之間完全隔離学辱,提交和回滾互不影響。
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(); // 事務(wù)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");
}
}
}
如何驗(yàn)證兩個(gè)事務(wù)之間互不影響环形?
我們先把 mehtodA
中的 deleteUserByName
方法的sql改成一個(gè)錯(cuò)的sql
@Sql("delete from t_user where name = ????????")
boolean deleteUserByName(String name);
現(xiàn)在再調(diào)用 methodA
策泣,你會(huì)發(fā)現(xiàn) “王五” 這個(gè)用戶并未插入到數(shù)據(jù)庫(kù)中, “張三” 也沒(méi)有被刪除,而 “趙六” 卻實(shí)實(shí)在在的插入到了數(shù)據(jù)庫(kù)中抬吟。
注意:假如 methodB
本身并不是一個(gè)單獨(dú)的事務(wù)方法 (未用 sqlla.transact
開啟)萨咕,那么他將使用外層的事務(wù)。所以要不要使用事務(wù)火本,一定要考慮好危队,不然可能會(huì)有一些意想不到的效果。
Sqlla雖然提供了注解的方式來(lái)操作SQL钙畔,但是事務(wù)并沒(méi)有使用注解的方式茫陆, 這和MyBatis一致。實(shí)際內(nèi)部實(shí)現(xiàn)也和 MyBatis 相差無(wú)幾擎析。