Pregunta Hibernate, @SequenceGenerator y allocationSize


Todos conocemos el comportamiento predeterminado de Hibernate cuando usamos @SequenceGenerator - aumenta la secuencia de la base de datos real uno, multiplique este valor por 50 (valor predeterminado allocationSize valor) - y luego usa este valor como ID de entidad.

Este es un comportamiento incorrecto y está en conflicto con especificación que dice:

allocationSize - (Opcional) La cantidad que se incrementará al asignar números de secuencia de la secuencia.

Para ser claro: no me preocupo por las brechas entre los ID generados.

Me importan las identificaciones que son no consistente con secuencia de base de datos subyacente. Por ejemplo: cualquier otra aplicación (que p. Ej. Usa JDBC simple) puede querer insertar nuevas filas bajo IDs obtenidos de la secuencia, ¡pero Hibernate ya puede usar todos esos valores! Locura.

¿Alguien sabe alguna solución a este problema (sin configurar allocationSize=1 y, por lo tanto, degrada el rendimiento)?

EDITAR:
Para aclarar las cosas Si el último registro insertado tenía ID = 1, luego valores de uso de HB 51, 52, 53... para sus nuevas entidades PERO al mismo tiempo: el valor de la secuencia en la base de datos se establecerá en 2. Lo cual puede conducir fácilmente a errores cuando otras aplicaciones están usando esa secuencia.

Por otro lado: la especificación dice (a mi entender) que la secuencia de la base de datos debería haberse configurado para 51 y mientras tanto, HB debería usar valores del rango 2, 3 ... 50


ACTUALIZAR: 
Como Steve Ebersole mencionó a continuación: el comportamiento descrito por mí (y también el más intuitivo para muchos) se puede habilitar estableciendo hibernate.id.new_generator_mappings=true.

Gracias a todos.

ACTUALIZACIÓN 2:
Para futuros lectores, a continuación puede encontrar un ejemplo de trabajo.

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USERS_SEQ")
    @SequenceGenerator(name = "USERS_SEQ", sequenceName = "SEQUENCE_USERS")
    private Long id;
}

persistence.xml

<persistence-unit name="testPU">
  <properties>
    <property name="hibernate.id.new_generator_mappings" value="true" />
  </properties>
</persistence-unit>

74
2017-10-05 11:55


origen


Respuestas:


Para ser absolutamente claro ... lo que describes no no conflicto con la especificación de cualquier manera. La especificación habla sobre los valores que Hibernate asigna a sus entidades, no los valores realmente almacenados en la secuencia de la base de datos.

Sin embargo, existe la opción de obtener el comportamiento que está buscando. Primero, mira mi respuesta en ¿Hay alguna manera de elegir dinámicamente una estrategia @GeneratedValue utilizando anotaciones JPA e Hibernate?  Eso te dará los conceptos básicos. Siempre que esté configurado para usar ese SequenceStyleGenerator, Hibernate interpretará allocationSize usando el "optimizador agrupado" en el SequenceStyleGenerator. El "optimizador agrupado" se utiliza con bases de datos que permiten una opción de "incremento" en la creación de secuencias (no todas las bases de datos que admiten secuencias admiten un incremento). De todos modos, lea sobre las diversas estrategias de optimización allí.


30
2017-10-05 13:35



allocationSize=1 Es una micro optimización antes de obtener una consulta Hibernate intenta asignar un valor en el rango de allocationSize y así evitará consultar la base de datos para la secuencia. Pero esta consulta se ejecutará cada vez si la configura en 1. Esto apenas hace ninguna diferencia, ya que si su base de datos es accedida por alguna otra aplicación, entonces creará problemas si otra aplicación utiliza el mismo id.

La próxima generación de Sequence Id se basa en allocationSize.

Por defualt se mantiene como 50 que es demasiado También solo ayudará si vas a tener cerca 50 registros en una sesión que no se conservan y que se conservarán utilizando esta sesión y transacción particulares.

Entonces siempre deberías usar allocationSize=1 durante el uso SequenceGenerator. Como para la mayoría de las bases de datos subyacentes, la secuencia siempre se incrementa 1.


8
2017-10-05 12:04



Después de investigar el código fuente de hibernación y Debajo de la configuración va a Oracle db para el siguiente valor después de 50 inserciones. Así que haga que su INST_PK_SEQ incremente 50 cada vez que se llame.

Hibernate 5 se utiliza para la estrategia a continuación

Verifique también abajo http://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators-sequence

@Id
@Column(name = "ID")
@GenericGenerator(name = "INST_PK_SEQ", 
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
        @org.hibernate.annotations.Parameter(
                name = "optimizer", value = "pooled-lo"),
        @org.hibernate.annotations.Parameter(
                name = "initial_value", value = "1"),
        @org.hibernate.annotations.Parameter(
                name = "increment_size", value = "50"),
        @org.hibernate.annotations.Parameter(
                name = SequenceStyleGenerator.SEQUENCE_PARAM, value = "INST_PK_SEQ"),
    }
)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "INST_PK_SEQ")
private Long id;

1
2018-03-03 22:52



Verificaría el DDL para la secuencia en el esquema. La implementación de JPA es responsable únicamente de la creación de la secuencia con el tamaño de asignación correcto. Por lo tanto, si el tamaño de la asignación es 50, su secuencia debe tener el incremento de 50 en su DDL.

Este caso puede ocurrir típicamente con la creación de una secuencia con tamaño de asignación 1 y luego configurado para el tamaño de asignación 50 (o predeterminado) pero la secuencia DDL no se actualiza.


0
2017-10-05 19:13



Steve Ebersole y otros miembros,
¿Podría explicarnos el motivo de una identificación con una brecha mayor (por defecto 50)? Estoy usando Hibernate 4.2.15 y encontré el siguiente código en org.hibernate.id.enhanced.OptimizerFactory cass.

if ( lo > maxLo ) {
   lastSourceValue = callback.getNextValue();
   lo = lastSourceValue.eq( 0 ) ? 1 : 0;
   hi = lastSourceValue.copy().multiplyBy( maxLo+1 ); 
}  
value = hi.copy().add( lo++ );

Cada vez que golpea el interior de la declaración if, el valor de hi es mucho más grande. Por lo tanto, mi id durante la prueba con el frecuente reinicio del servidor genera los siguientes identificadores de secuencia:
1, 2, 3, 4, 19, 250, 251, 252, 400, 550, 750, 751, 752, 850, 1100, 1150.

Sé que ya dijiste que no entraba en conflicto con la especificación, pero creo que esta será una situación muy inesperada para la mayoría de los desarrolladores.

La opinión de cualquier persona será de mucha ayuda.

Jihwan

ACTUALIZAR: ne1410s: gracias por la edición.
cfrick: OK. Lo haré. Fue mi primer post aquí y no estaba seguro de cómo usarlo.

Ahora, entendí mejor por qué maxLo se usó para dos propósitos: como el hibernate llama a la secuencia de DB una vez, siga aumentando la identificación en el nivel de Java y la guarda en la base de datos, el valor de identificación de nivel Java debe considerar cuánto se cambió sin llamar la secuencia DB cuando llama a la secuencia la próxima vez.

Por ejemplo, el ID de secuencia era 1 en un punto e hibernación ingresaba 5, 6, 7, 8, 9 (con allocationSize = 5). La próxima vez, cuando obtengamos el siguiente número de secuencia, DB devuelve 2, pero hibernate necesita usar 10, 11, 12 ... Por eso, "hi = lastSourceValue.copy (). MultiplyBy (maxLo + 1)" es usado para obtener una próxima identificación 10 de las 2 devueltas de la secuencia DB. Parece que lo único que molestaba era durante el reinicio frecuente del servidor y este era mi problema con la brecha más grande.

Entonces, cuando usamos el ID de SECUENCIA, el ID insertado en la tabla no coincidirá con el número de SECUENCIA en DB.


0
2017-09-28 19:46