//刘梦冰发表于2015-6-19
单边的多对多关系
多对一、一对多的关系只需要两个表即可,使用子表的外键与主表的主键连接就能存储主从关系。而对多对多的关系至少需要三个表,其中两个表保存两个实体类,另一个表保存关系。
下面以一个论坛文章管理为例来深入学习:
现在论坛的每个帖子都挺有相关的标签,一个帖子可以属于多个标签,因此它们之间是多对多的关系。本例就使用多对多来处理标签与帖子之间的业务逻辑。
标签类Tag中没有Post属性,但是帖子类Post中有Tag属性,它们之间是单边的多对多的关系。Tag、Post都是多方,它们的多对多关系保存在第三个表(中间表tb_tag_post)中。
Tag标签实体类只有一个id主键以及String类型的name普通属性。
Tag.java
package com.lmb.hibernate.bean;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="tb_tag")
public class Tag {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Post帖子实体类中有id主键、标题、内容等普通属性,还有一个Set类型的多对多集合属性tags。从java代码上来看,一对多属性与多对多属性没什么区别,都是set或者list类型的集合属性。但是,它们的hibernate配置是完全不一样的,存储机制也是完全不一样的。
Post.java
package com.lmb.hibernate.bean;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
@Entity
@Table(name="tb_post")
public class Post {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String title;
@ManyToMany(fetch=FetchType.EAGER,
cascade={CascadeType.PERSIST}
)
@JoinTable( //中间关系表配置
name="tb_tag_post",
joinColumns= @JoinColumn(name="post id",referencedColumnName="id"),
inverseJoinColumns=@JoinColumn(name="tag_id",referencedColumnName="id"))
private Set<Tag> tags=new HashSet<Tag>();
@Column(columnDefinition="text")
private String content;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Set<Tag> getTags() {
return tags;
}
public void setTags(Set<Tag> tags) {
this.tags = tags;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
注意:内容content使用@Column的columnDefination指定在数据库中使用text大文本类型。而在多对多属性tags上,使用@ManyToMany指定该列为多对多属性,fetch配置加载方式, cascade配置级联保存属性。多对多属性必须使用@JoinTable指定中间表的配置,其中name指定表名,joinColumns指定该表(也就是Post对应的表)与中间表的对应关系,inverseJoinColumns指定另一端(也就是Tag对应的表)与中间表的对应关系。@joinColumn的name属性post_id、tag_id都指中间表中的列,referencedColumnName指的是实体类的关联列,默认为主键。如果为主键,referencedColumnName可省略。
将两个实体类配置到hibernate配置文件中去;
hibernate.cfg.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
<session-factory>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="connection.url">jdbc:mysql://localhost:3306/hibernate?characterEncoding=UTF-8</property>
<property name="connection.username">lmb</property>
<property name="connection.password">lmb</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<mapping class="com.lmb.hibernate.bean.Post" />
<mapping class="com.lmb.hibernate.bean.Tag" />
</session-factory>
</hibernate-configuration>