将单个表映射到JPA中的可嵌入集合

时间:2012-02-16 20:13:28

标签: java hibernate jpa

我有一个相当独特的情况,试图将单个表映射到JPA中的多个实体。我已经阅读了@Embeddable和@ElementCollection,但我不确定如何在我的情况下使用它们(或者如果可以的话)。一个数据库表包含课程信息。表格中可以有行,除了一些值(例如房间号和日期)外,课程中的所有内容都相同。例如:

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

有没有办法可以从上面的两行中提取数据,并将公共数据放在一个对象中,将不同的值放在一个单独的对象集合中?以下是我希望如何定义类的示例:

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

我也很困惑,一旦我将它们映射到这种情况,我的JPQL会是什么样子。

编辑:

如果我将@Id添加到TERM_CODE列,则会返回一个没有Hibernate错误的Course对象,但属于该课程的CourseSchedule Collection为空。

编辑2:

我试图将Course和CourseSchedule视为两个独立的表(即使它们不是),但我似乎无法使用@OneToMany和@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;

    ...

}

CourseIdCourseScheduleId是用于复合ID的简单类。)上面的映射返回以下错误:

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)

如果这有助于简化课程,我不需要将CourseSchedule引回课程。

有什么想法吗?我唯一的另一个想法是将它们定义为完全独立的实体(未加入),然后以某种方式使用JPQL将它们映射在一起。

2 个答案:

答案 0 :(得分:3)

我一直在尝试一些事情,而我最接近你想要的是这个(检查setCourse类中的setter CourseSchedule):

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

生成的数据库表

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

基本上是相反的方式。它并不完全符合您的期望,但它可能会激励您获得更好的解决方案,如果您有更多想法或发现有趣的东西,请随时更新......

答案 1 :(得分:0)

这就是我提出的(除非有人想出更好的主意):

将Course和CourseSchedule映射到同一个表,但不要加入它们:

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

    ...

}

在DAO对象中,分别查询Course和CourseSchedule并将CourseSchedule集合添加到课程中:

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