Hibernate 多对多 一对多 多对一 配置解析案例

什么是Hibernate

Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。

此案例用于理解脏检查,以及Hibernate关系映射的配置

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com</groupId>
<artifactId>HibernateCase</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.12.Final</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.hynnet/oracle-driver-ojdbc -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>12.1.0.2</version>
</dependency>
</dependencies>

</project>

数据库的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
--部门
CREATE TABLE depa(
ID NUMBER NOT NULL,
NAME NVARCHAR2 (50) NOT NULL
);

--职员
CREATE TABLE emp(
ID NUMBER NOT NULL,
NAME NVARCHAR2 (50) NOT NULL,
depa_id NUMBER NOT NULL
);

--职位
CREATE TABLE post(
ID NUMBER NOT NULL,
NAME NVARCHAR2 (50) NOT NULL
);

--职员职位
CREATE TABLE emp_post(
emp_id NUMBER NOT NULL,
post_id NUMBER NOT NULL
);

emp_post 职员职位表为中间表,实体类可以不存在

实体类

Depa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.entity;

import java.util.HashSet;
import java.util.Set;

public class Depa {
private Integer id;
private String name;
private Set<Emp> emps = new HashSet<Emp>();

@Override
public String toString() {
return "Depa{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}

public Set<Emp> getEmps() {
return emps;
}

public void setEmps(Set<Emp> emps) {
this.emps = emps;
}

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

Emp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.entity;

import java.util.HashSet;
import java.util.Set;

public class Emp {
private Integer id;
private String name;
private Depa depaid;
private Set<Post> posts = new HashSet<Post>();

@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}

public Set<Post> getPosts() {
return posts;
}

public void setPosts(Set<Post> posts) {
this.posts = posts;
}

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

public Depa getDepaid() {
return depaid;
}

public void setDepaid(Depa depaid) {
this.depaid = depaid;
}
}

Post

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.entity;

import java.util.HashSet;
import java.util.Set;

public class Post {
private Integer id;
private String name;
private Set<Emp> emps = new HashSet<Emp>();

@Override
public String toString() {
return "Post{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}

public Set<Emp> getEmps() {
return emps;
}

public void setEmps(Set<Emp> emps) {
this.emps = emps;
}

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

Emp 实体类 和 Post 实体类 都有对方的 Set<?> 集合,这也是操作中间表 emp_post 的关键属性

对实体类关系映射配置

depa.hbm.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0"?>
<!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.entity">
<class name="Depa" table="depa">
<id name="id">
<generator class="increment"/>
</id>
<property name="name"/>
<!--一对多 name为实体类属性名 class为对应外键对应实体类-->
<!--table 实体类对象数据来源数据库表名 cascade维护方式-->
<!--inverse 指定由哪一方来维护之间的关联关系 当一方中指定了"inverse=false"(默认),那么那一方就有责任负责之间的关联关系-->
<set name="emps" table="emp" cascade="all" inverse="true">
<!--column 为emp数据库关联depa的外键名-->
<key column="depa_id"></key>
<!--class emps的单个实体数据为哪个实体类-->
<one-to-many class="Emp"/>
</set>
</class>
</hibernate-mapping>

emp.hbm.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!--package为命名空间 子节点有多个类的全在com.entity包里-->
<!--(如果不写,下面 <class name="Emp" name值"Emp"需要改成"com.entity.Emp")-->
<hibernate-mapping package="com.entity">
<class name="Emp" table="emp">
<!--主键-->
<id name="id">
<!--自增方式-->
<generator class="increment"/>
</id>
<!--普通数据列-->
<property name="name"/>
<!--多对一 name为实体类属性名 class为对应外键对应实体类 column为数据库外键名(如果与name值一样可以忽略)-->
<many-to-one name="depaid" class="Depa" column="depa_id"/>
<!--多对多 name为实体类属性名 table 关联的中间表 cascade 维护方式 save-update 为维护方式是新增或修改-->
<set name="posts" table="emp_post" cascade="save-update">
<!--中间表emp_post 对应emp的外键-->
<key column="emp_id"></key>
<!--class 多对多的实体类 column 中间表emp_post 对应Post的外键-->
<many-to-many class="Post" column="post_id"/>
</set>
</class>
</hibernate-mapping>

post.hbm.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0"?>
<!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.entity">
<class name="Post" table="post">
<id name="id">
<generator class="increment"/>
</id>
<property name="name"/>
<!--多对多 name为实体类属性名 table 关联的中间表 cascade 维护方式 save-update 为维护方式是新增或修改-->
<set name="emps" table="emp_post" cascade="save-update">
<!--中间表emp_post 对应post的外键-->
<key column="post_id"></key>
<!--class 多对多的实体类 column 中间表emp_post 对应Emp的外键-->
<many-to-many class="Emp" column="emp_id"/>
</set>
</class>
</hibernate-mapping>

将实体类的 XML 映射到 hibernate.cfg.xml 配置

hibernate.cfg.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.url">jdbc:oracle:thin:@localhost:1521:orcl</property>
<property name="connection.username">system</property>
<property name="connection.password">simon</property>
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="current_session_context_class">thread</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>

<mapping resource="com/entity/depa.hbm.xml"/>
<mapping resource="com/entity/emp.hbm.xml"/>
<mapping resource="com/entity/post.hbm.xml"/>
</session-factory>
</hibernate-configuration>

Hibernate工具类 HibernateUtil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.util;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
private static Configuration configuration;
private static final SessionFactory factory;

static {
configuration = new Configuration().configure("hibernate.cfg.xml");
factory = configuration.buildSessionFactory();
}

public static Session getSession() {
return factory.getCurrentSession();
}
}

测试类 Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package com.test;

import com.entity.Depa;
import com.entity.Emp;
import com.entity.Post;
import com.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;

public class Test {
public static void main(String[] args) {
Session session = HibernateUtil.getSession();
Transaction transaction = session.beginTransaction();

System.out.println("查询ID为1的职员数据");
Emp emp = session.get(Emp.class, 1);
System.out.println(emp);

System.out.println("通过职员得到中间表emp_post数据");
for (Post post : emp.getPosts()) {
System.out.println(post);
}

System.out.println("清除职员中间表emp_post的数据");
emp.getPosts().clear();

System.out.println("查询ID为2的职位数据");
Post post = session.get(Post.class, 2);
System.out.println(post);

System.out.println("将查到的职位给职员新增中间表emp_post数据");
emp.getPosts().add(post);

System.out.println("查询ID为1的职员数据");
emp = session.get(Emp.class, 1);
System.out.println(emp);

System.out.println("通过职员得到中间表emp_post数据");
for (Post temp : emp.getPosts()) {
System.out.println(temp);
}

System.out.println("通过职员得到部门表depa数据");
System.out.println(emp.getDepaid());

System.out.println("查询ID为1的部门数据");
Depa depa = session.get(Depa.class, 1);
System.out.println(depa);

System.out.println("将查到的部门depa给职员emp利用脏检查修改");
emp.setDepaid(depa);

System.out.println("查询ID为1的职员数据");
emp = session.get(Emp.class, 1);
System.out.println(emp);

System.out.println("通过职员得到部门表depa数据");
System.out.println(emp.getDepaid());

System.out.println("查询ID为2的部门数据");
depa = session.get(Depa.class, 2);
System.out.println(depa);

System.out.println("得到部门对象所属职员");
for (Emp e : depa.getEmps()) {
System.out.println(e);
}

System.out.println("利用脏检查删除部门对象所属的ID为1的职员");
depa.getEmps().remove(emp);

System.out.println("查询ID为2的部门数据");
depa = session.get(Depa.class, 2);
System.out.println(depa);

System.out.println("得到部门对象所属职员");
for (Emp e : depa.getEmps()) {
System.out.println(e);
}

//不提交,则上面的结果不会对数据库执行操作
// transaction.commit();
}
}

控制台输出

看看测试类代码对数据库的数据进行了哪些操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
查询ID为1的职员数据
Hibernate:
select
emp0_.id as id1_1_0_,
emp0_.name as name2_1_0_,
emp0_.depa_id as depa_id3_1_0_
from
emp emp0_
where
emp0_.id=?
Emp{id=1, name='张三'}
通过职员得到中间表emp_post数据
Hibernate:
select
posts0_.emp_id as emp_id1_2_0_,
posts0_.post_id as post_id2_2_0_,
post1_.id as id1_3_1_,
post1_.name as name2_3_1_
from
emp_post posts0_
inner join
post post1_
on posts0_.post_id=post1_.id
where
posts0_.emp_id=?
Post{id=1, name='职员'}
清除职员中间表emp_post的数据
查询ID为2的职位数据
Hibernate:
select
post0_.id as id1_3_0_,
post0_.name as name2_3_0_
from
post post0_
where
post0_.id=?
Post{id=2, name='主管'}
将查到的职位给职员新增中间表emp_post数据
查询ID为1的职员数据
Emp{id=1, name='张三'}
通过职员得到中间表emp_post数据
Post{id=2, name='主管'}
通过职员得到部门表depa数据
Hibernate:
select
depa0_.id as id1_0_0_,
depa0_.name as name2_0_0_
from
depa depa0_
where
depa0_.id=?
Depa{id=2, name='美工部'}
查询ID为1的部门数据
Hibernate:
select
depa0_.id as id1_0_0_,
depa0_.name as name2_0_0_
from
depa depa0_
where
depa0_.id=?
Depa{id=1, name='销售部'}
将查到的部门depa给职员emp利用脏检查修改
查询ID为1的职员数据
Emp{id=1, name='张三'}
通过职员得到部门表depa数据
Depa{id=1, name='销售部'}
查询ID为2的部门数据
Depa{id=2, name='美工部'}
得到部门对象所属职员
Hibernate:
select
emps0_.depa_id as depa_id3_1_0_,
emps0_.id as id1_1_0_,
emps0_.id as id1_1_1_,
emps0_.name as name2_1_1_,
emps0_.depa_id as depa_id3_1_1_
from
emp emps0_
where
emps0_.depa_id=?
Emp{id=1, name='张三'}
利用脏检查删除部门对象所属的ID为1的职员
查询ID为2的部门数据
Depa{id=2, name='美工部'}
得到部门对象所属职员

点击访问仓库

案例为重新整理的, MySQL连接为主, 字段名进行了修改, 整体区别不大