一级缓存
概念
*Session 能够在某些时间点, 按照缓存中对象的变化来执行相关的 SQL 语句,
来同步更新数据库, 这一过程被称为刷出缓存(flush)
* 一级缓存为session级别的缓存,既将数据保存session中。
* 一级缓存功能为hibernate内置,不能设置取消,必须使用的。
*一级缓存为提高性能
* session提供 map容器,用于执行一些指定的操作时,进行相应PO对象缓存的。
默认情况下 Session 在以下时间点刷出缓存:
一级缓存常见操作
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();
}
保存客户和订单(双向关联)
/**
* #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();
}
*级联删除:删除客户时,一并删除关联所有的订单。(客户和订单都删除了)
*孤儿删除:订单和客户解除关系时,一并删除订单。(客户存在,但订单删除的)
级联操操作总结
none:没有级联
save-update :级联保存或更新
delete:级联删除
delete-orphan:孤儿删除
all:save-update和delete总和
all-delete-orphan :所有
关联关系映射:多对多
*双向n-n关联必须使用中间表
*集合属性应增加 key 子元素用以映射外键列, 集合元素里还应增加many-to-many子元素关联实体类
*在双向 n-n 关联的两边都需指定连接表的表名及外键列的列名. 两个集合元素 set 的 table 元素的值必须指定,而且必须相同
配置
*其中,key 和 many-to-many 分别指定本持久化类和关联类在连接表中的外键列名
多对多双向关联-- 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();
}