¿Es posible usar una secuencia DB para alguna columna que no es el identificador / no es parte de un identificador compuesto ?
Estoy usando hibernate como proveedor de jpa, y tengo una tabla que tiene algunas columnas que son valores generados (usando una secuencia), aunque no son parte del identificador.
Lo que quiero es usar una secuencia para crear un nuevo valor para una entidad, donde la columna para la secuencia NO es (parte de) la clave principal:
@Entity
@Table(name = "MyTable")
public class MyEntity {
//...
@Id //... etc
public Long getId() {
return id;
}
//note NO @Id here! but this doesn't work...
@GeneratedValue(strategy = GenerationType.AUTO, generator = "myGen")
@SequenceGenerator(name = "myGen", sequenceName = "MY_SEQUENCE")
@Column(name = "SEQ_VAL", unique = false, nullable = false, insertable = true, updatable = true)
public Long getMySequencedValue(){
return myVal;
}
}
Entonces cuando hago esto:
em.persist(new MyEntity());
se generará la identificación, pero la mySequenceVal
propiedad también será generada por mi proveedor de JPA.
Solo para aclarar las cosas: quiero que Hibernate genere el valor de la mySequencedValue
propiedad. Sé que Hibernate puede manejar valores generados por la base de datos, pero no quiero usar un disparador ni ninguna otra cosa que no sea Hibernate para generar el valor de mi propiedad. Si Hibernate puede generar valores para las claves primarias, ¿por qué no puede generar para una propiedad simple?
@GeneratedValue
en campos que no son id. Vote para ser incluido en 2.2 java.net/jira/browse/JPA_SPEC-113Descubrí que
@Column(columnDefinition="serial")
funciona perfecto pero solo para PostgreSQL. Para mí, esta fue la solución perfecta, porque la segunda entidad es la opción "fea".fuente
columnDefinition=
bit básicamente le dice a Hiberate que no intente generar la definición de columna y en su lugar use el texto que ha proporcionado. Esencialmente, su DDL para la columna será literalmente name + columnDefinition. En este caso (PostgreSQL),mycolumn serial
es una columna válida en una tabla.@Column(columnDefinition = "integer auto_increment")
@Column(insertable = false, updatable = false, columnDefinition="serial")
evitar que hibernate intentara insertar valores nulos o actualizar el campo. Luego debe volver a consultar la base de datos para obtener la identificación generada después de una inserción si necesita usarla de inmediato.Sé que esta es una pregunta muy antigua, pero se mostró en primer lugar en los resultados y jpa ha cambiado mucho desde la pregunta.
La forma correcta de hacerlo ahora es con la
@Generated
anotación. Puede definir la secuencia, establecer el valor predeterminado en la columna para esa secuencia y luego asignar la columna como:fuente
Hibernate definitivamente apoya esto. De los documentos:
"Las propiedades generadas son propiedades que tienen sus valores generados por la base de datos. Por lo general, las aplicaciones de Hibernate necesitaban actualizar los objetos que contenían propiedades para las cuales la base de datos generaba valores. Sin embargo, marcar las propiedades como generadas permite que la aplicación delegue esta responsabilidad a Hibernate. Esencialmente, cada vez que Hibernate emite un INSERTAR o ACTUALIZAR SQL para una entidad que ha definido las propiedades generadas, inmediatamente emite una selección para recuperar los valores generados ".
Para las propiedades generadas solo en la inserción, su asignación de propiedades (.hbm.xml) se vería así:
Para las propiedades generadas al insertar y actualizar su mapeo de propiedades (.hbm.xml) se vería así:
Desafortunadamente, no conozco JPA, así que no sé si esta función está expuesta a través de JPA (sospecho que posiblemente no)
Alternativamente, debería poder excluir la propiedad de las inserciones y actualizaciones, y luego llamar "manualmente" session.refresh (obj); después de haberlo insertado / actualizado para cargar el valor generado desde la base de datos.
Así es como excluiría la propiedad de ser utilizada en declaraciones de inserción y actualización:
Nuevamente, no sé si JPA expone estas características de Hibernate, pero Hibernate las admite.
fuente
Como seguimiento, así es como lo hice funcionar:
fuente
SQLQuery sqlQuery = getSession().createSQLQuery("select NAMED_SEQ.nextval seq from dual"); sqlQuery.addScalar("seq", LongType.INSTANCE); return (Long) sqlQuery.uniqueResult();
Arreglé la generación de UUID (o secuencias) con Hibernate usando la
@PrePersist
anotación:fuente
Aunque este es un hilo viejo, quiero compartir mi solución y espero obtener algunos comentarios al respecto. Tenga en cuenta que solo probé esta solución con mi base de datos local en algunos casos de prueba JUnit. Por lo tanto, esta no es una característica productiva hasta ahora.
Resolví ese problema para mí introduciendo una anotación personalizada llamada Secuencia sin propiedad. Es solo un marcador para los campos a los que se les debe asignar un valor de una secuencia incrementada.
Usando esta anotación marqué mis entidades.
Para mantener la base de datos independiente, introduje una entidad llamada SequenceNumber que contiene el valor actual de la secuencia y el tamaño del incremento. Elegí el className como clave única para que cada clase de entidad obtenga su propia secuencia.
El último paso y el más difícil es un PreInsertListener que maneja la asignación del número de secuencia. Tenga en cuenta que usé la primavera como contenedor de frijoles.
Como puede ver en el código anterior, el oyente utilizó una instancia de SequenceNumber por clase de entidad y se reserva un par de números de secuencia definidos por el incrementValue de la entidad SequenceNumber. Si se queda sin números de secuencia, carga la entidad SequenceNumber para la clase de destino y reserva los valores incrementValue para las próximas llamadas. De esta manera, no necesito consultar la base de datos cada vez que se necesita un valor de secuencia. Tenga en cuenta la sesión sin estado que se está abriendo para reservar el siguiente conjunto de números de secuencia. No puede usar la misma sesión en la que persiste la entidad de destino, ya que esto conduciría a una ConcurrentModificationException en EntityPersister.
Espero que esto ayude a alguien.
fuente
Si está utilizando postgresql
Y lo estoy usando en spring boot 1.5.6
fuente
seq_order
y hacer referencia desde el campo,nextval('seq_order'::regclass)
Corro en la misma situación que usted y tampoco encontré ninguna respuesta seria si es básicamente posible generar propiedades que no sean de identificación con JPA o no.
Mi solución es llamar a la secuencia con una consulta JPA nativa para configurar la propiedad a mano antes de persuadirla.
Esto no es satisfactorio, pero por el momento funciona como una solución alternativa.
Mario
fuente
Encontré esta nota específica en la sesión 9.1.9 Anotación GeneratedValue de la especificación JPA: "[43] Las aplicaciones portátiles no deben usar la anotación GeneratedValue en otros campos o propiedades persistentes". Por lo tanto, supongo que no es posible generar automáticamente un valor para valores de clave no primarios, al menos utilizando simplemente JPA.
fuente
Parece que el hilo es viejo, solo quería agregar mi solución aquí (Uso de AspectJ - AOP en primavera).
La solución es crear una anotación personalizada de la
@InjectSequenceValue
siguiente manera.Ahora puede anotar cualquier campo en la entidad, de modo que el valor del campo subyacente (Largo / Entero) se inyectará en tiempo de ejecución utilizando el siguiente valor de la secuencia.
Anotar así.
Hasta ahora hemos marcado el campo que necesitamos para inyectar el valor de la secuencia, así que veremos cómo inyectar el valor de la secuencia en los campos marcados, esto se hace creando el corte de punto en AspectJ.
Activaremos la inyección justo antes de
save/persist
que se ejecute el método. Esto se hace en la siguiente clase.Ahora puede anotar cualquier entidad de la siguiente manera.
fuente
"No quiero usar un activador ni ninguna otra cosa que no sea Hibernate para generar el valor de mi propiedad"
En ese caso, ¿qué tal crear una implementación de UserType que genere el valor requerido y configurar los metadatos para usar ese UserType para la persistencia de la propiedad mySequenceVal?
fuente
Esto no es lo mismo que usar una secuencia. Al usar una secuencia, no está insertando ni actualizando nada. Simplemente está recuperando el siguiente valor de secuencia. Parece que hibernate no lo admite.
fuente
Si tiene una columna con el tipo UNIQUEIDENTIFIER y la generación predeterminada es necesaria en la inserción pero la columna no es PK
En db tendrás
En este caso no definirá generador para un valor que necesita (será automáticamente gracias a
columnDefinition="UNIQUEIDENTIFIER"
). Lo mismo que puedes probar para otros tipos de columnafuente
He encontrado una solución para esto en las bases de datos MySql usando @PostConstruct y JdbcTemplate en una aplicación Spring. Puede ser factible con otras bases de datos, pero el caso de uso que presentaré se basa en mi experiencia con MySql, ya que utiliza auto_increment.
Primero, intenté definir una columna como auto_increment usando la propiedad ColumnDefinition de la anotación @Column, pero no funcionaba ya que la columna necesitaba ser una clave para ser incremental automático, pero aparentemente la columna no se definiría como un índice hasta después de que se definió, causando un punto muerto.
Aquí es donde llegué con la idea de crear la columna sin la definición de auto_increment y agregarla después de crear la base de datos. Esto es posible utilizando la anotación @PostConstruct, que hace que se invoque un método justo después de que la aplicación haya inicializado los beans, junto con el método de actualización de JdbcTemplate.
El código es el siguiente:
En mi entidad:
En una clase PostConstructComponent:
fuente
Quiero proporcionar una alternativa junto a la solución aceptada de @Morten Berg, que funcionó mejor para mí.
Este enfoque permite definir el campo con el
Number
tipo realmente deseado ,Long
en mi caso de uso, en lugar deGeneralSequenceNumber
. Esto puede ser útil, por ejemplo, para la (des) serialización JSON.La desventaja es que requiere un poco más de sobrecarga de la base de datos.
Primero, necesitamos un
ActualEntity
en el que queremos auto-incrementogenerated
de tipoLong
:A continuación, necesitamos una entidad auxiliar
Generated
. Lo coloqué junto al paquete privadoActualEntity
para mantenerlo como un detalle de implementación del paquete:Finalmente, necesitamos un lugar para conectar justo antes de guardar el
ActualEntity
. Allí, creamos y persistimos unaGenerated
instancia. Esto proporciona una secuencia de base de datos generadaid
de tipoLong
. Hacemos uso de este valor escribiéndolo enActualEntity.generated
.En mi caso de uso, implementé esto usando un Spring Data REST
@RepositoryEventHandler
, que se llama justo antesActualEntity
de que persista el get. Debe demostrar el principio:No lo probé en una aplicación de la vida real, así que disfruta con cuidado.
fuente
He estado en una situación como usted (secuencia JPA / Hibernate para el campo que no es @Id) y terminé creando un disparador en mi esquema db que agrega un número de secuencia único en la inserción. Nunca lo hice funcionar con JPA / Hibernate
fuente
Después de pasar horas, esto me ayudó a resolver mi problema:
Para Oracle 12c:
Para H2:
También haz:
fuente