一级缓存
概念
*在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java集合构成了Session缓存.
只要 Session 实例没有结束生命周期, 存放在它缓存中的对象也不会结束生命周期
*当session的save()方法持久化一个对象时,该对象被载入缓存,
以后即使程序中不再引用该对象,只要缓存不清空,该对象仍然处于生命周期中。
当试图get()、 load()对象时,会判断缓存中是否存在该对象,有则返回,此时不查询数据库。没有再查询数据库
*Session 能够在某些时间点, 按照缓存中对象的变化来执行相关的 SQL 语句,
来同步更新数据库, 这一过程被称为刷出缓存(flush)
*一级缓存为session级别的缓存,既将数据保存session中。
*一级缓存功能为hibernate内置,不能设置取消,必须使用的。
*一级缓存为提高性能
* session提供 map容器,用于执行一些指定的操作时,进行相应PO对象缓存的。
默认情况下 Session 在以下时间点刷出缓存:
1.当应用程序调用 Transaction 的 commit()方法的时, 该方法先刷出缓存(session.flush()),然后在向数据库提交事务(.commit())
2.当应用程序执行一些查询操作时,如果缓存中持久化对象的属性已经发生了变化,会先刷出缓存,以保证查询结果能够反映持久化对象的最新状态
3.调用Session的flush()
方法
一级缓存常见操作
1. flush它就是用于刷新一级缓存的。
2. clear 清空一级缓存中所有对象
3. evict 清空一级缓存中指定对象
4. refresh 它是使用数据库的数据同步一级缓存与快照 (重新查询数据库,更新快照和一级缓存)
证明一级缓存存在
@Test public void demo01(){ //证明:一级缓存 Session session = H3Utils.getCurrentSession(); session.beginTransaction(); // 1 执行select ,将结果缓存到一级缓存 Category category = (Category) session.get(Category.class, "142729-19960808-1231"); System.out.println(category); //2 如果一级缓存有数据,将直接获取 Category category2 = (Category) session.get(Category.class, "142729-19960808-1231"); System.out.println(category2); session.getTransaction().commit(); }
一级缓存的FlushMode
(设置缓存的flush模式)
session.setFlushMode(FlushMode.AUTO);
ALWAYS和AUTO的区别:
当hibernate缓存中的对象被改动之后,会被标记为脏数据(即与数据库不同步了)。
如果session设置为FlushMode.AUTO时,hibernate在进行查询的时候会判断缓存中的数据是否为脏数据,是则刷数据库,不是则不刷,
而always是直接刷新,不进行任何判断。很显然auto比always要高效得多
Hibernate 快照Snapshot
概念
当session加载了customer对象后,会为customer对象的值类型的属性复制一份快照。
当刷出缓存时,通过比较对象的当前属性和快照,来判断对象的哪些属性发生了变化
*Hibernate快照可以让持久化状态的对象具有自动更新能力。
快照时机:一级缓存内容备份,只要数据进行一级缓存,快照就生成了。但一般情况只在查询时使用快照。
快照区它就是用于存储一级缓存中的对象的散列数据
持久对象常用操作
1.1delete 方法
*方法既可以删除一个脱管对象, 也可以删除一个持久化对象
*如果删除脱管,先将脱管对象与 Session 关联,然后再删除
*执行delete,先删除一级缓存数据,在session.flush操作时,删除数据表中数据
1.2 save方法
* 将瞬时态转换 持久态。
* 当OID类型为:代理主键,执行save方法时,将触发insert语句,确定OID的值。直到commit数据才进入数据库。
* 当OID类型为:自然主键,执行save方法时,此时不触发insert语句,直到commit才触发insert语句,数据进入数据库。
如果OID在数据库中已经存在记录将抛异常。\
1.3 saveOrUpdate方法
* 对象时瞬时态,执行saveOrUpdate,底层执行save方法。如果对象时脱管态,执行saveOrUpdate,底层执行update方法。
* OID 代理主键,OID如果没有值,执行save方法;如果有值(setUid(,,)),执行update方法。
如果设置oid,但数据库中没有对应记录,将抛异常。
<id name="uid"unsaved-value="3">
判断OID是否存在,与“unsaved-va*ue”设置内容进行比较,如果没有设置,此内容为类型对应默认值。例如:Stringnu** ; int 0等
* OID 自然主键,先执行se*ect查询,获得主键的值,OID不存在save,OID存在update。
1.4 update方法
* 脱管态转换 持久态
* 当执行update方法,默认情况都将执行update语句,即使数据没有变更。
通过映射文件配置 select-before-update 在更新前先查询,如果数据一样,将不行update。
注意:使用于更新不频繁的应用。
<hibernate-mapping>
<classselect-before-update="true">
<id name="uid">
Hibernate关联关系映射
系统设计中三种实体关系
图
回顾-表与表之间建立关系:
一对一
person 人 idcard 身份证
表
</pre><pre name="code" class="plain">Create table person( Id int primary key auto_increment, Name varchar(20), Iid int, //foreign key(id) references idcard(pid) ) Create table idcard( Id int primary key auto_increment, number varchar(20), //pid int foreign key(id) references person(iid) )
类与类之间(1:1)
Class Person{ Privateint id; PrivateString name; PrivateIdCard card; } Class IdCard{ Privateint id; PrivateString number; PrivatePerson p; }
一对多 多对一
在开发中这种关系是最常见的。
Customer 客户 Order订单
表
Create table Customer( Idint primary key auto_increment, Namevarchar(20) ) createtable Orders( Idint primary key auto_increment, Pricedouble, cidint Forengnkey(cid) reference customer(id) )
类
class A{ //一对多:一个A 拥有 多个 B -- 采用容器(array、list、set、map等) // 建议:不重复、没有顺序 优选Set集合 private Set<B> setB = new HashSet<B>(); //建议:容器都要进行实例化,方便使用。 } class B{ //多对一: 多个B 属于 【一个A】 private A a; }
Class Customer{ Intid Stringname; Set<Order>orders; } Class Order{ Intid Doubleprice; Customerc; }
多对多
学生Student 课程Course
表
Create table Student( Idint primary key auto_increment, Namevarchar(20) ) Create table Course( Idint primary key auto_increment, Namevarchar(20) )
会产生中间表来描述关系
Create table s_c( Sidint, cid,int Foreignkey(sid) reference student(id), Foreignkey(cid) reference course(id), Primarykey(sid,cid) )
类
class A { // 多个A 所有 【不同 B】 private Set<B> setB = new ... } class B{ // 多个B属于 【不同A】 private Set<A> setA = new ...; }
Class Student{ Intid; Stringname; Set<Course>cs } Class Course{ Intid; Stringname; Set<Student>ss; }
客户和订单是典型的一对多 关联关系
在多方添加一方关联
配置文件
在 Order.hbm.xml 配置<many-to-one>
<many-to-one name="customer" class="cn.itcast.one2many.Customer" column="customer_id"> </many-to-one>
* name:设定待映射的持久化类的名字。
* column:设定和持久化类的属性对应的表的外键。
* class:设定持久化类的属性的类型。
* not-null:是否允许为空。
在一方添加多方集合对象
配置文件
在Customer.hbm.xml 添加<set> 元素
<set name="orders"> <key column="customer_id" not-null="true"></key> <one-to-many class="cn.itcast.one2many.Order"/> </set>
*name :设定Customer中集合类型属性名称
*<key column :设置生成数据表中外键字段名称
*not null :设置外键字段非空约束
*<one-to-many > :设置一对多映射关系
*class :设置映射关联目标PO类
一对多数据库操作
保存客户和订单(单向关联)
/** * #3 提供客户和订单,客户关联订单,只保存客户 -- 抛异常 * * 现象:持久态 Customer 关联 瞬时态的Order,之后操作要求数据必须一致。 * * 方案1:将order从瞬时态 转换成持久态 save(order) */ @Test public void demo03(){ Session session = factory.openSession(); session.beginTransaction(); //1 提供客户 Customer customer = new Customer(); //瞬时态 customer.setCname("dzd"); //2提供订单 Order order = new Order(); //瞬时态 order.setPrice("1000"); //3客户关联订单 customer.getOrderSet().add(order); //4 保存客户 session.save(customer); //持久态 , insert session.save(order); //insert session.getTransaction().commit(); //管理数据 update session.close(); }
持久对象关联瞬时对象异常:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing:
保存客户和订单(双向关联)
/** * #4 提供客户和订单,客户关联订单,订单关联客户,保存客户和订单 */ @Test public void demo04(){ Session session = factory.openSession(); session.beginTransaction(); //1 提供客户 Customer customer = new Customer(); //瞬时态 customer.setCname("dzd"); //2提供订单 Order order = new Order(); //瞬时态 order.setPrice("1000"); //3 双向关联 //3.1客户关联订单 customer.getOrderSet().add(order); //触发update语句,commit时执行 //3.2订单关联客户 order.setCustomer(customer); //4 保存客户 session.save(customer); //持久态 , insert session.save(order); //insert session.getTransaction().commit(); //管理数据 update session.close(); } /** * #4 提供客户和订单,客户关联订单,订单关联客户,保存客户和订单 * * 多方必须维护外键信息,如果一方没有OID值,将触发update语句。 * * 一方默认详情对外键信息进行维护,建议一方放权对外键信息的维护。 * 配置:Customer.hbm.xml <set inverse="true"> * 如果设置成true表示一方将放弃对外键信息的维护。 */ @Test public void demo05(){ Session session = factory.openSession(); session.beginTransaction(); //1 提供客户 Customer customer = new Customer(); customer.setCname("dzd"); //2提供订单 Order order = new Order(); order.setPrice("1000"); //3 双向关联 //3.1客户关联订单 customer.getOrderSet().add(order); //4触发update,更新外键信息 //3.2订单关联客户 order.setCustomer(customer); //3 触发update,更新外键信息 //4 保存客户 session.save(order); //1 执行insert order语句 , customer_id == null session.save(customer); //2执行insert customer 语句 session.getTransaction().commit(); session.close(); } }
一对多:inverse配置
修改Customer.hbm.xml
<set name="orderSet" inverse="true"> <key column="customer_id"></key> <one-to-many class="com.itheima.b_onetomany.Order"/> </set>
一方默认详情对外键信息进行维护,建议一方放权对外键信息的维护。
* 配置:Customer.hbm.xml <set inverse="true">
* 如果设置成true表示一方将放弃对外键信息的维护。
级联保存
<set name="orderSet" cascade="save-update"> <key column="customer_id"></key> <one-to-many class="com.itheima.c_onetomany.Order"/> </set>
/** * 级联操作: * *方案2: save-update : 级联保存或更新。A关联瞬时态B,当保存A时,自动将瞬时态B,转换成持久态B * 客户级联保存订单,Customer.hbm.xml <set cascade="save-update"> */ @Test public void demo01(){ Session session = factory.openSession(); session.beginTransaction(); //1 提供客户 Customer customer = new Customer(); customer.setCname("dzd"); //2提供订单 Order order = new Order(); order.setPrice("1000"); //3.1客户关联订单 customer.getOrderSet().add(order); //4 保存客户 session.save(customer); session.getTransaction().commit(); session.close(); }
级联删除
/** * 级联删除 * * 默认:查询客户,删除客户 --将关联订单的外键设置成null,然后再删除。 * * 级联删除:删除客户时,一并将订单删除。 * Customer.hbm.xml <set cascade="delete"> */ @Test public void demo02(){ Session session = factory.openSession(); session.beginTransaction(); //1 提供客户 Customer customer = (Customer) session.get(Customer.class, 9); //2删除 session.delete(customer); session.getTransaction().commit(); session.close(); }
孤儿(孤子)删除
/** 孤儿删除(孤子) * 默认:将指定订单与客户 解除关系 将从表的外键设置null * 一对多,存在父子关系,主表父表,从表子表。 * 孤儿删除,解除关系之后,从表外键设置null,从表记录就是孤儿(没有爹),一并进行删除。 * Customer.hbm.xml <set cascade="delete-orphan"> */ @Test public void demo03(){ Session session = factory.openSession(); session.beginTransaction(); //1 查询客户 Customer customer = (Customer) session.get(Customer.class, 8); //2 查询订单 Order order = (Order) session.get(Order.class, 7); //3 将指定订单与客户 解除关系 customer.getOrderSet().remove(order); session.getTransaction().commit(); session.close(); }
级联删除和孤儿删除 对比
*级联删除:删除客户时,一并删除关联所有的订单。(客户和订单都删除了)
*孤儿删除:订单和客户解除关系时,一并删除订单。(客户存在,但订单删除的)
级联操操作总结
所有cascade属性
none:没有级联
save-update :级联保存或更新
delete:级联删除
delete-orphan:孤儿删除
all:save-update和delete总和
all-delete-orphan :所有
关联关系映射:多对多
分析
*双向 n-n 关联需要两端都使用集合属性
*双向n-n关联必须使用中间表
*集合属性应增加 key 子元素用以映射外键列, 集合元素里还应增加many-to-many子元素关联实体类
*在双向 n-n 关联的两边都需指定连接表的表名及外键列的列名. 两个集合元素 set 的 table 元素的值必须指定,而且必须相同
配置
*set元素的两个子元素:key 和 many-to-many 都必须指定 column 属性,
*其中,key 和 many-to-many 分别指定本持久化类和关联类在连接表中的外键列名
*注意:对于双向 n-n 关联, 须把其中一端的 inverse 设置为 true, 否则可能会造成主键冲突
↑org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
多对多双向关联-- inverse
/** * 提供学生和课程,双向关联,保存学生和课程? --抛异常,两个对象都对中间表进行维护 * insert (student_id,course_id) values(4,5) * insert (course_id, student_id) values(5,4) * 数据重复。 * 一般在多对多中有一方配置放权。<set inverse="true"> */ @Test public void demo06(){ Session session = factory.openSession(); session.beginTransaction(); //1 学生 Student student = new Student(); student.setSname("刘丰"); //2课程 Course course = new Course(); course.setContent("金瓶梅"); //3学生关联课程 student.getCourseSet().add(course); course.getStudentSet().add(student); //4保存学生 session.save(student); session.save(course); session.getTransaction().commit(); session.close(); }
inverse 使用总结
*一对多:
一方可以维护外键数据,多方必须维护外键数据。一方可以放权。
在一方配置inverse使一方放弃对外键维护。
*多对多:
两个多方默认都对中间表的数据进行维护,有可能存在操作异常(重复),其中一方必须使用inverse进行放权。
一个多方将放弃对中间表数据维护。
inverse和cascade综合案例
多对多双向级联删除
/** * 在 hbm.xml 配置双向级联删除 * Student.hbm.xml * <set name="courseSet" table="m_student_course" cascade="delete"> * Course.hbm.xml * <set name="studentSet" table="m_student_course" cascade="delete"> */ @Test public void demo04(){ Session session = factory.openSession(); session.beginTransaction(); //1 学生 Student student = (Student) session.get(Student.class, 1); //2删除 session.delete(student); session.getTransaction().commit(); session.close(); }
双向级联删除(一方添加inverse)
/** * 在 hbm.xml 配置双向级联删除 * Student.hbm.xml * <set name="courseSet" table="m_student_course" cascade="delete"> * Course.hbm.xml * <set name="studentSet" table="m_student_course" cascade="delete" inverse="true"> */ @Test public void demo05(){ Session session = factory.openSession(); session.beginTransaction(); //1 学生 Student student = (Student) session.get(Student.class, 1); //2删除 session.delete(student); session.getTransaction().commit(); session.close(); }