J-Hi 開發日記(二) – JAVA編程語言程序開發技術文章

張昊


最近在做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的屬性中

發佈留言