高级部分
高级部分
ResultSet结果集到Bean的转化
数据库返回的ResultSet将根据Pojo对象的属性来做适当的转化,比如对于数据库如果定义了一个浮点类型,而Java端属性如果是double,则转成double,如果是BigDecimal,则转成BigDecial,如果定义为int类型,则转为int类型。BeanProcessor 类负责处理这种转化,开发者也可以实现自己的BeanProcessor来为特定的sql做转化,比如将数据库日期类型转为Java的Long类型。如在BeanProcessor.createBean代码里
Class<?> propType = prop.getPropertyType();
tp.setTarget(propType);
JavaSqlTypeHandler handler = this.handlers.get(propType);
if(handler==null){
handler = this.defaultHandler;
}
Object value = handler.getValue(tp);
this.callSetter(bean, prop, value,propType);
BeanProcessor 会根据属性类型取出对应的处理类,然后处理ResultSet,如果你先自定义处理类,你可以重新添加一个JavaSqlTypeHandler到handlers
ResultSet结果集到Map的转化
ResultSet转为Map的时候,有不一样则,根据数据库返回的列类型来做转化,数据库如果定义了一个浮点类型,则使用默认的BigDecimal类型
如在BeanProcessor.toMap代码里
String columnName = rsmd.getColumnLabel(i);
if (null == columnName || 0 == columnName.length()) {
columnName = rsmd.getColumnName(i);
}
int colType = rsmd.getColumnType(i);
Class classType = JavaType.jdbcJavaTypes.get(colType);
JavaSqlTypeHandler handler = handlers.get(classType);
if(handler==null){
handler = this.defaultHandler;
}
tp.setIndex(i);
tp.setTarget(classType);
Object value = handler.getValue(tp);
JavaType 定义了默认的数据库类型到Java类型的转化,从而获取适当的 JavaSqlTypeHandler,如果没有定义,则使用默认的handler,仅仅使用resultSet.getObject(i)来获取值需要注意的是,尽量不要使用默认resultSet.getObject(i)来取值,这样会导致不同数据库取的类型不一样导致不兼容不同数据库。JavaType已经定义了绝大部分数据库类型到Java类型的转化,少量很少使用的类型没有定义,直接使用resultSet.getObject(i)取值
//JavaType.java
jdbcJavaTypes.put(new Integer(Types.LONGNVARCHAR), String.class); // -16
// 字符串
jdbcJavaTypes.put(new Integer(Types.NCHAR), String.class); // -15 字符串
jdbcJavaTypes.put(new Integer(Types.NVARCHAR), String.class); // -9 字符串
jdbcJavaTypes.put(new Integer(Types.ROWID), String.class); // -8 字符串
jdbcJavaTypes.put(new Integer(Types.BIT), Boolean.class); // -7 布尔
jdbcJavaTypes.put(new Integer(Types.TINYINT), Integer.class); // -6 数字
jdbcJavaTypes.put(new Integer(Types.BIGINT), Long.class); // -5 数字
jdbcJavaTypes.put(new Integer(Types.LONGVARBINARY), byte[].class); // -4
// 二进制
jdbcJavaTypes.put(new Integer(Types.VARBINARY), byte[].class); // -3 二进制
jdbcJavaTypes.put(new Integer(Types.BINARY), byte[].class); // -2 二进制
jdbcJavaTypes.put(new Integer(Types.LONGVARCHAR), String.class); // -1
//......
有些框架,在使用Map的时候,添加了更多的灵活性,比如通过columnName 来片段是否该字段是字典字段,比如都有后缀"_dict",如果是,则从缓存或者查询响应的字典数据,放到ThreadLocal里,以一次性将查询结果,相关字典数据返回
PreparedStatment
BeanProcessor.setPreparedStatementPara用于JDBC设置参数,内容如下:
public void setPreparedStatementPara(String sqlId,PreparedStatement ps,List<SQLParameter> objs) throws SQLException {
for (int i = 0; i < objs.size(); i++) {
SQLParameter para = objs.get(i);
Object o = para.value;
if(o==null){
ps.setObject(i + 1, o);
continue ;
}
// 兼容性修改:oralce 驱动 不识别util.Date
if(this.dbName.equals("oracle")){
Class c = o.getClass();
if(c== java.util.Date.class){
o = new Timestamp(((java.util.Date) o).getTime());
}
}
if(Enum.class.isAssignableFrom(o.getClass())){
o = EnumKit.getValueByEnum(o);
}
//clob or text
if(o.getClass()==char[].class){
o = new String((char[])o);
}
int jdbcType = para.getJdbcType();
if(jdbcType==0){
ps.setObject(i + 1, o);
}else{
//通常一些特殊的处理
throw new UnsupportedOperationException(jdbcType+",默认处理器并未处理此jdbc类型");
}
}
}
SQLParameter 包含了sql对应参数的值,也包含参数对应的变量名,如果该变量还有类型说明,则jdbcType不为0,如下某个sql
select * from user where create_time>#createTime,typeofDate#
此时,SQLParameter.value 是createTime对应的值,SQLParameter.expression是字符串"createTime",
由于使用了typeof开头的格式化函数,typeofDate 意思是指此值应当着java.sql.Types.Date 来处理(然而,默认的BeanProcessor 并不会处理SQLParameter.jdbcType)
也可以使用“”jdbc“作为格式化函数,比如
select * from user where create_time>#createTime,jdbc="date"#
jdbc的值来自于java.sql.Types的public 属性
自定义BeanProcessor
你可以为Beeetsql指定一个默认的BeanProcessor,也可以为某些特定的sqlid指定BeanProcessor,SqlManager提供了两个方法来完成
public void setDefaultBeanProcessors(BeanProcessor defaultBeanProcessors) {
this.defaultBeanProcessors = defaultBeanProcessors;
}
public void setProcessors(Map<String, BeanProcessor> processors) {
this.processors = processors;
}
事务管理
BeetlSql 是一个简单的Dao工具,不含有事务管理,完全依赖web框架的事务管理机制,监听开始事务,结束事务等事件,如果你使用Spring,JFinal框架,无需担心事务,已经集成好了,如果你没有这些框架,也可以用Beetlsq
提供的DSTransactionManager 来指定事务边界,是事实,Spring的事务集成也使用了DSTransactionManager
SQLManager sql = new SQLManager(style,loader,cs,new UnderlinedNameConversion(), inters);
//.......
DSTransactionManager.start();
User user = new User();
sql.insert(user);
sql.insert(user);
DSTransactionManager.commit();
注意:ConnectionSourceHelper.getSimple() 获得的是一个简单的cs,没有事务管理器参与,建议你用
getSingle(DataSource ds),返回DefaultConnectionSource具备事务管理。
设置自己的BaseMapper
Beetlsql提供了BaseMapper来内置了CRUD等方法,你可以自己定制属于你的“BaseMapper”
// 自定义一个基接口, 并获取基接口配置构建器
MapperConfigBuilder builder = this.sqlManager.setBaseMapper(MyMapper.class).getBuilder();
/*
* 这两个方法名与 MyMapper接口保持一致. 为了告诉beetlsql, 遇见这个方法名, 帮我用对应的实现类来处理. 这样扩展性更高,
* 更自由.不必等着开源作者来提供实现.
*
* 里面已经内置BaseMapper的所有方法, 用户只需要在自定义的基接口上定义与BaseMapper相同的方法名就可以使用
*/
builder.addAmi("selectCount", new AllCountAmi());
builder.addAmi("selectAll", new AllAmi());
UserDao dao = sqlManager.getMapper(UserDao.class);
long count = dao.selectCount();
如上代码设置了MyMapper 为SQLManager的基础mapper,MyMapper定义如下,仅仅定义了三个内置方法
public interface MyMapper<T> {
long selectCount();
List<T> selectAll();
List<Integer> selectIds();
}
通过builder.addAmi可以为每个方法指定一个是新的实现,Beetlsql已经内置了一些列的实现类,你可以扩展,实现
public interface MapperInvoke {
public Object call(SQLManager sm,Class entityClass,String sqlId,Method m,Object[] args);
}
如果你想定制自己的"BaseMapper",请参考org.beetl.sql.core.mapper.internal.* 所有类
性能测试
性能测试代码在 https://gitee.com/xiandafu/dao-benchmark
- git clone https://gitee.com/xiandafu/dao-benchmark
- mvn clean package
- java -jar -Dtest.target=jpa target/dao-0.0.1-SNAPSHOT.jar
- 测试目标可更换为jpa,beetlsql,mybatis,jdbc
- 在result目录检测测试文本结果
如下是测试结果
JDBC 作为基准,无疑是最快的,在ORM测视中,BeetlSQL性能基本上是其他JPA,MyBatis的3-7倍
内置sql语句生成
AbstractDBStyle 提供了跨平台的内置sql语句,比如根据class或者table 生成insert,update,del 等语句,你可以覆盖AbstractDBStyle方法来生成特定的sql语句,比如Mysql 支持insert ignore into 语句,你可以覆盖generalInsert方法来生成特定的insert ignore into语句