Java Blog for Alex Wan

Let life be beautiful like summer flowers and death like autumn leaves.

统计

留言簿(10)

BlogJava

Blogs

DIV+CSS

JQuery相关

友情链接

常去的地方

数据供应

阅读排行榜

评论排行榜

[hibernate]hibernate中自定义主键生成器

背景:
Hibernate(目前使用的版本是3.2)中提供了多种生成主键的方式.在下面的文章中有列出来
[hibernate]Hibernate主键生成方式 Key Generator

然而当前的这么多种生成方式未必能满足我们的要求.
比如increment,可以在一个hibernate实例的应用上很方便的时候,但是在集群的时候就不行了.
再如 identity ,sequence ,native 是数据局提供的主键生成方式,往往也不是我们需要,而且在程序跨数据库方面也体现出不足.
还有基于算法的生成方式生成出来的主键基本都是字符串的.

我们现在需要一种生成方式:使用Long作为主键类型,自动增,支持集群.
那么我们需要自定义一个我们的主键生成器才能实现了.

实现代码:
package hibernate;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.id.Configurable;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.type.Type;


public class IncrementGenerator implements IdentifierGenerator, Configurable {
    
private static final Log log = LogFactory.getLog(IncrementGenerator.class);
    
private Long next;
    
private String sql;
    
public Serializable generate(SessionImplementor session, Object object)
            
throws HibernateException {
        
if (sql!=null{
            getNext( session.connection() );
        }

       
return next;

    }

    
    
public void configure(Type type, Properties params, Dialect d) throws MappingException {
        String table 
= params.getProperty("table");
        
if (table==null) table = params.getProperty(PersistentIdentifierGenerator.TABLE);
        String column 
= params.getProperty("column");
        
if (column==null) column = params.getProperty(PersistentIdentifierGenerator.PK);
        String schema 
= params.getProperty(PersistentIdentifierGenerator.SCHEMA);
        sql 
= "select max("+column +") from " + ( schema==null ? table : schema + '.' + table );
        log.info(sql);
    }

    
     
private void getNext(Connection conn) throws HibernateException {
            
try {
                PreparedStatement st 
= conn.prepareStatement(sql);
                ResultSet rs 
= st.executeQuery();
                
if ( rs.next() ) {
                    next 
= rs.getLong(1+ 1;
                }

                
else {
                    next 
= 1l;
                }

            }
catch(SQLException e)
            
{
                
throw new HibernateException(e);
            }

            
finally {
                
try{
                conn.close();
                }
catch(SQLException e)
                
{
                    
throw new HibernateException(e);
                }

            }

        }

}



配置:
在对应的hbm文件里面将id的配置如下:
        <id name="id" type="long" column="id" >
            
<generator class="hibernate.IncrementGenerator" /> 
        
</id>

ps:此生成方式仅通过两个hibernate实例测试,如发现有问题,请留言.


Let life be beautiful like summer flowers and death like autumn leaves.

posted on 2008-09-02 11:59 Alexwan 阅读(3915) 评论(8)  编辑  收藏 所属分类: J2EE数据库

评论

# re: [hibernate]hibernate中自定义主键生成器 2008-09-02 13:25 隔叶黄莺

如果当前某个表的ID值是2,某一个实例用这个自定义主键生成器得到主键是3,但还未插入记录,这时另一Hibernate实例拿到的主键也是3,并执行完 insert 操作后,第一个实例执行 insert 操作。

像这种情况不知如何保证主键的唯一性

我觉得直接用数据库来产生主键是能保证唯一性的,如果自己程序在有 Hibernate 多实例时确保产生的主键唯,也须要加上同步,是否可以把这个同步放到数据库这一层去,让程序更简单。  回复  更多评论   

# re: [hibernate]hibernate中自定义主键生成器 2008-09-02 14:23 Alexwan

说的很有道理.

另外一个问题凸显出来了,就是hibernate是在插入数据前去获取主键的,如果是这样无论用什么办法去做都是有机会导致主键不唯一啊(多hibernate实例的情况下),只是视乎几率的大小而已.

如果将生成主键的工作放到数据库这一层,那么在插入之前hibernate是不知道主键是多少的,这样的话,我又如何去获取我刚刚插入那条数据呢?有的时候需要返回新的记录对象.  回复  更多评论   

# re: [hibernate]hibernate中自定义主键生成器 2008-09-02 14:32 隔叶黄莺

看看 Hibernate 的 session.save() 操作,它返回的就是一个主键,从源代码就知道,它是首先取得主键,然后再 insert ........
而不是通常像我们那样(比如说在 Oracle)insert into ...... values(oneSeq.nextval.....  回复  更多评论   

# re: [hibernate]hibernate中自定义主键生成器 2008-09-02 15:45 Alexwan

刚用这个主键生成配置后,用两台机器各自运行一个hibernate的实例,同时往一个数据表不停的插数据,数据量各为1万,未出现主键不唯一的情况.

可以放心使用了!  回复  更多评论   

# re: [hibernate]hibernate中自定义主键生成器 2008-09-02 16:06 隔叶黄莺

测试代码中业务逻辑要体现出来,比如两个长短不一样的逻辑(通常是有的,针对用户的输入可能复杂度不一样的),长逻辑的实例先拿到主键(2),这时短逻辑的实例进来也拿到主键(2),然后很快 insert 了记录,而长逻辑的实例之后才执行 insert,这种情况也就出问题了。

再者,把 Hibernate 实例换成了多线程环境中的线程,即使不是在集群环境下也是会出问题的。  回复  更多评论   

# re: [hibernate]hibernate中自定义主键生成器 2008-09-02 16:59 Alexwan

从事务的角度来看确实会有问题.

那这样的话,您认为如果要在集群的环境下保持主键唯一,使用什么方式要一点呢?有没有这方面的经验呢?  回复  更多评论   

# re: [hibernate]hibernate中自定义主键生成器 2008-09-02 17:20 隔叶黄莺

当然还是数据库,产生主键的机制设置为 Native,数据库方言也会选择正确的数据库对象来产生主键,如 Oracle 的序列,Db2 的 indentity,某些数据库的自增字段。  回复  更多评论   

# re: [hibernate]hibernate中自定义主键生成器 2008-09-02 17:38 Alexwan

OK,谢谢指教  回复  更多评论   


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


网站导航: