java - Infracción de restricciones en Hibernate. El mapeo unidireccional de OneToMany con JoinTable y OrderColumn al eliminar elementos.
jpa jpa-2.0 (4)
Tengo un problema al eliminar elementos de una lista asignada como se describe anteriormente. Aquí está el mapeo:
@Entity @Table( name = "foo") class Foo { private List bars; @OneToMany @OrderColumn( name = "order_index" ) @JoinTable( name = "foo_bar_map", joinColumns = @JoinColumn( name = "foo_id" ), inverseJoinColumns = @JoinColumn( name = "bar_id" ) ) @Fetch( FetchMode.SUBSELECT ) public List getBars() { return bars; } }
Insertar instancias de barra y guardar el Foo funciona bien, pero cuando elimino un elemento de la lista y vuelvo a guardar, se viola la restricción única en bar_id en la tabla de mapeo. Las siguientes sentencias de SQL se emiten por hibernación, y estas parecen bastante extrañas:
LOG: execute : delete from foo_bar_map where foo_id=$1 and order_index=$2 DETAIL: parameters: $1 = ''4'', $2 = ''6'' LOG: execute S_5: update foo_bar_map set bar_id=$1 where foo_id=$2 and order_index=$3 DETAIL: parameters: $1 = ''88'', $2 = ''4'', $3 = ''0'' ERROR: duplicate key value violates unique constraint "foo_bar_map_bar_id_key"
El error tiene mucho sentido, dadas las declaraciones generadas por Hibernate (hay cinco elementos en la lista, elimino el primero y Hibernate elimina la fila de mapeo con el último índice y trata de actualizar los restantes, comenzando con el primero) .
¿Qué hay de malo con la asignación de arriba?
No, no creo que sea un error de hibernación, y como verá si busca este error de hibernación citado por Pascal Thivent es un error conocido desde 2006 y nunca se ha solucionado.
Por qué ?
Porque creo que el problema está en las restricciones de la tabla y no en hibernación.
No entiendo por qué hay una restricción única en bar_id
Usar un índice de orden significa que su colección es una Lista (y no un Conjunto). Y una Lista es una colección donde puede especificar el índice de los elementos que agregue (corresponde a OrderColumn).
La diferencia entre una Lista y un Conjunto es que usted y los mismos datos pueden dos veces (o más), pero los mismos datos estarán en índices diferentes. Entonces, puede tener el mismo bar_id en un índice diferente, no tiene que especificar una restricción única en bar_id. Y la clave principal no puede ser (foo_id, order_index) porque la Lista de patrones autoriza los mismos datos en diferentes índices. Tal vez tu PK debería ser (foo_id, bar_id, order_index)?
Creo que el problema es de esta manera :)
Por lo general, cuando se une a través de una tabla de unión, la relación es ManyToMany no OneToMany. Prueba esto
@ManyToMany
@OrderColumn( name = "order_index" )
@JoinTable( name = "foo_bar_map", joinColumns = @JoinColumn( name = "foo_id" ), inverseJoinColumns = @JoinColumn( name = "bar_id" ) )
@Fetch( FetchMode.SUBSELECT )
public List getBars() {
return bars;
}
Supongo que lo que necesitas es el mapeo inverso correcto.
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/tutorial.html#tutorial-associations-bidirectional
Su asignación es totalmente válida y funciona con EclipseLink como implementación JPA 2.0 (sin la anotación Fetch
por supuesto), pero de hecho falla con Hibernate.
Aquí está el DDL con Hibernate:
create table foo_bar_map (foo_id bigint not null, bar_id bigint not null, order_index integer not null, primary key (foo_id, order_index), unique (bar_id))
alter table foo_bar_map add constraint FK14F1CB7FA042E82 foreign key (bar_id) references Bar4022509
alter table foo_bar_map add constraint FK14F1CB7B6DBCCDC foreign key (foo_id) references Foo4022509
Así que digamos que Foo#1
tiene una lista con la Bar#1
, Bar#2
, Bar#3
, la tabla de unión contiene:
foo_id | bar_id | order_index
1 | 1 | 1
1 | 2 | 2
1 | 3 | 3
Al eliminar, diga el primer elemento de la lista, Hibernate primero delete
la última fila (WTF?) De la tabla de unión:
foo_id | bar_id | order_index
1 | 1 | 1
1 | 2 | 2
Y luego intenta update
la columna bar_id
en la tabla de unión en lugar de order_index
(WTF !?) para reflejar el orden "nuevo" de los elementos en la lista. Primero (esquemáticamente):
foo_id | bar_id | order_index
1 | 2 | 1
1 | 2 | 2
donde el siguiente paso resultaría en:
foo_id | bar_id | order_index
1 | 2 | 1
1 | 3 | 2
Obviamente, este enfoque no suena bien y no funciona debido a la restricción unique
en bar_id
. Más generalmente, ¿por qué diablos Hibernate se bar_id
con el bar_id
lugar de actualizar la columna order_index
?
Considero que esto es un error de Hibernate (reportado como HHH-5694 , vea HHH-1268 ahora).