持久化类的编写规则
一、持久化类的概述
1、什么是持久化类
持久化:将内存中的一个对象持久化到数据库中的过程。
持久化类:一个Java对象与数据库中的表建立了映射关系,那么这个类在Hibernate中就被称为是持久化类。
二、编写规则
1、持久化类的编写规则
对持久化类提供一个无参的构造方法 : Hibernate底层需要使用反射生成实例。 属性需要私有 ,对私有属性提供public的get和set方法 : Hibernate中获取,设置对象的值。 对持久化类提供一个唯一标识符OID与数据库主键对应 : Java中通过对象确定是否是同一个对象,数据库中通过主键确定是否是同一个记录,在Hibernate中通过持久化类的OID的属性区分是否是同一个对象 。 持久化类中的属性尽量使用包装类类型 (Integer,Long,Double等):因为基本数据类型默认是0,那么0就会有很多歧义。而包装类类型默认值是null。 持久化类不要使用final进行修饰 :延迟加载本身是Hibernate的一个优化手段,返回的是一个代理对象(javassist可以对没有实现接口的类产生代理——使用了非常底层的字节码增强技术,继承这个类进行代理)。如果使用final之后,则不能被继承,就不能产生代理对象,延迟加载也就失效了;此时,load方法与get方法一致。
主键生成策略
一、主键的分类
1、自然主键
自然主键 : 主键的本身就是表中的一个字段(实体中的一个具体的属性)。
创建一个人员表,每个人员都会有一个身份证号(唯一的不可重复的),并且使用了身份证号作为主键,这种主键被称为是自然主键。
2、代理主键
代理主键 : 主键的本身不是表中必须的一个字段(不是实体中的某个具体的属性)。
创建一个人员表,没有使用人员表中的身份证号作为主键,而是用了一个与这个表不相关的字段ID(如PNO)作为主键。这种主键被称为是代理主键。 在实际开发中,要尽量使用代理主键 。
因为一旦自然主键参与到业务逻辑中,后期就有可能需要修改源代码 。 一个好的程序设计需要满足OCP原则 : 对程序的扩展是open的,对修改源码是close的 。
二、主键生成策略
1、Hibernate的主键生成策略
在实际开发过程中一般是不允许用户进行手动设置主键的,一般是将主键交给数据库、手动编写程序进行设置的。在Hibernate中为了减少程序的编写,提供了多种主键生成策略。
increament : hibernate中提供了自动增长机制 ,适用于short、int、long类型的主键,并且只能在单线程程序中使用。
首先先发送一条语句:select max(id)from 表;然后让id+1作为下一条记录的主键,这个时候在多线程下就容易引发进程问题。 identity:适用于short、int、long类型的主键,它使用的是数据库底层的自动增长机制。注意:仅适用于有自动增长机制的数据库(如:MySQL、MSSQL),Oracle没有自动增长机制,故不适用 。 sequence:适用于short、int、long类型的主键,采用的是序列的方式。Oracle支持序列,但是像MySQL就不能使用sequence。 uuid:适用于字符串类型的主键,它使用的是hibernate中的随机方式生成字符串主键,类似java中的uuid。 native: 本地策略,可以认为它是在identity和sequence之间进行自动切换。就是说如果你把这个本地策略配置成MySQL,此时native就相当于identity;如果你把本地策略配置成Oracle,那么此时的native就当于是sequence。 assigned:hibernate放弃外键的管理,需要通过手动编写程序或者用户自己设置。 foreign:外部的,只有在一对一的一种关联映射的情况下使用。有一种情况就是,主键对应的情况,即我的主键也是你的主键,这个时候可以用,否则都不能用这个。
持久化类的三种状态
一、持久化类的三种状态
Hibernate是持久层框架,通过持久化类完成ORM操作。Hibernate为了更好的管理持久化类,将持久化类分成三种状态。
1、 瞬时态:transient
这种对象没有唯一的标识OID,没有被session管理,称为瞬时态对象。
2、 持久态:persistent
这种对象有唯一标识OID,被session管理,称为持久态。
3、 脱管态(游离态):detached
这种对象有唯一标识OID,但是没有被session管理,称为脱管态对象。
4、区分三种状态对象:
@Test
// 区分三种状态对象
public void demo1() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
Customer customer = new Customer(); // 瞬时态对象:没有唯一标识OID,没有被session管理
customer.setCust_name("小明");
// 可以获取保存的这条数据的id
Serializable id = session.save(customer); // 持久态对象: 有唯一标识OID,被session管理,即有session操作
session.get(Customer.class, id);
transaction.commit();
session.close();
System.out.println("客户名:"+customer.getCust_name()); // 脱管态对象,有位移标识OID,但是没有被session管理
}
二、持久化类的状态转换(了解)
1、三种状态转换图
2、瞬时态对象
瞬时态对象
获得:直接创建即可,new一个
Customer customer = new Customer(); 状态转换:
瞬时–>持久:调用session的save、saveOrUpdate方法
save(Object obj)、saveOrUpdate(Object obj); 瞬时–>脱管:给创建的对象设置一个id
3、持久态对象
持久态对象
获得:调用session的get、load、find、iterate方法即可
get()、load()、find()、iterate() Customer customer = session.get(Customer.class,1l); 状态转换
持久–>瞬时:调用session的delete方法
持久–>脱管:调用session的close、clear、evict方法
close()、clear()(清空所有)、evict(Obeject obj)(清空某一个);
4、托管态对象
脱管态对象
获得:没有直接获得的办法,但可以创建完之后设置一个id
Customer customer = new Customer(); customer.setCust_id(1l); 状态转换
脱管–>持久:调用session的update、saveOrUpdate方法
update(Object obj)、saveOrUpdate(Object obj); 脱管–>瞬时:将id设置为null即可
customer.setCust_id(null);
三、持久态对象特性
1、持久化类持久态对象自动更新数据库
@Test
// 持久态对象自动更新数据库
public void demo2() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
// 获得持久态对象
Customer customer = session.get(Customer.class, 1l); // 持久态对象,有自动更新数据库的功能
customer.setCust_name("李小宝");
// session.update(customer);
transaction.commit();
session.close();
}
当设置的名字和原来的一样的时候,只进行查询,不进行更新,很智能。
原理 :依赖了hibernate的一级缓存。
Hibernate的一级缓存
一、缓存的概述
1、什么是缓存
缓存:是一种优化的方式,将数据存入到内存中,使用的时候直接从缓存中获取,不用通过存储源。
二、Hibernate的缓存
1、Hibernate的一级缓存
Hibernate框架中提供了多种多样的优化手段:缓存、抓取策略。
Hiernate中提供了两种缓存机制:
一级缓存
Hibernate的一级缓存称为是Session级别的缓存,以及缓存生命周期与session一致。即如果session创建了,则以及缓存也就存在了,session如果被销毁,那么一级缓存也就没有了,因为一级缓存是由session中的一系列Java集合构成的。 一级缓存是自带的不可卸载的。 二级缓存
Hibernate二级缓存是SessionFactory级别的缓存,需要配置的缓存,也就是说,二级缓存默认是不开启的,如果要使用二级缓存,则需要你自己去进行配置。 现在企业中一般很少用二级缓存,基本上不用,替代的是redis。
2、证明一级缓存的存在
@Test
// 证明一级缓存的存在
public void demo1() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
/*Customer customer1 = session.get(Customer.class, 1l); // 发送sql语句
System.out.println(customer1);
Customer customer2 = session.get(Customer.class, 1l); // 不发送sql语句,是从缓存里获取,不去数据库查询
System.out.println(customer2);
System.out.println(customer1 == customer2);*/
Customer customer = new Customer();
customer.setCust_name("王峰");
Serializable id = session.save(customer);
Customer customer2 = session.get(Customer.class, id); // 不发送sql语句
System.out.println(customer2);
transaction.commit();
session.close();
}
三、Hibernate的一级缓存的内部结构
1、一级缓存中的特殊区域:快照区
@Test
// 一级缓存的快照区
public void demo2() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
Customer customer = session.get(Customer.class, 1l); // 发送SQL语句,同时放到一级缓存中去
customer.setCust_name("王大锤");
transaction.commit();
session.close();
}
Hibernate的事务管理
一、事务的回顾
1、什么是事务
事务:事务指的是逻辑上的一组操作,组成这组操作的各个逻辑单元要么全部成功,要么全部失败。
2、事务特性
原子性:代表事务不可分割; 一致性:代表事务执行的前后,数据的完整性保持一致; 隔离性:代表一个事务执行的过程中,不应该受到其他事务的干扰; 持久性:代表事务执行完成后,数据就持久到数据库中。
3、如果不考虑隔离性,则会引发安全性问题
读问题
脏读:一个事务读到另一个事务未提交的数据; 不可重复读:一个事务读到另一个事务已经提交的update数据,导致在前一个事务多次查询结果不一致; 虚读:一个事务读到另一个事务已经提交的insert数据,导致在前一个事务多次查询结果不一致。 写问题(了解)
4、读问题的解决
设置事务的隔离级别
Read uncommitted : 以上读问题都会发生; Read committed : 解决脏读,但是不可重复读和虚读有可能发生;(Oracle采用) Repeatable read : 解决脏读和不可重复读,但是虚读有可能发生;(MySQL采用) Serializable : 解决所有读问题,不能有事务的并发。(效率最低,安全性最高,故基本不用)
二、Hibernate中设置事务隔离级别
1、Hibernate中设置事务的隔离级别
1 --Read uncommitted : 以上读问题都会发生; 2 --Read committed : 解决脏读,但是不可重复读和虚读有可能发生;(Oracle采用) 4 --Repeatable read : 解决脏读和不可重复读,但是虚读有可能发生;(MySQL采用) 8 --Serializable : 解决所有读问题。(效率最高,安全性最低,故基本不用)
三、Service层事务
1、Hibernate解决Service的事务管理
改写工具类 public class HibernateUtils {
// 定义session创建所需要的Configuration,SessionFactory,都定义成静态的
public static final Configuration cfg;
public static final SessionFactory sf;
// 对上面的两个对象进行赋值,静态代码块
static{
cfg = new Configuration().configure();
sf = cfg.buildSessionFactory();
}
// 对外提供一个方法,返回openSession()
public static Session openSession(){
return sf.openSession();
}
public static Session getCurrentSession() {
return sf.getCurrentSession();
}
}
配置完成
Hibernate的其他API
一、Hibernate的其他API
1、Query
Query借口给用户接受HQL,查询多个对象;
HQL: Hibernate Query Language Hibernate查询语言,这种语言与SQL的语法极其类似,是面向对象的查询语言。 @Test
// Query
public void demo1() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
// 通过Session获得Query接口
// 查询所有数据
// String hql="from Customer";
// 条件查询,如:查询所有姓王的
// String hql = "from Customer where cust_name like ?";
//分页查询
String hql = "from Customer";
Query query = session.createQuery(hql);
// 设置条件
// query.setParameter(0, "王%");
// 设置分页
query.setFirstResult(3);
query.setMaxResults(3);
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
}
2、Criteria
Creteria : QBC(Query By Criteria)
更加面向对象的一种查询方式。 @Test
// Criteria
public void demo2() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
// 通过session获得Criteria的对象
/*Criteria criteria = session.createCriteria(Customer.class);
List<Customer> list = criteria.list();*/
// 条件查询
/*Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.like("cust_name", "李%"));*/
// 还有一种写法,like后面是三个参数,最后一个是%,END是在后面,START是在前面,ANYWHERE是恰后都有
//criteria.add(Restrictions.like("cust_name", "李", MatchMode.ANYWHERE));
// 分页查询
Criteria criteria = session.createCriteria(Customer.class);
criteria.setFirstResult(3);
criteria.setMaxResults(4);
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
}
3、SQLQuery
SQLQuery用于接收SQL。在sql语句特别复杂的时候使用SQLQuery,否则一般情况下使用前两种方法就可以了。