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

一级缓存

概念

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

时间: 2024-10-09 21:20:59

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

hibernate框架学习之一级缓存

l缓存是存储数据的临时空间,减少从数据库中查询数据的次数 lHibernate中提供有两种缓存机制 ?一级缓存(Hibernate自身携带) ?二级缓存(使用外部技术) lHibernate的一级缓存即Hibernate操作数据时所对应的临时数据存储区域,这个区域是绑定Session对象的,也就是说每开启一个Session对象,就会产生对应的一级缓存空间,当Session对象关闭时,该空间内的数据,也就是其中保存的PO对象,会转化为DO对象. lHibernate的一级缓存是Session级别的

java框架篇---hibernate之缓存机制

一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据. 二.what(Hibernate缓存原理是怎样的?)Hibernate缓存包括两大类:Hibernate一级缓存和Hibernate二级缓存. 1.Hibernate一级缓存又称为“Session的

转载 hibernate一级缓存和二级缓存的区别

文章来源:http://blog.csdn.net/defonds/article/details/2308972     hibernate一级缓存和二级缓存的区别 缓存是介于应用程序和物理数据源之间,其作用是为了降低应用程序对物理数据源访问的频次,从而提高了应用的运行性能.缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据. 缓存的介质一般是内存,所以读写速度很快.但如果缓存中存放的数据量非常大时,也会用硬盘作为缓存介质.

Hibernate一级缓存(补)

------------------siwuxie095 什么是缓存 缓存是介于应用程序和永久性数据存储源(如:硬盘上的文件 或 数据库) 之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提 高应用的运行性能 针对数据库,则: 数据是存到数据库中的,而数据库本身也是文件系统,使用流方式操作文 件的效率并不是很高 如果把数据存到内存里面,不需要使用流方式,就可以直接读取内存中的 数据,提高读取的效率 Hibernate 缓存 1.Hibernate 框架中提供了很多优化方式,Hibe

hibernate 一级缓存和二级缓存

hibernate一级缓存:session缓存即事务级缓存,session关闭,缓存自动销毁,开发人员不用管理,由hibernate管理,save.update.saveoOrUpdate.lock.load.list会自动向一级缓存中存放数据,get,load,list会自动从一级缓存中取数据,可调用evict(Object object)和clear()清除缓存. hibernate二级缓存:sessionFactory缓存即进程级别缓存,由缓存插件实现,如OSCache,对hibernat

(10)Hibernate一级缓存

1.为什么要用缓存? 目的:减少对数据库的访问次数!从而提升hibernate的执行效率! 2.Hibernate中缓存分类 一级缓存.二级缓存 3.一级缓存的概念 1)Hibenate中一级缓存,也叫做session的缓存,它可以在session范围内减少数据库的访问次数!  只在session范围有效! Session关闭,一级缓存失效! 2)当调用session的save/saveOrUpdate/get/load/list/iterator方法的时候,都会把对象放入session的缓存中

hibernate一级缓存,二级缓存和查询缓存

一级缓存 (必定存在)  session里共享缓存,伴随session的生命周期存在和消亡: 1. load查询实体支持一级缓存 2. get查询实体对象也支持 3. save保存的实体对象会缓存在一级缓存 4. clear evict会清除session缓存 5. save巨大数据,每20个数据,一般flush执行sql将数据持久化然后clear缓存,防止内存溢出,save放最后. 6. iterate使用一级缓存(creatQuery中的查询实体对象list会使用一级缓存,查询对象实体属性不

在Hibernate框架中详谈一级缓存

在学习Hibernate的过程中我们肯定会碰上一个名词---缓存,一直都听说缓存机制是Hibernate中的一个难点,它分为好几种,有一级缓存,二级缓存和查询缓存 今天呢,我就跟大家分享分享我所理解的一级缓存 要想完美的体现出缓存机制的话,我想通过查询语句生成的sql应该就能够很清楚的看到 那些Hibernate的配置信息我就不展示了,直接看关键代码 场景:我要查询同一个对象,查询两次,观察在不同的情况下,sql语句的生成情况 我事先准备了一个HibernateUtil工具类,具体如下 pack

Hibernate一级缓存

1.三种查询方式: 1.1.HQL语句:批量查询时,查询结果会进入缓存中.(先查询list,再查询单条记录,只打印一条语句),HQL不会使用一级缓存.(重复的查询,结果只打印一条SQL) 1.2.SQL查询:如果把查询结果封装到对象中,对象会放入到一级缓存中,如果没有将查询结果放到对象中,不会放到一级缓存中 1.3.criteria查询:会将查询结果放入到一级缓冲中,但是查询不会使用一级缓存(与HQL查询的结论相似) 2.缓存中的数据与数据库中的不同步,会优先使用缓存中的. 使用JDBC,在一级