2010-09-03 9 views
6

Sto cercando di mappare due oggetti tra loro utilizzando un'associazione ManyToMany, ma per qualche ragione quando uso la proprietà mappedBy, Hibernate sembra essere sempre confuso su esattamente quello che sto mappando. L'unica cosa strana della mia mappatura qui è che l'associazione non viene eseguita su un campo chiave primaria in una delle voci (il campo è però unico).Hibernate in mancanza anteponendo nome completo della classe a nome della proprietà riguardante l'associazione ManyToMany

Le tabelle sono:

Sequence (
    id NUMBER, 
    reference VARCHAR, 
) 

Project (
    id NUMBER 
) 

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

Gli oggetti assomigliano (annotazioni sono sul getter, metterli sui campi di condensare un po '):

class Sequence { 
    @Id 
    private int id; 

    private String reference; 

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

E il lato possessore:

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

Questo non riesce con un MappingException:

012.

property-ref [_test_local_entities_Project_sequences] non trovato sul entità [test.local.entities.Project]

Sembra di anteporre stranamente il nome completo della classe, divise da caratteri di sottolineatura. Come posso evitare che ciò accada?

MODIFICA: Ho giocato un po 'di più con questo. Cambiare il nome della proprietà mappedBy genera un'eccezione diversa, vale a dire:

org.hibernate.AnnotationException: mappedBy riferimento a un ignoto proprietà entità di destinazione: test.local.entities.Project.sequences

Così l'annotazione viene elaborata correttamente, ma in qualche modo il riferimento alla proprietà non viene aggiunto correttamente alla configurazione interna di Hibernate.

+0

può sembrare un SQ, ma fa del progetto hanno getSequences pubblici e setSequences pubbliche su di esso? – Affe

+0

@Affe I'll doubleckeck, penso che al momento sia pubblico/protetto. – wds

+0

Nel caso, cosa succede se si rinomina la proprietà 'sequences' in qualcos'altro, ad es. 'Foo'? –

risposta

3

Ho fatto lo stesso scenario proposto dalla domanda. E, come previsto, ottengo la stessa eccezione. Proprio come compito complementare, ho fatto lo stesso scenario ma con uno-a-molti molti a uno utilizzando una chiave non primaria come la colonna unita come riferimento. Ottengo ora

SecondaryTable JoinColumn non riferimento un non chiave primaria

Beh, può essere un bug può ??? Bene, sì (e la soluzione alternativa funziona correttamente (+1)). Se si desidera utilizzare una chiave non primaria come chiave primaria, è necessario assicurarsi che sia univoco. Forse spiega perché Hibernate non consente di utilizzare la chiave non primaria come chiave primaria (gli utenti non noti possono ottenere comportamenti imprevisti).

Se si desidera utilizzare la stessa mappatura, è possibile dividere il vostro rapporto @ManyToMany in @ OneToMany-ManyToOne Utilizzando incapsulamento, non c'è bisogno di preoccuparsi per la vostra classe unito

Progetto

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

} 

Sequenza

@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

in realtà è esattamente quello che ho fatto. Lo accetto perché il codice potrebbe aiutare gli altri. :-) – wds

+0

@ wds Grazie! Spero possa essere utile –

2

Finalmente l'ho capito, più o meno. Penso che questo sia fondamentalmente un bug ibernato.

edit: ho cercato di risolvere il problema cambiando il lato possessore dell'associazione:

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

Questo ha funzionato, ma ha causato problemi altrove (vedi commento). Così ho rinunciato e modellato l'associazione come entità con molte associazioni in Sequenza e Progetto. Penso che questo sia per lo meno un bug di documentazione/gestione degli errori (l'eccezione non è molto pertinente, e la modalità di errore è semplicemente sbagliata) e proverà a segnalarlo agli sviluppatori di Hibernate.

+0

Un ulteriore esame ha rivelato che questo non ha funzionato, perché ora hibernate sta provando ad unirsi alla colonna String nella tabella di associazione con la colonna id dell'entità. Sembra che questo semplice sia impossibile da fare, in qualche modo. – wds

0

IMHO ciò che si sta tentando di ottenere non è possibile con le annotazioni JPA/Hibernate. Sfortunatamente, l'APIDoc di JoinTable è un po 'poco chiaro qui, ma tutti gli esempi che ho trovato utilizzano le chiavi primarie durante il mapping delle tabelle di join.

Abbiamo avuto lo stesso problema come te in un progetto in cui non è stato possibile modificare lo schema del database precedente. L'unica opzione praticabile era di scaricare Hibernate e usare MyBatis (http://www.mybatis.org) dove si ha la piena flessibilità di SQL nativo per esprimere condizioni di join più complesse.

+0

Penso che potresti avere ragione.In effetti posso cambiare parte dello schema, quindi in teoria potrei rifare la tabella di join per usare gli ID, ma sarebbe ancora più lavoro e così ho finito usando solo una soluzione che sembra funzionare per ora. – wds

0

mi imbatto in questo problema una dozzina di volte e l'unica soluzione che ho trovato è facendo la configurazione del @JoinTable due volte con colonne scambiati al di là del rapporto:

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; 

} 

non ho ancora provato con una colonna diversa dalla chiave primaria.

+0

Qui stai definendo due associazioni unidirezionali molti-a-molti, questo è semanticamente molto diverso (e non sono nemmeno sicuro che tutto funzioni correttamente). –

+0

Sono in realtà una relazione perché io uso la stessa tabella di join e le stesse colonne di join (appena cambiate). – whiskeysierra

Problemi correlati