2012-06-01 39 views
20

Tengo dos entidades a las que me gustaría unirme a través de varias columnas. Estas columnas se comparten con un objeto @Embeddable que comparten ambas entidades. En el ejemplo siguiente, Foo puede tener solo un Bar pero Bar puede tener varios Foo s (donde AnEmbeddableObject es una clave única para Bar). Aquí está un ejemplo:Columna múltiple Unir en Hibernate/JPA Anotaciones

@Entity 
@Table(name = "foo") 
public class Foo { 
    @Id 
    @Column(name = "id") 
    @GeneratedValue(generator = "seqGen") 
    @SequenceGenerator(name = "seqGen", sequenceName = "FOO_ID_SEQ", allocationSize = 1) 
    private Long id; 
    @Embedded 
    private AnEmbeddableObject anEmbeddableObject; 
    @ManyToOne(targetEntity = Bar.class, fetch = FetchType.LAZY) 
    @JoinColumns({ 
     @JoinColumn(name = "column_1", referencedColumnName = "column_1"), 
     @JoinColumn(name = "column_2", referencedColumnName = "column_2"), 
     @JoinColumn(name = "column_3", referencedColumnName = "column_3"), 
     @JoinColumn(name = "column_4", referencedColumnName = "column_4") 
    }) 
    private Bar bar; 

    // ... rest of class 
} 

Y la clase de barras:

@Entity 
@Table(name = "bar") 
public class Bar { 
    @Id 
    @Column(name = "id") 
    @GeneratedValue(generator = "seqGen") 
    @SequenceGenerator(name = "seqGen", sequenceName = "BAR_ID_SEQ", allocationSize = 1) 
    private Long id; 
    @Embedded 
    private AnEmbeddableObject anEmbeddableObject; 

    // ... rest of class 
} 

Finalmente el AnEmbeddedObject clase:

@Embeddable 
public class AnEmbeddedObject { 
    @Column(name = "column_1") 
    private Long column1; 
    @Column(name = "column_2") 
    private Long column2; 
    @Column(name = "column_3") 
    private Long column3; 
    @Column(name = "column_4") 
    private Long column4; 

    // ... rest of class 
} 

Obviamente el esquema es poco normalizada, es una restricción que AnEmbeddedObject ' s campos se repiten en cada tabla.

El problema que tengo es que recibo este error cuando intento para poner en marcha Hibernate:

org.hibernate.AnnotationException: referencedColumnNames(column_1, column_2, column_3, column_4) of Foo.bar referencing Bar not mapped to a single property 

He intentado marcar los JoinColumns no son insertables y actualizable, pero sin suerte. ¿Hay alguna manera de expresar esto con las anotaciones de Hibernate/JPA?

+0

¿Qué sucede si elimina la incrustación de 'Foo'? – siebz0r

Respuesta

9

Si esto no funciona, me quedo sin ideas. De esta manera usted obtiene las 4 columnas en ambas tablas (como Bar las posee y Foo las usa para hacer referencia a Bar) y los ID generados en ambas entidades. El conjunto de 4 columnas debe ser único en Bar, por lo que la relación muchos a uno no se convierte en muchos.

@Embeddable 
public class AnEmbeddedObject 
{ 
    @Column(name = "column_1") 
    private Long column1; 
    @Column(name = "column_2") 
    private Long column2; 
    @Column(name = "column_3") 
    private Long column3; 
    @Column(name = "column_4") 
    private Long column4; 
} 

@Entity 
public class Foo 
{ 
    @Id 
    @Column(name = "id") 
    @GeneratedValue(generator = "seqGen") 
    @SequenceGenerator(name = "seqGen", sequenceName = "FOO_ID_SEQ", allocationSize = 1) 
    private Long id; 
    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumns({ 
     @JoinColumn(name = "column_1", referencedColumnName = "column_1"), 
     @JoinColumn(name = "column_2", referencedColumnName = "column_2"), 
     @JoinColumn(name = "column_3", referencedColumnName = "column_3"), 
     @JoinColumn(name = "column_4", referencedColumnName = "column_4") 
    }) 
    private Bar bar; 
} 

@Entity 
@Table(uniqueConstraints = @UniqueConstraint(columnNames = { 
    "column_1", 
    "column_2", 
    "column_3", 
    "column_4" 
})) 
public class Bar 
{ 
    @Id 
    @Column(name = "id") 
    @GeneratedValue(generator = "seqGen") 
    @SequenceGenerator(name = "seqGen", sequenceName = "BAR_ID_SEQ", allocationSize = 1) 
    private Long id; 
    @Embedded 
    private AnEmbeddedObject anEmbeddedObject; 
} 
+0

Ese es el problema, el "AnEmbeddedObject" debe existir en ambos objetos. La identificación generada es preferible por razones de rendimiento. – bowsie

+0

¿Entonces las 4 columnas más la clave generada se asignan a la barra? Eso es muy muy malo.Si su solución es generar una clave sustituta para 'Bar' y no hacer las columnas en la tecla externa' Foo', usted tiene que establecer las columnas en 'Foo' usted mismo. Si estoy en lo cierto, puedo ajustar mi respuesta. ;-) – siebz0r

+0

@bowsie Modifiqué mi respuesta por lo que 'Foo' y' Bar' tienen las 4 columnas y 'Bar' se referencia en' Foo' con su ID. – siebz0r

4

Hibernate no le va a facilitar hacer lo que está tratando de hacer. Desde Hibernate documentation:

Tenga en cuenta que cuando utiliza referenciaColumnName a una columna de clave no primaria, la clase asociada debe ser Serializable. También tenga en cuenta que el nombre de columna referenciado a una columna de clave no primaria se debe asignar a una propiedad que tiene una sola columna (otros casos podrían no funcionar).(énfasis añadido)

Así que si no están dispuestos a hacer AnEmbeddableObject El identificador de bar, entonces Hibernate no va a perezosamente, recuperar automáticamente bar para ti. Puede, por supuesto, seguir usando HQL para escribir consultas que se unan en AnEmbeddableObject, pero pierde la recuperación automática y el mantenimiento del ciclo de vida si insiste en usar una clave no primaria de múltiples columnas para Bar.

+0

Diría que vale la pena intentarlo; en teoría, debería ser posible utilizar varias claves externas que hacen referencia a una restricción única. Si no es el OP no tiene suerte. +1 para esto es posiblemente la respuesta. – siebz0r

+0

@ siebz0r, el OP ha tomado la fotografía y ha obtenido casi exactamente el mensaje de error que esperaría para esta condición de error: "referenciadosColumnNames ... *** no asignados a una sola propiedad ***". Debería decir "no asignado a una propiedad con una sola columna", pero es más o menos lo mismo. –

+0

Supongo que es un juego entonces ;-) – siebz0r

10

Esto funcionó para mí. En mi caso 2 tablas foo y boo tienen que unirse sobre 3 columnas diferentes. Tenga en cuenta que en mi caso, en boo las 3 columnas comunes no son clave principal

es decir, mapeo uno a uno basado en 3 columnas diferentes

@Entity 
@Table(name = "foo") 
public class foo implements Serializable 
{ 
    @Column(name="foocol1") 
    private String foocol1; 
    //add getter setter 
    @Column(name="foocol2") 
    private String foocol2; 
    //add getter setter 
    @Column(name="foocol3") 
    private String foocol3; 
    //add getter setter 
    private Boo boo; 
    private int id; 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "brsitem_id", updatable = false) 
    public int getId() 
    { 
     return this.id; 
    } 
    public void setId(int id) 
    { 
     this.id = id; 
    } 
    @OneToOne 
    @JoinColumns(
    { 
     @JoinColumn(updatable=false,insertable=false, name="foocol1", referencedColumnName="boocol1"), 
     @JoinColumn(updatable=false,insertable=false, name="foocol2", referencedColumnName="boocol2"), 
     @JoinColumn(updatable=false,insertable=false, name="foocol3", referencedColumnName="boocol3") 
    } 
    ) 
    public Boo getBoo() 
    { 
     return boo; 
    } 
    public void setBoo(Boo boo) 
    { 
     this.boo = boo; 
    } 
} 





@Entity 
@Table(name = "boo") 
public class Boo implements Serializable 
{ 
    private int id; 
    @Column(name="boocol1") 
    private String boocol1; 
    //add getter setter 
    @Column(name="boocol2") 
    private String boocol2; 
    //add getter setter 
    @Column(name="boocol3") 
    private String boocol3; 
    //add getter setter 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "item_id", updatable = false) 
    public int getId() 
    { 
     return id; 
    } 
    public void setId(int id) 
    { 
     this.id = id; 
    } 
}