您的位置:首页 > 其它

框架 day32 Hibernate,一级缓存,关联关系映射(一对多,多对多)

2016-05-21 01:34 387 查看

一级缓存





概念

*在 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.调用Sessionflush()
方法

一级缓存常见操作

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();
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: