34

シーケンスから設定する必要のあるNON-IDフィールドを持つエンティティがあります。現在、シーケンスの最初の値をフェッチしてクライアント側に保存し、その値から計算しています。

しかし、私はこれを行うための「より良い」方法を探しています。次のシーケンス値をフェッチする方法を実装しました。

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

ただし、この方法ではパフォーマンスが大幅に低下します(〜5000オブジェクトの作成は3分の1に遅くなります-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;
    }
}

ただし、このアプローチも機能しませんでした(返されたIDはすべて0でした)。

誰かがHibernateを効率的に使用して次のシーケンス値をフェッチする方法を教えてもらえますか?

編集:調査の結果、 Hibernateがで記述されたシーケンスにアクセスするときにフェッチの数を減らすことができたため、呼び出しは-使用するQuery query = session.createSQLQuery( "select nextval('mySequence')" );よりもはるかに非効率的であることがわかりました。@GeneratedValue@GeneratedValue

たとえば、70,000個のエンティティを作成すると(つまり、同じシーケンスから70,000個の主キーがフェッチされると)、必要なものがすべて取得されます。

ただし、Hibernateは1404 select nextval ('local_key_sequence')コマンドのみを発行します。注:データベース側では、キャッシュは1に設定されています。

すべてのデータを手動でフェッチしようとすると、70,000回の選択が必要になるため、パフォーマンスに大きな違いが生じます。Hibernateの内部機能と、それを手動で再現する方法を知っている人はいますか?

4

10 に答える 10

29

次のように、データベースの独立性のためにHibernateDialectAPIを使用できます。

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;
    }

}
于 2014-04-24T11:10:15.997 に答える
25

これが私にとってうまくいったことです(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();
}

ここのポスターに感謝します:springsource_forum

于 2012-01-20T23:32:55.267 に答える
6

私は解決策を見つけました:

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() ;
        }

}
于 2011-06-20T13:15:02.000 に答える
3

Oracleを使用している場合は、シーケンスのキャッシュサイズを指定することを検討してください。日常的に5Kのバッチでオブジェクトを作成する場合は、1000または5000に設定するだけです。代理主キーに使用されるシーケンスに対して実行し、Javaで手書きされたETLプロセスの実行時間が短縮されたことに驚いています。半分に。

フォーマットされたコードをコメントに貼り付けることができませんでした。シーケンスDDLは次のとおりです。

create sequence seq_mytable_sid 
minvalue 1 
maxvalue 999999999999999999999999999 
increment by 1 
start with 1 
cache 1000 
order  
nocycle;
于 2011-06-17T14:02:30.187 に答える
2

新しいIDを取得するにはflush、エンティティマネージャーだけが必要です。getNext()以下の方法を参照してください。

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

    public long getId() {
        return id;
    }

    public static long getNext(EntityManager em) {
        SequenceFetcher sf = new SequenceFetcher();
        em.persist(sf);
        em.flush();
        return sf.getId();
    }
}
于 2011-06-17T14:05:42.013 に答える
1

POSTGRESQL

String psqlAutoincrementQuery = "SELECT NEXTVAL(CONCAT(:psqlTableName, '_id_seq')) as id";

Long psqlAutoincrement = (Long) YOUR_SESSION_OBJ.createSQLQuery(psqlAutoincrementQuery)
                                                      .addScalar("id", Hibernate.LONG)
                                                      .setParameter("psqlTableName", psqlTableName)
                                                      .uniqueResult();

MYSQL

String mysqlAutoincrementQuery = "SELECT AUTO_INCREMENT as id FROM information_schema.tables WHERE table_name = :mysqlTableName AND table_schema = DATABASE()";

Long mysqlAutoincrement = (Long) YOUR_SESSION_OBJ.createSQLQuery(mysqlAutoincrementQuery)
                                                          .addScalar("id", Hibernate.LONG)
                                                          .setParameter("mysqlTableName", mysqlTableName)                                                              
                                                          .uniqueResult();
于 2013-07-09T08:27:53.947 に答える
1

興味深いことに、それはあなたのために働きます。ソリューションを試したところ、「タイプの不一致:SQLQueryからQueryに変換できません」というエラーが発生しました。->したがって、私のソリューションは次のようになります。

SQLQuery query = session.createSQLQuery("select nextval('SEQUENCE_NAME')");
Long nextValue = ((BigInteger)query.uniqueResult()).longValue();

そのソリューションでは、パフォーマンスの問題は発生しませんでした。

また、情報提供の目的で知りたいだけの場合は、値をリセットすることを忘れないでください。

    --nextValue;
    query = session.createSQLQuery("select setval('SEQUENCE_NAME'," + nextValue + ")");
于 2016-08-04T08:04:40.107 に答える
1

Spring 5には、そのためのいくつかの組み込みヘルパークラスがあります: org / springframework / jdbc / support / incrementer

于 2019-07-16T15:44:44.527 に答える
0

これが私がそれをする方法です:

@Entity
public class ServerInstanceSeq
{
    @Id //mysql bigint(20)
    @SequenceGenerator(name="ServerInstanceIdSeqName", sequenceName="ServerInstanceIdSeq", allocationSize=20)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ServerInstanceIdSeqName")
    public Long id;

}

ServerInstanceSeq sis = new ServerInstanceSeq();
session.beginTransaction();
session.save(sis);
session.getTransaction().commit();
System.out.println("sis.id after save: "+sis.id);
于 2016-03-10T18:52:11.407 に答える
0

SequenceGeneratorの偽のエンティティに関するあなたのアイデアは良いです。

@Id
@GenericGenerator(name = "my_seq", strategy = "sequence", parameters = {
        @org.hibernate.annotations.Parameter(name = "sequence_name", value = "MY_CUSTOM_NAMED_SQN"),
})
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_seq")

キー名「sequence_name」のパラメータを使用することが重要です。HibernateクラスSequenceStyleGeneratorでデバッグセッションを実行します。configure(...)メソッドは、final QualifiedName sequenceName = describeSequenceName(params、dialect、jdbcEnvironment);の行にあります。シーケンス名がHibernateによってどのように計算されるかについての詳細を確認します。そこには、使用できるデフォルトがいくつかあります。

偽のエンティティの後、CrudRepositoryを作成しました。

public interface SequenceRepository extends CrudRepository<SequenceGenerator, Long> {}

JUnitでは、SequenceRepositoryのsaveメソッドを呼び出します。

SequenceGenerator sequenceObject = new SequenceGenerator(); SequenceGeneratorの結果=sequenceRepository.save(sequenceObject);

これを行うためのより良い方法がある場合(おそらく、Idだけでなく、任意のタイプのフィールドでジェネレーターをサポートする)、この「トリック」の代わりにそれを使用できれば幸いです。

于 2017-08-10T15:12:30.310 に答える