Hibernate 主键生成策略、一级缓存及事务管理
1.持久化类的编写规则
- 对持久化类提供一个无参的构造方法 底层:反射生成实例
- 属性需要私有,提供public 的set和get方法 :Hibernate 中获取,设置对象的值。
- 对持久化类提供一个唯一标识的 OID 与数据库主键对应 :java中通过对象地址区分是否是同一对象,数据库中通过主键确认是否是同一个记录,在hibernate中通过持久化类的OID的属性区分是否是同一对象。
- 持久化类中的属性尽量使用包装类属性,包装类型默认值为null,基本数据类型默认值为0,有歧义。
- 持久化类不要使用final修饰 :延迟加载本来就是Hibernate 的一种优化手段,返回的是一个代理对象(javassist,可以对没有实现接口的类产生代理—是用来非常底层字节码增强技术,继承这个类进行代理)。如果类被final 修饰不能被继承了,就不能产生代理对象,延迟加载也就失效,此时的get 方法和load方法 一致。
2.主键生成策略
2.1主键的分类
2.1.1自然主键
- 主键本身就是表中的一个字段(实体中的一个具体的属性)
- 创建一个人员表,人员都会有一个身份证号(唯一不可重复),人员的身份证号作为主键,这种主键为自然主键。
2.1.2代理主键(尽量使用)
- 主键的本身不是表中必须的一个字段(不是实体类的牧歌具体的属性)
- 创建一个人员表,没有使用人员中的身份证号,用了一个与这个表不相关的字段ID(PNO),这种主键就是代理主键。
- 在实际开发中,尽量使用主键。、
- 一旦主键参与到业务逻辑中,后期可能要修改源代码。
- 好的程序设计满足OCP 原则:对程序的扩展是open的,对修改源码是close的。
2.1.3主键生成策略
2.1.3.1 Hibernate 的主键生成策略
- 在实际开发中一般不容许用户手动设置主键,一般将主键交给数据库,手动编写程序进行设置,在Hibernate中为减少程序编写,提供了很多种主键的生成策略。
<hibernate-mapping>
<!--建立类与表的影射-->
<!--当类名与表名同名时,可省略表名的映射-->
<class name="com.sunlong.hibernate01.domain.Custom" table="cst_customer">
<!--建立类中的属性与表中的主键对应-->
<id name="cust_id" column="cust_id">
<!--主键的生成策略-->
<generator class="native"/>
</id>
</hibernate-mapping>
- increment
Hibernate 中提供的自动增长机制,适用 short、int、long类型的主键,在单线程中使用。 - identity
适用short、int、long类型的主键,使用的是数据库底层的自动增强机制。适用于有自动增器机制数据库(MySQL、MSSQL),Oracle 没有自增长机制。 - sequence
适用short、int、long类型的主键,采用的是序列的方式。(Oracle支持序列)MySQL 不能使用。 - uuid
适用于字符串类型主键。使用Hibernate 中的随机方式生成字符串主键。 - native
本地策略,根据数据库的能力选择 identity、sequence。 - assigned
Hibernate 放弃外键的管理,需要通过手动编写程序或者用户自己设置。 - foreign
外部的,一对一的一种关联映射的情况下使用。
2.2 持久化类的三种状态
- Hibernate 是持久层框架,通过持久化类完成ORM操作。Hibernate 为了更好的管理持久化类,将持久化类分为三种状态。 持久化类 = java类 + 映射。
2.2.1 瞬时态
-
这种对象没有唯一的标识OID ,没有被session 管理,称为瞬时态对象。
-
瞬时态对象
-
获得
Customer c = new Custonmer(); -
状态转化
- 瞬时—>持久
save() saveOrUpdate() - 瞬时—>脱管
- 瞬时—>持久
2.2.2持久态*
- 这种对象有唯一的标识OID ,被session管理,称为持久态对象。
- 可以自动更新到数据库。
- 获得
get() load() find() iterte()
Customer c = session.get(Custonmer.class,1L);
- 状态转化
- 持久—>瞬时
-delete() - 持久—>脱管
-close() clear() evict()
- 持久—>瞬时
- 获得
Custonmer c = new Constmer();
c.setCust_id(1L); - 状态转化
- 脱管—>持久
update() saveOrUpdate() - 脱管—>瞬时
customer.setCust_id(null);
- 脱管—>持久
2.2.3 脱管态
- 这种对象有唯一的标识OID,没有被session管理,称为脱管态对象。
2.2.4 持久态对象可以自动更新数据库
@Test
//修改操作
public void demo03() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
//直接创建对象进行修改
/*Custom custom = new Custom();
custom.setCust_id( 1L );
custom.setCust_name( "悟空" );
session.update( custom );*/
//先查询,在修改(推荐)
Custom custom1 = session.get( Custom.class, 1L );
custom1.setCust_name( "八戒" );
//持久化对象可以自动更新数据库数据,如果更新数据和数据库信息一样,那就只进行查询,不进行更新语句操作
//一级缓存
//session.update( custom1 );//不写也更新
transaction.commit();
session.close();
}
2.3 Hibernate 的一级缓存
2.3.1 缓存概述
2.3.1.1 什么是缓存
- 是一种优化方式,将数据存入到内存中,使用的时候直接从缓存中获取,不用通过存储源。
2.3.2 Hibernate 的缓存
2.3.2.1 Hibernate 的一级缓存
- Hibernate 框架中提供了优化手段:缓存、抓取策略。
- Hibernate 提供了两种缓存机制:一级缓存 二级缓存
- Hibernate 的一级缓存:称为session缓存
2.3.2.2 一级缓存证明
@Test
public void demo01() {
/*
* 一级缓存
* */
Configuration cfg = new Configuration().configure( "com/sunlong/hibernate02/hibernate.cfg.xml" );
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
Customer customer = session.get( Customer.class, 2L );
System.out.println( customer );
//清空所有缓存,控制台打印两次查询语句,否则第二次查询同一对象,将会从一级缓存中获取
//session.clear();
//清空指定缓存
session.evict( customer );
Customer customer1 = session.get( Customer.class, 2L );
System.out.println( customer1 );
transaction.commit();
}
2.4 Hibernate 的事务管理
2.4.1 事务回顾
2.4.1.1什么是事务
- 事务:逻辑上的一组操作,组成这组操作的各个逻辑单元要么成功要么失败。
2.4.1.2事物特性
- 原子性:事务不可分割
- 一致性:事务执行的前后,数据的完整性保持一致
- 隔离性:一个事务执行过程中,不应该是受到其他事务的干预。
- 持久性:事务执行完成后,数据就持久到数据库中。
2.4.1.3 如果不考虑隔离性,引发安全性问题
- 读问题
- 脏读:一个事务读到另一个事务未提交的事务.
- 不可重复读:一个事务读到另一个事务已经提交的update数据,导致在前一个事务对此查询的结果不一致。
- 幻读:一个事务读到另一个事务已经提交的insert 数据,导致在前一个事务多次查询结果不一致。
- 写问题
- 引发两类丢失更新
2.4.1.4 读问题的解决
- 设置事物的隔离级别
- 未提交读 :以上读问题都会发生
- 读已提交 :解决脏读,但是不可重复读和虚读有可能发生
- 可重复读 ;解决脏读和不可重复读,但是幻读有可能发生
- 可串行化 :解决所有读问题
2.4.2 Hibernate 中设置事务的隔离级别
- 1 :读未提交
- 2 :读已提交
- 4 :可重复读
- 8 :可串行化
<!--设置事务隔离级别-->
<property name="hibernate.connection.isolation">4</property>
2.4.3 Service 事务管理
2.4.3.1 Hibernate 解决service 的事务管理
- 事务管理的方式
<1> 可以在业务层获取到 Session ,并将 Session 作为参数传递到 DAO。
<2> (最优方案) 可以使用 ThreadLocal 将业务层获取的 Session 绑定到当前线程中,然后在 DAO 中获取 Session 的时候,都从当前线程中获取。
-Hibernate 提供 sessionFactory.getCurrentSession() 创建一个 session 和 ThreadLocal 绑定方法。
<!--配置当前线程绑定的Session-->
<property name="hibernate.current_session_context_class">thread</property>
- 配置成功后即可使用 SessionFactory的getCurrentSession() 方法
2.5 Hibernate 的其他API
2.5.1 Query (HQL)
- Query 代表面向对象的一个 Hibernate 查询操作。在 Hibernate 中,通常使用 session.createQuery() 方法接收一个 HQL 语句,然后调用 Query 的 list() 或 uniqueResult() 方法执行查询。所谓的HQL 是 Hibernate Query Language 的缩写,其语法很像SQL 语句,但它是完全面向对象的。
//Query
public void demo02() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
//简单查询
//String hql = "from Customer";
//条件查询
//String hql = "from Customer where cust_name like ?";
//分页查询
String hql = "from Customer";
Query query = session.createQuery( hql );
//设置分页
query.setFirstResult( 3 );
query.setMaxResults( 3 );
//以下两种设置占位符的方式都可以
//query.setParameter( 0 ,"孙%");
//query.setString( 0,"孙%" );
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println( customer );
}
transaction.commit();
}
2.5.2 Criteria :QBC (Query By Criteria)
- 更加面向对象的一种方式查询
- 通过session获得 session.createCriteria()
//Criteria更加面向对象的一种方式查询
public void demo03() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
//查询所有数据
Criteria criteria = session.createCriteria( Customer.class );
//条件查询
criteria.add( Restrictions.like( "cust_name", "孙%" ) );
//分页查询
criteria.setFirstResult( 3 );
criteria.setMaxResults( 3 );
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println( customer );
}
transaction.commit();
}
2.5.3 SQLQuery
-
SQLQuery 用于接收SQL ,在特别复杂的情况下使用,一般建议使用以上两种。
-
以上简单介绍了三种检索方式,后续会在专门章节详细介绍。