Hibernate--从认识到细化了解
Posted progor
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hibernate--从认识到细化了解相关的知识,希望对你有一定的参考价值。
本文内容
- Hibernate的介绍与执行流程
- Hibernate运行环境搭建
- Hibernate的基础示例
- 持久类
- 核心文件配置
- 映射文件配置
- 建立与数据库的连接
- 增删查改CRUD
- 缓存
- 事务管理
- 关系映射
- 日志
首发日期:2018-07-31
Hibernate的介绍与执行流程
- 是一个开源的ORM框架,是一个持久层(通常都是将数据保存到数据库中)的框架。
- 根据ORM框架的特性,它可以根据对象的创建和保存(对于表的插入、修改),类的更改(对应表结构更改)等操作自动生成对应的SQL语句,而不需要像JDBC那样需要自己去拼接SQL语句,可以说操作对象就是操作表。
题外话:什么是ORM(对象关系映射)?
ORM 将数据库中的表与面向对象语言中的类建立了一种对应关系,【ORM可以说是参照映射来处理数据的模型,比如说:需要创建一个表,可以定义一个类,而这个类存在与表相映射的属性,那么可以通过操作这个类来创建一个表】
运行流程:
- 当运行时,Hibernate先去读取一个核心配置文件,读取Hibernate的核心配置(数据库连接信息、日志管理、数据表映射关系等)【在读取的过程中,会将读取的配置信息逐一配置给对应的组件(连接池、日志等等)。】;
- 载入核心配置文件的过程的最后是读取核心配置文件中标注的“映射关系文件”,载入“映射关系文件”,建立持久化类与数据表之间的映射(同时是否有对应的数据表,如果没有表可能会进行创建,表建立之后,再建立映射);
- 在核心配置和映射关系都读取完毕之后。Hibernate的基本运行配置就配置完毕了。在前面的加载配置中,返回的是一个Configuration对象,Configuration对象主要是用来获取配置文件的。
- 通过Configuration对象获取一个sessionFactory对象,sessionFactory对象真正负责解析配置文件中的配置选项并配置给各个组件,在很多时候,sessionFactory对象相当于一个连接池,它解析了hibernate的配置并负责分发会话连接。
- 通过sessionFactory对象来获取一个Session对象,有了Session对象就相当于有了JDBC中的Connection对象,它相当于与数据库的会话连接。它可以帮助我们操作数据库。
- 对于增删改操作,我们需要事务管理的协助。通过Session对象获取一个Transaction对象,Transaction对象负责数据库的事务管理。
- 通过Session对象操作数据库。
Hibernate运行环境搭建
下面的运行环境基于的是Hibernate5.3.0
1.在官网下载Hibernate5
2.解压下载的压缩包
3.在解压出来的文件夹中,lib存放着Hibernate运行所需要的jar包。
4.因为这里仅仅做最基础的运行,所以仅仅将required中的11个jar包导入到工程中。
如果你还需要其他功能,按照前面图中标注不同功能的文件夹来选择添加。比如想要使用C3P0,可以将在optional文件夹中c3p0文件夹下的包都导入。
5.导入Hibernate之外的包:
- 数据库驱动包:
- 日志包:
6.导入包之后,运行环境就基本搭建完毕了。后面都是具体需求具体配置了。
Hibernate的基础示例
这个基础示例作用是让你了解Hibernate的使用需要做什么?先让你了解需要做什么,后面再讨论每个里面能具体怎么做。
1.运行环境搭建,导入需要的依赖包。
2.创建持久类
public class Person { private Long pid; private String username; private Integer age; private Integer address; public Long getPid() { return pid; } public void setPid(Long pid) { this.pid = pid; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getAddress() { return address; } public void setAddress(Integer address) { this.address = address; } }
3.创建映射文件Person.hbm.xml
【这里还有个可选操作:创建数据表,数据表Hibernate也可以帮你创建(十分建议提前创建,不然你可能会遇到其他问题),这里就省略了。当然,数据库应该是已经创建好了的。但我已经踩过坑了,所以我这里就懒了。】
4.创建核心配置文件:hibernate.cfg.xml
5.读取核心配置文件
6.与数据库交互
持久类
- 持久类是用于跟数据表产生映射关系的类。 一个没有映射关系的类不是持久化类。
- 持久类的定义与普通的javabean没有什么区别,如果你了解javabean,那么你应该知道javabean有三个标准:
- 1.属性私有化
- 2.有无参构造函数 (hibernate同样需要利用反射)
- 3.提供共有的属性getter和setter函数。
- 除了以上与javabean相同的要求之外,还需要一个唯一标识属性。它相当于数据表中的主键,hibernate利用唯一标识属性来特异性地识别每个对象。
public class Person { private Long pid;//唯一标识属性,用来映射表中的主键 private String username; private Integer age; private Integer address; public Long getPid() { return pid; } public void setPid(Long pid) { this.pid = pid; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getAddress() { return address; } public void setAddress(Integer address) { this.address = address; } }
几个考虑遵守的规则:
1.持久化类的属性尽量使用包装类类型。【因为基础数据类型通常存在默认值并且通常不为null,null在数据表中是空的意思,但如果是0的话,意思就是不明确了--比如某个字段用0,1代表某种状态,那么默认值为0的话就不好知道是否是忘记设置状态了。】
2.持久化类不要使用final修饰,不然无法使用延迟加载功能(这个东西将会在后面的CRUD中的查询中讲),延迟加载功能依赖于代理对象,使用final修饰之后就无法产生代理对象了。
补充:
- 持久类还有三种状态需要认识,因为涉及到与数据库的交互和缓存区,所以留到后面缓存那里再讲。
核心文件配置
核心配置文件的配置方式有使用properties和xml这两种,我们主要使用xml版,因为xml版可以导入映射文件,而使用properties版的就需要自己在代码中引入映射文件。
使用XML配置核心文件:
- 命名要求:hibernate.cfg.xml 【保存路径:src目录下,否则需要在.configure()的时候传入它的路径】
- dtd文件去哪里找:依赖包下的下的中。
- 配置选项:
- 基础配置:这些配置是必需的。
- 数据驱动
- 格式:<property name="hibernate.connection.driver_class" >数据驱动</property>
- 例如:<property name="hibernate.connection.driver_class" >com.mysql.jdbc.Driver</property>
- url
- 格式:<property name="hibernate.connection.url">url</property>
- 例如:<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate</property>
- 用户名 hibernate.connection.username
- 密码 hibernate.connection.password
- SQL方言:定义了怎么转换SQL语句,就好像使用谷歌翻译需要确定转成哪种语言
- 格式:<property name="hibernate.dialect">方言</property>
- 例如:<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
- 映射关系:【注意,映射关系要放在最下面!】
- 格式:<mapping resource="hbm文件的路径"/>
- 例如:<mapping resource="work/domain/Person.hbm.xml"/>
- 数据驱动
- 可选配置:下面的配置是可选的
- 显示对应的sql语句:使用hibernate操作对象,会转成对应的sql语句,可以设置是否显示。
- 格式:<property name="hibernate.show_sql">true或false</property>【true是显示,false是不显示】
- 例如:<property name="hibernate.show_sql">true</property>
- 格式化显示的sql语句 :当选择显示sql语句后,默认的是一行显示的,使用格式化后,是多行的。
- 格式:<property name="hibernate.format_sql">true或false</property>
- 例如:<property name="hibernate.format_sql">true</property>
- 是否自动建表hibernate.hbm2ddl.auto :
- 格式:<property name="hibernate.hbm2ddl.auto">create或creat-drop或update或validate</property>
- 不配的时候:不使用hibernate的建表功能
- create:不论有没有表,每次都重新创建表。
- update:如果没有表,就创建;如果有了,就用已有;如果定义的表结构跟现有的不符合,那么修改成定义的表结构。
- create-drop:不论有没有表,每次都重新创建表,并且在用完后就会删除。
- validate:不会创建表,只会使用已经有了的表,所以如果结构不符合会报错。
- 例如:<property name="hibernate.hbm2ddl.auto">update</property>
- 格式:<property name="hibernate.hbm2ddl.auto">create或creat-drop或update或validate</property>
- 显示对应的sql语句:使用hibernate操作对象,会转成对应的sql语句,可以设置是否显示。
- 基础配置:这些配置是必需的。
那么,去哪里看能配什么,配哪些值呢?
在解压包的project的etc下面有一个hibernate.properties,里面有hibernate的核心配置信息。想使用什么功能,ctrl+f查找一下即可。
使用properties配置核心配置:
- 命名要求:hibernate.properties
- 配置选项:在xml的配置方法中,相信你已经了解了有哪些选项可以配置了。xml版的和properties版的配置选项是一样的。
- 格式:类似hibernate.connection.driver_class=com.mysql.jdbc.Driver
核心配置文件的加载:
- xml版本:
- properties版本:
c3p0连接池的配置:
首先需要配置hibernate的连接提供者:
然后配置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">20</property> <!--设定数据库连接的过期时间,以秒为单位 --> <property name="hibernate.c3p0.timeout">120</property>
最后,要记得导入c3p0的依赖包,它在hibernate文件夹的optional的c3p0中。
补充:
- 选项hibernate.XXX前面的hibernate.是可以省去的。
- 事务管理也需要核心配置文件的配置,这部分将在事务管理那里再讲。
- 还有很多的配置,但都相对少用,有需要再去properties查找一下即可。
映射文件配置
映射文件的结构大概如下:
怎么配置映射文件:
1. 映射文件名要求:类名.hbm.xml
2. 标准xml首部:<?xml version="1.0" encoding="UTF-8"?>
3. dtd约束:对于映射文件,它的dtd可以在中获取。打开文件,你能看到dtd写在了注释里面。hibernate5与hibernate3的相似,通常使用的都是3.0版本的。
4.创建标签hibernate-mapping
后续的内容都在hibernate-mapping中配置,下面讲hibernate-mapping怎么配置:
- hibernate-mapping
- 常用子标签:class标签
- class标签:用来建立类与表的映射关系
- 属性:
- name:对应的持久类的全路径
- table:对应的数据表的名字
- catalog:数据库名【比较少用】
- 常用子标签:id,property,set(涉及一对多,多对多),many-to-one(涉及一对多)
- 属性:
- id标签:用来建立类中的属性与表中的主键的对应关系
- 属性:
- name:对应的持久类的唯一标识属性的名字
- column:对于表中的主键名【如果表中的主键与持久类中的唯一标识属性同名,那么可以省去column】
- length:定义数据类型的长度
- type:字段的数据类型
- 常用子标签:generator
- 属性:
- generator标签:定义主键生成策略。这里暂时不讲,这是一个知识点,留到主键生成策略那里再讲。
- property标签:用来建立类中的普通属性与表的字段的对应关系
- 属性:
- name:对应的持久类的属性名
- column:对于表中的字段名【如果表中的字段名与持久类中的属性同名,那么可以省去column】
- length:定义数据类型的长度
- type:字段的数据类型【type和length主要是用来定义字段的属性的,如果你不关心字段的属性,只需要映射关系,那么可以省去。】【定义的时候,会对应去更改表结构;没有表的时候,会根据这些属性来创建新表;如果没表也没有这些关于字段的属性的属性,那么会使用默认值。比如varchar长度会达到255】
- not-null:字段是否有非空属性。【同样的,也是用来定义字段的属性的,不关系可以不用】
- unique:字段是否有唯一属性。【同样的,也是用来定义字段的属性的,不关系可以不用】
- 属性:
主键生成策略:
id标签用来配置主键,generator标签是用来配置主键生成策略的(主键的值的策略),generator的class的值常用的有如下几个
- increment:使用Hibernate自增长机制来生成主键。使用这个属性之后,使用自动建表,表会有主键属性。但它是线程不安全的,它的原理是获取数据库中主键当前最大值来确定新主键的值。所以它只能用于单线程。
- identity: 使用数据库自带的自增长机制,适用于DB2、SQL Server、MySQL、Sybase。
- sequence:使用数据库自带的自增长机制,适用于oralce、DB、SAP DB、PostgerSQL。
- uuid:随机生成字符串作为主键的值。
- native:使用数据库自带的自增长机制,会根据不同的数据库来采用identity或sequence。
- assigned:放弃管理,由自己调用setter函数给主键设置值。
- foreign:使用关联表的主键值作为主键的值。主要用于一对一关系中。【这个这里不讲】
补充:
- 映射文件的配置中还有一个重头戏-- 一对多、多对多关系的配置,因为这是一个重点内容,所以分到下面的关系映射中来讲。
建立与数据库的连接
上面已经讲述了配置核心配置文件,配置映射文件,下面就是与数据库的交互了。要与数据库交互要了解以下几个类
Configuration:
- 作用:它负责加载核心配置
- 使用:
- 对于xml版的核心配置文件:只需要Configuration cfg = new Configuration().configure() 返回的是读取了hibernate.cfg.xml配置的Configuration对象
- 对于properties版的核心配置文件:由于properties版的无法导入映射文件,所以需要加载配置:Configuration cfg = new Configuration(),然后再导入映射文件:configuration.addResource("com/itheima/hibernate/demo1/Customer.hbm.xml");【这里为了帮助了解使用,直接给了一个参照路径】
SessionFactory:
- 作用:加载了核心配置文件之后,就需要进行hibernate与数据库的连接了,SessionFactory对象在很多时候都仅仅相当于一个连接池对象。
- 使用:SessionFactory factory = configure.buildSessionFactory()
Session:
- 作用:SessionFactory对象相当于一个连接池对象,那么可以从它那里获取连接,返回的是一个Session对象,Session对象就相当于jdbc中的Connection对象,我们可以使用Session对象来操作数据库。
- 使用:Session session = factory.openSession();
- 如果使用session来操作数据库,我们下面再讲。这里主要讲连接。
Transaction:
- 作用:Hibernate的增删改操作需要事务管理,Transaction是事务管理对象,它可以通过Session对象来获取。
- 使用:
- 获取:Transaction transaction = session.beginTransaction();
- 提交事务:transaction.commit();
- 回滚事务:transaction.rollback();
了解上面对象的作用与使用之后,我们就可以得出以下与数据库建立连接并交互的步骤:
增删查改CRUD
了解了怎么建立与数据库的连接之后,下面就是CRUD的内容了。
新增:
1.创建持久类对象
2.session对象调用save(Object obj)来保存到数据库
查询:
查询是一个很重要的内容,考虑到这篇博文已经很长了的,所以我留到另外一篇博文去讲述了。
详情点击下面超链接。
修改:
- 修改有两种方法:
- 创建一个对象,给对象的唯一标识属性赋予一个值,给各个属性赋值,然后session.update(这个对象)【问题是需要给各个属性赋值】
- 先查询,再修改【推荐操作】:先查询出对象,修改好对象的指定属性,然后session.update(这个对象)
【这里给的例子使用的查询是OID查询,更多查询方法请查看"查询"那里的用法】
public void test1() { Configuration cfg = new Configuration().configure(); SessionFactory factory = cfg.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); Person person = session.get(Person.class, 2L); person.setUsername("葫芦娃"); session.update(person); transaction.commit(); }
删除:
- 删除有两种方法:
- 创建一个对象,给对象的唯一标识属性赋予一个值,然后session.delete(这个对象)
- 先查询,再删除【推荐操作】:先查询出对象,然后session.delete(这个对象)
【这里给的例子使用的查询是OID查询,更多查询方法请查看"查询"那里的用法】
public void test2() { Configuration cfg = new Configuration().configure(); SessionFactory factory = cfg.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); Person person = session.get(Person.class, 2L); session.delete(person); transaction.commit(); }
缓存
缓存:是一种优化的方式,将数据存入到内存中,使用的时候直接从缓存中获取,不用通过存储源。
- session管理着Hibernate的一级缓存。
- 缓存区中有一块特殊的区域--快照区,缓存区存储着本地对象,快照区存储着"查询到的对象"。
- 缓存区是用来存储被session管理的持久类对象的(查询得来的、新建并通过session保存的对象都会保存到缓存区),通常来说都是处于“持久态”的对象;而快照区存储的是查询来的对象,主要用于校验本地对象的数据跟数据表是否一致。
- 一个新建的对象是不会存储到缓存区的,只有它被session管理了,才会存储到缓存区中,并且不会存储到快照区中。
缓存对效率的影响:
- 查询一个持久类对象时,如果这个持久类对象缓存区已经有了,那么不会再发送SQL语句。
- 修改一个持久类对象后,如果缓存区的对象与快照区的对象的数据不一样,那么会自动更新到数据表中。
持久化类的三种状态:
瞬时态:新建的持久类,唯一标识属性没有值的时候,没有被session管理的时候。
持久态:唯一标识属性有值,被session管理的时候(要么是用session从数据库中查询出来的;要么是已经使用过session保存到数据库中,并且还没有被删除的。)。持久态的对象会存储到缓存区中,用来提高效率。
脱管态:唯一标识属性有值,不被session管理的时候。
持久类状态转换图:
事务管理
事务的开启、提交与回滚:
开始事务:Transaction transaction = session.beginTransaction();
事务提交:transaction.commit();
事务回滚:transaction.rollback();
事务隔离级别配置:
事务隔离级别配置要在核心配置文件中配置:
<property name="hibernate.connection.isolation">4</property>
值可以为:
1:Read Uncommitted
2:Read Committed
4:Repeatable Read
8:Serializable
不同函数中的事务管理:
使用Hibernate之后,操作数据库转成了操作一个个对象,当在一个函数中操作对象中,事务管理可以直接在函数中使用Transaction来管理。
但在使用Hibernate来实现DAO层的功能的时候,如果你把功能细化了,比如加钱是一个函数,减钱是一个函数。当需要考虑到事务管理的时候,你可能需要把一个session传入到这两个函数中才能实现事务管理。你可能会想到把session作为形参传进去,这是一个解决方法。但Hibernate提供了一个更好的解决方案。
这时候我们可以使用getCurrentSession来获取session,getCurrentSession是通过当前线程来获取一个session,同一个线程获取的session是同一个,所以同一个线程执行不同的函数依然可以很好的进行事务管理。
所以很多时候,factory.getCurrentSession()取代了Session session = factory.openSession()。
getCurrentSession的正常使用需要核心配置文件加上一个配置选项:
<property name="hibernate.current_session_context_class">thread</property>
关系映射:
在上面的基础示例中,演示的是一个单表的例子。而事实上,项目中的表与表之间是有关系的。并且我们经常需要利用这些关系来获取数据。
一对多关系:
【为了帮助理解,步骤与演示一起讲述,引用框里面不是引用,是例子演示。以班级与学生之间的一对多关系(假设一个班级可以有多个学生,一个学生只能有一个班级)为例】
1.创建“多”方的类,在普通的单表的基础上要多出一个“多”一方的类的对象,这个类的对象是用来映射成外键字段的,所以这个类不需要另外创建映射到外键的“属性”(一对多都是基于外键,不使用Hibernate的时候,可能需要手动去创建外键外键和手动去给外键指定值;但这里使用了Hibernate之后,它会自动映射成外键。)。
建立学生类Student,添加一个班级类Grade的对象--用来映射学生所对应的班级;
2.创建“一”方的类,在普通的单表的基础上要多出一个“一”一方的对象组成的集合(这里最好提前new一下,免得后面再去赋值,new了之后,后面就可以直接get来add了)。这个集合是用来帮助Hibernate处理它们之间的关系的,不会在表中生成新的字段。
建立班级类Grade,添加一个set集合,用来映射班级所对应的多个学生
3.建立"多"一方的hbm,像单表配置一样配置完“多”一方的属性(除去那个“一”一方的对象);配置剩下的那个“一”一方的对象需要使用many-to-one标签来配置,name属性是那个“一”一方的对象的变量名,class属性是“一”一方的类的全路径,column属性是“一”一方的对象要映射成的外键的字段名(这个字段名会出现在“多”一方的表中)。
建立Student.hbm.xml:
4.建立“一”一方的hbm,像单表配置一样配置完“一”一方的属性(除去那个“多”一方的集合对象);配置剩下的那个“多”一方的集合对象需要使用set标签来配置,name属性是那个“多”一方的集合对象的变量名,key中的column属性是”多“一方的外键字段名。one-to-many的class属性是“多”一方的类的全路径。
Grade.hbm.xml
5.在hibernate.cfg.xml中导入映射文件
导入映射文件
6.编写测试类,将”一“的一方的对象和”多“的一方的对象互相建立上关系(把一一方的对象设置到多的一方的对象的属性中,把多的一方的对象添加到一的一方的集合中),然后保存起来。
创建两个学生(李白、杜甫)、一个班级(唐朝);给学生的grade属性设置值,将两个学生加入到班级的学生集合中;把三个对象都保存起来。
public void test3() { Configuration configure = new Configuration().configure(); SessionFactory factory = configure.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); //创建两个学生一个班级 Student s1=new Student(); Student s2=new Student(); s1.setName("李白"); s2.setName("杜甫"); Grade g=new Grade(); g.setCname("唐朝"); //设置关系 s1.setGrade(g); s2.setGrade(g); g.getStudents().add(s1); g.getStudents().add(s2); //保存 session.save(s1); session.save(s2); session.save(g); transaction.commit(); }
多对多关系:
(假设一方为A,一方为B)
1.创建A的持久类,里面有一个B类对象组成的集合【例子演示以一个商品可以属于多个购物车,一个购物车可以有多个商品为例。】
创建购物车类ShopCar:
2.创建B的持久类,里面有一个集合,
创建产品类Product:
3.创建A的hbm,使用property配置好基本属性,使用set配置配置集合:set中的name的值是自身存储的对方对象的集合的变量名,table是中间表的名称(自动创建);key中的column的值是自身在中间表映射的外交字段名;many-to-many中class的值是对方类的全路径,column的值是对方类在中间表映射的外键字段名。
创建ShopCar.hbm.xml:
4.创建B的hbm,像第三步一样配置好
创建Product.hbm.xml:
5.在hernate.cfg.xml中导入映射文件
导入Product.hbm.xml和ShopCar.hbm.xml
6.编写测试类,将一个A类对象与多个B类对象建立关联,将一个B类对象与多个A类对象建立关联,把所有A和B的对象保存,查看中间表的数据。
编写测试类,建立两个Product,建立两个ShopCar,让他们之间相互都建立起关联。
public void test4() { Configuration configure = new Configuration().configure(); SessionFactory factory = configure.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); //新建两个订单,两个产品 ShopCar o1=new ShopCar(); ShopCar o2=new ShopCar(); o1.setCustomer("马云"); o2.setCustomer("马化腾"); Product p1=new Product(); Product p2=new Product(); p1.setProduct_name("聚宝盆"); p2.setProduct_name("招财猫"); //相互建立关系 o1.getProducts().add(p1); o2.getProducts().add(p1); o1.getProducts().add(p2); o2.getProducts().add(p2); //保存 session.save(o1); session.save(o2); session.save(p1); session.save(p2); transaction.commit(); }
级联操作:
什么是级联操作?当表之间有关系的时候,对一个表做操作(删除、修改),对应去更改关联的表。【注意!这里的关系相对于表而言,但对于对象来说,是对象中是否存储着另一方的对象(当然还需要映射关系的帮助),所以只有关联上的才会级联,没有关联上的不会级联】
一对多的级联操作设置:多中的set中的cascade,一中的many-to-one中的cascade
<many-to-one name="grade" class="work.domain.Grade" column="gid" cascade="save-update"></many-to-one>
多对多的级联操作设置:set中的cascade
<set name="shopcars" table="shopcar_product" cascade="save-update"> <key column="product_id"></key> <many-to-many class="work.domain.ShopCar" column="shopcar_id"></many-to-many> </set>
- cascade的值:
- save-update 【级联保存或修改】
- set中配置了,那么"一"一方的对象保存时,会同时保存建立了关系的"多"一方的对象。
- many-to-one中配置了,那么"多"一方的对象保存时,会同时保存建立了关系的"一"一方的对象。
- delete【级联删除】
- 这个比较少有与多对多,通常使用于一对多中的一一方(一个球队解散了,就删除对应的球员信息)。
- save-update 【级联保存或修改】
示例:
在学生hbm中加上cascade="save-update":那么只保存学生对象,也可以同时保存学生对象所管理的班级对象。
<many-to-one name="grade" class="work.domain.Grade" column="gid" cascade="save-update"></many-to-one>
public void test5() { Configuration configure = new Configuration().configure(); SessionFactory factory = configure.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); //创建两个学生一个班级 Student s1=new Student(); s1.setName("林则徐"); Grade g=new Grade(); g.setCname("清朝"); //设置关系 s1.setGrade(g); g.getStudents().add(s1); //保存 session.save(s1);//只保存学生,可以同时保存这个学生对象所关联的班级对象 transaction.commit(); }
题外话:对于一对多级联操作(假设一一方为A,一方为B),可能会产生多余SQL语句,原因是缓存区中A,B的数据都发生了变化了,使得不与快照区的数据映像相同,Hibernate默认就会对更新了的对象进行修改处理。
在新建的时候,不涉及到快照区,所以不会发生上述问题;在修改外键的时候就会发生,比如先查询出来一个A,两个B(数据会存储到缓存区和快照区),A原本仅仅与B1关联,现在增加了与B2的关联,那么这时候缓存区中的A对象发生了数据更改,B2对象也发生了更改。在提交的时候,校验缓存区和快照区的数据发现缓存区的A和B2与快照区的数据不同了,这时候hibernate会发起对A的数据更新(外键新增)以及对B2的数据更新(外键新增)。所以发起了两次外键新增,这就产生了多余的SQL语句。【而多对多中,Hibernate考虑到如果两个都有的话,会在中间表插入重复的,所以多对多不会发生这个问题(测试过!)】
解决方案:inverse是Hibernate定义的一种维护外键的属性,当这个属性设置为true时,那么设置的一方会放弃外键维护权(代表外键更新了它也不会去管理),对于一对多,通常都是在set上配置inverse=”true”,让多的一方去维护外键 。
日志
如果你已经导入前面说讲的那几个日志包了的话,那么你还需要一个log4j.properties就可以配置好你的日志功能了。
- 日志包:
至于log4j.properties怎么写,project/etc中有一个log4j.properties,可以参照那个来写。
# # Hibernate, Relational Persistence for Idiomatic Java # # License: GNU Lesser General Public License (LGPL), version 2.1 or later. # See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. # ### direct log messages to stdout ### log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n ### direct messages to file hibernate.log ### #log4j.appender.file=org.apache.log4j.FileAppender #log4j.appender.file.File=hibernate.log #log4j.appender.file.layout=org.apache.log4j.PatternLayout #log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n ### set log levels - for more verbose logging change ‘info‘ to ‘debug‘ ### log4j.rootLogger=warn, stdout #log4j.logger.org.hibernate=info log4j.logger.org.hibernate=debug ### log HQL query parser activity #log4j.logger.org.hibernate.hql.ast.AST=debug ### log just the SQL #log4j.logger.org.hibernate.SQL=debug ### log JDBC bind parameters ### log4j.logger.org.hibernate.type=info #log4j.logger.org.hibernate.type=debug ### log schema export/update ### log4j.logger.org.hibernate.tool.hbm2ddl=debug ### log HQL parse trees #log4j.logger.org.hibernate.hql=debug ### log cache activity ### #log4j.logger.org.hibernate.cache=debug ### log transaction activity #log4j.logger.org.hibernate.transaction=debug ### log JDBC resource acquisition #log4j.logger.org.hibernate.jdbc=debug ### enable the following line if you want to track down connection ### ### leakages when using DriverManagerConnectionProvider ### #log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace
写在最后:Hibernate毕竟是一个框架,市面上的书写它都能写它几百页,在一篇博文内不可能把内容写得详尽。所以这篇博文很大程度上主要是你让你了解Hibernate,剩下的就要你自己去了解了。获取百度才是你最好的老师?
这篇博文断断续续写了三天,很多时候都是在想以什么一种方式来讲述才能更好地让大家理解。最终成了这个样子,希望能帮到大家。
!--EndFragment-->!--EndFragment-->!--EndFragment-->
以上是关于Hibernate--从认识到细化了解的主要内容,如果未能解决你的问题,请参考以下文章
具有运行时 pojos 的带有 Hibernate 的 OSGi 片段包
面试问hibernate和mybatis的区别和对mybatis的认识