java - org - Hibernate, @SequenceGenerator y allocationSize
jpa entitymanager (5)
Todos conocemos el comportamiento predeterminado de Hibernate cuando usamos @SequenceGenerator
: aumenta la secuencia real de la base de datos en uno , multiplica este valor por 50 (valor predeterminado de allocationSize
) y luego utiliza este valor como ID de entidad.
Este es un comportamiento incorrecto y entra en conflicto con la specification 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 ID que no son consistentes con la secuencia de base de datos subyacente. Por ejemplo: cualquier otra aplicación (que p. Ej. Use JDBC simple) puede querer insertar nuevas filas bajo IDs obtenidos de la secuencia, ¡pero todos esos valores ya pueden ser utilizados por Hibernate! Locura.
¿Alguien sabe alguna solución a este problema (sin establecer allocationSize=1
y por lo tanto degradando el rendimiento)?
EDITAR:
Para aclarar las cosas Si el último registro insertado tenía ID = 1
, entonces HB usa los valores 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 establecido en 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) puede habilitarse configurando 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>
Después de indagar en 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
Consulte también a continuación 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;
Para ser absolutamente claro ... lo que describes no está en conflicto con la especificación de ninguna 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 vea mi respuesta ¿Existe alguna forma 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
utilizando el "optimizador agrupado" en 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í.
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
ACTUALIZACIÓN: 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.
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.
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 la consulta de 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 va a tener cerca de 50
registros en una sesión que no se conservan y que se conservarán con esta sesión y transacción en particular.
Por lo tanto, siempre debe usar allocationSize=1
mientras usa SequenceGenerator
. Como para la mayoría de las bases de datos subyacentes, la secuencia siempre se incrementa en 1
.