J-Hi 开发日记(二)

J-Hi 开发日记(二)
最近在做J-Hi融合SpringJDBC时遇到一个棘手的问题,那就是在insert一条记录时如何取回记录主键值的?问题主要让我纠结在对跨数据库SpringJDBC的处理上,大家都知道象SQLServer或MyServer主键的值是以自增的方式,而象Oracle主建的值通过序列生成并通过insert将值直接插入到表中的。为此SpringJDBC提供了两种机制,
    1、主键自增的解决方案
        KeyHolder keyHolder  =   new  GeneratedKeyHolder(); 
        
this .getJdbcTemplate().update( new  PreparedStatementCreator(){

            
public  PreparedStatement createPreparedStatement(Connection con)
                    
throws  SQLException {
   
PreparedStatement ps
= con.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
 
                
return  ps;
            }
            
        }, keyHolder);
        
    
return  keyHolder.getKey().intValue();
keyHolder 数据库自增主键值的持有者,它监听 PreparedStatement的返回的值 Statement.RETURN_GENERATED_KEYS获取主键值,并存放在自己的池中(实际上是一个list)一般来说,一个keyHolder实例只绑定一个 PreparedStatement的执行,当然最好也只是插入一条数据库记录,这样才能保证池中只有一个主键值。
当keyHolder获得主键值后,您可以在任何时候通过访问keyHolder对象得到这个主键值,也就是说只要它的生命期存在,这个主键的值就一直不会丢失。
总结:1)、在执行
PreparedStatement之前创建自增主键的持有者对象keyHolder
      2)、在创建
PreparedStatement对象时一定要声明返回主键值,列如 con.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS)
      3)、只要keyHolder的生命期存在,那么主键的值在任何时候与位置你都可以取得到

    2、检索数据库序列生成的主键的解决方案
        OracleSequenceMaxValueIncrementer incr  =   new  OracleSequenceMaxValueIncrementer(dataSource,  " SIMPLE_SEQUENCE " );
        
return  incr.nextIntValue();
象Oracle这样的数据库SpringJDBC的解决方案一目了解,通过给定数据源dataSource与序列名"SIMPLE_SEQUENCE"就可以这个序列的最大值。当然还可以通过这个类设计缓冲区大小通过setCacheSize方法,该方法可以一次性取出多个值以减少与数据库的访问次数(数据库的交互是很耗时与耗费资源的)

J-Hi的问题与解决方法
    因为J-Hi要实现跨数据库跨多个ORM框架因此对于SpringJDBC这两种方案必须要融合到一起,并且在总体设计上还要与其它的ORM框架(目前J-Hi已融合的ORM框架有hibernate、ibatis2、ibatis3)的接口声明相兼容,因此在对SpringJDBC集成的总体设计上我借鉴了hibernate的方言思想,通过方言将SpringJDBC两种方案融合在J-Hi之中以实现对不同类型数据库主键管理的差异性。
        KeyHolder keyHolder  =   new  GeneratedKeyHolder(); 
        
this .getJdbcTemplate().update( new  PreparedStatementCreator(){

            
public  PreparedStatement createPreparedStatement(Connection con)
                    
throws  SQLException {
  
ISpringJDBCHiDialect dialect 
=  sessionFactory.getDialect();   
PreparedStatement ps
= con.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
 
                    
if (stepFlage  ==  primaryKeyIndex  &&  valueClass.getPropertyName().equals(primaryKeyName)){
                        Number _id 
=  dialect.getSelectKey(entity.getEntityName(), getJdbcTemplate().getDataSource());
                
return  ps;
            }
            
        }, keyHolder);
        
        
if (obj.getPrimarykey()  ==   null )
            BeanUtil.setPropertyValue(obj, 
" id " , keyHolder.getKey().intValue());

从原生的SQL语句上讲Oracle在insert时要插入主键值,而SQLServer恰好相反必须不能插入主键的值,我在设计上就是抓住这一特性,再结合方言,实现了跨数据库的SpringJDBC, dialect.getSelectKey()方法,对应不同的数据库方言,如果是oracle就会生成主键的值,而如果是SQLServer这个方法不会返回任何值,代码如下
Oracle的方言方法:
     public  Number getSelectKey(String entityName, DataSource dataSource) {
        OracleSequenceMaxValueIncrementer incr 
=   new  OracleSequenceMaxValueIncrementer(dataSource,  " HIBERNATE_SEQUENCE " );
        
return  incr.nextIntValue();
    }
SQLServer的方言方法:
     public  Number getSelectKey(String entityName, DataSource dataSource) {
//         自增主键不用实现该方法
         return   null ;
    }
通过返回主键值是否为null,还判断在拼写sql时是否插入主键字段的值
最后通过
        if (obj.getPrimarykey()  ==   null )
            BeanUtil.setPropertyValue(obj, 
" id " , keyHolder.getKey().intValue());
Pojo对象是否主键值(如果没有就说明是自增型的如SQLServer,如果有就说明是序列生成的如Oracle),来将其赋值到POJO的属性中.

你可能感兴趣的:(J-Hi 开发日记(二))