在双向关联中,一方要作为主控方,另一方作为被控方,往往需要通过该标注来维护双方之间的关联关系。其作用相当于inverse=true,标注所在类将作为被控方,标注所在方法返回值对应的实体将作为主控方。
而@JoinColumn简单理解即设置外键,即在该标注所在类对应的数据库表设置一个外键关联到标注所在方法的返回值。
举个例子:如果我们在Customer和Order之间建立一对多的的双向关联关系:(注意看注释,尤其有关以上两个注解的注释)
/** *Amendment No. : *Modify By : zxp *Decription : 注意导入的包:javax.persistence.* *Date : 2013-8-15 */ //标志JavaBean作为持久化类,默认所有的属性字段均映射到数据库中的持久化字段, //如果无须映射则在该字段添加@Transient注解 //@Entity通常配合@Table使用,由于自定义命名策略,此处我们可以不用设置@Table注解 @Entity public class Customer implements Serializable { private static final long serialVersionUID = 1L; private Integer id; private String cname; private String bank; private String phone; private Set<Order> orders=new HashSet<Order>(); public Customer() { } public Customer(String cname, String bank, String phone) { this.cname = cname; this.bank = bank; this.phone = phone; } //指定挡墙属性作为ID标志属性,通常配合Generatedvalue注解使用 @Id //指定ID标注生成器,配合@Id注解使用 @GeneratedValue(generator = "generator") @GenericGenerator(name = "generator", strategy = "increment") public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } //指定当前属性作为数据库表中的字段。 @Column(nullable=false,length=20) public String getCname() { return cname; } public void setCname(String cname) { this.cname = cname; } @Column(length=40) public String getBank() { return bank; } public void setBank(String bank) { this.bank = bank; } @Column(length=20) public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } //一对多关联(通常会设置级联和延迟加载) //mappedBy相当于inverse=true,即当前由Orders方来维护彼此之间的关联关系,标注所在类customer作为被控方 //mappedBy还有另一种理解:即返回的order对应的ORDERS表中存在一个外键指向该order对应的customers属性,因此接下来我们到Order类中设置外键(该@JoinColumn出场了) @OneToMany(mappedBy="customer",cascade=CascadeType.ALL,fetch=FetchType.LAZY) public Set<Order> getOrders() { return orders; } public void setOrders(Set<Order> orders) { this.orders = orders; } }
/** *Amendment No. : *Modify By : zxp *Decription : 订单类 *Date : 2013-8-16 */ @Entity public class Order implements Serializable { private static final long serialVersionUID = 1L; private Integer id; private String orderno; private Double money; private Customer customer; public Order() { } public Order(String orderno, Double money,Customer customer) { super(); this.orderno = orderno; this.money = money; this.customer=customer; } @Id @GeneratedValue(generator = "generator") @GenericGenerator(name = "generator", strategy = "increment") public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(length=20) public String getOrderno() { return orderno; } public void setOrderno(String orderno) { this.orderno = orderno; } @Column(precision=10) public Double getMoney() { return money; } public void setMoney(Double money) { this.money = money; } //多对一关联(通常和@JoinColumn配合使用,设置多方关联一方的外键) //@JoinColumn简单理解即设置外键,标志当前方法返回值所关联的连接列。 //由于我们需要在Order表设置一个外键关联到Customer表,因此我们在此处 //通过@JoinColumn设置外键列:CUSTOMER_ID,并关联到返回的customer。 //多对一关联(通常和@JoinColumn配合使用,设置多方关联一方的外键) @ManyToOne @JoinColumn(name="CUSTOMER_ID") public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } }
作为初学者,对于在多对多的双向关联中出现的标注感觉不是很理解,经过一番摸索,算是搞明白了。
先不管多对多还是一对多,在双向关联中,我们总是要明确主控方和被控方。明确了这一点,我们继续想想,主控方中多对多需要做什么?被控方需要做什么?
举个例子:就拿学生Student和课程Course之间的双向多对多例子来说,如果我们将Student方作为主控方,Course方作为被控方,先考虑主控方的,既然是主控方,那就必须通过物理映射告诉Hibernate我与Course方时多对多关联,具体是通过一个中间表STU_COURSE来告知,因此主控方主要任务是设置中间表来维护和Course表的关联关系;那么STU_COURSE当然需要设置两个外键,分别关联到Student表和Course表。
明确了这一点,我们再来看看被控方Course,这时候,它仅仅只需要指定Student来维护和他之间的关联关系。因此只需要标注mappedBy来绑定关联关系。
明确双方的主要任务,接下来我们开始编码:
/** *Amendment No. : *Modify By : zxp *Decription : 课程类 *Date : 2013-8-16 */ @Entity public class Course implements Serializable { private static final long serialVersionUID = 1L; private String id; private String cname; private Set<Student> students=new HashSet<Student>(); public Course() { super(); } public Course(String cname) { super(); this.cname = cname; } @Id @GeneratedValue(generator="uuid") @GenericGenerator(name="uuid",strategy="uuid") public String getId() { return id; } public void setId(String id) { this.id = id; } @Column(length=30) public String getCname() { return cname; } public void setCname(String cname) { this.cname = cname; } //注意mappedBy=courses表示inverse=true,即student和course之间的关联关系由student来维护,即student是主控方 @ManyToMany(mappedBy="courses") public Set<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } }
/** *Amendment No. : *Modify By : zxp *Decription : 学生类 *Date : 2013-8-16 */ @Entity public class Student implements Serializable { private static final long serialVersionUID = 1L; private String id; private String sno; private String sname; private Set<Course> courses=new HashSet<Course>(); public Student() { } public Student(String sno, String sname) { this.sno = sno; this.sname = sname; } @Id @GeneratedValue(generator="uuid") @GenericGenerator(name="uuid",strategy="uuid") public String getId() { return id; } public void setId(String id) { this.id = id; } @Column(length=20) public String getSno() { return sno; } public void setSno(String sno) { this.sno = sno; } @Column(length=20) public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } //中间表(设置两个外键,分别关联Student表和Course表) @ManyToMany(fetch=FetchType.EAGER) @Cascade(value = {org.hibernate.annotations.CascadeType.SAVE_UPDATE }) @JoinTable( name="STU_COURSE", joinColumns=@JoinColumn(name="STUDENTID"), inverseJoinColumns=@JoinColumn(name="COURSEID")) public Set<Course> getCourses() { return courses; } public void setCourses(Set<Course> courses) { this.courses = courses; } }
@ManyToMany 注释表示Student 是多对多关系的一端。@JoinTable 描述了多对多关系的数据表关系。name 属性指定中间表名称,joinColumns 定义中间表与Student 表的外键关系。上面的代码中,中间表STU_COURSE的STUDENTID 列是Student 表的主键列对应的外键列,inverseJoinColumns 属性定义了中间表与另外一端(Course)的外键关系。
说到这里,感觉其实Course很轻松,因为她不需要做什么事,整个过程中,她只是支会Customer一声,Customer就赴汤蹈火,还要拜托一个中间人STU_COURSE来维持他和Course之间的关系。
第一次发文章,讲得比较粗浅,希望大家见谅。