2012-02-16 13 views
5

Tengo una situación bastante única al tratar de asignar una sola tabla a varias entidades en JPA. He leído sobre @Embeddable y @ElementCollection, pero no estoy seguro de cómo usarlos en mi situación (o si puedo). La tabla de una base de datos contiene información del curso. Puede haber filas en la tabla donde todo en el curso sea el mismo excepto por algunos valores, como el número de habitación y el día. Por ejemplo:Asignación de una sola tabla a la colección incrustable en JPA

TERM_CODE SUBJECT_CODE ROOM DAY INSTRUCTOR_ID 
201220  EGRE   0101 TR  123 
201220  EGRE   0103 W  124 

¿Hay alguna manera de que pueda extraer datos de dos filas que el anterior, y poner los datos comunes en un objeto y los diferentes valores en una colección de objetos separados? Aquí está un ejemplo de cómo me gustaría tener las clases definidas:

@Entity 
public class Course implements Serializable { 

    @Id 
    @Column(name = "TERM_CODE") 
    private Long termCode; 

    @Column(name = "SUBJECT_CODE") 
    private String subjectCode; 

    @Embedded 
    Collection<CourseSchedule> schedule; 

    public Long getTermCode() { 
    return termCode; 
    } 

    public void setTermCode(Long termCode) { 
    this.termCode = termCode; 
    } 

    public String getSubjectCode() { 
    return subjectCode; 
    } 

    public void setSubjectCode(String subjectCode) { 
    this.subjectCode = subjectCode; 
    } 
} 

CourseSchedule:

@Embeddable 
public class CourseSchedule { 
    private String room; 
    private String day; 

     public String getRoom() { 
     return room; 
     } 

     public void setRoom(String room) { 
     this.room = room; 
     } 

     public String getDay() { 
     return day; 
     } 

     public void setDay(String day) { 
     this.day = day; 
     } 

     public String getInstructorId() { 
     return instructorId; 
     } 

     public void setInstructorId(String instructorId) { 
     this.instructorId = instructorId; 
     } 
} 

también estoy confundido en cuanto a lo que mi JPQL se vería en esta situación una vez que los consigo mapeado.

EDIT:

Si añado @Id a la columna de TERM_CODE, un objeto del curso se devuelve sin errores Hibernate, pero la colección CourseSchedule que forma parte del curso es nula.

EDIT 2:

he tratado de jugar con el tratamiento de golf y CourseSchedule como dos tablas separadas (aunque no lo son), pero me parece que no puede conseguir que unen utilizando @OneToMany y @ManyToOne.

@Entity 
@IdClass(CourseId.class) 
@Table(name = "course_table") 
public class Course implements Serializable { 

    @OneToMany(mappedBy = "course") 
    private Collection<CourseSchedule> schedule; 

    @Id 
    @Column(name = "TERM_CODE") 
    private Long termCode; 

    @Id 
    @Column(name = "SUBJECT_CODE") 
    private Long subjectCode; 

    ... 
} 

@Entity 
@IdClass(CourseScheduleId.class) 
@Table(name = "course_table") 
public class CourseSchedule implements Serializable { 

    @ManyToOne 
    @JoinColumns({ 
    @JoinColumn(name="TERM_CODE", referencedColumnName="TERM_CODE"), 
    @JoinColumn(name = "SUBJECT_CODE", referencedColumnName="SUBJECT_CODE") 
    }) 
    private Course course; 

    @Column(name = "TERM_CODE") 
    private Long termCode; 

    @Column(name = "SUBJECT_CODE") 
    private Long subjectCode; 

    @Id 
    private String room; 

    @Id 
    private String day; 

    @Id 
    @Column(name = "INSTRUCTOR_ID") 
    private String instructorId; 

    ... 

} 

(El CourseId y CourseScheduleId son clases simples que se usan para el ID compuesto.) El mapeo por encima de devolver el siguiente error:

org.hibernate.MappingException: Foreign key (FK82D03688F590EF27:course_table [TERM_CODE,SUBJECT_CODE])) must have same number of columns as the referenced primary key (course_table [ROOM,DAY,INSTRUCTOR_ID) 

No necesito el CourseSchedule refiriéndose de nuevo al curso si eso ayuda a que sea más fácil.

¿Alguna idea? Mi único otro pensamiento es definirlos como entidades completamente separadas (no unidas), y luego de alguna manera mapearlas usando JPQL.

+0

Supongo que este es un DB existente? por lo que no puede usar una clave externa al TERM_CODE en una tabla de CourseSchedule? Es posible que tenga una buena razón para hacer esto, pero duplicar algunos datos solo para almacenar la información de ROOM y DAY. ¿Qué informa hibernate cuando lo mapea así? – bvanvelsen

+0

Lamentablemente no puedo cambiar la base de datos, y todo está en una tabla. Ojalá pudiera convencerlos de normalizarlo, pero eso no está sucediendo. – acvcu

+0

Si lo ejecuto en su forma actual, aparece el error "No se ha especificado ningún identificador para entidad: Curso", que casi debería haber esperado. Otro problema es que la tabla de la base de datos no define una clave principal. – acvcu

Respuesta

3

He estado tratando algunas cosas, y sea lo más cercano a lo que desea es esto (comprobar el colocador setCourse en la clase CourseSchedule):

Curso

@Embeddable 
public class Course implements Serializable { 

@Column(name = "TERM_CODE") 
private Long termCode; 

@Column(name = "SUBJECT_CODE") 
private String subjectCode; 

@Transient 
Collection<CourseSchedule> schedule = new ArrayList<CourseSchedule>(); 

public void setSchedule(Collection<CourseSchedule> schedule) { 
    this.schedule = schedule; 
} 
public Collection<CourseSchedule> getSchedule() { 
    return schedule; 
} 

public Long getTermCode() { 
return termCode; 
} 

public void setTermCode(Long termCode) { 
this.termCode = termCode; 
} 

public String getSubjectCode() { 
return subjectCode; 
} 

public void setSubjectCode(String subjectCode) { 
this.subjectCode = subjectCode; 
} 
} 

CourseSchedule

@Entity(name="Course") 
public class CourseSchedule { 
private String room; 
private String day; 

@Id 
@GeneratedValue 
private int id; 

public int getId() { 
    return id; 
} 

public void setId(int id) { 
    this.id = id; 
} 

@Embedded 
private Course course; 

public Course getCourse() { 
    return course; 
} 

public void setCourse(Course course) { 
    course.schedule.add(this); 
    this.course = course; 
} 

public String getRoom() { 
    return room; 
} 

public void setRoom(String room) { 
    this.room = room; 
} 

public String getDay() { 
    return day; 
} 

public void setDay(String day) { 
    this.day = day; 
} 
} 

tabla de base de datos generada

id subject_code term_code day room 
1  "EGRE"   201220  "TR" "0101" 
2  "EGRE"   201220  "W" "0103" 

Básicamente al revés. No es completamente lo que espera, pero podría inspirarlo para una mejor solución, por favor manténgame actualizado si tiene más ideas o si encuentra algo interesante ...

+0

No es una mala idea, lo echaré un vistazo. El único problema es que, de la forma en que lo tienes, ¿no tendría que generar otra tabla de base de datos? Probablemente tenga acceso de lectura/escritura a la base de datos, pero no soy dueño de los datos y no puedo crear nada nuevo en el servidor. Tampoco me permite tener CourseSchedule como una Colección de Cursos, lo que tiene un sentido más lógico cuando se utilizan los objetos desde la perspectiva del cliente. – acvcu

+0

Volví e intenté esto sin el ID generado, pero no pude obtener ninguna de mis consultas para devolver los cursos sin colecciones nulas de CourseSchedule. En lugar de usar la ID generada, utilicé una identificación compuesta en CourseSchedule. – acvcu

0

Esto es lo que ocurrió con (a menos que alguien se le ocurre una idea mejor):

un mapa del recorrido y CourseSchedule a la misma mesa, pero no unirse a ellos:

@Entity 
@IdClass(CourseId.class) 
@Table(name = "course_table") 
public class Course implements Serializable { 

    @Transient 
    private Collection<CourseSchedule> schedule; 

    @Id 
    @Column(name = "TERM_CODE") 
    private Long termCode; 

    @Id 
    @Column(name = "SUBJECT_CODE") 
    private Long subjectCode; 

    ... 
} 

@Entity 
@IdClass(CourseScheduleId.class) 
@Table(name = "course_table") 
public class CourseSchedule implements Serializable { 

    @Column(name = "TERM_CODE") 
    private Long termCode; 

    @Column(name = "SUBJECT_CODE") 
    private Long subjectCode; 

    @Id 
    private String room; 

    @Id 
    private String day; 

    @Id 
    @Column(name = "INSTRUCTOR_ID") 
    private String instructorId; 

    ... 

} 

En el objeto DAO, consulta el curso y el programa horario por separado y agrega la colección CourseSchedule al curso:

public Collection<Course> getCourses() { 
    String jpql = "SELECT DISTINCT course FROM Course course"; 

    TypedQuery<Course> typedQuery = entityManager 
     .createQuery(jpql, Course.class); 

    // get the Courses and set their schedules 
    Collection<Course> courses = typedQuery.getResultList(); 
    setCourseSchedules(courses); 

    return courses; 
} 

private void setCourseSchedules(Collection<Course> courses) { 
    // query for getting CourseSchedules 
    String jpql = "SELECT DISTINCT schedule FROM CourseSchedule schedule " 
      + "WHERE schedule.subjectCode = :subjectCode " 
      + "AND schedule.termCode = :termCode"; 

    for (Course c : courses) { 
     TypedQuery<CourseSchedule> typedQuery = entityManager 
       .createQuery(jpql, CourseSchedule.class) 
       .setParameter("subjectCode", c.getSubjectCode()) 
       .setParameter("termCode", c.getTermCode()); 
     c.setSchedule(typedQuery.getResultList()); 
    } 
} 
Cuestiones relacionadas