基本层级设计和说明
Model层: 实现 Serializable,构造基类BaseObject,添加变量dirtyColumnList,Hibernate在update对象,如果属性为null,是不会更新,但如果要强制更新的需求,就需要用到脏字段设置。
DAO层:基于Spring jdbc 的jdbcTemplate,作为基本的jdbc操作已经足够,并集成StoreProcedure满足存储过程和cursor返回的需求;
Service层:针对Hibernate、Mybatis、MybatisPlus提供不同的实现实例,同时基于自定义的MappingEntity与MappingField标签,完成自己对于的ORM轮子;
相关源码地址
https://github.com/robinhood-jim/JavaFramework/tree/develop/core
Model层实现
参见 com.robin.core.model 所在package
BaseObject 自定义的模型基类
BaseModel 基于MybatisPlus的基类,包含默认字段设定;
BasePrimaryObject 多主键定义基类
DAO层实现
参见 com.robin.core.dao 所在package
IJdbcDao DAO层接口
JdbcDao DAO层实现,集成自Spring的JdbcDaoSupport
HibernateGenericDao 基于Hibernate的DAO实现类
SqlMapperDao 开发的支持Mybatis mapper配置文件语法的DAO实现
SimpleJdbcDao 基于动态数据源需求,基于Apache DBUtils开发的DAO实现类
Service层实现
参见 com.robin.core.service 所在package
IBaseAnnotationJdbcService IJdbcDao类的对外接口
BaseAnnotationJdbcService JdbcDao类的调用方,使用Spring transaction 事务管理;
AbstractMybatisService MybatisPlus ServiceImpl 实现的增强
DAO 层实现详述
IJdbcDao接口
方法名 | 参数 | 功能描述 |
createVO | BaseObject T,Class<P> | 根据注解,向对于映射表插入数据,返回主键对象 如果是复合主键,返回复合主键对象 |
updateByKey | Class<T> clazz, T obj | 根据Primary Key更新实体对象 |
deleteVO | Class<T> clazz, P[] value | 以in查询删除指定数据 |
getEntity | Class<T> clazz, P value | 通过主键查询唯一记录 |
queryByField | Class<T>,PropertyFunction<T,?> Const.OPERATOR Object... | MybatisPlus 模式查询,传入对于字段的Function |
getByField | Class<T>,PropertyFunction<T,?> Const.OPERATOR Object... | 返回单条 |
deleteByField | Class<T>,PropertyFunction<T,?> Object | 以字段等于条件删除 |
deleteByLogic | 逻辑删除 | |
queryByCondition | Class<T> FilterCondition PageQuery<T> | 复杂条件查询,支持分页 |
queryBySelectId | PageQuery<Map> | 以自定义的配置式XML模式查询,并分页 |
executeByNamedParam | String,Map | 以Named parameter模式执行SQL |
executeCall | String List<SqlParameter> Map | 调用存储过程 |
executeBySelectId | PageQuery<Map> | 以自定义的配置式XML模式执行SQL语句 |
createVO实现
public <T extends BaseObject, P extends Serializable> P createVO(T obj, Class<P> clazz) throws DAOException {
Long retval=null;
P retObj = null;
try {
//function as mybatis-plus MetaObjectHandler
MetaObjectHandler handler = SpringContextHolder.getBean(MetaObjectHandler.class);
if (!ObjectUtils.isEmpty(handler)) {
Map<String, FieldContent> fieldContentMap = AnnotationRetriever.getMappingFieldsMapCache(obj.getClass());
handler.insertFill(new MetaObject(obj, fieldContentMap));
}
List<FieldContent> fields = AnnotationRetriever.getMappingFieldsCache(obj.getClass());
EntityMappingUtil.InsertSegment insertSegment = EntityMappingUtil.getInsertSegment(obj, sqlGen, this, fields);
String insertSql = insertSegment.getInsertSql();
if (log.isDebugEnabled()) {
log.debug("insert sql={}", insertSql);
}
FieldContent generateColumn;
//pk model insert
if (insertSegment.isHasPrimaryKey()) {
PreparedStatementCreatorFactory factory = new PreparedStatementCreatorFactory(insertSql, insertSegment.getParamTypes());
KeyHolder keyHolder = new GeneratedKeyHolder();
if (insertSegment.isHasSequencePk()) {
factory.setGeneratedKeysColumnNames(insertSegment.getSeqColumn().getFieldName());
generateColumn = insertSegment.getSeqColumn();
} else {
factory.setReturnGeneratedKeys(true);
generateColumn = insertSegment.getIncrementColumn();
}
returnTemplate().update(factory.newPreparedStatementCreator(insertSegment.getParams()), keyHolder);
if(keyHolder.getKey()!=null) {
retval = keyHolder.getKey().longValue();
}
if (!ObjectUtils.isEmpty(retval) && (generateColumn != null)) {
FieldContent pkColumn = AnnotationRetriever.getPrimaryField(fields);
if (pkColumn == null) {
throw new DAOException("model " + obj.getClass().getSimpleName() + " does not have primary key");
}
Object targetVal = ReflectUtils.getIncrementValueBySetMethod(generateColumn.getSetMethod(), retval);
if (pkColumn.getPrimaryKeys() == null) {
generateColumn.getSetMethod().invoke(obj, targetVal);
retObj = (P) targetVal;
} else {
for (FieldContent field : pkColumn.getPrimaryKeys()) {
if (field.isIncrement() || field.isSequential()) {
field.getSetMethod().invoke(generateColumn.getGetMethod().invoke(obj), retval);
}
}
retObj = (P) pkColumn.getGetMethod().invoke(obj);
}
}
} else {
//no pk model insert
if (!insertSegment.isContainlob()) {
executeUpdate(insertSql, fields, obj);
} else {
LobCreatingPreparedStatementCallBack back = new LobCreatingPreparedStatementCallBack(lobHandler, fields, obj);
this.returnTemplate().execute(insertSql, back);
}
return null;
}
} catch (Exception ex) {
logError(ex);
throw new DAOException(ex);
}
return retObj;
}
功能详解
1.MetaObjectHandler,参考MybatisPlus的实现,实现缺省的时间字段,当前用户信息,时间信息,租户信息等,可以在具体项目中定义;
2.List<FieldContent> 获取注解Model类相关的字段名和属性名对于关系信息,使用cache;
3.InsertSegment,对映射关系,建立插入的SQL语句和对应参数,如果包含自增字段或者Sequence字段,相应的把信息放对象中;
4.支持带主键和不带主键的两种模式插入,Mybatis和JPA都需要表结构带主键,有些特定场合,比如数据仓库的事实表或业务表不一定是有主键的,这事Mybatis和JPA都不能支持,本类可以支持无主键ORM实体的插入;
5.有主键情况下,判断是自增还是Sequence作为主键,自增字段设置setReturnGeneratedKeys为true,Sequence字段setGeneratedKeysColumnNames 设定sequnce指定字段;
6.得到KeyHolder的结构,需要通过反射,讲数据回写到model对象对应的property中,if (pkColumn.getPrimaryKeys() == null)分支的else处理复合主键对象