使用hibernate从数据库中获取下一个序列值

ili*_*den 31 sql hibernate sequence nextval

我有一个具有NON-ID字段的实体,必须从序列中设置.目前,我获取序列的第一个值,将其存储在客户端,并从该值进行计算.

但是,我正在寻找一种"更好"的方式来做到这一点.我已经实现了一种获取下一个序列值的方法:

public Long getNextKey()
{
    Query query = session.createSQLQuery( "select nextval('mySequence')" );
    Long key = ((BigInteger) query.uniqueResult()).longValue();
    return key;
}
Run Code Online (Sandbox Code Playgroud)

但是,这种方式会显着降低性能(〜5000个对象的创建速度减慢了3倍 - 从5740ms减少到13648ms).

我试图添加一个"假"实体:

@Entity
@SequenceGenerator(name = "sequence", sequenceName = "mySequence")
public class SequenceFetcher
{
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
    private long                      id;

    public long getId() {
        return id;
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,这种方法也不起作用(所有返回的Ids都是0).

有人可以告诉我如何有效地使用Hibernate获取下一个序列值吗?

编辑:经过调查,我发现调用的Query query = session.createSQLQuery( "select nextval('mySequence')" );效率远低于使用@GeneratedValue- 因为Hibernate 以某种方式设法减少访问所描述的序列时的提取次数@GeneratedValue.

例如,当我创建70,000个实体时(因此从同一序列中获取70,000个主键),我得到了我需要的一切.

但是,Hibernate只发出1404个 select nextval ('local_key_sequence')命令.注意:在数据库端,缓存设置为1.

如果我尝试手动获取所有数据,则需要70,000次选择,因此性能差异很大.有谁知道Hibernate的内部功能,以及如何手动重现它?

小智 24

这对我有用(特定于Oracle,但使用scalar似乎是关键)

Long getNext() {
    Query query = 
        session.createSQLQuery("select MYSEQ.nextval as num from dual")
            .addScalar("num", StandardBasicTypes.BIG_INTEGER);

    return ((BigInteger) query.uniqueResult()).longValue();
}
Run Code Online (Sandbox Code Playgroud)

感谢这里的海报:springsource_forum

  • 作为注释,您还可以使用StandardBasicTypes.LONG而不是StandardBasicTypes.BIG_INTEGER ... (3认同)

Pun*_*tel 21

您可以使用Hibernate Dialect API实现数据库独立性,如下所示

class SequenceValueGetter {
    private SessionFactory sessionFactory;

    // For Hibernate 3
    public Long getId(final String sequenceName) {
        final List<Long> ids = new ArrayList<Long>(1);

        sessionFactory.getCurrentSession().doWork(new Work() {
            public void execute(Connection connection) throws SQLException {
                DialectResolver dialectResolver = new StandardDialectResolver();
                Dialect dialect =  dialectResolver.resolveDialect(connection.getMetaData());
                PreparedStatement preparedStatement = null;
                ResultSet resultSet = null;
                try {
                    preparedStatement = connection.prepareStatement( dialect.getSequenceNextValString(sequenceName));
                    resultSet = preparedStatement.executeQuery();
                    resultSet.next();
                    ids.add(resultSet.getLong(1));
                }catch (SQLException e) {
                    throw e;
                } finally {
                    if(preparedStatement != null) {
                        preparedStatement.close();
                    }
                    if(resultSet != null) {
                        resultSet.close();
                    }
                }
            }
        });
        return ids.get(0);
    }

    // For Hibernate 4
    public Long getID(final String sequenceName) {
        ReturningWork<Long> maxReturningWork = new ReturningWork<Long>() {
            @Override
            public Long execute(Connection connection) throws SQLException {
                DialectResolver dialectResolver = new StandardDialectResolver();
                Dialect dialect =  dialectResolver.resolveDialect(connection.getMetaData());
                PreparedStatement preparedStatement = null;
                ResultSet resultSet = null;
                try {
                    preparedStatement = connection.prepareStatement( dialect.getSequenceNextValString(sequenceName));
                    resultSet = preparedStatement.executeQuery();
                    resultSet.next();
                    return resultSet.getLong(1);
                }catch (SQLException e) {
                    throw e;
                } finally {
                    if(preparedStatement != null) {
                        preparedStatement.close();
                    }
                    if(resultSet != null) {
                        resultSet.close();
                    }
                }

            }
        };
        Long maxRecord = sessionFactory.getCurrentSession().doReturningWork(maxReturningWork);
        return maxRecord;
    }

}
Run Code Online (Sandbox Code Playgroud)

  • +1我认为这是唯一与数据库无关的答案,这就是我所需要的.(我们有一个使用Hibernate 4的应用程序需要从db序列中获取非id值,使用HSQLDB进行测试,使用Oracle进行真正的交易.)使用java 7的try-with-resources可以缩短代码a位.在将它传递给`resolveDialect()`之前,我必须使用`DatabaseMetaDataDialectResolutionInfoAdapter`来包装`connection.getMetaData()`的结果.@punitpatel谢谢! (2认同)

ili*_*den 5

我找到了解决方案:

public class DefaultPostgresKeyServer
{
    private Session session;
    private Iterator<BigInteger> iter;
    private long batchSize;

    public DefaultPostgresKeyServer (Session sess, long batchFetchSize)
    {
        this.session=sess;
        batchSize = batchFetchSize;
        iter = Collections.<BigInteger>emptyList().iterator();
    }

        @SuppressWarnings("unchecked")
        public Long getNextKey()
        {
            if ( ! iter.hasNext() )
            {
                Query query = session.createSQLQuery( "SELECT nextval( 'mySchema.mySequence' ) FROM generate_series( 1, " + batchSize + " )" );

                iter = (Iterator<BigInteger>) query.list().iterator();
            }
            return iter.next().longValue() ;
        }

}
Run Code Online (Sandbox Code Playgroud)

  • 请注意:由于类表明这仅适用于PostgreSQL,因为SQL语法是Oracle的参考. (2认同)