pobranie następnej wartości sekwencji z bazy danych za pomocą hibernate
Mam encję, która ma pole nie-ID, które musi być ustawione z sekwencji. Obecnie pobieram dla pierwszej wartości sekwencji, przechowuję ją po stronie klienta i obliczam z tej wartości.
Szukam jednak "lepszego" sposobu na zrobienie tego. Zaimplementowałem sposób pobierania następnej wartości sekwencji:public Long getNextKey()
{
Query query = session.createSQLQuery( "select nextval('mySequence')" );
Long key = ((BigInteger) query.uniqueResult()).longValue();
return key;
}
Jednak w ten sposób znacznie zmniejsza wydajność (tworzenie ~5000 obiektów zostaje spowolnione o współczynnik 3 - z 5740ms do 13648ms).
Próbowałem dodać" fałszywy " byt:
@Entity
@SequenceGenerator(name = "sequence", sequenceName = "mySequence")
public class SequenceFetcher
{
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
private long id;
public long getId() {
return id;
}
}
Jednak to podejście również nie zadziałało(wszystkie zwrócone identyfikatory były równe 0).
Czy ktoś może mi doradzić jak efektywnie pobrać następną wartość sekwencji używając Hibernate?
Edit: po zbadaniu odkryłem, że wywołanie Query query = session.createSQLQuery( "select nextval('mySequence')" );
jest o wiele bardziej nieefektywne niż użycie @GeneratedValue
- z powodu Hibernate jakoś udaje się zmniejszyć liczbę pobrań podczas uzyskiwania dostępu do sekwencji opisane przez @GeneratedValue
.
Na przykład, kiedy tworzę 70 000 encji, (a więc z 70 000 kluczy podstawowych pobranych z tej samej sekwencji), dostaję wszystko, czego potrzebuję.
Jednak , problemy z hibernacją tylko 1404 select nextval ('local_key_sequence')
polecenia. UWAGA: Po Stronie bazy danych buforowanie jest ustawione na 1.
Jeśli spróbuję pobrać wszystkie dane ręcznie, zajmie mi to 70,000 selekcji, a więc ogromna różnica w wydajności. Czy ktoś zna wewnętrzne funkcjonowanie Hibernate i jak odtworzyć ręcznie?
10 answers
Możesz użyć Hibernate dialekt API dla niezależności bazy danych w następujący sposób
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;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-27 14:52:29
Oto, co dla mnie zadziałało (specyficzne dla Oracle, ale użycie scalar
wydaje się być kluczem)
Long getNext() {
Query query =
session.createSQLQuery("select MYSEQ.nextval as num from dual")
.addScalar("num", StandardBasicTypes.BIG_INTEGER);
return ((BigInteger) query.uniqueResult()).longValue();
}
Dzięki plakatom tutaj: springsource_forum
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-04 12:24:39
Znalazłem rozwiązanie:
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() ;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-20 13:15:02
Jeśli używasz Oracle, rozważ podanie rozmiaru pamięci podręcznej dla sekwencji. Jeśli rutynowo tworzysz obiekty w partiach 5K, możesz po prostu ustawić je na 1000 lub 5000. Zrobiliśmy to dla sekwencji używanej dla zastępczego klucza podstawowego i byliśmy zdumieni, że czas wykonania procesu ETL ręcznie napisanego w Javie spadł o połowę.
Nie mogłem wkleić sformatowanego kodu do komentarza. Oto Sekwencja DDL:
create sequence seq_mytable_sid
minvalue 1
maxvalue 999999999999999999999999999
increment by 1
start with 1
cache 1000
order
nocycle;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-17 14:20:00
Aby uzyskać nowy identyfikator, wystarczy flush
menedżer encji. Patrz metoda getNext()
poniżej:
@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();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-17 14:05:42
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();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-04 13:06:26
Ciekawe, że to działa. Kiedy próbowałem rozwiązania wystąpił błąd, mówiąc, że "Type mismatch: cannot convert from sqlquery to Query". -- >Dlatego moje rozwiązanie wygląda tak:
SQLQuery query = session.createSQLQuery("select nextval('SEQUENCE_NAME')");
Long nextValue = ((BigInteger)query.uniqueResult()).longValue();
Dzięki temu rozwiązaniu nie napotkałem problemów z wydajnością.
I nie zapomnij zresetować wartości, jeśli chcesz tylko wiedzieć w celach informacyjnych.
--nextValue;
query = session.createSQLQuery("select setval('SEQUENCE_NAME'," + nextValue + ")");
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-04 08:04:40
Wiosna 5 ma do tego kilka klas pomocników: org / springframework / jdbc / support / incrementer
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-16 15:44:44
Oto Jak to robię:
@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);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-10 18:52:11
Twój pomysł z SequenceGenerator fake entity jest dobry.
@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")
Ważne jest, aby użyć parametru o nazwie klucza "sequence_name". Uruchom sesję debugowania na sekwencestylegenerator klasy hibernate, configure(...) method at the line final QualifiedName sequenceName = determineSequenceName (params, dialect, jdbcEnvironment); to see more details about how the sequencename is computed by Hibernate. Istnieje kilka domyślnych tam można również użyj.
Po fałszywym bycie, stworzyłem CrudRepository:
public interface SequenceRepository extends CrudRepository<SequenceGenerator, Long> {}
W Junit wywołuję metodę save z SequenceRepository.
SequenceGenerator sequenceObject = new SequenceGenerator(); SequenceGenerator result = sequenceRepository.save (sequenceObject);
Jeśli istnieje lepszy sposób, aby to zrobić (może wsparcie dla generatora na dowolnym typie pola zamiast tylko Id), byłbym bardziej niż szczęśliwy, aby użyć go zamiast tej "sztuczki".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-10 15:12:30