2010-09-03 18 views
6

Estoy tratando de asignar dos objetos entre sí mediante una asociación ManyToMany, pero por alguna razón cuando uso la propiedad mappedBy, hibernate parece estar confundido sobre exactamente lo que estoy mapeando. Lo único extraño de mi mapeo aquí es que la asociación no se realiza en un campo de clave principal en una de las entradas (aunque el campo es único).Hibernate falla al anteponer nombre de clase completamente calificado al nombre de propiedad en asociación ManyToMany

Las tablas son:

Sequence (
    id NUMBER, 
    reference VARCHAR, 
) 

Project (
    id NUMBER 
) 

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

Los objetos parecen (anotaciones están en el captador, ponerlos en campos de condensar un poco):

class Sequence { 
    @Id 
    private int id; 

    private String reference; 

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

Y el lado propietario:

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

Esto falla con una excepción de mapeo:

property-ref [_test_local_entities_Project_sequences] no se encuentra en la entidad [test.local.entities.Project]

Parece extrañamente anteponer el nombre de clase completo, dividido por guiones. ¿Cómo puedo evitar que esto suceda?

EDITAR: He jugado con esto un poco más. Cambiar el nombre de la propiedad mappedBy emite una excepción diferente, a saber:

org.hibernate.AnnotationException: Referencia mappedBy un desconocido propiedad de entidad de destino: test.local.entities.Project.sequences

Así la anotación se procesa correctamente, pero de alguna manera la referencia de propiedad no se agrega correctamente a la configuración interna de Hibernate.

+0

puede parecer una SQ pero ¿tienen Proyecto getSequences públicas y setSequences públicas en él? – Affe

+0

@Affe Revisaré dos veces, creo que actualmente es público/protegido. – wds

+0

Por las dudas, ¿qué ocurre si cambia el nombre de la propiedad 'secuencias' a otra cosa, p. 'foo'? –

Respuesta

3

He hecho el mismo escenario propuesto por su pregunta. Y, como era de esperar, recibo la misma excepción. Solo como tarea complementaria, hice el mismo escenario pero con uno a muchos muchos uno a uno usando una clave no primaria como columna unida como referencia. Me sale ahora

SecondaryTable JoinColumn no referencia una clave primaria no

Bueno, puede ser un error puede ??? Bueno, sí (y su solución funciona bien (+1)). Si desea utilizar una clave no primaria como clave principal, debe asegurarse de que sea único. Quizás explica por qué Hibernate no permite usar claves no primarias como claves principales (los usuarios no conscientes pueden tener comportamientos inesperados).

Si desea utilizar la misma asignación, puede dividir su relación @ManyToMany en @ OneToMany-ManyToOne Mediante el uso de encapsulación, usted no tiene que preocuparse acerca de su clase se unió

Proyecto

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

} 

Secuencia

@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

en realidad eso es exactamente lo que hice. Estoy aceptando esto porque el código podría ayudar a otros. :-) – wds

+0

@wds ¡Gracias! Espero que pueda ser útil –

2

Finalmente lo descubrí, más o menos. Creo que esto es básicamente un error de hibernación.

editar: He intentado solucionarlo cambiando el lado propietario de la asociación:

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

Esto funcionó pero causó problemas en otros lugares (ver comentario). Así que me di por vencido y modelé la asociación como una entidad con asociaciones de muchos a uno en Sequence and Project. Creo que esto es al menos un error de documentación/manejo de fallas (la excepción no es muy pertinente, y el modo de falla es simplemente incorrecto) e intentará informarlo a los desarrolladores de Hibernate.

+0

Un examen más detallado reveló que esto no funcionó, porque ahora hibernate está intentando unirse a la columna String en la tabla de asociación con la columna id de la entidad. Parece que esto simple es imposible de hacer, de alguna manera. – wds

0

En mi humilde opinión, lo que está tratando de lograr no es posible con las anotaciones JPA/Hibernate. Desafortunadamente, el APIDoc de JoinTable no está claro aquí, pero todos los ejemplos que encontré usan claves primarias al mapear tablas de unión.

Tuvimos el mismo problema que tú en un proyecto en el que tampoco pudimos cambiar el esquema de la base de datos heredada. La única opción viable era la de volcar Hibernate y usar MyBatis (http://www.mybatis.org) donde tiene la flexibilidad total de SQL nativo para expresar condiciones de unión más complejas.

+0

Creo que tienes razón.De hecho, puedo cambiar parte del esquema, así que en teoría podría volver a hacer la tabla de unión para usar ID, pero sería aún más trabajo y terminé usando una solución alternativa que parece funcionar por el momento. – wds

0

me encuentro con este problema de una docena de veces y la única solución que he encontrado es haciendo la configuración de la @JoinTable dos veces con columnas intercambiadas en el otro lado de la relación:

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; 

} 

yo aún no probado con una columna diferente de la clave principal.

+0

Aquí está definiendo dos asociaciones unidireccionales de muchos a muchos, esto es semánticamente muy diferente (y ni siquiera estoy seguro de que todo funcione correctamente). –

+0

En realidad, son una relación porque utilizo la misma tabla de unión y las mismas columnas de unión (recién conmutadas). – whiskeysierra

Cuestiones relacionadas