张昊

J-Hi(http://www.j-hi.net)

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  45 Posts :: 1 Stories :: 110 Comments :: 0 Trackbacks
最近在做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的属性中.
posted on 2011-04-21 00:19 张昊 阅读(1939) 评论(3)  编辑  收藏

Feedback

# re: J-Hi 开发日记(二) 2011-04-21 09:36 playbook
Good job!  回复  更多评论
  

# re: J-Hi 开发日记(二) 2011-04-21 15:01 好看的电影
呵呵,最近也在学这个!  回复  更多评论
  

# re: J-Hi 开发日记(二) 2011-04-23 21:22 新能源
写的详细呀  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: