BeetSql是一個全功能DAO工具, 同時具備Hibernate 優勢 & Mybatis優勢功能,適用於認可以SQL爲中心,同時又需求工具能自動能生成大量經常使用的SQL的應用。 java
爲了快速嘗試BeetlSQL,須要準備一個Mysql數據庫,而後執行以下sql腳本 mysql
DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL, `name` varchar(64) DEFAULT NULL, `age` int(4) DEFAULT NULL, `userName` varchar(64) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
編寫一個Pojo類,與數據庫表對應 git
public class User { Integer id; String name; Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
寫一個java的Main方法,內容以下 spring
// 建立一個簡單的ConnectionSource,只有一個master ConnectionSource source = ConnectionSourceHelper.simple(driver,url,userName,password); // 採用mysql 習俗 DBStyle mysql = new MysqlStyle(); // sql語句放在classpagth的/sql 目錄下 SQLLoader loader = new ClasspathLoader("/sql"); // 數據庫命名跟java命名採用駝峯轉化 NameConversion nc = new HumpNameConversion(); // 最後,建立一個SQLManager SqlManager sqlManager = new SqlManager(source,mysql,loader); //使用內置的生成的sql 新增用戶 User user = new User(); user.setAge(19); user.setName("xiandafu"); sqlManager.insert(user); //使用內置sql查詢用戶 int id = 1; user = sqlManager.unque(User.class,id); //使用user.md 文件裏的select語句,參考下一節 User query = new User(); query.setName("xiandafu"); List<User> list = sqlManager.select("user.select",User.class,query)
爲了能執行user.select,須要在classpath裏創建一個user.md 文件,內容以下 sql
select === select * from user where 1=1 @if(!isEmpty(age)){ and age = #age# @} @if(!isEmpty(name)){ and name = #name# @}
關於如何寫sql模板,會稍後章節說明,以下是一些簡單說明。 數據庫
@ 和回車符號是定界符號,能夠在裏面寫beetl語句。 json
"#" 是站位符號,生成sql語句得時候,將輸出?,若是你想輸出表達式值,須要用text函數,或者任何以db開頭的函數,引擎則認爲是直接輸出文本。 api
isEmpty是beetl的一個函數,用來判斷變量是否爲空或者是否不存在. 數組
sql模板採用beetl緣由是由於beetl 語法相似js,且對模板渲染作了特定優化,相比於mybatis,更加容易掌握和功能強大。 瀏覽器
SQLManager 是系統的核心,他提供了全部的dao方法。得到SQLManager,能夠直接構造SQLManager.並經過過單例獲取如:
ConnectionSource source = ConnectionSourceHelper.simple(driver,url,userName,password); // 採用mysql 習俗 DBStyle mysql = new MysqlStyle(); // sql語句放在classpagth的/sql 目錄下 SQLLoader loader = new ClasspathLoader("/sql"); // 數據庫命名跟java命名採用駝峯轉化 NameConversion nc = new HumpNameConversion(); // 最後,建立一個SQLManager SqlManager sqlManager = new SqlManager(source,mysql,loader);
更常見的是,已經有了DataSource,建立ConnectionSource 能夠採用以下代碼
ConnectionSource source = ConnectionSourceHelper.single(datasource);
若是是主從Datasource
ConnectionSource source = ConnectionSourceHelper.getMasterSlave(master,slaves)
<bean id="sqlManager" class="org.beetl.sql.ext.SpringBeetlSql"> <property name="cs" > <bean class="org.beetl.sql.ext.SpringConnectionSource"> <property name="master" ref="dataSource"></property> </bean> </property> <property name="dbStyle"> <bean class="org.beetl.sql.core.db.MySqlStyle"> </bean> </property> <property name="sqlLoader"> <bean class="org.beetl.sql.core.ClasspathLoader"> <property name="sqlRoot" value="/sql"></property> </bean> </property> <property name="nc"> <bean class="org.beetl.sql.core.HumpNameConversion"> </bean> </property> <property name="interceptors"> <list> <bean class="org.beetl.sql.ext.DebugInterceptor"></bean> </list> </property> </bean>
cs: 指定ConnectionSource,能夠用系統提供的DefaultConnectionSource,支持按照CRUD決定主從。例子裏只有一個master庫
dbStyle: 數據庫類型,目前只支持org.beetl.sql.core.db.MySqlStyle
sqlLoader: sql語句加載來源
nc: 命名轉化,有駝峯的HumpNameConversion,有數據庫下劃線的UnderlinedNameConversion
interceptors:DebugInterceptor 用來打印sql語句,參數和執行時間
注意: 任何使用了Transactional 註解的,將統一使用Master數據源,例外的是@Transactional(readOnly=true),這將讓Beetsql選擇從數據庫。
public class MyServiceImpl implements MyService { @Autowired SpringBeetlSql beetlsql ; @Override @Transactional() public int total(User user) { SQLManager dao = beetlsql.getSQLMananger(); List<User> list = dao.all(User.class); int total = list .size(); dao.deleteById(User.class, 3); User u =new User(); u.id = 3; u.name="hello"; u.age = 12; dao.insert(User.class, u); return total; } }
能夠參考demo https://git.oschina.net/xiandafu/springbeetlsql
在configPlugin 裏配置BeetlSql
JFinalBeetlSql.init();
默認會採用c3p0 做爲數據源,其配置來源於jfinal 配置,若是你本身提供數據源或者主從,能夠以下
JFinalBeetlSql.init(master,slaves);
因爲使用了Beetlsql,所以你無需再配置 數據庫鏈接池插件,和ActiveRecordPlugin,能夠刪除相關配置。
在controller裏,能夠經過JFinalBeetlSql.dao 方法獲取到SQLManager
SQLManager dao = JFinalBeetlSql.dao(); BigBlog blog = getModel(BigBlog.class); dao.insert(BigBlog.class, blog);
若是想控制事物,還須要註冊Trans
public void configInterceptor(Interceptors me) { me.addGlobalActionInterceptor(new Trans()); }
而後業務方法使用
@Before(Trans.class) public void doXXX(){....+
這樣,方法執行完畢纔會提交事物,任何RuntimeException將回滾,若是想手工控制回滾.也能夠經過
Trans.commit() Trans.rollback()
若是習慣了JFinal Record模式,建議用戶建立一個BaseBean,封裝SQLManager CRUD 方法便可。而後其餘模型繼承此BaseBean
能夠參考demo https://git.oschina.net/xiandafu/jfinal_beet_beetsql_btjson
模板類查詢(自動生成sql)
經過sqlid查詢,sql語句在md文件裏
public T selectSingle(String id,Object paras, Class target) 根據sqlid查詢,將對應的惟一值映射成指定的taget對象,RowMapper mapper 也隨着這些api提供,不在此列出了
public T selectSingle(String id,Map paras, Class target) 同上,參數是map
public Integer intValue(String id,Object paras) 查詢結果映射成Integer,輸入是objct
public Integer intValue(String id,Map paras) 查詢結果映射成Integer,輸入是map, 其餘還有 longValue,bigDecimalValue
自動生成sql
經過sqlid更新
對於自動生成的sql,默認不須要任何annotaton,類名對應於表名(經過NameConverstion類),getter方法的屬性名對應於列明(也是經過NameConverstion類),但有些狀況仍是須要anntation。
@<table>(name="xxxx") 告訴beetlsql,此類對應xxxx表。好比數據庫有User表,User類對應於User表,也能夠建立一個UserQuery對象,也對應於User表
@<table>(name="user") public class QueryUser ..
@AutoID,做用於getter方法,告訴beetlsql,這是自增主鍵
@AssignID,做用於getter方法,告訴beetlsql,這是主鍵,且由代碼設定主鍵
@SeqID(name="xx_seq",做用於getter方法,告訴beetlsql,這是序列主鍵。
(注,若是想要獲取自增主鍵或者序列主鍵,須要在SQLManager.insert中傳入一個KeyHolder)
BeetlSQL是一個全功能DAO工具,支持的模型也很全面,包括
Pojo, 也就是面向對象Java Object
Map/List, 對於一些敏捷開發,能夠直接使用Map/List 做爲輸入輸出參數
混合模型,推薦使用混合模型。兼具靈活性和更好的維護性。Pojo能夠實現QueryResult,或者繼承QueryResultBean,這樣查詢出的ResultSet 除了按照pojo進行映射外,沒法映射的值將按照列表/值保存。以下一個混合模型:
/*混合模型*/ public User extends QueryResultBean{ private int id ; pirvate String name; private int roleId; /*如下是getter和setter 方法*/ }
對於sql語句:
selectUser === select u.*,r.name r_name from user u left join role r on u.roleId=r.id .....
執行查詢的時候
List<User> list = sqlManager.select("user.selectUser",User.class,paras); for(User user:list){ System.out.println(user.getId()); System.out.println(user.get("rName")); }
程序能夠經過get方法獲取到未被映射到pojo的值,也能夠在模板裏直接 ${user.rName} 顯示(對於大多數模板引擎都支持)
BeetlSQL集中管理SQL語句,SQL 能夠按照業務邏輯放到一個文件裏,如User對象放到user.md 裏,文件能夠按照模塊邏輯放到一個目錄下。文件格式拋棄了XML格式,採用了Markdown,緣由是
目前SQL文件格式很是簡單,僅僅是sqlId 和sql語句自己,以下
文件一些說明,放在頭部無關緊要,若是有說明,能夠是任意文字 SQL標示 === SQL語句 SQL標示2 === SQL語句 2
全部SQL文件建議放到一個sql目錄,sql目錄有多個子目錄,表示數據庫類型,這是公共SQL語句放到sql目錄下,特定數據庫的sql語句放到各自自目錄下 當程序獲取SQL語句得時候,先會根據數據庫找特定數據庫下的sql語句,若是未找到,會尋找sql下的。以下代碼
List<User> list = sqlManager.select("user.select",User.class);
SqlManager 會根據當前使用的數據庫,先找sql/mysql/user.md 文件,確認是否有select語句,若是沒有,則會尋找sql/user.md
(注:默認的ClasspathLoader採用了這種方法,你能夠實現SQLLoader來實現本身的格式和sql存儲方式,如數據庫存儲)
SQL語句能夠動態生成,基於Beetl語言,這是由於
beetl執行效率高效 ,所以對於基於模板的動態sql語句,採用beetl很是合適
beetl 語法簡單易用,能夠經過半猜半式的方式實現,杜絕myBatis這樣難懂難記得語法。BeetlSql學習曲線幾乎沒有
利用beetl能夠定製定界符號,徹底能夠將sql模板定界符好定義爲數據庫sql註釋符號,這樣容易在數據庫中測試,以下也是sql模板(定義定界符爲"--" 和 "null",null是回車意思);
selectByCond === select * form user where 1=1 --if(age!=null) age=#age# --}
beetl 錯誤提示很是友好,減小寫SQL腳本編寫維護時間
beetl 能容易與本地類交互(直接訪問Java類),能執行一些具體的業務邏輯 ,也能夠直接在sql模板中寫入模型常量,即便sql重構,也會提早解析報錯
beetl語句易於擴展,提供各類函數,好比分表邏輯函數,跨數據庫的公共函數等
若是不瞭解beetl,可先本身嘗試按照js語法來寫sql模板,若是還有疑問,能夠查閱官網 http://ibeetl.com
BeetlSql能夠在執行sql先後執行一系列的Intercetor,從而有機會執行各類擴展和監控,這比已知的經過數據庫鏈接池作Interceptor更加容易。以下Interceptor都是有可能的
你也能夠自行擴展Interceptor類,來完成特定需求。 以下,在執行數據庫操做前會執行befor,經過ctx能夠獲取執行的上下文參數,數據庫成功執行後,會執行after方法
public interface Interceptor { public void before(InterceptorContext ctx); public void after(InterceptorContext ctx); }
InterceptorContext 以下,包含了sqlId,實際得sql,和實際得參數
public class InterceptorContext { private String sqlId; private String sql; private List<Object> paras; private Map<String,Object> env = null; }
BeetlSql管理數據源,若是隻提供一個數據源,則認爲讀寫均操做此數據源,若是提供多個,則默認第一個爲寫庫,其餘爲讀庫。用戶在開發代碼的時候,無需關心操做的是哪一個數據庫,由於調用sqlScrip 的 select相關api的時候,老是去讀取從庫,add/update/delete 的時候,老是讀取主庫。
sqlManager.insert(User.class,user) // 操做主庫,若是隻配置了一個數據源,則無所謂主從 sqlManager.unique(id,User.class) //讀取從庫
主從庫的邏輯是由ConnectionSource來決定的,以下DefaultConnectionSource 的邏輯
@Override public Connection getConn(String sqlId,boolean isUpdate,String sql,List<?> paras){ if(this.slaves==null||this.slaves.length==0) return this.getWriteConn(sqlId,sql,paras); if(isUpdate) return this.getWriteConn(sqlId,sql,paras); boolean onlyMaster = localMaster.get(); if(onlyMaster) return this.getMaster(); return this.getReadConn(sqlId, sql, paras); }
對於於不一樣的ConnectionSource 完成邏輯不同,對於spring,jfinal這樣的框架,若是sqlManager在事務環境裏,老是操做主數據庫,若是是隻讀事務環境 則操做從數據庫。若是沒有事務環境,則根據sql是查詢仍是更新來決定。
以下是SpringConnectionSource 提供的主從邏輯
@Override public Connection getConn(String sqlId,boolean isUpdate,String sql,List paras){ //只有一個數據源 if(this.slaves==null||this.slaves.length==0) return this.getWriteConn(sqlId,sql,paras); //若是是更新語句,也得走master if(isUpdate) return this.getWriteConn(sqlId,sql,paras); //若是api強制使用master boolean onlyMaster = localMaster.get(); if(onlyMaster) return this.getMaster(); //在事物裏都用master,除了readonly事物 boolean inTrans = TransactionSynchronizationManager.isActualTransactionActive(); if(inTrans){ boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); if(!isReadOnly){ return this.getMaster(); } } return this.getReadConn(sqlId, sql, paras); }
開發者也能夠經過在Sql 模板裏完成分表邏輯而對使用者透明,以下sql語句
insert into #text("log_"+ getMonth(date())# values () ...
注:text函數直接輸出表達式到sql語句,而不是輸出?。
log表示按照必定規則分表,table能夠根據輸入的時間去肯定是哪一個表
select * from #text("log"+log.date)# where
注:text函數直接輸出表達式到sql語句,而不是輸出?。
一樣,根據輸入條件決定去哪一個表,或者查詢全部表
@ var tables = getLogTables(); @ for(table in tables){ select * from #text(table)# @ if(!tableLP.isLast) print("union"); @} where name = #name#
如前所述,BeetlSql 能夠經過sql文件的管理和搜索來支持跨數據庫開發,如前所述,先搜索特定數據庫,而後再查找common。另外BeetlSql也提供了一些誇數據庫解決方案
使用方式同Beetl,能夠在btsql-ext.properties裏添加自定義的函數. 須要注意的是,beetlsql在**站位符裏**老是輸出 ?,除非你的函數名是以db開頭,如db.ifNull,dbLog等。 或者使用內置的text 函數。對於以下sql語句
select * from ${dbLog()} where id = ${id} and status = "${text(@Constants.RUNNING)}"
會生成以下語句
select * from xxxLog where id = ? and status = "on".
問號對應的的值是變量id