Orm数据库持久化框架
一、概述
1、注解描述
注解有两种类型,一种是标识类的做用、一种是标识实体类的字段
名称 | 类型 | 包 | 参数 | 描述 |
Dao | 类注解 | org.smile.commons.ann | name 用于注入到IOC容器的名称 [可选] 默认为类名前一个字母小写 | Dao注解用于标记是DAO数据库操作的接口 |
Table | 类注解 | org.smile.commons.ann | tableName 必选 数据库表名 | Table 注解用于标记与数据库表映射的实体类 |
Mapper | 类注解 | org.smile.commons.ann | name [可选] 映射的名称 默认为类的全名 | 注解用于标记与数据库映射的查询类可不与数据库字段数相同,可是多个表的字段,查询的别名 |
Id | 成员变量注解 | org.smile.commons.ann | column [可选] 数据库的表字段名 默认属性变量名
uuid [可选] 是否是需要自动创建的uuid主键
autoincrement [可选] 是否是自动增加的主键 | 主键字段 |
Property | 成员变量注解 | org.smile.commons.ann | column [可选] 数据库的表字段名 默认属性变量名
store [可选] 是否是需要是数据库存贮的字段
autoincrement [可选] 是否是自动增长的 | 属性字段 |
2、使用示例
此框架使用的方式与mybatis和hibernate等框架主体设计非常相相似,如果对mybatis和hibernate框架有一定的了解,则可以直接上手开发
实体类代码
@Table(tableName="dgb01")
public class TGroupBadBase{
/**基础信息序号 自动生成*/
@Id(column="dgba01",autoincrement=true)
protected long id;
/**发生地区*/
@Property(column="dgba02")
protected String eventArea;
}
Dao 接口
public interface IGroupBadDao extends BaseDAO{
/**
* @param query
* @return
*/
public List<TGroupBadBase> queryGroupBadList(BadQuery query);
}
Xml 配置
在Dao接口类包下创建一个文件名与类名一样的xml文件
<mapper sqlType="osql">
<select id="queryGroupBadList">
<![CDATA[
select * from TGroupBadBase where 1=1
<#if startDate?? && startDate!=''>
and firstEventDate>=:startDate
</#if>
<#if endDate?? && endDate!=''>
and firstEventDate<='${endDate} 23:59:59'
</#if>
]]>
</select>
</mapper>
Service 调用代码
@Service
public class GroupBadService {
@Resource
private IGroupBadDao iGroupBadDao;
@Transactional(propagation = Propagation.REQUIRED)
public List<TGroupBadBase> queryGroupBadList(BadQuery query) {
return iGroupBadDao.queryGroupBadList(query);
}
}
3、Spring中的配置使用
Orm框架是一个可以和spring完美整合的一个框架,只需要在spring的配置文件中做一此配置就可以很好的自定义出一套适合自己的开发方案
3.1、基本的daoSupport使用
DaoSupport是一个兼容Spring框架的org.smile.orm.base.BaseDAO接口的实现,此类可以在Service层直接引用也可以在Dao接口中直接继承。使用时只需要注入到IOC容器中即可。
可配置的两个属性:
whereSqlParser :用于解析where语句的工具
参数需要实现 org.smile.orm.base.impl.WhereSqlParser接口。
在框架中已经有两种实现:
1)org.smile.orm.base.impl. BaseWhereSqlParser
普通sql语句的实现 语句里只能使用数据库字段做为过滤条件
如:iGroupBadDao.delete(TGroupBadDrug.class, "event_id=?",model.getId() );
2) org.smile.orm.base.impl.OrmWhereSqlParser
可以支持实体类的属性和数据库字段两种方式过滤条件
如:iGroupBadDao.delete(TGroupBadDrug.class, "event_id=?",model.getId() );
iGroupBadDao.delete(TGroupBadDrug.class, "eventId=?",model.getId() );
两种方法用可以达到过滤的效果
dataSource:用于数据源的配置
<bean id="ormDaoSupport" class="org.smile.orm.spring.DaoSupport">
<property name="whereSqlParser">
<bean class="org.smile.orm.base.impl.OrmWhereSqlParser"></bean>
</property>
<property name="dataSource" ref="dataSource"></property>
</bean>
3.2 OrmSessionFactory的使用
框架提供了一个可以在Spring中偡的OrmSessionFactory实现,只需要配置到IOC容器中即可以使用ORM的方法快速对数据库操作。org.smile.orm.spring.SpringOrmSessionFactoryBean提供了丰富的配置属性来定制自己的开发框架。
3.2.1 例子引导
首一个个配置的例子来详细说明一下org.smile.orm.spring.SpringOrmSessionFactoryBean的使用。
<bean id="ormSessionFactory" class="org.smile.orm.spring.SpringOrmSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="packageString">
<list>
<value>com.pharmacist.orm,com.pharmacist.dao</value>
<value>com.pharmacist.report.dao</value>
<value>com.pharmacist.model</value>
</list>
</property>
<property name="baseTarget" ref="ormDaoSupport"></property>
<property name="interceptors" >
<list>
<bean class="org.smile.orm.plugin.PageQueryInterceptor"></bean>
<bean class="org.smile.orm.plugin.OrderbyInterceptor"></bean>
<bean class="org.smile.orm.plugin.TopInterceptor"></bean>
</list>
</property>
<property name="mapperFlagHandler" >
<bean class="org.smile.orm.mapper.flag.MultipleMapperFlagHandler">
<property name="handlers">
<list>
<bean class="org.smile.orm.mapper.flag.SmileMapperFlagHandler"/>
<bean class="org.smile.orm.mapper.flag.HibernateMapperFlagHandler"/>
</list>
</property>
</bean>
</property>
<property name="sqlConverters">
<map>
<entry key="sql" value="org.smile.orm.executor.sql.NamedBaseSqlConverter"></entry>
<entry key="osql" value="org.smile.orm.executor.sql.NamedOrmSqlConverter"></entry>
</map>
</property>
<property name="tableMapperClass" value="org.smile.orm.mapper.NamedOrmTableMapper"></property>
<property name="autoReLoad" value="true"></property>
<property name="reloadXmlDelay" value="30"></property>
</bean>
由上面的例子可以看出个大概主要有以下几个属性:
数据源配置:配置数据库操作的数据源
ORM框架扫描的包路径:包括Mapper Table Dao 几个注解的扫描
默认的Dao扩展方法实现目标:是在DAO接口中定义了方法但又没有xml和注解配置方法实现语名的时候 就会用到扩展的目标来实现此方法,对于每一个Dao 接口都可以在xml中 配置他的扩展目标,如果没有配置就会使用ormSessionFactory的 basetarget属性来实现。
拦截器配置:配置的拦截器可以用来实现对框架中的原有实现进行代理,以使用自己的扩展
当然框架中也有一些默认实现的拦截器可用来配置自定义出适合自己的框架,自定义的拦截器需要实现org.smile.plugin.Interceptor接口
映射注解加载处理类:对映射实体类的注解与数据库对应的方式加载的实现,需实现org.smile.orm.mapper.flag.MapperFlagHandler接口,框架提供了两种实现方式和一种多方式实现
Sql语句转换方式配置:配置对Sql处理的实现方式
表映射生成SQL语句配置:配置对实体类生成Sql语句的方式
是否自动重加载Dao xml文件:是否自动定时间隔对xml文件重加载
自动重加载Dal xml文件的时间:自动扫描xml加载的时单间隔单位秒
3.2.2 配置属性
属性名称 | 类型 | 是否 必选 | 可配置值 | 默认值 | 详情 |
dataSource | javax.sql.DataSource | 是 |
|
| 操作的数据库 |
packageString | java.util.List | 是 | 需要扫描的包名 |
| 每一个list的元素都可以是多个包名的字符串,可以用;和,分开 |
baseTarget | 实现了Dao接口部分父接口的实现类 | 否 |
|
| 用于配置dao接口的扩展实现 |
interceptors | java.util.List | 否 | 一个拦截器的列表 |
| 第一个拦截器需实现 org.smile.plugin.Interceptor接口
|
mapperFlagHandler | org.smile.orm.mapper.flag.MapperFlagHandler | 否 | 配置实例类加载方式 | org.smile.orm.mapper.flag.SmileMapperFlagHandler 此配置值是会加载以@Table标记的类 以@Proerpty标记的属性为字段 @Id标记主键 | 可以同时支持多种加载方式org.smile.orm.mapper.flag.MultipleMapperFlagHandler 如上例所示 就是支持两种方式 |
sqlConverters | java.util.Map | 否 | 配置sql语句转换的方式 | sql:OrmSqlConverter osql:BaseSqlConverter | 可以在dao接口的xml文件中指定多种语句类型 只要在此配置转换器即可 |
tableMapperClass | org.smile.orm.mapper.OrmTableMapper | 否 | 生成sql 方式 | org.smile.orm.mapper. BoundOrmTableMapper | 有几种方式: 以%{propertyName}占位 以:propertyName占位 |
4、可选择的配置
4.1 tableMapperClass
框架中预设了两种实现类:
4.1.1 第一种
org.smile.orm.mapper.NamedOrmTableMapper
使用:propertyName占位生成Sql语句
4.1.2 第二种
org.smile.orm.mapper.BoundOrmTableMapper
使用%{propertyName}占位生成Sql语句
4.2 sqlConverters
具有四种实现,分为两类:
只支持普通Sql的 :
1) org.smile.orm.executor.sql.BaseSqlConverter
可以处理使用%{propertyName}方式占位的sql
2) org.smile.orm.executor.sql.NamedBaseSqlConverter
可以处理使用:propertyName方式占位的sql
3) org.smile.orm.executor.sql .OrmSqlConverter
可以处理以%{propertyName}方式占位的带实体Table类做为字段和表名的sql语句
4) org.smile.orm.executor.sql .NamedOrmSqlConverter
可以处理以:propertyName方式占位的带实体Table类做为字段和表名的sql语句
5 Sql占位符
对于sql的占位符是解析sql语句的关键所在,所以必须要正解配置
如果不对tableMapperClass、sqlConverters 两个属性进行配置,系统默认的是以%{propertyName}做为占位符的。
5.1 方式统一
配置时必须要做到tableMapperClass,sqlConverters是使用的同一种占位符,不然可能会出现自动生成的语句在解析的时候无法正确解析出占位情况。
5.2 占位符函数
框架中还支持在占位符中使用自定义函数,用来对属性替换的时候进行转换
如:%{trim:propertyName} 或 :trim:propertyName
5.2.1 内置的函数
orm框架当前内置了如下的几种函数:
like 模糊查询
where name like %{like:name} 如果name="小明" 相当与 where name like '%小明%'
leftlike 左匹配模糊查询
where name like %{like:name} 如果name="小明" 相当与 where name like '小明%'
rightlike 右匹配模糊查询
where name like %{like:name} 如果name="小明" 相当与 where name like '%小明'
startdate 用于构建一个用于起始查询的时间
where create_date >=%{startdate:date} 那么不管理date的值是否存在时分秒的信息 都会相当于把时分秒去掉了 where create_date>='2017-05-06'
enddate 用于构建一个用于起始查询的时间
where create_date <=%{enddate:date} 那么不管理date的值是否存在时分秒的信息 都会相当于后一天的前一秒 where create_date<='2017-05-06 23:59:59'
5.2.2 添加自定义
需要添加自定义函数需要实现接口org.smile.function.Function
public interface Function {
/**
* 获取函数的值
* @param param 函数传入的参数
* @return 函数结果
*/
public Object getFunctionValue(Object ...args);
/**
* 获取函数的名称
* @return
*/
public String getFunctionName();
}
然后把函数注册:
org.smile.db.sql.parameter.function. DefaultFunctionContext的注册方法
/**
* 注册一个函数
* @param function
*/
public void registFunction(Function function){
functionMap.put(function.getFunctionName(), function);
}
调用:
DefaultFunctionContext.instance.registFunction(function);
二、Dao接口配置
1、xml方式
1.1mapper标签
标签属性
属性 | 名称 | 必选 | 默认 | 描述 |
target | 扩展实现类名 | 否 |
| 用于实现接口的扩展 |
template | 模板类型 | 否 | freemark | Sql广西模板处理 |
single | 是否单例 | 否 | true | Dao实现类是否是单例,建设都用单例 |
sqlType | Sql语句类型 | 否 | sql | 用于sql转换器的使用,在sessionFactory中配置 |
标签可以用多个插入 删除 更新 更新 批量 代码片断 的子标签
<snippet id="testsql">
<![CDATA[
<% if(name!=null&&name!=""){%> and name in (%{name}) <%}%>
<% if(id!=null&&id!=""){%> and id = %{id}<%}%>
]]>
</snippet>
<select id="queryStudentMap" mapper="map">
<![CDATA[
select * from student where 1=1
<#include 'testsql'>
]]>
</select>
1.2 操作方法标签
操作方法标签的 delete update insert select batch 5种
方法操作标签属性参数
属性 | 名称 | 必选 | 默认 | 描述 |
id | ID | 是 |
| 对应dao的方法名 |
template | 模板类型 | 否 | freemark | Sql广本模板处理,如未指定使用mapper中的属性, 框架支持freemarker groovy两种方式 |
sql | Sql语句广西 | 标签体内容 | true | 如未指定sql语句框架会根据方法 的参数与返回值生成sql语句,但方法的参数需为数据表映射实体 |
sqlType | Sql语句类型 | 否 | sql | 用于sql转换器的使用,在sessionFactory中配置 |
include | Sql语句文本外部文件 | 否 |
| 如不指定sql 可以指定外部文件来包含sql语句 |
mapper | 返回映射 | 否 |
| 如果不指定,可以从方法返回值的类型中获取,如是collection类型 则会从泛型号获取,如果泛型中指定的是接口则必需在mapper指定。 如泛型是Map 且不指定 则使用默认resultSetMap返回 |
include例子
<insert id="saveSamplingPrescriptionDetail" include="saveSamplingPrescriptionDetail.sql">
</insert>
2、注解方式
@Update @Delete @Select @Insert @Batch 几种标签 属性配置参考xml属性
例如:
@Select(sql="select s.* from TDrugSafeUseInfo s inner join TDrugCheckAlertControl c on s.alertLevel=c.code where c.enable =1 and s.prescriptId=:param")
public List<TDrugSafeUseInfo> drugSafeUseCheck(String prescriptId);
3、@ArrayBound 注解
此注解用于标注此方法参数是以数组方式赋值
如:
@ArrayBound
public List<ReviewedCommonInfo> queryReviewCommonReviewed(Object[] param);
刚sql配置可以是:以? 做为占位符
<select id="queryReviewCommonReviewed">
<![CDATA[
select * from TPrescriptionCommonReviewed cr
inner join TReviewedProblemInfo pi on pi.viewTypeNo=cr.reviewClassifyNo and pi.viewProblemNo=cr.reviewIssueNo
where cr.assignId=?
]]>
</select>
4、方法参数
4.1Map参数
占位符可以是map的key 、模板中对应有也是map的key
4.2 List 数组 参数
在模板中会转成参数名称 固定 list 不论方法的参数名是什么
public List<SelectData> queryDepartments(String[] ids);
在模板中就要写 <%if(list!=null&&list.size()>0){%> and BIC05 in (:list) <%}%> 而不是写 ids
4.3 String Integer 无属性的类型
在模板中固定参数名为param
public List<SelectData> queryApothecaryData(String orgNo);
在模板中写
r.role_key='ROLE_YAOSHI' and u.organization_key=:param 而不是写orgNo
4.4 其它自定义对象类型
模板中的变量为对象的成员变量
public List<TBadFeedAction> getBadFeedList(ReportQuery query);
模板中应为:
<select id="getBadFeedList" template="groovy" sqlType="osql">
<![CDATA[
select * from TBadFeedAction where 1=1
<% if(startTime!=null&&startTime!=""){%>
and reportAgencyDate >= :startTime
<%}%>
<% if(endTime!=null&&endTime!=""){%>
and reportAgencyDate <= :endTime
<%}%>
]]>
</select>
三、BaseDAO接口
1、在Service中直接使用
在IOC容器中配置org.smile.orm.base.BaseDAO 接口的实现类
例如Spring窗口中可配置
<bean id="ormDaoSupport" class="org.smile.orm.spring.DaoSupport">
<property name="whereSqlParser">
<bean class="org.smile.orm.base.impl.OrmWhereSqlParser"></bean>
</property>
<property name="dataSource" ref="dataSource"></property>
</bean>
在Service中就可以使用注解注入到属性中使用:
@Service
public class OrmBaseService {
@Resource
protected BaseDAO ormDaoSupport;
@Resource
protected JdbcTemplate dbTemplate;
public void insert(List objs) throws SQLException{
ormDaoSupport.add(objs);
}
public void add(Object obj) throws SQLException{
ormDaoSupport.add(obj);
}
public void delete(List objs) throws SQLException{
ormDaoSupport.deleteBatch(objs);
}
public void deleteByIds(Class clazz,Object[] ids) throws SQLException{
ormDaoSupport.deleteByIds(clazz, ids);
}
public void updateBatch(List objs,String[] fields) throws SQLException{
ormDaoSupport.updateBatch(objs,fields);
}
public void update(List objs) throws SQLException{
ormDaoSupport.updateBatch(objs);
}
//省略其它方法
}
2、Dao 接口继承使用
只需在Dao接口继承org.smile.orm.base.BaseDAO接口 就可以获取 接口中的方法
需要在sessionFactory中指定baseTarget 属性为BaseDAO的实现类。
<property name="baseTarget" ref="ormDaoSupport"></property>
四、IBaseDao 接口
只需在Dao接口中继承org.smile.orm.base.IBaseDao接口,就可以使dao获得了IBaseDao的方法并不需要xml配置,IBaseDao需要指定泛型,才能保存每一个dao调用update add 等方法的时候是一个类型。
由于此接口不存在 拼where语句的方法,可以使代码风格更加统一,但BaseDAO可能更加灵活,具有更多的方法调用方式。
1、与BaseDAO接口对比
IBaseDao:
是使用框架自动行成sql语句方式实现
BaseDAO:
是使用代理方式,使用实现类的方法接管
五、拦截器的使用
1、拦截器的配置
拦截器需要配置到sessionFactory的属性中,例如下就配置了3个拦截器
<property name="interceptors" >
<list>
<bean class="org.smile.orm.plugin.PageQueryInterceptor"></bean>
<bean class="org.smile.orm.plugin.OrderbyInterceptor"></bean>
<bean class="org.smile.orm.plugin.TopInterceptor"></bean>
</list>
</property>
2、拦截器定义
需要实现org.smile.plugin.Interceptor接口,还可以使用拦截器注解标记要拦截的方法
例如:orderyby 插件实现
@Intercept(type=SqlConverter.class,method="convertToSql")
public class OrderbyInterceptor implements Interceptor {
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public Object intercept(Invocation invocation) throws Exception {
OrderbyParam pageParam=OrderbyHelper.getOrderbyParam();
if(CollectionUtils.isEmpty(pageParam)){
return invocation.proceed();
}else{
try{
Object[] args=invocation.getArgs();
MappedOperator operator=(MappedOperator)args[1];
if(operator.getOperator() instanceof SelectOperator){
String sql=(String)(args[0]);
invocation.setArgs(new Object[]{doOrderby(sql, pageParam),args[1],args[2]});
}
return invocation.proceed();
}finally{
OrderbyHelper.remove();
}
}
}
private String doOrderby(String sql,OrderbyParam pageParam){
StringBuffer sb=new StringBuffer(sql);
sb.append(" ORDER BY ");
int index=0;
for(Map.Entry<String, String> entry:pageParam.entrySet()){
sb.append(entry.getKey()).append(entry.getValue());
if(index<pageParam.size()-1){
sb.append(",");
}
}
return sb.toString();
}
}
就指定了拦截SqlConverter接口的convertToSql方法
3、预设的拦截器使用
框架当前已经开发3个常的拦截器插件,分别是分页插件、排序插件、Top插件
3.1分页插件
只需要在调用查询方法之前,使用PagerHelper.startPager(pageint, pageSizeint); 方法进行分页即可使用自动分页效果
int pageint=Integer.valueOf(page);
int pageSizeint=Integer.valueOf(pageSize);
PagerHelper.startPager(pageint, pageSizeint);
3.2排序插件
只需要在调用查询方法前使用OrderbyHelper进行排序即可
OrderbyParam p=new OrderbyParam();
p.orderby(orderby, desc);
OrderbyHelper.oderby(p);
3.3 Top插件
在调用查询方法之前使用TopHelper进行操作
int topInt=Integer.parseInt(top);
TopHelper.startTop(topInt);
附录:
1、其中使用到的groovy 表达式 与 freemarker模板的知识请参考网上的资料,这些资料很齐全
2、些框架的使用也可参考hibernate 与 mybatis的使用方式,如果具有mybatis的基础学习起来可事半功倍
3、更多orm框架的内容,还需要使用中总结心得。