双向的many2one/one2many

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

我们既想保留对象中的双向的关系;又想减少额外的SQL,所以需要像下图一样配置映射文件

对象的删除:

1,many方都可以直接删除;
2,删除one方:
  1),inverse="false":可以正常删除;
    Hibernate: update Employee set DEPT_ID=null where DEPT_ID=?

    Hibernate: delete from Department where id=?
  2),inverse="true":不能正常删除:
  外键约束错误;

选择:
1,90%的情况,使用单向的many2one;(删除使用HQL;)
2,9%的情况使用双向的many2one/one2many:
  1),自连接(树状结构;);
  2),组合关系;
3,1%的情况使用单向的one2many;(使用两个one2many来实现many2many)

hibernate中的集合:
1,在hibernate中,集合只能使用接口
2,集合的使用;
  1),Set:代表集合中的对象不能重复;集合中的对象没有顺序;对于Set集合,只能使用<set>元素来映射;
  2),List:集合中的对象可以重复,集合中的对象是有顺序的;  

    	 <list name="ems" inverse="true">
<key column="Dept_ID"/>
<list-index column="SEQ"/>
<one-to-many class="Employee"/>
</list>

    1),要让one方知道many方的顺序,必须在many方的表中添加一列用来保存顺序;
    2),这个顺序只有one方知道,所以,这个列应该由one方来维护;
    3),在<list>元素中添加<list-index>来映射这个列;
    4),不管list的inverse=true,总要发送额外的SQL去维护这个many方的顺序;
  3),第二种映射List的方式:
    1),如果只是想把List作为一个集合,而不让hibernate来帮我们维护顺序,就可以是用<bag>
    2),bag元素不关心one中many方的顺序;

    	 <bag name="ems" inverse="true">
<key column="Dept_ID"/>
<one-to-many class="Employee"/>
</bag>

3,选择:
  1),确定集合中的对象是否能重复;
  2),如果使用List,尽量使用bag来映射;
  3),如果必须要让many方有顺序;在many方中添加一个表示顺序的属性,

1,在many方添加一个顺序的属性,并且在映射文件中设置

public class Emloyee{
private Long id;
private String name;
private Department dept;
private Integer sequence;
//省略get/set
}
    <class name="Employee" table="employee">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<property name="sequence" column="sequence"/>
<many-to-one name="dept" column="Dept_ID"/>
</class>

2,在one方的集合上面添加order-by属性

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

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

级联:
组合关系:强聚合关系,代表整体和部分之间不能分开,整体和部分单独存在没有意义;
分析:
  1,组合关系的两个对象都是在一个模块当中管理的;都是在整体那个对象的模块中管理;
  2,对于销售订单对象来说:
    1),存在一个列表;
    2),添加:在添加页面需要添加销售订单对象上的属性,还需要添加这个销售订单的明细数据;
    3),修改:仍然在销售订单的编辑页面中完成;
    4),删除:在销售订单列表中删除销售订单;
  3,对于销售订单明细对象来说:
    1),对于销售订单明细对象,不需要列表;
    2),添加:
      1),在销售订单对象的添加的时候,可以添加销售订单明细对象;
      2),在销售订单对象的保存,可以添加销售订单明细对象;
    3),修改:在销售订单对象的修改方法中完成的;
    4),删除:在销售订单对象的修改方法中完成的;在删除销售订单的时候,也要去删除这个销售订单的明细;
分析执行流程:
  1,只需要有销售订单对象的DAO接口就可以了,销售订单明细的CRUD都是在销售订单对象的DAO中完成的;
  2,save:
    1),保存销售订单对象;
    2),保存销售订单对象对应的销售订单明细对象;
  3,update:
    1),修改销售订单对象(游离);
    2),保存新的销售订单明细对象(临时对象);
    3),修改修改过的销售订单明细对象(游离);
    4),删除去掉了的销售订单明细对象(删除和主对象没有关系的销售订单明细对象);
  4,delete:
    1),删除销售订单对象的明细;
    2),删除主对象;

使用级联来完成SaleBillDAOImpl:(在组合关系中,一般把整体称为主对象,把部分称为子对象)
  1,在one方的集合上面,可以添加cascade属性,这个属性的意思就叫做级联;
  2,级联能根据配置的级联策略,对这个集合里面的对象自动做一些操作;

级联策略:
  1,save-update:在保存或者修改主对象的时候,去级联的持久化临时的子对象,修改游离的子对象;
  2,delete:在删除主对象的时候,去级联的删除所有的子对象;
  3,all:save-update+delete
  4,delete-orphan:删除和主对象打破关系的子对象;
  5,all-delete-orphan:all+delete-orphan
  6,一般来说,对于组合关系,我们就直接使用all-delete-orphan就可以了;

PS:
Hibernate.initialize(bill.getItems());
可以使用Hibernate.initialize去初始化一个代理对象;

一对一:
1,使用many2one来实现one2one

hibernate在这种方式映射one2one不是很好理解(不讲)

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

两种实现方式的区别:
1,使用第一种方式两个对象没有先后之分,都可以独立存在;
2,使用第二种方式,主对象必须先存在,再有从对象;

 映射文件(以QQ和QQ空间的关系为例):

    <class name="QQNumber" table="qqNumber">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="number" column="number"/>
<one-to-one name="zone"/>
</class> <class name="QQZone" table="qqZone">
<id name="id" column="id">
<!-- foreign表示参照其他主键来生成主键id -->
<generator class="foreign">
<!-- 根据属性number对应的类的主键生成主键 -->
<param name="property">number</param>
</generator>
</id>
<property name="title" column="title"/>
<!-- constrained表示在主键上面添加number属性的外键约束 -->
<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,从从对象拿主对象,使用延迟加载;
  1),必须在session关闭之前实例化主对象;
  2),一定存在主对象;
思考:为什么从对象拿主对象又可以使用延迟加载?

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

组件关系:

粗粒度的对象设计

public class Company {

	private Long id;
private String name; private String provice;
private String city;
private String street; private String regProvice;
private String regCity;
private String regStreet;
//省略get/set
}

细粒度的对象设计

public class Company {

	private Long id;
private String name; private Address address;
private Address regAddress;
//省略get/set
}
public class Address {

	private String provice;
private String city;
private String street;
//省略get/set
}

分析:
1,对象粒度细,分工明确;
2,地址表可以被其他的表重复使用;
3,需要连接很多表才能完成查询;

为了提高性能,我们可以把address的所有内容直接放到company表中,在表里面,相当于只有一张表,在对象中,有多个对象;
我们把单独存在没有意义,必须在其他对象中存在才有意义的对象(没有自己的表,它的属性都是在主表中),把这种对象称为组件对象
我们把组件对象依赖的那个主对象,称为组件对象的宿主对象;

分析
1,对于对象来说,对象粒度够细;
2,对于宿主对象来说,查询够快;
3,组件对象的内容不能被其他对象直接使用;

映射文件:

	<class name="Company" table="company">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/> <!-- 配置组合关系 -->
<component name="address">
<property name="provice"/>
<property name="city"/>
<property name="street"/>
</component> <component name="regAddress">
<property name="provice" column="REG_PROVICE"/>
<property name="city" column="REG_CITY"/>
<property name="street" column="REG_STREET"/>
</component>
</class>

说明
1,只有一个映射文件(宿主对象的映射文件);
2,组件对象使用component元素来映射,在component元素中,使用property子元素来映射组件对象中的属性;
3,如果一个宿主对象中存在同一个组件对象2个以上;必须在其中一个的property中修改列的名字;
4,查询的时候使用一条SQL查询,不存在延迟加载问题;

 

many2many(以老师和学生的关系做为例子):

表的设计和SQL

映射文件

	<class name="Teacher" table="teacher">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<set name="students" table="STU_TEA">
<key column="TEA_ID"/>
<many-to-many class="Student" column="STU_ID"/>
</set>
</class> <class name="Student" table="student">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="teachers" table="STU_TEA" inverse="true">
<key column="STU_ID"/>
<many-to-many class="Teacher" column="TEA_ID"/>
</set>
</class>

进一步研究:
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的时候,使用延迟加载;
  1),必须在session关闭之前初始化;
  2),不能使用ifnull来判断集合中是否有对象,只能使用size()来判断;
9,一般来说,我们使用单向的many2many;
10,在项目中,我们应该让哪边来维护关系?需要看具体的设计.页面里面;
哪个模块在管理关系,我们就把关系配置在哪边(如下图的设计,便是把关系配置在项目模块).

泛化关系(继承关系):
one-table:

  表结构和SQL

映射文件

	<class name="Product" table="product" discriminator-value="1">
<id name="id" column="id">
<generator class="native"/>
</id>
<!-- discriminator表示一个鉴别器,type表示鉴别器所属列的类型 -->
<discriminator column="TYPES" type="int"/>
<property name="name" column="name"/> <subclass name="Book" discriminator-value="2">
<property name="isbn"/>
<property name="author"/>
</subclass> <subclass name="Clothes" discriminator-value="3">
<property name="color"/>
</subclass>
</class>

分析:
1,整个继承体系的所有对象的所有属性全部放在一张表里面;
2,只需要一个映射文件,并且这个映射文件映射整个继承体系的根对象;
3,需要创建一个鉴别器的列;
4,需要为每一个类设置对应鉴别器列的值;
5,在多态查询的时候,需要查询出所有的列,并根据鉴别器的值选择需要实例化的真正对象;

one-table的优劣势
1,优点:简单;多态查询非常块;
2,缺点:表结构不稳定;数量增长很快,要查询某一个具体的类型很慢;null字段;
  没法使用非空约束;

pre-table:

表结构和SQL

映射文件

	<class name="Product" table="product">
<id name="id" column="id">
<generator class="increment"/>
</id>
<property name="name" column="name"/> <union-subclass name="Book" table="book">
<property name="isbn"/>
<property name="author"/>
</union-subclass> <union-subclass name="Clothes" table="clothes">
<property name="color"/>
</union-subclass>
</class>

小结:
1,继承体系中的每一个对象单独放在一个表里面;
2,为了防止id的冲突,所以per-table的方式要求id不能使用自动增长的方式;
3,使用union-subclass映射子类
4,在多态查询的时候,需要把所有的表使用union拼装成一个大表,并在这个大表中使用class来分辨具体的子类型;

优劣势:onetable的优势就是pertable的劣势;onetable的劣势就是pertable的优势

选择:
1,一般情况尽量避免使用继承;
2,如果真的需要使用继承,请使用onetable的方式;

 

映射枚举类型:

在枚举里面,一个枚举实例有两个值:
name:枚举的名字,可以使用枚举类型.valueOf(String)方法来还原这个枚举类型实例;
1,数据库直观;2,枚举类型位置随意变化;

ordinal:枚举在该类型中的位置;可以使用Sex.values()[0]来根据索引位置类还原这个枚举类型实例;
2,可以修改枚举类型的名字;

映射方式:

		<property name="sex">
<!-- 此处类型需要引入使用枚举类的类,全限定名如下 -->
<type name="org.hibernate.type.EnumType">
<!-- 引入枚举类的类型,写全限定名 -->
<param name="enumClass">com.rk1632._07_enum.Sex</param>
<!-- 是否使用枚举属性名作为数据库保存的方式 -->
<!-- 如果为false,则会使用枚举属性的顺序作为数据库保存数据的方式(从零开始) -->
<param name="useNamed">true</param>
</type>
</property>

1,如果使用名字的方式映射,设置useNamed为true,如果使用位置的方式,useNamed为false;
2,记得,枚举类型要写全限定名;

Hibernate-day03的更多相关文章

  1. Hibernate day03笔记

      Hibernate的关联关系映射:(多对多) 多对多的配置: 步骤一创建实体和映射: Student: public class Student {     private Integer sid ...

  2. Hibernate-ORM:11.Hibernate中的关联查询

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 本篇博客将讲述Hibernate中的关联查询,及其级联(cascade)操作,以及指定哪一方维护关联关系的(i ...

  3. Hibernate-ORM:09.Hibernate中的getCurrentSession()

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 本篇博客将讲述,以优雅的方式创建session对象,我将会说明优点,并提炼成工具类 优点: 1.无需手动关闭s ...

  4. hibernate多对多关联映射

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  5. 解决 Springboot Unable to build Hibernate SessionFactory @Column命名不起作用

    问题: Springboot启动报错: Caused by: org.springframework.beans.factory.BeanCreationException: Error creati ...

  6. hibernate多对一双向关联

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  7. Hibernate中事务的隔离级别设置

    Hibernate中事务的隔离级别,如下方法分别为1/2/4/8. 在Hibernate配置文件中设置,设置代码如下

  8. Hibernate中事务声明

    Hibernate中JDBC事务声明,在Hibernate配置文件中加入如下代码,不做声明Hibernate默认就是JDBC事务. 一个JDBC 不能跨越多个数据库. Hibernate中JTA事务声 ...

  9. spring applicationContext.xml和hibernate.cfg.xml设置

    applicationContext.xml配置 <?xml version="1.0" encoding="UTF-8"?> <beans ...

  10. [原创]关于Hibernate中的级联操作以及懒加载

    Hibernate: 级联操作 一.简单的介绍 cascade和inverse (Employee – Department) Casade用来说明当对主对象进行某种操作时是否对其关联的从对象也作类似 ...

随机推荐

  1. [物理学与PDEs]第5章第2节 变形的描述, 应变张量 2.1 变形梯度张量

    $$\bex \rd{\bf y}={\bf F}\rd {\bf x}, \eex$$ 其中 ${\bf F}=\n_x{\bf y}=\sex{\cfrac{\p y_i}{\p x_j}}$ 为 ...

  2. [物理学与PDEs]第4章第2节 反应流体力学方程组 2.3 混合气体状态方程

    1.  记号与假设 (1)  已燃气体的化学能为 $0$. (2)  单位质量的未燃气体的化学能为 $g_0>0$. 2.  对多方气体 (理想气体当 $T$ 不高时可近似认为), $$\bex ...

  3. Dilated Convolution

    各种各样的卷积方式, 详细见 各种卷积的 gif 图 Convolution animations  Padding, strides Transposed convolution animatio ...

  4. Easyui Tab使用

    常见问题: 1. easyui 在子tab页中打开新tab页(关于easyUI在子页面增加显示tabs的一个问题) https://blog.csdn.net/u014805893/article/d ...

  5. 【原创】运维基础之Zabbix(1)简介、安装、使用

    zabbix 4 官方:https://www.zabbix.com/ 一 简介 Monitor anythingSolutions for any kind of IT infrastructure ...

  6. Python-Django基础

    django目录 -settings -urls -views ******强调:setting中'django.middleware.csrf.CsrfViewMiddleware'中间件先注释掉 ...

  7. ES6随笔

    let, const 这两个的用途与var类似,都是用来声明变量的,但在实际运用中他俩都有各自的特殊用途.首先来看下面这个例子: var name = 'zach' while (true) { va ...

  8. 【sklearn】数据预处理 sklearn.preprocessing

    数据预处理 标准化 (Standardization) 规范化(Normalization) 二值化 分类特征编码 推定缺失数据 生成多项式特征 定制转换器 1. 标准化Standardization ...

  9. java 学习之环境配置

    准备JDK 到Java官网下载jdk即可,网址:http://www.oracle.com/technetwork/java/javase/downloads/index.html. 配置开发环境 鼠 ...

  10. xilinx和altera的fpga的不同之处!----如果不知道,你将为之付出代价! --转载

    本人从2004年接触fpga开始,至今已经8年了.开发过altera的flex系列和cyclone3系列:开发过xilinx的vii和v5系列.下面谈谈本人对二者的一些不同,以便引起开发者对一些细节上 ...