2010-09-03 2 views
6

Ich versuche, zwei Objekte mithilfe einer ManyToMany Zuordnung zuzuordnen, aber aus irgendeinem Grund scheint Hibernate verwirrt zu werden, wenn ich die Eigenschaft mappedBy verwende über genau das, was ich kartiere. Das einzig Seltsame an meiner Zuordnung ist, dass die Zuordnung in einem Primärschlüsselfeld in einem der Einträge nicht erfolgt (das Feld ist jedoch eindeutig).Hibernate schlägt fehl, indem der Name der Eigenschaft Fully qualified für den Namen der Eigenschaft ManyToMany-Verknüpfung vorangestellt wird

Die Tische sind:

Sequence (
    id NUMBER, 
    reference VARCHAR, 
) 

Project (
    id NUMBER 
) 

Sequence_Project (
    proj_id number references Project(id), 
    reference varchar references Sequence(reference) 
) 

Die Objekte aussehen (Anmerkungen auf dem Getter sind, setzen sie auf den Feldern etwas zu kondensieren):

class Sequence { 
    @Id 
    private int id; 

    private String reference; 

    @ManyToMany(mappedBy="sequences") 
    private List<Project> projects; 
} 

Und die besitzende Seite:

class Project { 
    @Id 
    private int id; 

    @ManyToMany 
    @JoinTable(name="sequence_project", 
       [email protected](name="id"), 
       [email protected](name="reference", 
            referencedColumnName="reference")) 
    private List<Sequence> sequences; 
} 

Dieser schlägt mit einem MappingException:

Immobilien-ref [_test_local_entities_Project_sequences] nicht auf Einheit gefunden [test.local.entities.Project]

Es scheint, den vollständig qualifizierten Klassennamen von Unterstrichen geteilt weirdly Zierer. Wie kann ich das vermeiden?

EDIT: Ich spielte mit diesem ein bisschen mehr. Ändern des Namens des mappedBy Eigenschaft eine andere Ausnahme ausgelöst, und zwar:

org.hibernate.AnnotationException: mappedBy Referenz eine unbekannte Zieleinheit Eigenschaft: test.local.entities.Project.sequences

So Die Annotation wird korrekt verarbeitet, aber irgendwie wird die Eigenschaftsreferenz der internen Konfiguration von Hibernate nicht korrekt hinzugefügt.

+0

mag wie eine SQ scheinen aber hat Projekt öffentliche getSequences und öffentlichen setSequences auf sie? – Affe

+0

@Affe Ich werde doublecheck, ich denke derzeit ist es öffentlich/geschützt. – wds

+0

Nur für den Fall, was passiert, wenn Sie die Eigenschaft 'sequences' in etwas anderes umbenennen, z. 'foo'? –

Antwort

3

Ich habe das gleiche Szenario von Ihrer Frage vorgeschlagen. Und wie erwartet bekomme ich die selbe Ausnahme. Nur als ergänzende Aufgabe, habe ich das gleiche Szenario aber mit Eins-zu-viele viele-zu-eins mit einem Nicht-Primärschlüssel als verbundene Spalte wie Referenz getan. Ich bekomme jetzt

SecondaryTable JoinColumn kann nicht Referenz ein nicht Primärschlüssel

Nun, es kann ein Fehler sein ??? Nun ja (und Ihre Workaround funktioniert gut (+1)). Wenn Sie einen Nicht-Primärschlüssel als Primärschlüssel verwenden möchten, müssen Sie sicherstellen, es ist eindeutig. Vielleicht erklärt es, warum Hibernate es nicht erlaubt, einen Nicht-Primärschlüssel als Primärschlüssel zu verwenden (Nicht-Benutzer können unerwartetes Verhalten erhalten).

Wenn Sie die gleiche Zuordnung verwenden möchten, können Sie Ihre @ManyToMany Beziehung in @ OneToMany-ManyToOne Durch die Verwendung von Verkapselung aufgeteilt, brauchen Sie nicht über Ihre Klasse verbunden

Projekt

zu kümmern
@Entity 
public class Project implements Serializable { 

    @Id 
    @GeneratedValue 
    private Integer id; 

    @OneToMany(mappedBy="project") 
    private List<ProjectSequence> projectSequenceList = new ArrayList<ProjectSequence>(); 

    @Transient 
    private List<Sequence> sequenceList = null; 

    // getters and setters 

    public void addSequence(Sequence sequence) { 
     projectSequenceList.add(new ProjectSequence(new ProjectSequence.ProjectSequenceId(id, sequence.getReference()))); 
    } 

    public List<Sequence> getSequenceList() { 
     if(sequenceList != null) 
      return sequenceList; 

     sequenceList = new ArrayList<Sequence>(); 
     for (ProjectSequence projectSequence : projectSequenceList) 
      sequenceList.add(projectSequence.getSequence()); 

     return sequenceList; 
    } 

} 

Sequenz

@Entity 
public class Sequence implements Serializable { 

    @Id 
    private Integer id; 
    private String reference; 

    @OneToMany(mappedBy="sequence") 
    private List<ProjectSequence> projectSequenceList = new ArrayList<ProjectSequence>(); 

    @Transient 
    private List<Project> projectList = null; 

    // getters and setters 

    public void addProject(Project project) { 
     projectSequenceList.add(new ProjectSequence(new ProjectSequence.ProjectSequenceId(project.getId(), reference))); 
    } 

    public List<Project> getProjectList() { 
     if(projectList != null) 
      return projectList; 

     projectList = new ArrayList<Project>(); 
     for (ProjectSequence projectSequence : projectSequenceList) 
      projectList.add(projectSequence.getProject()); 

     return projectList; 
    } 

} 

ProjectSequence

@Entity 
public class ProjectSequence { 

    @EmbeddedId 
    private ProjectSequenceId projectSequenceId; 

    @ManyToOne 
    @JoinColumn(name="ID", insertable=false, updatable=false) 
    private Project project; 

    @ManyToOne 
    @JoinColumn(name="REFERENCE", referencedColumnName="REFERENCE", insertable=false, updatable=false) 
    private Sequence sequence; 

    public ProjectSequence() {} 
    public ProjectSequence(ProjectSequenceId projectSequenceId) { 
     this.projectSequenceId = projectSequenceId; 
    } 

    // getters and setters 

    @Embeddable 
    public static class ProjectSequenceId implements Serializable { 

     @Column(name="ID", updatable=false) 
     private Integer projectId; 

     @Column(name="REFERENCE", updatable=false) 
     private String reference; 

     public ProjectSequenceId() {} 
     public ProjectSequenceId(Integer projectId, String reference) { 
      this.projectId = projectId; 
      this.reference = reference; 
     } 

     @Override 
     public boolean equals(Object o) { 
      if (!(o instanceof ProjectSequenceId)) 
       return false; 

      final ProjectSequenceId other = (ProjectSequenceId) o; 
      return new EqualsBuilder().append(getProjectId(), other.getProjectId()) 
             .append(getReference(), other.getReference()) 
             .isEquals(); 
     } 

     @Override 
     public int hashCode() { 
      return new HashCodeBuilder().append(getProjectId()) 
             .append(getReference()) 
             .hashCode(); 
     } 

    } 

} 
+0

das ist genau das was ich gemacht habe. Ich akzeptiere das, weil der Code anderen helfen könnte. :-) – wds

+0

@wds Danke! Ich hoffe, es kann nützlich sein –

2

Ich habe es endlich herausgefunden, mehr oder weniger. Ich denke, das ist im Grunde ein Winterschlaffehler.

edit: Ich habe versucht, es zu beheben, indem Sie die besitzende Seite des Vereins zu ändern:

class Sequence { 
    @Id 
    private int id; 

    private String reference; 

    @ManyToMany 
    @JoinTable(name="sequence_project", 
      [email protected](name="id"), 
      [email protected](name="reference", 
         referencedColumnName="reference")) 
    private List<Project> projects; 
} 

class Project { 
    @Id 
    private int id; 

    @ManyToMany(mappedBy="projects") 
    private List<Sequence> sequences; 
} 

Das funktionierte aber verursachte Probleme an anderer Stelle (siehe Kommentar). Also gab ich auf und modellierte die Assoziation als eine Entität mit Eins-zu-Eins-Assoziationen in Sequenz und Projekt. Ich denke, das ist zumindest ein Dokumentations-/Fehlerbehandlungsfehler (die Ausnahme ist nicht sehr relevant und der Fehlermodus ist einfach falsch) und wird versuchen, es den Hibernate-Entwicklern zu melden.

+0

Eine weitere Überprüfung ergab, dass dies nicht funktionierte, da Hibernate jetzt versucht, der String-Spalte in der Zuordnungstabelle mit der ID-Spalte der Entität beizutreten. Es scheint so einfach zu sein, irgendwie unmöglich. – wds

0

IMHO was Sie versuchen zu erreichen ist nicht möglich mit JPA/Hibernate Annotationen. Leider ist die APIDoc von JoinTable hier ein wenig unklar, aber alle Beispiele, die ich fand, verwenden Primärschlüssel beim Mapping von Join-Tabellen.

Wir hatten das gleiche Problem wie Sie in einem Projekt, in dem wir auch das alte Datenbankschema nicht ändern konnten. Die einzige praktikable Option war es, Hibernate zu löschen und MyBatis (http://www.mybatis.org) zu verwenden, wo Sie die volle Flexibilität von nativem SQL haben, um komplexere Join-Bedingungen auszudrücken.

+0

Ich denke, Sie könnten Recht haben.Tatsächlich kann ich einen Teil des Schemas ändern, also könnte ich theoretisch die Join-Tabelle wiederholen, um IDs zu verwenden, aber es wäre noch mehr Arbeit und so habe ich am Ende nur einen Workaround benutzt, der jetzt zu funktionieren scheint. – wds

0

ich laufe in dieses Problem ein Dutzend Mal jetzt und die einzige Abhilfe, die ich gefunden macht die Konfiguration der @JoinTable zweimal mit vertauschten Spalten auf der anderen Seite der Beziehung:

class Sequence { 

    @Id 
    private int id; 

    private String reference; 

    @ManyToMany 
    @JoinTable(
     name = "sequence_project", 
     joinColumns = @JoinColumn(name="reference", referencedColumnName="reference"), 
     inverseJoinColumns = @JoinColumn(name="id") 
    ) 
    private List<Project> projects; 

} 

class Project { 

    @Id 
    private int id; 

    @ManyToMany 
    @JoinTable(
     name = "sequence_project", 
     joinColumns = @JoinColumn(name="id"), 
     inverseJoinColumns = @JoinColumn(name="reference", referencedColumnName="reference") 
    ) 
    private List<Sequence> sequences; 

} 

ich noch nicht versucht, es mit einer anderen Spalte als der Primärschlüssel.

+0

Sie definieren hier zwei unidirektionale Viele-zu-Viele-Assoziationen, das ist semantisch sehr unterschiedlich (und ich bin mir nicht einmal sicher, dass alles richtig funktionieren würde). –

+0

Sie sind eigentlich eine Relation, weil ich die gleiche Join-Tabelle und die gleichen Join-Spalten (nur geschaltet) verwende. – whiskeysierra

Verwandte Themen