JAVA 手搓ORM的一种实现思路(二)

基本层级设计和说明

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处理复合主键对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值