Web开发 | Hibernate - 12.Hibernate的关联关系映射之多对多映射

一、多对多的建表原则

多对多的建表原则
多对多的建表原则


二、编写常规的用户和角色的JavaBean程序与映射配置文件

SQL的建表

sys_user

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;

sys_role

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,引入必要的jar包、配置、util类

  /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>

四、多对多配置文件和具体的说明

多对多配置文件和具体的说明
多对多配置文件和具体的说明


五、多对多的级联保存

双向关联

  /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();
    }
}

主键冲突
主键冲突

  原因:由于双方都在维护外键,导致更新中间表失败,解决办法,任选一方配置 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>

  再次运行

运行效果
运行效果
运行效果

  总结:多对多进行双向关联的时候,必须有一方去放弃外键维护。


单向关联

  /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>

运行效果
运行效果
运行效果


六、多对多的级联删除

  /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();
    }
}

运行效果


  注意:千万不要在映射文件中配置 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();
    }   
}

运行效果
运行效果
运行效果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值