hibernate详解(三)---对象的关系之关联关系

对象的关系

(1)依赖关系:如果A对象离开了B对象就不能编译,那么A对象依赖B对象;

(2)关联关系:如果A对象依赖B对象,并且把B对象作为A对象的一个属性,我们就说,A对象和B对象是关联关系;

  • 关联关系从多重性来讲:
    一对多:一个A包含多个B;

    多对一:多个A属于一个B;并且一个A只能属于一个B;

    多对多:一个A属于多个B,一个B可以属于多个A;

    一对一:一个A属于一个B,一个B属于一个A;

  • 关联关系从导航性来讲(java代码):通过A的某一个属性能够访问到和这个属性关联的B对象,A能够在这个关系上面导航到B;

  • 单向;如果针对一个关联关系,只能由一边通过属性导航到另一边,

  • 双向;如果针对一个关联关系,A能够通过属性导航到B,并且B也能够通过属性导航到A;

    关系的判定:

    1,关系的判定是针对实体判断,不是针对类来判断;

    2,关系的判定是针对一对特定的属性判断;

    3,关系的判定要结合具体的需求;

(3)聚合关系:是一个关联关系,部分和整体的关系;部分和整体是可以单独存在的,在系统当中,一定是两个模块在分别管理整体和部分;

(4)组合关系:是一种强聚合关系,但是部分和整理是不能单独存在的,在系统当中,一定是在一个模块中管理的;

(5)泛化关系:继承;

1、关联关系

1.1、单向多对一

(1)表的设计和SQL

在这里插入图片描述

(2)关于单向多对一的使用方法如下:

配置文件

<hibernate-mapping package="com.it520.many2one">
<!--配置one方  -->
<class name="Department" >
    <id name="id" column="id">
        <generator class="native"></generator>
    </id>
    <property name="name"></property>
</class>
<!--配置many方  -->
<class name ="Employee">
    <id name="id">
        <generator class="native"></generator>
    </id>
    <property name="name"></property>
    <!-- 配置和one方的关系,column熟悉是外键的类名 -->
    <many-to-one name="dept" column="dept_id"></many-to-one>
</class>
</hibernate-mapping>

测试类代码:(后续关联关系的介绍中就不列出测试类代码了)

public class Many2oneTest {

    @Before
    public void save(){
        Department d = new Department();
        Department d1 = new Department();
        d.setName("开发部");
        d1.setName("运维部");
        
        Employee e = new Employee();
        e.setName("张三");
        e.setDept(d);
        Employee e1 = new Employee();
        e1.setName("李四");
        e1.setDept(d1);
        
        Session session = HibernateUtil.getinstance().getSession();
        Transaction tx = session.beginTransaction();
        session.save(e);
        session.save(e1);
        session.save(d1);
        session.save(d);
        tx.commit();
        session.close();
    }
    @Test
    public void get() {
        Session session = HibernateUtil.getinstance().getSession();  
        Employee  e = (Employee)session.get(Employee.class, 1L);
        Employee  e1 = (Employee)session.get(Employee.class, 2L);
        System.out.println(e);
        System.out.println(e1);
        
    }
}

执行结果和SQL:

Hibernate: insert into Department (name) values (?)
Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (name, dept_id) values (?, ?)
Hibernate: insert into Employee (name, dept_id) values (?, ?)
Hibernate: select employee0_.id as id1_1_0_, employee0_.name as name2_1_0_, employee0_.dept_id as dept3_1_0_ from Employee employee0_ where employee0_.id=?
Hibernate: select employee0_.id as id1_1_0_, employee0_.name as name2_1_0_, employee0_.dept_id as dept3_1_0_ from Employee employee0_ where employee0_.id=?
Hibernate: select department0_.id as id1_0_0_, department0_.name as name2_0_0_ from Department department0_ where department0_.id=?
Employee [id=1, name=张三, dept=Department [id=2, name=开发部]]
Hibernate: select department0_.id as id1_0_0_, department0_.name as name2_0_0_ from Department department0_ where department0_.id=?
Employee [id=2, name=李四, dept=Department [id=1, name=运维部]]

(3)进一步研究get方法

  • 通过many方拿one方使用延迟加载,测试代码如下
public void get() {
    Session session = HibernateUtil.getinstance().getSession();  
    Employee  e = (Employee)session.get(Employee.class, 1L);
    Department d = e.getDept();
    System.out.println("==============");
    System.out.println(d);
    System.out.println(e);
}

结果:

Hibernate: select employee0_.id as id1_1_0_, employee0_.name as name2_1_0_, employee0_.dept_id as dept3_1_0_ from Employee employee0_ where employee0_.id=?
==============
Hibernate: select department0_.id as id1_0_0_, department0_.name as name2_0_0_ from Department department0_ where department0_.id=?
//department的select语句在==============之后,使用延迟加载;
Department [id=2, name=开发部]
Employee [id=1, name=张三, dept=Department [id=2, name=开发部]]
  • 通过ifnull来判断many方是否有one方关联;
@Test
public void get() {
    Session session = HibernateUtil.getinstance().getSession();  
    Employee e = (Employee) session.get(Employee.class, 1L);
    Employee e1 = (Employee) session.get(Employee.class, 2L);
    if(e.getDept()!=null) {
        System.out.println(e.getName()+"是属于"+e.getDept().getName());
    }else {
        System.out.println(e.getName()+"是没有部门的");
    }
    if(e1.getDept()!=null) {
        System.out.println(e1.getName()+"是属于"+e1.getDept().getName());
    }else {
        System.out.println(e1.getName()+"是没有部门的");
    }
}
  • 拿到的one方需要在session关闭之前实例化,不然就会出现no Session的错误
@Test
public void get() {
    Session session = HibernateUtil.getinstance().getSession();  
    Employee e1 = (Employee) session.get(Employee.class, 2L);
    Department dept = e1.getDept();
    session.close();
    System.out.println(dept);//session关闭之后实例化的,会有no Session的错误
}

注意:所有使用延迟加载拿到的对象都要在session关闭之前实例化。

(4)进一步研究save 方法

先保存many方再保存one方,会产生额外的SQL,这些SQL是由于持久化对象(many)的脏数据造成的;

先保存one方发出的SQL:

Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (name, dept_id) values (?, ?)

先保存many方发出的SQL:(会多出一条update的SQL)

Hibernate: insert into Employee (name, dept_id) values (?, ?)
Hibernate: insert into Department (name) values (?)
Hibernate: update Employee set name=?, dept_id=? where id=?

1.2、单向一对多

(1)表的设计和SQL

在这里插入图片描述
(2)配置文件

<!--配置one方  -->
<class name="Department">
    <id name="id">
        <generator class="native"/>
    </id>
    <property name="name"/>
    <set name="emps">
        <key column = "DEPT_ID"/>
        <one-to-many class = "Employee"/>
    </set>
</class>
<!--配置many方  -->	
<class name="Employee">
    <id name="id">
        <generator class="native"/>
    </id>
    <property name="name"/>
</class>

(3)进一步研究get方法

  • 通过one方的到many方使用延迟加载,在session关闭之前要使用many方的集合,不然会出现no Session的错误。
  • 不能提供ifnull来判断one方是否有many方,要通过集合的size()方法判断。
  • one方中的many方式一个集合,这时候只能使用集合接口。

1.3、双向多对一,一对多

双向many2one/one2many的问题:

(1)一定有额外的SQL发出;

在数据库里面,外键表示many2one或者one2many的关系,外键是没有方向的;但是在面向对象里面,关系有方向,所以两个方向都要去管理这个外键,造成的额外的SQL(这些多余的SQL都是ONE方想去管理many方造成的);

我们既想保留对象中的双向的关系;又想减少额外的SQL,就需要一方放弃对另一方的管理。

<class name="Department" >
    <id name="id">
        <generator class="native"></generator>
    </id>
    <property name="name"></property>
    <!-- inverse属性告诉Hibernate是否放弃对集合中对象的管理权,默认值为false(管理集合对象) -->
    <set name="emps" inverse="true">
        <key column="dept_id"></key>
        <one-to-many class="Employee"/>
    </set>
</class>
<class name="Employee">
    <id name="id">
        <generator class="native"/>
    </id>
    <property name="name"/>
    <many-to-one name="dept" column="DEPT_ID" />
</class>

save对象时没有设置inverse="true"时的SQL

Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (name, DEPT_ID) values (?, ?)
Hibernate: insert into Employee (name, DEPT_ID) values (?, ?)
Hibernate: update Employee set dept_id=? where id=?
Hibernate: update Employee set dept_id=? where id=?

save对象设置inverse="true"后的SQL

Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (name, DEPT_ID) values (?, ?)
Hibernate: insert into Employee (name, DEPT_ID) values (?, ?)

(2)对象的删除:

1、many方都可以直接删除;

2、删除one方:

  • inverse=“false”:可以正常删除;
Hibernate: update Employee set DEPT_ID=null where DEPT_ID=?
Hibernate: delete from Department where id=?
  • inverse=“true”:不能正常删除:
    外键约束错误;

(3)单向多对一,单向一对多,双向多对一一对多的选择。

1、90%的情况,使用单向的many2one;(删除使用HQL;)

2、9%的情况使用双向的many2one/one2many:( 自连接(树状结构); ,组合关系;)

3、1%的情况使用单向的one2many;(使用两个one2many来实现many2many)

1.4、hibernate中的集合

(1)在hibernate中,集合只能使用接口

(2)集合的使用;

  • Set:代表集合中的对象不能重复;集合中的对象没有顺序;对于Set集合,只能使用元素来映射;

  • List:集合中的对象可以重复,集合中的对象是有顺序的;
    1,要让one方知道many方的顺序,必须在many方的表中添加一列用来保存顺序;

    ​ 2,这个顺序只有one方知道,所以,这个列应该由one方来维护;

    ​ 3,在元素中添加来映射这个列;

    4,不管list的inverse=true,总要发送额外的SQL去维护这个many方的顺序;

<list name="emps" inverse="true">
    <key column="DEPT_ID" />
    <list-index column="SEQ" />
    <one-to-many class="Employee"/>
</list>
Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (name, DEPT_ID) values (?, ?, ?)
Hibernate: update Employee set SEQ=? where id=?//额外SQL维护这个many方的顺序,many方数据库也会多出SEQ列
  • 第二种映射List的方式:
    1,如果只是想把List作为一个集合,而不让hibernate来帮我们维护顺序,就可以是用

    2,bag元素不关心one中many方的顺序;

<bag name="emps" inverse="true" >
    <key column="DEPT_ID" />
    <one-to-many class="Employee"/>
</bag>
Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (name, sequence, DEPT_ID) values (?, ?, ?)
//没有额外的SQL,many方数据库也不会多出SEQ列

(3)选择

  • 确定集合中的对象是否能重复;

  • 如果使用List,尽量使用bag来映射;

  • 如果必须要让many方有顺序;在many方中添加一个表示顺序的属性

    第一步:在many方添加一个顺序的属性

    public class Employee {
        private Long id;
        private String name;
        private Integer sequence;//用来记录顺序的属性
        private Department dept;
    }
    

    第二步:在one方的集合上面添加order-by属性

    <bag name="emps" inverse="true" order-by="sequence" >
        <key column="DEPT_ID" />
        <one-to-many class="Employee"/>
    </bag>
    

    order-by属性的意思是,在加载many方集合的时候,使用order by语句按照many方的指定属性来查询;

    Hibernate: select * where emps0_.DEPT_ID=? order by emps0_.sequence
    

1.5、一对一

(1)表设计和SQL:实现一对一有两种方式

  • 使用many2one来实现one2one
    hibernate在这种方式映射one2one不是很好理解(不讲)
    在这里插入图片描述

  • 使用主键外键的方式来实现one2one

在这里插入图片描述
(2)映射文件

<!--  配置主对象-->
<class name="QQNumber">
    <id name="id">
        <generator class="native"/>
    </id>
    <property name="number" />
    <one-to-one name="zone" />
</class>
<!--  配置从对象-->	
<class name="QQZone">
    <id name="id">
        <!--  配置从对象-->
        <generator class="foreign">
            <param name="property">number</param>
        </generator>
    </id>
    <property name="title" />
    <one-to-one name="number" constrained="true"/>
</class>

1、在从对象中,从对象的主键需要参照number属性的主键来生成,所以从对象的主键生成策略为foreign;

2、在foreign策略中,需要配置一个property(属性),告诉hibernate,从对象的主键是根据哪个属性对应的主键来生成;

3、默认情况下,one2one并没有生成从对象主键的外键约束,在从对象中的one-to-one元素中添加constrained属性来为从表的主键添加外键约束;

4、不管是先保存从对象还是先保存主对象,甚至直接保存从对象,hibernate都会先保存主对象,在保存从对象;

5、从主对象拿从对象,不存在延迟加载的问题,hibernate使用一条SQL就把主对象和对应的从对象一起查询出来了;

思考:为什么hibernate在这个地方不用延迟加载,他是不想用,还是不能用?为什么?

6,从从对象拿主对象,使用延迟加载;(必须在session关闭之前实例化主对象;一定存在主对象;)

思考:为什么从对象拿主对象又可以使用延迟加载?

(3)选择:

  • 一般情况下,都不会使用one2one关系,我们一般都直接使用单向的many2one+业务逻辑来完成one2one;
  • 如果非得使用one2one,使用主键外键的方式实现;

1.6、多对多

(1)表设计和SQL
在这里插入图片描述
(2)映射文件

<class name="Teacher">
    <id name="id">
        <generator class="native"/>
    </id>
    <property name="name"/>
    <set name="students" table="STU_TEA">
        <key column="TEACHER_ID" />
        <many-to-many class="Student" column="STUDENT_ID"/>
    </set>
</class>

<class name="Student">
    <id name="id">
        <generator class="native"/>
    </id>
    <property name="name"/>
    <set name="teachers" table="STU_TEA" inverse="true">
        <key column="STUDENT_ID" />
        <many-to-many class="Teacher" column="TEACHER_ID"/>
    </set>
</class>

(3)进一步分析

1、在映射文件里面,set需要table属性,来告知中间表;

2、set中的key子元素代表,在中间表里面那格列作为外键关联到我自己;

3、many-to-many元素:和集合中的对象是多对多的关系;

4、many-to-many的column属性代表:在中间表里面那个列作为外键关联到对方表的主键;

5、many-to-many双方的配置其实是相反的;

6、如果配置的集合是set,hibernate会为中间表创建一个复合主键,

7、不管集合是list还是set,都需要让一边放弃对关系的维护inverse=true;

8、get的时候,使用延迟加载;

  • 必须在session关闭之前初始化;
  • 不能使用ifnull来判断集合中是否有对象,只能使用size()来判断;

9、一般来说,我们使用单向的many2many;
10、在项目中,我们应该让哪边来维护关系?需要看具体的设计.页面里面;哪个模块在管理关系,我们就把关系配置在哪边.

两个老师同时是两位学生的老师,save方法的SQL

Hibernate: insert into Teacher (name) values (?)
Hibernate: insert into Teacher (name) values (?)
Hibernate: insert into Student (name) values (?)
Hibernate: insert into Student (name) values (?)
Hibernate: insert into STU_TEA (TEACHER_ID, STUDENT_ID) values (?, ?)
Hibernate: insert into STU_TEA (TEACHER_ID, STUDENT_ID) values (?, ?)
Hibernate: insert into STU_TEA (TEACHER_ID, STUDENT_ID) values (?, ?)
Hibernate: insert into STU_TEA (TEACHER_ID, STUDENT_ID) values (?, ?)

数据库的表格
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值