Hibernate_2_Hibernate中对象状态及转化_一级缓存_Session详解_HQL/SQL/Criteria_一对多关系_级联操作

Hibernate中的对象状态在Hibernate中持久化对象具有三种状态: 瞬时态, 持久态, 游离态.瞬时态: 对象没有与Hibernate产生关联(transient,session中没有缓存), 数据库中也没有对应记录 => 对象无id, 没有关联持久态: 对象与Hibernate产生关联(persistent, session中有缓存), 数据库中会存在记录 => ...
摘要由CSDN通过智能技术生成

Hibernate中的对象状态

在Hibernate中持久化对象具有三种状态: 瞬时态, 持久态, 游离态.

瞬时态: 对象没有与Hibernate产生关联(transient,session中没有缓存), 数据库中也没有对应记录
	=> 对象无id, 没有关联
持久态: 对象与Hibernate产生关联(persistent, session中有缓存), 数据库中会存在记录
	=> 对象存有id, 有关联
游离态: 对象没有与Hibernate产生关联(detached,session中没有缓存), 数据库中有记录
	=> 对象有id, 没有关联

持久化对象状态之间的相互转化

  • 瞬时(无id, 无关联)=>持久(有id, 有关联) save操作:
    修改id, 与session的关联状态
让瞬时对象拥有id, 并且与Hibernate产生关联
//u此时没有id, 没有与Hibernate产生关联
User u = new User(); //瞬时态
u.setName("tom"); //瞬时态
u.setPassword("123"); //瞬时态

//save操作, Hibernate根据id生成策略, 为u生成id
//然后存入数据库, 与Hibernate产生关联, 变为持久态
session.save(u);//持久态

注: 只有Hibernate的主键生成策略变为assigned才能手动设置id, 否则报错

  • **瞬时(无id, 无关联)=>游离(有id, 无关联) **
    修改id即可
//设置Hibernate主键生成策略为assigned
User u = new User();
u.setId(1);
  • 持久(有id, 有关联)=>瞬时(无id, 无关联)
    修改id与关联状态即可
1.直接通过session的get查询操作获取持久化对象
	关闭session切断关联, 再通过setId(null)修改id就变为瞬时态
User u = (User) session.get(User.class, 1); //持久态
session.close();
u.setId(null); //瞬时态

2.get查询到的对象, 使用evict方法切断user与session的联系,
	再设置id为null变为瞬时态
User u = session.get(User.class ,1);//持久态
session.evict(u);
u.setId(null); //瞬时态
  • 持久(有id, 有关联)=>游离(有id, 无关联)
    获得持久对象, 然后切断持久对象与session的联系
User u =(User) session.get(User.class, 1);
session.close();
//或者 session.evict(u);
  • 游离(有id, 无关联)=>瞬时(无id, 无关联)
    设置游离对象id为null
User u = (User) session.get(User.class, 1);
session.evict(u);
u.setId(null);
  • 游离(有id, 无关联)=>持久(有id, 有关联)
    重新将游离对象写入数据库, 使得与session产生联系
User u = (User) session.get(User.class, 1);
session.evict(u);
session.update(u); //写入数据库,变为持久态

注: 持久态的时候不允许修改持久化对象的id: u.setId(2), 修改直接报错, 对持久化对象直接进行修改:u.setName(“C”), 那么Hibernate将会执行update操作, 将修改的持久化对象的数据同步到数据库

一级缓存

一级缓存(session缓存), 用于存储持久化对象 , 内部储存结构是一个Map, 当需要使用持久化对象的时候, Hibernate会优先从缓存中获取, 当session关闭, 一级缓存销毁.

  • 快照
    快照: 缓存的复制品, 存放在session中
    快照主要用来与缓存中数据进行比较, 判断缓存中数据是否发生变化
缓存中数据发生变化:
	Hibernate就会执行update, insert操作
缓存中数据没有发生变化:
	说明数据库中数据与缓存中数据相同,
	Hibernate就会避免主动执行SQL的update, insert, 减少资源浪费
  • 缓存执行过程:
查询(select):
	session首次执行查询, 发送select到数据库, 将查询的结果封装放入session缓存,
	当再次执行查询操作, Hibernate会先去Session缓存中查找,
	没找到, 再去数据库中查找.
更新(update):
	使用get/load执行查询操作后, 持久化对象放入Session缓存中,
	然后对持久化对象update, 事务提交的时候将执行如下操作:
		先将Session缓存中修改后的对象与快照中数据进行比较,
		二者数据不相同的时候执行update, 数据更新到数据库中
		二者数据相同, 那么不执行任何操作.
插入(insert):
	使用快照中数据与缓存中数据进行比较, 
	根据比较结果判断是否继续执行insert操作
  • 缓存刷新时机:
//查询表中所有数据, 查询后的结果会覆盖缓存中数据
List<User> list = session.createQuery("from User").list();

//执行flush, 刷新缓存中数据, 
session.flush();
  • 缓存导致的问题:
    每次获取持久化对象, Hibernate优先去缓存中查找, 一定程度上提高了SQL的执行效率.
    缓存的存在, 出现的一个问题就是: 如果Hibernate获得持久化对象后, 数据库中数据又出现了修改, 当再次对该持久化对象进行操作的时候, Hibernate会优先从缓存中获得持久化对象, 导致数据库与Hibernate中数据不一致. 当出现这种问题的时候, 建议JDBC操作

注: 避免将相同的对象放入缓存中, 谨记缓存是一个Map, 看如下操作:

User u1 = session.get(User.class, 1);//缓存中放入u1
session.evict(u1);//变为游离态, 缓存中不存在
User u2 = session.get(User.class, 1);//获得id=1的持久化对象,缓存中存在
session.update(u1);//u1重新变为持久态, 缓存中存在
//u1, u2同时存在缓存中, 将会报错

session缓存常用API

  • evict(Object o): 将指定对象从session缓存中移除
  • clear(): 清除session缓存中所有的对象
  • refresh(Object o):
    强制刷新指定对象, 使持久化对象数据与数据库中数据一致, 一定程度上避免session缓存产生的数据不一致问题;
    对o对象重新执行SQL
  • flush():
    对比快照与缓存中的数据, 确保数据一致, 然后将缓存中数据提交到数据库, 类似于commit, 数据不一致的时候刷新缓存中的数据

对象的操作

  • save操作细节:
    当执行save的时候, 对象会从瞬时态=>持久态, 事务提交后将持久化对象存入数据库中
执行save操作, 先根据映射文件中的主键生成策略生成主键.
Hibernate将生成的主键赋值给瞬时态对象, 若该对象有id, 那么就会覆盖原有的id
最后执行insert, 将瞬时态对象变为持久化对象
  • persist操作:
    persist操作与save一样, 他们二者的区别在: persist会检查对象主键, save不会检查对象主键
    例如:
//u.setId(9); //save操作的时候Hibernate会自动进行主键生成,设置id无效 
//persist会检查Bean的id
//发现与Hibernate主键生成策略不符,
//报org.hibernate.PersistentObjectException: detached entity passed to persist: com.demo.User异常
///将主键生成策略改为assigned或不设置主键,将不报错
		u.setName("O");
		u.setPassword("66");
		//session.save(u);
		session.persist(u);
  • update操作细节:
    游离对象=>持久对象, 对持久对象属性修改后, 使用save, 执行的是update, 而非insert
    在映射文件的class标签中设置select-before-update=“true”, 那么执行update就会执行如下操作:
User u = new User();
u.setId(1); u.setName("P"); u.setPassword("pp");
session.update(u);
//当执行这样的操作的时候, 先执行select操作, 然后比较查询结果
//与查询结果一直, 那么就不执行update
  • saveOrUpdate:
    该方法就是save与update的结合, session.saveOrUpdate(u); 如果u存在id, 那么执行select, 然后再执行update, 没有id, 执行insert

HQL, SQL, Criteria与缓存的联系

下面通过例子说明:

体现一个问题: HQL都会执行select操作,将获取的list与缓存中的数据进行比较
		//如果相同, 每次获取缓存中的封装对象
		/*List<User> list1 = session.createQuery("from User").list();
		List<User> list2 = session.createQuery("from User").list();
		List<User> list3 = session.createQuery("from User").list();
		for(User u:list1){
			System.out.println(u);
		}
		System.out.println(list1.hashCode()+"--"+list2.hashCode()+"--"+list3.hashCode());*/
		
		//原生的SQL操作与HQL一致
		/*List<User> list1 = session.createSQLQuery("select * from t_User").addEntity(User.class).list();
		List<User> list2 = session.createSQLQuery("select * from t_User").addEntity(User.class).list();
		List<User> list3 = session.createSQLQuery("select * from t_User").addEntity(User.class).list();
		
		for(User u:list1){
			System.out.println(u);
		}
		System.out.println(list1.hashCode()+"--"+list2.hashCode()+"--"+list3.hashCode());*/
		
		//criteria操作同上
		/*List<User> list1 = session.createCriteria(User.class).list();
		List<User> list2 = session.createCriteria(User.class).list();
		List<User> list3 = session.createCriteria(User.class).list();
		
		for(User u:list1){
			System.out.println(u);
		}
		System.out.println(list1.hashCode()+"--"+list2.hashCode()+"--"+list3.hashCode());*/

通过程序的运行观察执行的SQL语句, 以及list对象的hashCode, 发现每次执行批量查询(HQL, SQL, Criteria)都会select * from t_user, 然后将查询的结果集与Session缓存中的数据进行比较.
说明:Hibernate把第一次执行的结果集放入缓存区, 在后面的查询中, 尽管Hibernate发送了SQL语句, 但是使用的数据依旧是缓存中的数据, 这个时候使用get操作的时候, 获取的数据也是从缓存区中得到

多表设计

表中存在的三种关系: 多对多, 一对多, 一对一

  • 数据库描述上述关系:
    在数据库中所有的关系都需要通过外键进行约束.

  • Bean对象描述上述的关系:

一对多:客户与订单
class Customer{
	//使用set描述客户订单列表
	private Set<Order> orderSet;
}
class Order{
	//多个商品订单属于一个客户
	private Customer customer;
}

多对多: 学生与课程
class Student{
	private Set<Course> courseSet;
}
class Course{
	private Set<Student> StudentSet;
}

一对一: 学生与学生证
class Student{
	private StudentCard sc;
}
class StudentCard{
	private Student s;
}

Hibernate的一对多关系实现

一对多操作的时候, 维护一个对象的时候会自动维护另一方的关系; 例如 Customer referenced Order, 当删除Order的时候,Hibernate会先update商品表中所有的外键为null, 然后再执行删除订单操作, 我们就不用显式修改商品表中的外键, 维护商品与订单之间的关系

测试类:

//消费者:
public class Customer {
	private Integer cid;
	private String cname;
	private Set<Order> orderSet = new HashSet<Order>();
	//get/set方法就不写了
}
//订单
public class Order {
	private Integer oid;
	private String price;
	private Customer customer;
	//get/set方法就不写了
}
  • Customer.hbm.xml与Order.hbm.xml编写
Customer.hbm.xml
<hibernate-mapping>
	<class name="com.test.Customer" table="t_customer">
		<id>
			<generator class="native"></generator>
		</id>
		<property name="cname"></property>
		<!--配置一对多关系标签-->
		<set name="ordertest" cascade="save-update">
			<key column="customer_id"></key>
			<one-to-many class="com.test.Order"/>
		</set>
	</class>
</hibernate-mapping>

Order.hbm.xml
<hibernate-mapping>
	<class name="com.test.Order" table="t_order">
		<id name="oid">
			<generator class="native"></generator>
		</id>
		<property name="price"></property>
		<many-to-one name="customer" class="com.test.Customer" column="customer_id"></many-to-one>
	</class>
</hibernate-mapping>

在Customer.hbm.xml中:

set标签用于确定容器(用于装备Order)
	name属性: 确定对象属性名
	cascade属性: 设置Customer与Order的级联操作
	inverse属性: 将关系的维护翻转给对方, 默认值false(我维护这个关系)
	key标签确定Customer主键名
	one-to-many标签确定从表Order

cascade详细级联操作: 级联操作就是, 当A与B绑定好关系后, 就比如Customer的set已经存储了B, 当A执行save的时候, B也会自动执行save操作, 少写session.save(B)的代码, 同样的也可以执行级联删除, 当A删除了, B也跟着自动删除
注: 级联操作并不会维护关系

cascade的取值如下:
save-update:级联保存与修改
	A保存,同时保存B
	在程序中修改A中的B, 对应到数据库中B将会级联修改
delete:删除A,同时删除B,AB都不存在
	删除过程中, 如果A在维护关系,那么A还会去处理外键,对外键设置为null,然后执行删除.
	如果设置了inverse为true,A不去维护关系,A删除,B就删除,A不去update外键,减少了SQL操作
delete-orphan:孤儿删除,解除关系,同时将B删除,A存在的。
	接触B与A的关系, 将B从A的集合内移除, B此时没有引用对象, 就自动delete
如果需要配置多项,使用逗号分隔。<set cascade="save-update,delete">

all : save-update 和 delete 整合
all-delete-orphan : 三个整合

此处注明: 千万不要A设置了级联删除,然后B也设置了级联删除

当删除B对象的时候, 由于级联删除, B会select所有A, 然后删除A, 但是A又触发级联删除, A会select所有的B, 最终删除所有的B, 以及所有的A, 就因为删除了一个B导致了如此严重的问题, 这个一定要避免!!!

在Order.hbm.xml中:

many-to-one标签中
	name属性: 确定属性名称
	class属性: 确定参照的类
	column属性: 确定Order表参照Customer表的外建名

往数据库中保存Customer与Order:

Customer c = new Customer();
c.setName("tom");
Order o1 = new Order();
o1.setName("o1");
Order o2 = new Order();
o2.setName("o2");
//往c中添加Order信息, 维护关系
c.getOrderSet().add(o1);//Customer去维护, 执行update
c.getOrderSet().add(o2);//执行update
//往Order对象中添加Customer, 维护关系
o1.setCustomer(c);//Order去维护, 在insert中修改cid的值
o2.setCustomer(c);
//保存到数据库
session.save(c);
session.save(o1);
session.save(o2);

执行上面的代码, Hibernate执行3次insert, 2次update, 需要注意的是在c, o1, o2 insert的过程中, 就已经在维护关系(对Order表的cid外键进行设置), 但是后面又对Order表执行了2次update, 产生的问题就是重复
所以通过上面的代码也可以看出, 当维护关系的时候只需要维护一方, 另一方的关系就能得到维护

同理, 执行delete操作: session.delete©; 执行这条语句的时候, Hibernate会先将o1, o2的cid设置为null, 然后再对c进行delete, 从Customer的角度维护关系, 但是Customer不去维护关系的时候, 就需要遍历Customer的orderSet, 将所有的Order对象setCustomer(null)主动切断与Customer的关系, 设置所有Order的外键为null

总结: 设置一对多关系下, 可以只让一方维护关系, 另一方不维护, 放弃维护关系的对象就是–非外键所在的对象, 就比如上面的操作, 让Order 维护关系, Customer不去维护关系, 这种一方去维护关系也可以使用set标签中的inverse属性, 使得关系的维护交给对方

未完待续~~

上面有错, 还请指出, 如果认为我写的还不错, 还请点个赞, 多多支持一下, O(∩_∩)O~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值