05. 关联关系映射(多对多)
关联关系映射
多对多关联关系映射(Engineer和Technology)
这里我们学习Hibernate中多对多的关联关系映射,我们使用的例子是Engineer(工程师)和Technology(技术),一个Engineer可以掌握多门技术,一门Technology也可以被多个工程师掌握。
多对多双向关联关系的表结构
Engineer表
CREATE TABLE t_engineer(
id NUMBER(10) PRIMARY KEY,
name VARCHAR2(20) NOT NULL,
gender VARCHAR(10),
age NUMBER(3) CHECK(age BETWEEN 1 AND 100),
company VARCHAR2(30)
);
Technology表
CREATE TABLE t_engineer(
id NUMBER(10) PRIMARY KEY,
name VARCHAR2(20) NOT NULL,
category VARCHAR2(20) NOT NULL
);
桥表(用于连接两张表,并描述两张表的多对多关系)。
该表存储上面两张表的各自主键,把那两列作为外键,对应外表的主键,并使用两个外表的主键作为该表的联合主键。
CREATE TABLE t_engineer_technology(
engineer_id NUMBER(10),
technology_id NUMBER(10),
PRIMARY KEY (engineer_id, technology_id),
FOREIGN KEY(engineer_id) REFERENCES t_engineer(id),
FOREIGN KEY(technology_id) REFERENCES t_technology(id)
);
多对多双向关联关系的类结构
Engineer类
package com.li.association.many2many.pojo;
import java.util.HashSet;
import java.util.Set;
public class Engineer {
private Long id;
private String name;
private String gender;
private Integer age;
private String company;
private Set<Technology> technologies=new HashSet<Technology>();
public Engineer() {
super();
}
public Engineer(Long id, String name, String gender, Integer age, String company, Set<Technology> technologies) {
super();
this.id = id;
this.name = name;
this.gender = gender;
this.age = age;
this.company = company;
this.technologies = technologies;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public Set<Technology> getTechnologies() {
return technologies;
}
public void setTechnologies(Set<Technology> technologies) {
this.technologies = technologies;
}
}
Technology类
package com.li.association.many2many.pojo;
import java.util.HashSet;
import java.util.Set;
public class Technology {
private Long id;
private String name;
private String category;
private Set<Engineer> engineers=new HashSet<Engineer>();
public Technology() {
super();
}
public Technology(Long id, String name, String category, Set<Engineer> engineers) {
super();
this.id = id;
this.name = name;
this.category = category;
this.engineers = engineers;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public Set<Engineer> getEngineers() {
return engineers;
}
public void setEngineers(Set<Engineer> engineers) {
this.engineers = engineers;
}
}
多对多双向关联关系的映射文件
Engineer.hbm.xml
set标签中name=”technology”,表示Engineer有一个集合属性为technology
table=”T_ENGINEER_TECHNOLOGY”:指示桥表
cascade=”all”:级联所有操作,只需在一侧设置即可
inverse=”true”:取消插入语句之后的更新操作,在多对多关系中必须设置,也只需在一侧设置即可
key标签中的column属性设置的是该表主键对应到桥表的外键列
many-to-many标签中,指定另一个对应的表的主键列和该表对应的类
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.li.association.many2many.pojo">
<class name="Engineer" table="ENGINEER">
<id name="id" type="long" column="ID"></id>
<property name="name" type="string" column="NAME"></property>
<property name="gender" type="string" column="GENDER"></property>
<property name="age" type="integer" column="AGE"></property>
<property name="company" type="string" column="COMPANY"></property>
<set name="technologies" table="T_ENGINEER_TECHNOLOGY" cascade="all" inverse="true">
<key column="ENGINEER_ID"></key>
<many-to-many column="TECHNOLOGY_ID" class="Technology"/>
</set>
</class>
</hibernate-mapping>
Technology.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.li.association.many2many.pojo">
<class name="Technology" table="T_TECHNOLOGY">
<id name="id" type="long" column="ID"></id>
<property name="name" type="string" column="NAME"></property>
<property name="category" type="string" column="CATEGORY"></property>
<set name="engineers" table="T_ENGINEER_TECHNOLOGY">
<key column="TECHNOLOGY_ID"/>
<many-to-many column="ENGINEER_ID" class="Engineer"/>
</set>
</class>
</hibernate-mapping>
配置 核心配置文件 hibernate.cfg.xml
将两个映射文件添加到配置文件中
<mapping resource="com/li/association/many2many/pojo/Engineer.hbm.xml"/>
<mapping resource="com/li/association/many2many/pojo/Technology.hbm.xml"/>
测试
package com.li.test;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.li.association.many2many.pojo.Engineer;
import com.li.association.many2many.pojo.Technology;
import com.li.common.HibernateSessionFactory;
public class Association4 {
public static void main(String[] args) {
Engineer engineer1=new Engineer();
engineer1.setId(12L);
engineer1.setName("Wang");
engineer1.setGender("male");
engineer1.setCompany("alibaba");
engineer1.setAge(20);
Engineer engineer2=new Engineer();
engineer2.setId(34L);
engineer2.setName("Lisa");
engineer2.setGender("female");
engineer2.setCompany("alibaba");
engineer2.setAge(20);
Technology t1=new Technology();
t1.setId(56L);
t1.setName("Hibernate");
t1.setCategory("Java");
Technology t2=new Technology();
t2.setId(78L);
t2.setName("Spring");
t2.setCategory("Java");
/*建立双向关联关系*/
engineer1.getTechnologies().add(t1);
engineer1.getTechnologies().add(t2);
engineer2.getTechnologies().add(t1);
engineer2.getTechnologies().add(t2);
t1.getEngineers().add(engineer1);
t1.getEngineers().add(engineer2);
t2.getEngineers().add(engineer1);
t2.getEngineers().add(engineer2);
Session session=HibernateSessionFactory.getSession();
Transaction trans=null;
try {
trans=session.beginTransaction();
/*级联保存,因为我们的级联设置在Engineer一侧,所以我们保存Engineer对象会级联保存Technology,我们也可以将级联同时设置在两侧,这样我们不管保存哪一侧都会级联保存*/
session.save(engineer1);
session.save(engineer2);
/*级联检索*/
Engineer engineer = session.get(Engineer.class, 12L);
System.out.println(engineer);
/*级联删除,连同两个Technology对象都会在数据库中删除*/
session.delete(engineer);
/*提交事务*/
trans.commit();
} catch (Exception e) {
e.printStackTrace();
trans.rollback();
}
}
}
级联删除设置 cascade=”all”时,如果我们删除一个Engineer对象记录(如上面的engineer1),将会删除所有跟此记录的记录,如T_TECHNOLOGY表中对应t1,t2的记录,此时T_TECHNOLOGY和桥表中的记录都空了。当然,有时我们不希望这么做,我们希望,在删除engineer1的时候,只删除t_engineer表中的该记录以及桥表中对应的该记录,那么我们可以设置只在保存和更新的时候记录,而删除的时候不记录,更改为 cascade=”save-update”。
上一篇: 04. 关联关系映射(一对多)