码迷,mamicode.com
首页 > Web开发 > 详细

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

时间:2016-05-21 06:43:52      阅读:231      评论:0      收藏:0      [点我收藏+]

标签:

一级缓存



概念

*在 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:孤儿删除

allsave-updatedelete总和

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();
	}
技术分享

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

标签:

原文地址:http://blog.csdn.net/opopopwqwqwq/article/details/51465126

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!