表之间关系

1. 一对多

  • 一个部门有多个员工,一个员工只能属于某一个部门
  • 一个班级有多个学生,一个学生只能属于一个班级

2. 多对多

  • 一个老师教多个学生,一个学生可以被多个老师教
  • 一个学生可以先择多门课程,一门课程可以被多个学生选择

3. 一对一

  • 一个公司只能对应一个注册地址

表之间关系建表原则

1. 一对多

在多的一方创建一个外键,指向一的一方的主键

Hibernate 关系配置 随笔 第1张

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

2. 多对多

创建一个中间表,中间表至少有两个字段,分别作为外键指向多对多双方的主键

Hibernate 关系配置 随笔 第2张

3. 一对一

唯一外键对应

Hibernate 关系配置 随笔 第3张

主键对应

Hibernate 关系配置 随笔 第4张

一对多关系配置

一、 建立表

1. 客户

  • 一个客户可以有多个联系人
CREATE TABLE `customer` (
  `cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
  `cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
  `cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
  `cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
  `cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
  `cust_phone` varchar(64) DEFAULT NULL COMMENT '固定电话',
  `cust_mobile` varchar(16) DEFAULT NULL COMMENT '移动电话',
  PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2. 销售联系人

  • 一个联系人只能属于某一个客户
CREATE TABLE `linkman` (
  `link_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
  `link_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名',
  `link_gender` char(1) DEFAULT NULL COMMENT '联系人性别',
  `link_phone` varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
  `link_mobile` varchar(16) DEFAULT NULL COMMENT '联系人手机',
  `link_email` varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
  `link_qq` varchar(16) DEFAULT NULL COMMENT '联系人qq',
  `link_position` varchar(16) DEFAULT NULL COMMENT '联系人职位',
  `link_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注',
  `link_cust_id` bigint(32) NOT NULL COMMENT '客户id',
  PRIMARY KEY (`link_id`),
  KEY `FK_cst_lkm` (`link_cust_id`),
  CONSTRAINT `FK_cst_lkm` FOREIGN KEY (`link_cust_id`) REFERENCES `customer` (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

关系图

Hibernate 关系配置 随笔 第5张

二、建立ORM

1.实体类与数据库中字段进行建立

客户实体类
@Getter@Setter
public class Customer {
    private Long cust_id;
    private String cust_name;
    private String cust_source;
    private String cust_industry;
    private String cust_level;
    private String cust_phone;
    private String cust_mobile;
}
销售实体类
@Getter@Setter
public class Linkman {
    private Long link_id;
    private String link_name;
    private String link_gender;
    private String link_phone;
    private String link_mobile;
    private String link_email;
    private String link_qq;
    private String link_position;
    private String link_memo;
    private String link_cust_id;
}

2.设置两表之间的关系

  • 一个客户可以有多个联系人
@Getter@Setter
public class Customer {
    private Long cust_id;
    private String cust_name;
    private String cust_source;
    private String cust_industry;
    private String cust_level;
    private String cust_phone;
    private String cust_mobile;

    // 一个客户可以有多个联系人
    private Set<Linkman> linkmens = new HashSet<>();

    @Override
    public String toString() {
        return "Customer{" + "cust_id=" + cust_id + ", cust_name='" + cust_name + '\'' + ", cust_source='" + cust_source
                + '\'' + ", cust_industry='" + cust_industry + '\'' + ", cust_level='" + cust_level + '\''
                + ", cust_phone='" + cust_phone + '\'' + ", cust_mobile='" + cust_mobile + '\'' + '}';
    }
}
  • 一个联系人只能属于某一个客户
@Getter@Setter
public class Linkman {
    private Long link_id;
    private String link_name;
    private String link_gender;
    private String link_phone;
    private String link_mobile;
    private String link_email;
    private String link_qq;
    private String link_position;
    private String link_memo;
    private String link_cust_id;
    // 一个联系人只对应一个客户
    private Customer customer;

    @Override
    public String toString() {
        return "Linkman{" + "link_id=" + link_id + ", link_name='" + link_name + '\'' + ", link_gender='" + link_gender
                + '\'' + ", link_phone='" + link_phone + '\'' + ", link_mobile='" + link_mobile + '\''
                + ", link_email='" + link_email + '\'' + ", link_qq='" + link_qq + '\'' + ", link_position='"
                + link_position + '\'' + ", link_memo='" + link_memo + '\'' + ", link_cust_id='" + link_cust_id + '\''
                + ", customer=" + customer + '}';
    }
}

三、添加配置文件

1.客户(Customer)实体类的配置文件

<?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="com.xzh.hibernate.domain.Customer" table="customer">
        <!--建立类属性哪一个是主键 还要跟数据库当中主键进行对象 -->
        <id name="cust_id" column="cust_id">
            <generator class="native" />
        </id>
        <!--建立类中的普通属性与数据库当中字段进行关联 -->
        <property name="cust_name" column="cust_name" />
        <property name="cust_source" column="cust_source" />
        <property name="cust_industry" column="cust_industry" />
        <property name="cust_level" column="cust_level" />
        <property name="cust_phone" column="cust_phone" />
        <property name="cust_mobile" column="cust_mobile" />

        <!--一对多 -->
        <set name="linkmens" cascade="save-update,delete" inverse="true"><!--set属性名称 -->
            <key column="link_cust_id"></key><!--外键 -->
            <one-to-many class="com.xzh.hibernate.domain.Linkman"></one-to-many>
        </set>

    </class>
</hibernate-mapping>

2.联系人(LinkMan)实体类配置文件

<?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="com.xzh.hibernate.domain.Linkman" table="linkman">
        <!--建立类属性哪一个是主键 还要跟数据库当中主键进行对象 -->
        <id name="link_id" column="link_id">
            <generator class="native" />
        </id>
        <!--建立类中的普通属性与数据库当中字段进行关联 -->
        <property name="link_name" column="link_name" />
        <property name="link_gender" column="link_gender" />
        <property name="link_phone" column="link_phone" />
        <property name="link_mobile" column="link_mobile" />
        <property name="link_email" column="link_email" />
        <property name="link_qq" column=" link_qq" />
        <property name="link_position" column=" link_position" />
        <property name="link_memo" column=" link_memo" />

        <many-to-one name="customer" cascade="save-update"
            class="com.xzh.hibernate.domain.Customer" column="link_cust_id" />
    </class>
</hibernate-mapping>

四、添加核心配置文件

<!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>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3307/hibernate</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">abcd</property>
        <!-- 配置Hibernate的方言 -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
        <!-- 打印SQL -->
        <property name="hibernate.show_sql">true</property>
        <!-- 格式化SQL -->
        <property name="hibernate.format_sql">true</property>
        <!-- 自动创建表 -->
        <property name="hibernate.hbm2ddl.auto">update</property>

        <!-- C3P0 -->
        <property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
        <!--在连接池中可用的数据库连接的最少数目 -->
        <property name="c3p0.min_size">5</property>
        <!--在连接池中所有数据库连接的最大数目 -->
        <property name="c3p0.max_size">20</property>
        <!--设定数据库连接的过期时间,以秒为单位, 如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
        <property name="c3p0.timeout">120</property>
        <!--每3000秒检查所有连接池中的空闲连接 以秒为单位 -->
        <property name="c3p0.idle_test_period">3000</property>
        
        <!--设置事务的隔离级别-->
        <property name="hibernate.connection.isolation">4</property>
        <!--创建一个session绑定到当前线程-->
        <property name="current_session_context_class">thread</property>
        
        <!--加载映射文件 -->
        <mapping resource="com/xzh/hibernate/domain/customer.hbm.xml" />
        <mapping resource="com/xzh/hibernate/domain/linkman.hbm.xml" />
    </session-factory>
</hibernate-configuration>

五、引入工具类

public class HibernateUtil {
    public static final SessionFactory sessionFactory;
    static {
        //1.加载配置文件
        Configuration configure = new Configuration().configure();
        //2.创建sessionFactory   --JDBC 连接池
        sessionFactory = configure.buildSessionFactory();
    }
    public static Session openSession(){
        Session session = sessionFactory.openSession();
        return  session;
    }
    public static Session getCurrentSession(){
        Session session = sessionFactory.getCurrentSession();
        return  session;
    }

}

六、编写测试类

@Test
public void add(){
    Session currentSession = HibernateUtil.getCurrentSession();
    Transaction transaction = currentSession.beginTransaction();

    Customer customer1 = new Customer();
    customer1.setCust_name("customer1");
    Customer customer2 = new Customer();
    customer2.setCust_name("customer2");
    Customer customer3 = new Customer();
    customer3.setCust_name("customer3");

    Linkman linkman1 = new Linkman();
    linkman1.setLink_name("linkman1");
    Linkman linkman2 = new Linkman();
    linkman2.setLink_name("linkman2");
    Linkman linkman3 = new Linkman();
    linkman3.setLink_name("linkman3");

    /* 配置关系  双向维护*/
    customer1.getLinkmens().add(linkman1);
    customer1.getLinkmens().add(linkman2);
    customer2.getLinkmens().add(linkman3);

    linkman1.setCustomer(customer1);
    linkman2.setCustomer(customer1);
    linkman3.setCustomer(customer2);

    /* 保存数据 */
    currentSession.save(customer1);
    currentSession.save(customer2);
    currentSession.save(customer3);

    currentSession.save(linkman1);
    currentSession.save(linkman2);
    currentSession.save(linkman3);

    transaction.commit();

}

懒加载:

在 linkman.hbm.xml 中修改

<many-to-one name="customer" cascade="save-update"
            class="com.xzh.hibernate.domain.Customer" column="link_cust_id" lazy="false"/>

Hibernate 关系配置 随笔 第6张

@Test
public void get(){
    Session currentSession = HibernateUtil.getCurrentSession();
    Transaction transaction = currentSession.beginTransaction();
    Linkman linkman = currentSession.get(Linkman.class, 3L);
    transaction.commit();
    System.out.println(linkman.getLink_name());
    System.out.println(linkman.getCustomer().getCust_name());
}
@Test
public void delete(){
    Session currentSession = HibernateUtil.getCurrentSession();
    Transaction transaction = currentSession.beginTransaction();
    //删除  默认
    //打断两个表之间关系 ,再去删除记录 并没有级联去删除操作
    Customer customer = currentSession.get(Customer.class, 1L);
    currentSession.delete(customer);
    transaction.commit();
}
@Test
public void update() {
    Session currentSession = HibernateUtil.getCurrentSession();
    Transaction transaction = currentSession.beginTransaction();
    // 更新
    Linkman linkman1 = currentSession.get(Linkman.class, 1L);
    Customer customer2 = currentSession.get(Customer.class, 2L);
    // 关联
    // 单向维护
    customer2.getLinkmens().add(linkman1);
    // linkman1.setCustomer(customer2);

    // 双向维护 让一方放弃外键维护权
    // inverse="true" true 放弃外键维护权; false 不放弃外键维护权
    customer2.getLinkmens().add(linkman1);
    linkman1.setCustomer(customer2);
    transaction.commit();
}

级联操作

问题: 在两张表建立一对多关系时,如果只保存一边的对象,就会发异常

示例
Hibernate 关系配置 随笔 第7张

一、什么是级联

在操作一个对象的时候,是否会操作其关联的对象。

二、级联分类

  • 级联保存或更新
  • 级联删除

三、级联是有方向性

  • 在操作一的一方,是否会操作多的一方
  • 操作多的一方时, 是否会操作一的一方

四、级联保存或更新

1. 级联保存和级联更新

  • 操作的主体是谁,就要在谁的映射配置文件当中进行配置
  • 在开始配置的set当中添加一个新的属性 cascade="save-update"

Hibernate 关系配置 随笔 第8张

  • 再去运行,就不会报异常,两条记录都会被添加
  • 在一的一方添加级联

Hibernate 关系配置 随笔 第9张

2. 对象导航

  • 两方如果都加了级联,这种我们也称为双向导航
  • 设置双向导航时,当对象存在关系时, 就会做出对应的操作

五、级联删除

  • 删除一边数据时,同时将另一边的数据一并删除

1. 不设置级联删除。

默认:先打断表之间的关系,把外键改为空,然后再删除记录。

2.设置级联删除

示例代码
Hibernate 关系配置 随笔 第10张

六、双向级联

在双向级联的过程当中,会产生一些多余的sql语句

原因:当双向维护时,两都都维护了外键,当做更新操作时, 两边的外键都要去修改

解决办法:

1.使用单向维护

有些地方还是会有问题

2.一方放弃维护权

  • 在一的一方放弃外键维护权
  • 在配置文件当中添加一个inverse="false/true"
  • true为放弃外键维护权,false为不放弃外键维护权

3. cascade与inverse

  • cascade控制有没有关联对象
  • inverse控制有没有外键

多对多关系配置

一、建立表

1. 用户表

  • 一个用户可以有多个角色
CREATE TABLE `user` (
  `user_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '用户id',
  `user_code` varchar(32) DEFAULT NULL COMMENT '用户账号',
  `user_name` varchar(64) NOT 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=1 DEFAULT CHARSET=utf8;

2. 角色表

  • 一个角色可以被多个用户选择
CREATE TABLE `role` (
  `role_id` bigint(32) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(32) NOT NULL COMMENT '角色名称',
  `role_memo` varchar(128) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

3. 中间表

CREATE TABLE `user_role` (
  `role_id` bigint(32) NOT NULL,
  `user_id` bigint(32) NOT NULL,
  PRIMARY KEY (`role_id`,`user_id`),
  KEY `FK_user_id` (`user_id`),
  CONSTRAINT `FK_role_id` FOREIGN KEY (`role_id`) REFERENCES `role` (`role_id`),
  CONSTRAINT `FK_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

关系图

Hibernate 关系配置 随笔 第11张

二、建立ORM

1. 用户

@Setter@Getter
public class User {
    private Long user_id;
    private String user_code;
    private String user_name;
    private String user_password;
    private String user_state;
    // 一个用户选择多个角色
    private Set<Role> roles = new HashSet<Role>();

    @Override
    public String toString() {
        return "User{" + "user_id=" + user_id + ", user_code='" + user_code + '\'' + ", user_name='" + user_name + '\''
                + ", user_password='" + user_password + '\'' + ", user_state='" + user_state + '\'' + ", roles=" + roles
                + '}';
    }
}

2. 角色

@Setter@Getter
public class Role {
    private Long role_id;
    private String role_name;
    private String role_memo;
    // 角色下面的所有用户
    private Set<User> users = new HashSet<User>();

    @Override
    public String toString() {
        return "Role{" + "role_id=" + role_id + ", role_name='" + role_name + '\'' + ", role_memo='" + role_memo + '\''
                + '}';
    }
}

三、添加配置文件

1. 用户

<?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="com.xzh.hibernate.domain.User" table="user" >
        <!--建立类属性哪一个是主键  还要跟数据库当中主键进行对象-->
        <id name="user_id" column="user_id" >
            <generator class="native"/>
        </id>
        <!--建立类中的普通属性与数据库当中字段进行关联-->
        <property name="user_code" column="user_code" />
        <property name="user_name" column="user_name" />
        <property name="user_password" column="user_password" />
        <property name="user_state" column="user_state" />

        <!--多对多关系
          name:当前集合属性名称
          table:  多对多中间表  表名
          <key column=""></key>  :当前表的外键
          <many-to-many class="" column=""/>
                     class:集合中对象的全路径
                     column:集合中对象的外键
        -->
        <set name="roles" table="user_role" cascade="save-update" lazy="true">
            <key column="user_id"></key>
            <many-to-many class="com.xzh.hibernate.domain.Role" column="role_id"/>
        </set>
    </class>
</hibernate-mapping>

2. 角色

<?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="com.xzh.hibernate.domain.Role" table="role" >
        <!--建立类属性哪一个是主键  还要跟数据库当中主键进行对象-->
        <id name="role_id" column="role_id" >
            <generator class="native"/>
        </id>
        <!--建立类中的普通属性与数据库当中字段进行关联-->
        <property name="role_name" column="role_name" />
        <property name="role_memo" column="role_memo" />

        <!--多对多关系
          name:当前集合属性名称
          table:  多对多中间表  表名
          <key column=""></key>  :当前表的外键
          <many-to-many class="" column=""/>
          class:集合中对象的全路径
           column:集合中对象的外键
        -->
        <set name="users" table="user_role" inverse="true">
            <key column="role_id"></key>
            <many-to-many class="com.xzh.hibernate.domain.User" column="user_id"/>
        </set>
    </class>
</hibernate-mapping>

四、在核心配置文件当中添加两个新配置

Hibernate 关系配置 随笔 第12张

五、编写测试类

双向维护

  • 双向维护时,必须要有一方放弃外键维护
  • 如果两边都有维护的话, 就会有重复的的记录,由于关系表是两个字段作为共同主键,不能有相同的记录
  • 解决办法:通常都是让被动方放弃,用户选角色,角色为被动方
    ·
@Test
public void add() {
    Session currentSession = HibernateUtil.getCurrentSession();
    Transaction transaction = currentSession.beginTransaction();
    // 创建用户
    User user1 = new User();
    user1.setUser_name("user1");
    User user2 = new User();
    user2.setUser_name("user2");

    // 创建角色
    Role role1 = new Role();
    role1.setRole_name("role1");
    Role role2 = new Role();
    role2.setRole_name("role2");
    Role role3 = new Role();
    role3.setRole_name("role3");

    // 配置关系 单向维护
    user1.getRoles().add(role1);
    user1.getRoles().add(role2);
    user2.getRoles().add(role2);
    user2.getRoles().add(role3);

    // 双向维护 必须得要有一方放弃外键维护权 inverse="true"
    // 被动(被选择)的一方放权 如果没有放弃 会报异常 主键重复
    role1.getUsers().add(user1);
    role2.getUsers().add(user1);
    role2.getUsers().add(user2);
    role3.getUsers().add(user2);

    // 保存
    currentSession.save(user1);
    currentSession.save(user2);
    // cascade="save-update"
    // currentSession.save(role1);
    // currentSession.save(role2);
    // currentSession.save(role3);

    transaction.commit();
}

@Test
public  void test2(){
    //关系操作  操作内部集合
    Session currentSession = HibernateUtil.getCurrentSession();
    Transaction transaction = currentSession.beginTransaction();
    //用户1 添加一个新的角色3
    User user1 = currentSession.get(User.class, 3L);
    Role role3 = currentSession.get(Role.class, 6L);
    user1.getRoles().add(role3);
    role3.getUsers().add(user1);

    transaction.commit();
}

@Test
public  void test3(){
    Session currentSession = HibernateUtil.getCurrentSession();
    Transaction transaction = currentSession.beginTransaction();

    //把用户2的角色3   修改成角色1
    User user2 = currentSession.get(User.class, 2L);
    Role role3 = currentSession.get(Role.class, 3L);
    Role role1 = currentSession.get(Role.class, 1L);

    user2.getRoles().remove(role3);
    user2.getRoles().add(role1);
    transaction.commit();
}
扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄