Todos conocemos el comportamiento predeterminado de Hibernate al usarlo @SequenceGenerator
: aumenta la secuencia de la base de datos real en uno , multiplica este valor por 50 ( allocationSize
valor predeterminado ) y luego usa este valor como ID de entidad.
Este es un comportamiento incorrecto y entra en conflicto con la especificación que dice:
LocationSize - (Opcional) La cantidad a incrementar cuando se asignan números de secuencia de la secuencia.
Para ser claros: no me preocupo por las brechas entre los ID generados.
Me preocupan las identificaciones que no son consistentes con la secuencia de la base de datos subyacente. Por ejemplo: cualquier otra aplicación (que, por ejemplo, use JDBC simple) puede querer insertar nuevas filas bajo los ID obtenidos de la secuencia, ¡pero Hibernate ya puede usar todos esos valores! Locura.
¿Alguien conoce alguna solución a este problema (sin configurar allocationSize=1
y, por lo tanto, degradar el rendimiento)?
EDITAR:
Para aclarar las cosas. Si el último registro insertado tenía ID = 1
, entonces HB usa 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 que puede conducir fácilmente a errores cuando otras aplicaciones utilizan esa secuencia.
Por otro lado: la especificación dice (según tengo entendido) que la secuencia de la base de datos debería haberse configurado 51
y, mientras tanto, HB debería usar valores del rango 2, 3 ... 50
ACTUALIZACIÓN:
Como Steve Ebersole mencionó a continuación: el comportamiento descrito por mí (y también el más intuitivo para muchos) se puede habilitar configurando hibernate.id.new_generator_mappings=true
.
Gracias a todos.
ACTUALIZACIÓN 2:
Para futuros lectores, a continuación puede encontrar un ejemplo práctico.
@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>
save
necesitan consultar la base de datos para el siguiente valor de la secuencia.SequenceGenerator
Hibernate consultará la base de datos solo cuando seallocationsize
agote la cantidad de ID especificados por . Si lo configura,allocationSize = 1
entonces es la razón por la que Hibernate consulta la base de datos para cada inserción. Cambie este valor y listo.hibernate.id.new_generator_mappings
entorno es realmente importante. Espero que sea la configuración predeterminada que no tenga que dedicar tanto tiempo a investigar por qué el número de identificación se vuelve loco.Respuestas:
Para ser absolutamente claro ... lo que describe no entra en conflicto con la especificación de ninguna manera. La especificación habla de los valores que Hibernate asigna a sus entidades, no de los valores realmente almacenados en la secuencia de la base de datos.
Sin embargo, existe la opción de obtener el comportamiento que busca. Primero vea mi respuesta en ¿Hay alguna manera de elegir dinámicamente una estrategia @GeneratedValue usando anotaciones JPA e Hibernate? Eso te dará lo básico. Siempre que esté configurado para usar ese SequenceStyleGenerator, Hibernate interpretará
allocationSize
usando 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 que existen.fuente
org.hibernate.id.enhanced.SequenceStyleGenerator
. Me sorprendiste.allocationSize=1
Es una micro optimización antes de obtener la consulta. Hibernate intenta asignar un valor en el rango de tamaño de asignación y, por lo tanto, trata de evitar consultar la base de datos para la secuencia. Pero esta consulta se ejecutará cada vez que la establezca en 1. Esto apenas hace ninguna diferencia, ya que si alguna otra aplicación accede a su base de datos, se generarán problemas si, mientras tanto, otra aplicación utiliza la misma identificación.La próxima generación de Sequence Id se basa en deploymentSize.
Por defualt se mantiene como lo
50
que es demasiado. También será útil si va a tener cerca de50
registros en una sesión que no se conservan y que se conservarán utilizando esta sesión y transacción en particular.Por lo tanto, siempre debe usar
allocationSize=1
mientras usaSequenceGenerator
. Como ocurre con la mayoría de las bases de datos subyacentes, la secuencia siempre se incrementa en1
.fuente
allocationSize=1
Hibernate en cadasave
operación es necesario realizar el viaje a la base de datos para obtener un nuevo valor de identificación.allocationSize
y, por lo tanto, trata de evitar consultar la base de datos por secuencia. Pero esta consulta se ejecutará cada vez que la establezca en 1. Esto apenas hace ninguna diferencia, ya que si alguna otra aplicación accede a su base de datos, se generarán problemas si otra aplicación utiliza la misma identificación mientras tantoSteve Ebersole y otros miembros,
¿podrían explicar el motivo de una identificación con una brecha más grande (por defecto 50)? Estoy usando Hibernate 4.2.15 y encontré el siguiente código en org.hibernate.id.enhanced.OptimizerFactory cass.
Siempre que golpea el interior de la declaración if, su valor aumenta mucho. Entonces, mi identificación durante la prueba con el reinicio frecuente 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 dijo 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 aportación de cualquiera será de mucha ayuda.
Jihwan
ACTUALIZACIÓN: ne1410s: Gracias por la edición.
cfrick: Está bien. Lo haré. Fue mi primera publicación aquí y no estaba seguro de cómo usarla.
Ahora, entendí mejor por qué maxLo se usó para dos propósitos: dado que la hibernación llama a la secuencia de la base de datos una vez, sigue aumentando la identificación en el nivel de Java y la guarda en la base de datos, el valor de la identificación del nivel de Java debe considerar cuánto se cambió sin llamar la secuencia de base de datos cuando llama a la secuencia la próxima vez.
Por ejemplo, la identificación de la secuencia fue 1 en un punto y la hibernación ingresó 5, 6, 7, 8, 9 (con tamaño de asignación = 5). La próxima vez, cuando obtengamos el siguiente número de secuencia, DB devuelve 2, pero hibernación necesita usar 10, 11, 12 ... Entonces, es por eso que "hi = lastSourceValue.copy (). MultiplyBy (maxLo + 1)" es utilizado para obtener un siguiente id 10 de los 2 devueltos de la secuencia DB. Parece que lo único molesto fue durante el reinicio frecuente del servidor y este fue 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.
fuente
Después de profundizar en el código fuente de hibernación y la configuración inferior va a la base de datos de Oracle para obtener 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 usa para la siguiente estrategia
Consulte también a continuación http://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators-sequence
fuente
allocationSize
niinitialValue
globalmente para todas las entidades (a menos que use solo un generador, pero en mi humilde opinión no es muy legible).Yo también enfrenté este problema en Hibernate 5:
Recibí una advertencia como esta a continuación:
Luego cambié mi código a SequenceStyleGenerator:
Esto resolvió mis dos problemas:
fuente
Verificaría el DDL para la secuencia en el esquema. La implementación de JPA es responsable solo 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 un tamaño de asignación 1 y luego configurada con un tamaño de asignación 50 (o predeterminado) pero la secuencia DDL no se actualiza.
fuente
ALTER SEQUENCE ... INCREMENTY BY 50;
no resolverá nada, porque el problema sigue siendo el mismo. El valor de la secuencia aún no refleja los ID de entidades reales.