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

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~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468682.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python正则表达式操作指南_第二篇详细Python正则表达式操作指南(re使用)

接下来昨天的内容执行匹配一旦你有了已经编译了的正则表达式的对象&#xff0c;你要用它做什么呢&#xff1f;RegexObject 实例有一些方法和属性。这里只显示了最重要的几个&#xff0c;如果要看完整的列表请查阅 Python Library Reference如果没有匹配到的话&#xff0c;match…

Android binder 框架和学习资料

&#xff11;Android binder 是学习 Android 系统一定要啃得硬骨头&#xff0c;可能你刚开始的时候并不理解其中的精髓&#xff0c;但是在 android 系统的很多地方你都会遇到它。不过要我自己写明白其中的逻辑脉络需要花费太多的时间和精力&#xff0c;而且传播效果也不是非常好…

Spring_Bean配置_生命周期_注解

Spring Spring是一个开源框架&#xff0c;Spring是于2003 年兴起的一个轻量级的Java 开发框架&#xff0c;由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之…

完全理解Gson(1):简单入门

GSON是Google开发的Java API&#xff0c;用于转换Java对象和Json对象。本文讨论并提供了使用API的简单代码示例。更多关于GSON的API可以访问&#xff1a;http://sites.google.com/site/gson/. 本文是GSON系列文章的第一篇。本文是其他文章的基础&#xff0c;因此不需要任何GSON…

python自由落体_VPython - example - 模拟自由落体运动

作者&#xff1a;liuyuan_jq2011-04-10from visual import *scene.width 400scene.height 300scene.autoscale 0scene.range (100,100,100)scene.center (0,40,0)ball sphere(pos(0,100,0),radius2)ground box(pos(0,-1,0),size(10,2,10))gravity 9.8 # m/s**2seconds …

创业碎碎念

&#xff11;今天&#xff0c;跟几条跟我玩的比较好的篮球狗在讨论人生&#xff0c;其中有一条特别感慨&#xff0c;「为何看上去别人做起来这么容易的事&#xff0c;我们做很难&#xff1f;」。我突然不知道如何去回答这样的问题&#xff1f;这个问题也一直困扰着我。读我文章…

压缩过的js代码怎么还原_Fundebug 前端 JS插件更新至 1.7.0,拆分录屏代码,还原部分 Script error....

摘要&#xff1a; BUG 监控插件压缩至 18K。1.7.0拆分了录屏代码&#xff0c;BUG 监控插件压缩至18K&#xff0c;另外我们还原了部分 Script error&#xff0c;帮助用户更方便地 Debug。请大家及时更新哈~拆分录屏代码从1.7.0版本开始&#xff0c;我们拆分了录屏代码。如果需要…

SpringAOP描述及实现_AspectJ详解_基于注解的AOP实现_SpringJdbcTemplate详解

AOP AOP特点: 面向切面编程, 利用AOP对业务逻辑的各个部分进行抽取公共代码, 降低耦合度, 提高代码重用性, 同时提高开发效率.采取横向抽取, 取代传统纵向继承体系重复性代码解决事务管理, 性能监视, 安全检查, 缓存, 日志等问题Spring AOP在运行期, 通过反向代理的方式解决类…

(十九)java多线程之ForkJoinPool

本人邮箱: kco1989qq.com 欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco github: https://github.com/kco1989/kco 代码已经全部托管github有需要的同学自行下载 引言 java 7提供了另外一个很有用的线程池框架,Fork/Join框架 理论 Fork/Join框架主要有以下两个类组…

串口,com口,ttl,max232你应该知道的事

&#xff11;今天&#xff0c;说几个比较基础的知识&#xff0c;大家在开发过程中经常会遇到但是又不是特别注意的知识点。TTL电平&#xff1a;TTL是Transistor-Transistor Logic&#xff0c;即晶体管-晶体管逻辑的简称&#xff0c;它是计算机处理器控制的设备内部各部分之间通…

视觉slam十四讲 pdf_视觉SLAM十四讲|第12讲 回环检测

1. 什么是回环检测前面有说过累积误差的问题&#xff0c;前一时刻的误差会积累到后面&#xff0c;导致画不成圈圈&#xff0c;如图12-1所示&#xff0c;而画圈圈&#xff08;全局一致性&#xff09;很重要&#xff0c;所以需要有一个步骤来纠正当前的计算偏差。回环检测通过判断…

Caffe2 的基本数据结构(Basics of Caffe2 - Workspaces, Operators, and Nets)[4]

这篇文章主要介绍Caffe2的基本数据结构&#xff1a; WorkspacesOperatorsNets在开始之前最好先阅读以下Intro Turorial首先&#xff0c;导入caffe2。其中core和worksapce模块&#xff0c;这是必须的两个模块。如果你要使用Caffe2生成的protocol buffers&#xff0c;那么你也需要…

学习Java中遇到的问题积累_1

1.奇数性 看下面代码时候是否能判断参数 i 是奇数? public static boolean isOdd(int i){ return i % 2 1; }答案是: NO 看似正确的判断奇数, 但是如果 i 是负数, 那么它返回值都是false 造成这种现象的是 > 从思想上固化, 认为奇数只在正数范围, 故判断负数将报错, 在…

Linux 开发者最应该知道的命令汇总

&#xff11;最近发现一个好东西&#xff0c;在 github 上发现的&#xff0c;我觉得非常适合大家研究 linux&#xff0c;说白了就是一些命令而已&#xff0c;只不过是作者帮忙总结好了&#xff0c;比较适合大家开发和学习 linux 系统&#xff0c;so , 推荐给大家学习下。https:…

华为任职资格_华为采购总部专业任职资格标准|

目 录 序 言 概述 .........................第一部分 级别定义.....................第二部分 资格标准 ....................1、采购工程师&#xff08;生产采购&#xff09;任职资格标准........2、采购员&#xff08;生产采购&#xff09;任职资格标准............3、采购员…

VirtualBox设置共享目录(主机win7,虚拟机Ubuntu)

1、安装增强功能包 启动虚拟机后&#xff0c;在 设备 -》 分配光驱 选择VBoxGuestAdditions.iso增强包镜像&#xff08;在virtualbox安装目录下&#xff09; 在虚拟机中挂载光驱镜像&#xff1a; #mkdir /mnt/cdrom#mount /dev/cdrom /mnt/cdrom 执行增强包安装程序&#xff…

C 语言内存分配

&#xff11;昨天有一个群里的同学问我&#xff0c;他问我的问题是 c 语言函数是存在哪里的&#xff1f;是如何执行的&#xff1f;我下意识的觉得这位同学应该是个初学者&#xff0c;所以今天就写下一些基础方面的内容&#xff0c;「C语言的内存布局」。程序代码可以看做是一个…

正则表达式符号特殊详解_常用正则表达式_Java中正则表达式的使用

正则表达式符号详解 限定符: 指定一个组件必须出现多少次才能满足. 1.使用 “*”, “”, “?” 作为限定符: "*": 匹配前面的子表达式零次或多次。例如&#xff0c;zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。"": 匹配前面的子表达…

python中求包含5的数_Python 内置函数 ( ) 可以返回列表、元组、字典、集合、字符串以及 range 对象中元素个数。_学小易找答案...

【简答题】实例1:求两数相除的结果。 先后输入2个数据,计算第一个数除以第二个数的结果。 要求能够处理输入数据为非数字、除数为零、文件末尾EndOfFile 和用户使用Ctrl + C 命令终止程序等异常,提高用户体验。 当输入数据为非数字时提示错误信息并让用户重新输入。 input()函数…

没有梦想,你跟咸鱼有什么分别?

&#xff11;标题起的有点夸张&#xff0c;其实这个就是一个招聘贴&#xff0c;之前从来没有用发头条文章来招聘&#xff0c;实在不好意思&#xff0c;这个招聘对我非常重要&#xff0c;这是一个非常好的朋友公司的招聘信息&#xff0c;也希望大家帮忙扩散一下&#xff0c;因为…