持久化类和主键
持久化类
- 持久化类的概念
- 相当于一个Java类(咱们编写的JavaBean),这个Java类与表建立了映射关系就可以成为持久化类。持久化类 = JavaBean + 映射(xxx.hbm.xml)。
- 持久化类编写规范
- 提供一个无参public访问控制符的构造器:底层需要进行反射。
- 提供一个标识属性,映射数据表主键字段:唯一标识OID。数据库中通过主键。Java对象通过地址确定对象。持久化类通过唯一标识OID确定记录。
- 所有属性提供public访问控制符的set或者get方法。
- 标识属性应尽量使用基本数据类型的包装类型。
- 基本上与JavaBean的编写规范一致
主键
- 自然主键
- 对象本身的一个属性。例如创建一个人员表,每个人都有一个身份证号(唯一的)。使用身份证号作为表的主键。即自然主键。(开发中不会使用这种方式)
- 代理主键
- 不是对象本身的一个属性。例如创建一个人员表,为每个人员单独创建一个字段。用这个字段作为主键,这个字段没有任何实际含义只用来当主键。即代理主键。(实际开发中使用)
- 主键生成策略
- increment:适用于short,int,long类型作为主键,先查询主键的已有最大值,再最大值+1。不能在集群环境下或者有并发访问的情况下使用。
- identity:适用于short,int,long类型作为主键,采用的是数据库底层的自动增长机制,所选的数据库必须支持自动增长机制才可以,例如MySQL支持自动增长机制,Oracle则不支持。
- sequence:适用于short,int,long作为主键。底层使用的是序列的增长方式。所选的数据库必须支持序列增长才可以,例如Oracle支持序列增长,MySQL则不支持。
- uuid:适用于char,varchar类型的作为主键。使用随机的字符串作为主键。(实际开发中经常使用)
- native:适用于short,int,long类型作为主键,采用本地策略。根据底层的数据库不同,自动选择适用于该种数据库的生成策略。(实际开发中经常使用)
- assigned:取消Hibernate自动管理主键,自己手动设置主键。
持久化对象
持久化对象的三个状态
- 瞬时态:Transient Object
- 没有持久化标识OID, 没有被纳入到Session对象的管理。
- 持久态:Persistent Object
- 有持久化标识OID,已经被纳入到Session对象的管理。
- 脱管态:Detached Object
- 有持久化标识OID,没有被纳入到Session对象的管理。
- 案例12345678910111213141516@Testpublic void test3(){Session session = HibernateUtils.getSession();Transaction tr = session.beginTransaction();//持久化Student对象//进入瞬时态,没有持久化标识OID, 没有被纳入到Session对象的管理。Student s = new Student();s.setS_name("小明");//使用session保存持久化对象s,把s对象也保存到session的缓存中。//进入持久态,有持久化标识OID, 被纳入到Session对象的管理。Serializable id = session.save(s);tr.commit();//进入脱管态,有持久化标识OID,没有被纳入到Session对象的管理。session.close();}
Hibernate持久化对象的状态的转换
- 瞬时态
- 获取瞬时态对象:
User user = new User()
- 瞬时态——>持久态:
save()/saveOrUpdate()
- 瞬时态——>脱管态:
user.setId(1)
- 获取瞬时态对象:
- 持久态
- 获取持久态对象:
get()/load()
- 持久态——>瞬时态:
delete()
(进入特殊的状态删除态:Hibernate中不建议使用) - 持久态——>脱管态:
session.[close()/evict()/clear()]
- 获取持久态对象:
- 脱管态
- 获取脱管态对象:
User user = new User();user.setId(1);
(不建议直接获取脱管态对象) - 脱管态——>持久态:
update()/saveOrUpdate()/lock()
- 脱管态——>瞬时态:
user.setId(null)
- 获取脱管态对象:
- 注意:持久态对象具有自动更新数据库的能力!!!!!!!!
Session的一级缓存
Session的一级缓存机制(重点)
- 缓存的概念
-其实就是一块内存空间,将数据源(数据库或者文件)中的数据存放到缓存中。再次获取的时候 ,直接从缓存中获取,可以提升程序的性能! - Hibernate的缓存机制
- 一级缓存:自带的不可卸载的。一级缓存的生命周期与session一致。一级缓存称为session级别的缓存。
- 二级缓存:默认没有开启,需要手动配置才可以使用的。二级缓存可以在多个session中共享数据,二级缓存称为是sessionFactory级别的缓存。
- Session对象的缓存(一级缓存)概述
- Session接口中,有一系列的java的集合,这些java集合构成了Session级别的缓存(一级缓存).将对象存入到一级缓存中,session没有结束生命周期,那么对象在session中存放着。
- 内存中包含Session实例——>Session的缓存(一些集合)——> 集合中包含的是缓存对象!
- 证明一级缓存的存在
- 在同一个Session对象内连续进行两次查询,只有第一次有sql语句,后面的没有sql语句,这是因为第一次从数据库获取数据已经把对象缓存到session中了,后面的只要从session缓存中直接获取数据即可。
- Hibernate框架是如何做到数据发生变化时进行同步操作的呢?
- 使用get方法查询一个对象,然后设置该对象的一个属性,发现数据库的数据也改变了
- 利用快照机制来完成
快照机制
- 利用快照机制来保存自动更新数据库的能力12345678910111213141516171819/*** 测试快照机制*/@Testpublic void test4(){Session session = HibernateUtils.getSession();Transaction tr = session.beginTransaction();//获取持久化对象//先查询到对象,把查询结果保存到Student对象中,也缓存到session的缓存区域中和session的快照区域Student s = session.get(Student.class,"402881e763fe56140163fe5618a40000");//重新设置名称s.setS_name("小张"); //修改session缓存区域中的数据/*提交之前:自己比对一下缓存区域和快照区域的数据是否一致,如果是一致的,没问题* 如果不一致,修改数据库中的对应值,同时把快照区域的值更新。* */tr.commit();//session销毁了缓存,所有内容都没了session.close();}
控制Session的一级缓存
- Session.clear():清空缓存
- Session.evict(Object entity):清空指定对象的缓存
- Session.flush():刷新缓存
Hibernate中的事务与并发
事务
- 事务的概念
- 事务就是逻辑上的一组操作,组成事务的各个执行单元,操作要么全都成功,要么全都失败。例如银行转账。
- 事务的特性
- 原子性 :事务不可分割。
- 一致性 :事务执行的前后数据的完整性保持一致。
- 隔离性 :一个事务执行的过程中,不应该受到其他事务的干扰。
- 持久性 :事务一旦提交,数据就永久保持到数据库中。
- 如果不考虑隔离性会引发的问题
- 脏读:一个事务读到了另一个事务未提交的数据.
- 不可重复读: 一个事务读到另一个事务已提交的update数据,导致多次查询结果不一致。
- 虚读:一个事务读到了另一个事务已经提交的insert数据,导致多次查询结果不一致。
- 通过设置数据库隔离级别来解决上述问题
- 未提交读:以上的读的问题都有可能发生。
- 已提交读:避免脏读,但是不可重复读,虚读都有可能发生。
- 可重复读:避免脏读,不可重复读.但是虚读是有可能发生。
- 串行化:以上读的情况都可以避免。
- 在Hibernate框架中设置隔离级别
- 通过在hibernate.cfg.xml的配置文件中,通过
hibernate.connection.isolation
标签来配置 - 1:Read uncommitted isolation
- 2:Read committed isolation
- 4:Repeatable read isolation
- 8:Serializable isolation
- 通过在hibernate.cfg.xml的配置文件中,通过
丢失更新
- 丢失更新的概念
- 不考虑隔离性,也会产生写入数据的问题,这一类的问题叫丢失更新的问题。
- 两个事务同时对某一条记录做修改,就会引发丢失更新的问题。
- A事务和B事务同时获取到一条数据,同时再做修改
- 如果A事务修改完成后,提交了事务
- B事务修改完成后,不管是提交还是回滚,如果不做处理,都会对数据产生影响,B事务会覆盖掉A事务的修改操作,使A事务的操作丢失。
- 解决方案
- 悲观锁(不推荐)
- 采用的是数据库提供的一种锁机制,如果采用做了这种机制,在SQL语句的后面添加
for update
子句- 当A事务在操作该条记录时,会把该条记录锁起来,其他事务是不能操作这条记录的。
- 只有当A事务提交后,锁释放了,其他事务才能操作该条记录
- 采用的是数据库提供的一种锁机制,如果采用做了这种机制,在SQL语句的后面添加
- 乐观锁
- 采用版本号的机制来解决的。会给表结构添加一个字段version=0,默认值是0
- 当A事务在操作完该条记录,提交事务时,会先检查版本号,如果发生版本号的值相同时,才可以提交事务。同时会更新版本号version=1.
- 当B事务操作完该条记录时,提交事务时,会先检查版本号,如果发现版本不同时,程序会出现错误。
- 采用版本号的机制来解决的。会给表结构添加一个字段version=0,默认值是0
- 悲观锁(不推荐)
- 使用Hibernate框架解决丢失更新的问题
- 悲观锁(不推荐)
- 使用
session.get(Customer.class, 1,LockMode.UPGRADE)
方法
- 使用
- 乐观锁
- 1.在对应的JavaBean中添加一个属性,名称可以是任意的。例如:
private Integer version;
生成对应的get和set方法。 - 2.在映射的配置文件中,提供
<version name="version"/>
标签即可。
- 1.在对应的JavaBean中添加一个属性,名称可以是任意的。例如:
- 悲观锁(不推荐)
绑定本地的Session
- 在Hibernate框架中提供了ThreadLocal的方式来传递Session对象,使用Session对象来开启事务
- 修改hibernate.cfg.xml配置文件
<property name="hibernate.current_session_context_class">thread</property>
- 修改HibernateUtil的工具类,使用SessionFactory的getCurrentSession()方法,获取当前的Session对象。并且该Session对象不用手动关闭,线程结束了,会自动关闭。
- 注意:getCurrentSession()方法必须配置后才可以使用。
- 流程:在业务层从ThreadLocal获取Session对象,然后开启事务,在捕获异常中回滚事务,不需要在finally块中手动关闭session了。在数据层直接从ThreadLocal获取Session对象,然后把相应对象保存到Session对象中即可。
- 修改hibernate.cfg.xml配置文件
Hibernate框架的查询方式
Query查询接口
查询所有数据
12345678910111213141516171819/*** 测试Query的查询接口,查询所有数据* 持久化类相当于表*/@Testpublic void run1(){Session session = HibernateUtils.getSession();Transaction tr = session.beginTransaction();//查询方式HQL:相当于select * from userQuery query = session.createQuery("from User");//查询List<User> list = query.list();//打印查询结果for (User user : list) {System.out.println(user);}tr.commit();session.close();}通过
?
条件查询123456789101112131415161718192021/*** 测试Query的查询接口,条件查询方式一* 持久化类相当于表*/@Testpublic void run2(){Session session = HibernateUtils.getSession();Transaction tr = session.beginTransaction();//查询方式HQL:相当于select * from user where u_id = 1;Query query = session.createQuery("from User where id = ?");//设置?参数的值,?的下标从0开始query.setInteger(0, 1);//查询List<User> list = query.list();//打印查询结果for (User user : list) {System.out.println(user);}tr.commit();session.close();}通过
:字符串
条件查询123456789101112131415161718192021/*** 测试Query的查询接口,条件查询方式二* 持久化类相当于表*/@Testpublic void run3(){Session session = HibernateUtils.getSession();Transaction tr = session.beginTransaction();//查询方式HQL:相当于select * from user where u_name="张三";Query query = session.createQuery("from User where name like :a");//设置a参数的值query.setString("a", "%三");//查询List<User> list = query.list();//打印查询结果for (User user : list) {System.out.println(user);}tr.commit();session.close();}
Criteria查询接口(适合条件查询)
查询所有记录
1234567891011121314151617/*** 测试Criteria查询接口(适合条件查询)*/@Testpublic void run1(){Session session = HibernateUtils.getSession();Transaction tr = session.beginTransaction();//先获取到Criteria接口Criteria criteria = session.createCriteria(User.class);//没有添加条件,查询所有数据select * from t_userList<User> list = criteria.list();for (User user : list) {System.out.println(user);}tr.commit();session.close();}多条件查询
12345678910111213141516171819202122/*** 测试Criteria查询接口(适合条件查询)*/@Testpublic void run2(){Session session = HibernateUtils.getSession();Transaction tr = session.beginTransaction();//先获取到Criteria接口Criteria criteria = session.createCriteria(User.class);//select * from t_user where u_age > 18 and like '%测试%';//criteria是Hibernate框架提供的条件查询对象,想传入条件需要使用工具类Restrictions//Restrictions提供静态方法拼接查询条件criteria.add(Restrictions.gt("age",18));//继续添加条件criteria.add(Restrictions.like("name","%测试%" ));List<User> list = criteria.list();for (User user : list) {System.out.println(user);}tr.commit();session.close();}
最后更新: 2020年07月27日 03:37
原始链接: https://www.lousenjay.top/2018/06/14/Hibernate框架入门学习(二)/