secuencias secuencia incrementar ejemplos ejemplo create consultar cache sql hibernate sequence nextval

incrementar - secuencias sql ejemplos



obtener el siguiente valor de secuencia de la base de datos usando hibernate (9)

Aquí está lo que funcionó para mí (específico para Oracle, pero usar scalar parece ser la clave)

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

Gracias a los carteles aquí: springsource_forum

Tengo una entidad que tiene un campo NON-ID que debe establecerse a partir de una secuencia. Actualmente, busco el primer valor de la secuencia, lo almacena en el lado del cliente y calculo a partir de ese valor.

Sin embargo, estoy buscando una forma "mejor" de hacer esto. Implementé una forma de obtener el siguiente valor de secuencia:

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

Sin embargo, de esta forma se reduce significativamente el rendimiento (la creación de ~ 5000 objetos se ralentiza por un factor de 3, de 5740ms a 13648ms).

Intenté agregar una entidad "falsa":

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

Sin embargo, este enfoque tampoco funcionó (todos los Ids devueltos fueron 0).

¿Puede alguien recomendarme cómo recuperar el siguiente valor de secuencia utilizando Hibernate de manera eficiente?

Editar: Tras la investigación, descubrí que llamaba a Query query = session.createSQLQuery( "select nextval(''mySequence'')" ); es mucho más ineficiente que usar @GeneratedValue - debido a que Hibernate de alguna manera logra reducir el número de recuperaciones al acceder a la secuencia descrita por @GeneratedValue .

Por ejemplo, cuando creo 70,000 entidades (de esta manera, obtengo 70,000 claves primarias de la misma secuencia), obtengo todo lo que necesito.

SIN EMBARGO , Hibernate solo emite 1404 select nextval (''local_key_sequence'') comandos. NOTA: En el lado de la base de datos, el almacenamiento en caché se establece en 1.

Si trato de buscar todos los datos manualmente, me tomará 70,000 selecciones, por lo tanto, una gran diferencia en el rendimiento. ¿Alguien sabe el funcionamiento interno de Hibernate, y cómo reproducirlo manualmente?


Encontré la solución:

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


Esta es la forma en que lo hago:

@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);


Interesante, funciona para usted. Cuando probé la solución, apareció un error que decía "No coinciden los tipos: no se puede convertir de SQLQuery a Query". -> Por lo tanto, mi solución se ve así:

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

Con esa solución no tuve problemas de rendimiento.

Y no se olvide de restablecer su valor, si solo quisiera saberlo con fines informativos.

--nextValue; query = session.createSQLQuery("select setval(''SEQUENCE_NAME''," + nextValue + ")");


Para obtener la nueva identificación, todo lo que tienes que hacer es flush el administrador de la entidad. Vea el método getNext() continuación:

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


Puede usar Hibernate Dialect API para independencia de la base de datos de la siguiente manera

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


Si está utilizando Oracle, considere especificar el tamaño de caché para la secuencia. Si rutinariamente crea objetos en lotes de 5K, puede simplemente configurarlo en 1000 o 5000. Lo hicimos para la secuencia utilizada para la clave primaria sustituta y nos sorprendió que los tiempos de ejecución para un proceso ETL escrito a mano en Java cayeran a la mitad.

No pude pegar el código formateado en el comentario. Aquí está la secuencia DDL:

create sequence seq_mytable_sid minvalue 1 maxvalue 999999999999999999999999999 increment by 1 start with 1 cache 1000 order nocycle;


Tu idea con la entidad falsa SequenceGenerator es buena.

@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")

Es importante usar el parámetro con el nombre de clave "sequence_name". Ejecute una sesión de depuración en la clase de hibernación SequenceStyleGenerator, el método de configuración (...) en la línea final QualifiedName sequenceName = determineSequenceName (params, dialect, jdbcEnvironment); para ver más detalles sobre cómo Hibernate calcula el nombre de la secuencia. Hay algunos valores predeterminados que también podría usar.

Después de la entidad falsa, creé un CrudRepository:

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

En el Junit, llamo al método save del SequenceRepository.

SequenceGenerator sequenceObject = new SequenceGenerator (); SequenceGenerator result = sequenceRepository.save (sequenceObject);

Si hay una mejor manera de hacerlo (tal vez soporte para un generador en cualquier tipo de campo en lugar de simplemente Id), estaría más que feliz de usarlo en lugar de este "truco".


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