一、多对多的建表原则
![多对多的建表原则](https://i-blog.csdnimg.cn/blog_migrate/c79a5ed1554210b8626e5ed1d35cb034.jpeg)
多对多的建表原则
二、编写常规的用户和角色的JavaBean程序与映射配置文件
SQL的建表
CREATE TABLE `sys_user` (
`user_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '用户id',
`user_code` varchar(32) DEFAULT NULL COMMENT '用户名称',
`user_name` varchar(64) DEFAULT NULL COMMENT '用户密码',
`user_password` varchar(32) DEFAULT NULL COMMENT '用户密码',
`user_state` char(1) DEFAULT NULL COMMENT '1:正常,0:暂停',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
CREATE TABLE `sys_role` (
`role_id` bigint(32) NOT NULL AUTO_INCREMENT,
`role_name` varchar(32) DEFAULT NULL COMMENT '角色名称',
`role_memo` varchar(128) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
JavaBean
/Hibernate5_d03_c02/src/hibernate/domain/User.java
程序代码如下:
package hibernate.domain;
public class User {
private Long user_id; //用户id
private String user_code; //用户账号
private String user_name; //用户名称
private String user_password;//用户密码
private String user_state; //1:正常,0:暂停
public Long getUser_id() {
return user_id;
}
public void setUser_id(Long user_id) {
this.user_id = user_id;
}
public String getUser_code() {
return user_code;
}
public void setUser_code(String user_code) {
this.user_code = user_code;
}
public String getUser_name() {
return user_name;
}
public void setUser_name(String user_name) {
this.user_name = user_name;
}
public String getUser_password() {
return user_password;
}
public void setUser_password(String user_password) {
this.user_password = user_password;
}
public String getUser_state() {
return user_state;
}
public void setUser_state(String user_state) {
this.user_state = user_state;
}
}
/Hibernate5_d03_c02/src/hibernate/domain/Role.java
程序代码如下:
package hibernate.domain;
public class Role {
private Long role_id; //角色ID
private String role_name; //角色名称
private String role_memo; //备注
public Long getRole_id() {
return role_id;
}
public void setRole_id(Long role_id) {
this.role_id = role_id;
}
public String getRole_name() {
return role_name;
}
public void setRole_name(String role_name) {
this.role_name = role_name;
}
public String getRole_memo() {
return role_memo;
}
public void setRole_memo(String role_memo) {
this.role_memo = role_memo;
}
}
映射配置文件
/Hibernate5_d03_c02/src/hibernate/domain/User.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>
<class name="hibernate.domain.User" table="sys_user">
<id name="user_id" column="user_id">
<generator class="native"></generator>
</id>
<property name="user_code" column="user_code"></property>
<property name="user_name" column="user_name"></property>
<property name="user_password" column="user_password"></property>
<property name="user_state" column="user_state"></property>
</class>
</hibernate-mapping>
/Hibernate5_d03_c02/src/hibernate/domain/Role.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>
<class name="hibernate.domain.Role" table="sys_role">
<id name="role_id" column="role_id">
<generator class="native"></generator>
</id>
<property name="role_name" column="role_name"></property>
<property name="role_memo" column="role_memo"></property>
</class>
</hibernate-mapping>
/Hibernate5_d03_c02/src/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">
<hibernate-configuration>
<session-factory>
<!-- 必选配置 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- jdbc:mysql:///hibernate_day01 -->
<property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/hibernate_day03</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<!--数据库方言 使用的数据库类型 -->
<property name="hibernate.dialect org.hibernate.dialect.MySQLDialect"></property>
<!-- 可选配置 -->
<!-- 在控制台输出sql语句 -->
<property name="show_sql">true</property>
<!-- 在控制台输出的sql语句格式化 -->
<property name="hibernate.format_sql">true</property>
<!-- 配置c3p0连接池 -->
<!-- C3P0 的供应商 -->
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!-- 最小连接 -->
<property name="hibernate.c3p0.min_size">5</property>
<!-- 最大连接数 -->
<property name="hibernate.c3p0.max_size">10</property>
<!-- 每 120 秒检查空闲连接 -->
<property name="hibernate.c3p0.timeout">120</property>
<!-- 自动建表 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 把session绑定到当前线程中,使用getCurrentSession()获取当前session -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- 映射文件 -->
<mapping resource="hibernate/domain/Role.hbm.xml"/>
<mapping resource="hibernate/domain/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
三、编写Hibernate的用户和角色的JavaBean程序与映射配置文件
JavaBean
一个用户对应多个角色,一个角色对应多个用户,在Java中可以用集合来表示。
用户跟角色之间没有顺序,在Java的集合类型中可以选用set集合表示。
/Hibernate5_d03_c02/src/hibernate/domain/User.java
程序代码如下:
package hibernate.domain;
import java.util.HashSet;
import java.util.Set;
public class User {
...
//用set集合来存多个角色
private Set<Role> roles = new HashSet<>();
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
...
}
/Hibernate5_d03_c02/src/hibernate/domain/Role.java
程序代码如下:
package hibernate.domain;
import java.util.HashSet;
import java.util.Set;
public class Role {
...
//使用set集合来存放多个用户
private Set<User> users = new HashSet<>();
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
...
}
映射配置文件
/Hibernate5_d03_c02/src/hibernate/domain/User.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>
<class name="hibernate.domain.User" table="sys_user">
<id name="user_id" column="user_id">
<generator class="native"></generator>
</id>
<property name="user_code" column="user_code"></property>
<property name="user_name" column="user_name"></property>
<property name="user_password" column="user_password"></property>
<property name="user_state" column="user_state"></property>
<!-- 配置多对多 -->
<!-- 配置set集合描述User类中的roles属性和中间表信息 -->
<!-- name:User类中roles属性 -->
<!-- table:中间表名称,由框架产生,不需要自行建表 -->
<set name="roles" table="sys_user_role">
<!-- key column 中间表中用户的外键 -->
<key column="user_id"></key>
<!-- 配置角色表信息 -->
<!-- class:用户类关联的角色类全路径 -->
<!-- column:中间表角色的外键 -->
<many-to-many class="hibernate.domain.Role" column="role_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
/Hibernate5_d03_c02/src/hibernate/domain/Role.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>
<class name="hibernate.domain.Role" table="sys_role">
<id name="role_id" column="role_id">
<generator class="native"></generator>
</id>
<property name="role_name" column="role_name"></property>
<property name="role_memo" column="role_memo"></property>
<!-- 配置多对多 -->
<!-- 配置set集合描述Role类中的users属性和中间表信息 -->
<!-- name:Role类中users属性 -->
<!-- table:中间表名称,由框架产生,不需要自行建表 -->
<set name="users" table="sys_user_role">
<!-- key column 中间表中角色的外键 -->
<key column="role_id"></key>
<!-- 配置用户表信息 -->
<!-- class:角色类关联的用户类全路径 -->
<!-- column:中间表用户的外键 -->
<many-to-many class="hibernate.domain.User" column="user_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
四、多对多配置文件和具体的说明
![多对多配置文件和具体的说明](https://i-blog.csdnimg.cn/blog_migrate/7824c2cf9a10f46faf164f66f57aec72.png)
多对多配置文件和具体的说明
五、多对多的级联保存
双向关联
/Hibernate5_d03_c02/src/hibernate/test/TestMany2Many.java
程序代码如下:
package hibernate.test;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import hibernate.domain.Role;
import hibernate.domain.User;
import hibernate.util.HibernateUtils;
public class TestMany2Many {
//双向关联
@Test
public void testSave(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//1.新建2个用户
User u1 = new User();
u1.setUser_name("冠希");
User u2 = new User();
u2.setUser_name("柏芝");
//2.新建2个角色
Role r1 = new Role();
r1.setRole_name("演员");
Role r2 = new Role();
r2.setRole_name("歌手");
//3.双向关联
//冠希:演员+歌手
u1.getRoles().add(r1);
u1.getRoles().add(r2);
r1.getUsers().add(u1);
r2.getUsers().add(u2);
//柏芝:演员
u2.getRoles().add(r1);
r1.getUsers().add(u2);
//4.保存
session.save(u1);
session.save(u2);
session.save(r1);
session.save(r2);
tr.commit();
}
}
![主键冲突](https://i-blog.csdnimg.cn/blog_migrate/5ab94223f37927316e2fb71cbf8d77df.jpeg)
主键冲突
原因:由于双方都在维护外键,导致更新中间表失败,解决办法,任选一方配置 inverse=true
,放弃外键维护
修改 User.hbm.xml
/Hibernate5_d03_c02/src/hibernate/domain/User.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>
<class name="hibernate.domain.User" table="sys_user">
...
<!-- 需要有一方放弃外键维护 inverse="true" -->
<set name="roles" table="sys_user_role" inverse="true">
...
</class>
</hibernate-mapping>
再次运行
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/44eb5e7510f44d62d9c6869de797cad8.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/44812239576a6ba36f0ed7bff5276cbd.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/b782d8761d4a4d6956a578e4e941cd0a.jpeg)
总结:多对多进行双向关联的时候,必须有一方去放弃外键维护。
单向关联
/Hibernate5_d03_c02/src/hibernate/test/TestMany2Many.java
程序代码如下:
...
public class TestMany2Many {
...
//单向关联
@Test
public void testSave1(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//1.新建 2 个用户
User u1 = new User();
u1.setUser_name("冠希1");
User u2 = new User();
u2.setUser_name("柏芝1");
//2.新建 2 个角色
Role r1 = new Role();
r1.setRole_name("演员1");
Role r2 = new Role();
r2.setRole_name("歌手1");
//3.单向关联--用户关联角色
//冠希:演员+歌手
u1.getRoles().add(r1);
u1.getRoles().add(r2);
//柏芝:演员
u2.getRoles().add(r1);
//4.保存用户,hibernate使用快照机制保存角色
session.save(u1);
session.save(u2);
tr.commit();
}
}
/Hibernate5_d03_c02/src/hibernate/domain/User.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>
<class name="hibernate.domain.User" table="sys_user">
...
<!-- 配置由用户级联保存角色。cascade="save-update" -->
<set name="roles" table="sys_user_role" cascade="save-update">
...
</hibernate-mapping>
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/eef543caec2e09830df0d07bc06624dd.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/fab2a01e346ca1ffa48123af50daac0f.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/7702fa0742566e89e5555fb546bd1ea0.jpeg)
六、多对多的级联删除
/Hibernate5_d03_c02/src/hibernate/test/TestMany2Many.java
程序代码如下:
...
public class TestMany2Many {
...
//id为5的用户的角色为歌手+演员
//删除id为5的用户的歌手角色
@Test
public void testDelete(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//1.获取id为5的用户
User user = session.get(User.class, 5L);
//2.获取id为5的角色
Role role = session.get(Role.class, 5L);
//3.解除关系
user.getRoles().remove(role);
//在集合中操作,hibernate使用快照机制同步数据
tr.commit();
}
}
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/15792125589165f13f6f721a534d4358.jpeg)
注意:千万不要在映射文件中配置 cascade="delete"
,否则在删除一个用户时会把关联的角色删除,导致其他用户无法关联到角色。
/Hibernate5_d03_c02/src/hibernate/domain/User.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>
<class name="hibernate.domain.User" table="sys_user">
...
<!-- 不要在映射文件中配置 cascade="delete",
否则在删除一个用户时会把关联的角色删除,
导致其他用户无法关联到角色。
-->
<set name="roles" table="sys_user_role" cascade="delete">
...
</hibernate-mapping>
/Hibernate5_d03_c02/src/hibernate/domain/Role.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>
<class name="hibernate.domain.Role" table="sys_role">
...
<!-- 不要在映射文件中配置 cascade="delete",
否则在删除一个用户时会把关联的角色删除,
导致其他用户无法关联到角色。
-->
<set name="users" table="sys_user_role" cascade="delete">
...
</hibernate-mapping>
/Hibernate5_d03_c02/src/hibernate/test/TestMany2Many.java
程序代码如下:
...
public class TestMany2Many {
...
//级联删除
@Test
public void testDelete1(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//删除3号用户
User user = session.get(User.class, 3L);
session.delete(user);
tr.commit();
}
}
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/4dd7f376cffef70a9560636e7d52d7ad.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/da163c4ba1cf225bd269b881288b3d13.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/7bbb2c3733790870056f83b49df57a83.jpeg)