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