web-dev-qa-db-fra.com

Jointure à plusieurs colonnes dans les annotations Hibernate / JPA

J'ai deux entités que je voudrais joindre à travers plusieurs colonnes. Ces colonnes sont partagées par un @Embeddable objet partagé par les deux entités. Dans l'exemple ci-dessous, Foo ne peut avoir qu'un Bar mais Bar peut avoir plusieurs Foos (où AnEmbeddableObject est une clé unique pour Bar). Voici un exemple:

@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
}

Et la classe Bar:

@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
}

Enfin la classe AnEmbeddedObject:

@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
}

Evidemment le schéma est mal normalisé, c'est une restriction que les champs de AnEmbeddedObject soient répétés dans chaque table.

Le problème que j'ai est que je reçois cette erreur lorsque j'essaie de démarrer Hibernate:

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

J'ai essayé de marquer que les JoinColumns ne sont pas insérables et modifiables, mais sans succès. Existe-t-il un moyen d'exprimer cela avec des annotations Hibernate/JPA?

22
bowsie

Cela a fonctionné pour moi. Dans mon cas, 2 tableaux foo et boo doivent être joints sur la base de 3 colonnes différentes.Veuillez noter que dans mon cas, les 3 colonnes communes ne sont pas la clé primaire

c'est-à-dire un mappage un à un basé sur 3 colonnes différentes

@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;
    }
}
12
Mohammed Rafeeq

Si cela ne fonctionne pas, je suis à court d'idées. De cette façon, vous obtenez les 4 colonnes dans les deux tables (comme Bar les possède et Foo les utilise pour référencer Bar) et les ID générés dans les deux entités. L'ensemble de 4 colonnes doit être unique dans Bar pour que la relation plusieurs-à-un ne devienne pas un plusieurs-à-plusieurs.

@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;
}
10
siebz0r

Hibernate ne vous permettra pas de faire ce que vous essayez de faire facilement. De la documentation Hibernate :

Notez que lorsque vous utilisez referencedColumnName dans une colonne de clé non primaire, la classe associée doit être sérialisable. Notez également que le ReferencedColumnName à une colonne de clé non primaire doit être mappé à une propriété ayant une seule colonne (d'autres cas peuvent ne pas fonctionner). (italique ajouté)

Donc, si vous ne souhaitez pas faire de AnEmbeddableObject l'identifiant de la barre, Hibernate ne va pas paresseusement, récupérer automatiquement la barre pour vous. Bien sûr, vous pouvez toujours utiliser HQL pour écrire des requêtes qui se joignent à AnEmbeddableObject, mais vous perdez la récupération automatique et la maintenance du cycle de vie si vous insistez pour utiliser une clé non primaire à plusieurs colonnes pour Bar.

5
Old Pro