目录
Hibernate中的实体规则
Hibernate中的对象状态
Hibernate中的一级缓存
Hibernate中的事务
Hibernate中的批量查询
Hibernate中的实体规则
实体类创建的注意事项
- 持久化类提供无参数构造。这个是因为Hibernate创建对象运用的是反射技术也就是我们的newInstance()方法,而该方法默认走的是空参构造。
- 成员变量私有,提供共有get/set方法访问。也就是说需要提供属性。这里的属性指的是get/set方法而不是成员变量,需要特别注意!
- 持久化类中的属性,应尽量使用包装类型。这个很容易想,使用基本类型是没办法存储null值的。
- 持久化类需要提供OID,与数据库中的主键列对应。因为Hibernate判断是否为同一个持久化类的依据是OID。类似于Java中通过地址判断是否为同一个对象,Hibernate就是根据OID来进行区分的,Hibernate是不允许在内存中出现两个OID相同的持久化对象的。
- 持久化类尽量不要使用final进行修饰。Hibernate使用cglib代理生成代理对象。代理对象是继承被代理对象。如果被final修饰,将无法生产代理,那么Hibernate的延迟加载策略(一种优化手段)就会失效。
主键类型
- 自然主键(少见):把具有业务含义的字段作为主键,称之为自然主键。表的业务列中,有某业务列符合,必须有,并且不重复的特征时,该列可以作为主键使用.
- 代理主键(常见):把不具备业务含义的字段作为主键,称之为代理主键。表的业务列中,没有某业务列符合,必须有,并且不重复的特征时,创建一个没有业务意义的列作为主键
主键生成策略
代理主键:
- identity:主键自增。由数据库来维护主键值。录入时不需要指定主键。
- sequence:Oracle中的主键生成策略。
- increment(了解):主键自增。由Hibernate来维护。每次插入前会先查询表中id最大值。+1作为新主键值。
- hilo(了解):高低位算法。主键自增。由Hibernate来维护。开发时不使用。
- native:hilo+sequence+identity自动三选一策略。
- uuid:产生随机字符串作为主键。主键类型必须为String类型。
自然主键:
- assigned:自然主键生成策略。Hibernate不会管理主键值。由开发人员自己录入。
我们可以发现,昨天我们就使用了native代理主键生成策略。
Hibernate中的对象状态
对象的三种状态
- 瞬时状态:不存在持久化标识OID,没有在Session缓存中。
- 持久态:存在持久化标识OID,在Session缓存中。
- 脱管态:存在持久化标识OID,没有在Session缓存中
我们可以写个小demo来理解持久化对象的三种状态。
@Test
/**
* 测试持久化对象的三种状态
*/
public void demo1(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Customer customer = new Customer();//瞬时态,没有oid也没有在session缓存中
customer.setCust_name("Dokyun");
session.save(customer);//持久态,有oid,存在session缓存中
tx.commit();
session.close();
System.out.println(customer);//脱管态,有oid,但是没有session缓存中
}
customer对象由new关键字创建,此时还未与Session进行关联,它的状态为瞬时态;在执行了session.save(customer)操作后,customer对象纳入了Session的管理范围,这时的customer对象变成了持久态对象,此时Session的事务还没有被提交;程序执行完commit()操作并关闭了Session后,customer对象与Session的关联被关闭,此时customer对象就变成了脱管态。
三种状态的转换图
Hibernate中的一级缓存
证明一级缓存的存在
下面我们来看一个demo
@Test
/**
* 测试Hibernate一级缓存的存在
*/
public void demo2(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Customer c1 = session.get(Customer.class, 1l);
Customer c2 = session.get(Customer.class, 1l);
System.out.println(c1==c2);
tx.commit();
session.close();
}
控制台结果:
我们可以发现,我们调用了两次get方法,但是控制只输出了一次sql语句,并且比较c1和c2的结果为true。
在调用get方法查询时,hibernate会先从缓存中查看是否存在需要查询的对象,如果有,直接返回该对象,没有就发送sql进行数据库的查询操作。
一级缓存的内部结构:快照区
Hibernate向一级缓存放入数据时,同时复制一份数据放入到Hibernate快照中,当使用commit()方法提交事务时,同时会清理Session的一级缓存,这时会使用OID判断一级缓存中的对象和快照中的对象是否一致,如果比对不一致,则执行update语句,将缓存的内容同步到数据库,更更新快照;如果一致,则不执行update语句。Hibernate快照的作用就是确保一级缓存中的数据和数据库中的数据一致。
快照区的存在我们也可以写个demo来进行证明:
我们通过get方法拿到了customer持久化对象,先后设置了两次cust_name值,在数据库中,第一条数据的cust_name值原本为google,设置了两次之后,hibernate并没有执行update语句。
结论:快照区的作用减少了不必要的修改语句发送,提高了效率。
进阶知识,学有余力的同学可以看。
下面我们来看一个例子,
@Test
/**
* 证明持久化状态,就是持久化对象进入到了session缓存中
*/
public void demo4(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Customer c1 = new Customer();
c1.setCust_id(1l);//设置c1对象的主键,将其变成脱管态
session.update(c1);//通过update方法,将c1的状态由脱管态变为持久态,也就是说c1进入到了session缓存中
Customer c2 = session.get(Customer.class, 1l);
/*
这个时候执行get方法查询,会先在session缓存中查看是否有主键为1的对象,所以直接就拿到了c1对象,
注意,从session缓存中取到的c1对象,是没有快照的,因为没有经过数据库。
*/
tx.commit();//这个时候进行事务提交,会比对快照和缓存中的对象是否一致,但是快照中根本就没有c1对象,所以每次都会执行update方法
session.close();
}
Hibernate中的事务
事务
事务特性
- 原子性:将事务看作物理上不可分割的原子,事务中所包裹的操作,要么全执行,要么全都不执行。
- 一致性:在事务提交之前和提交之后,所有的数据总量保持一致。
- 隔离性:提供隔离级别,解决事务并发产生的问题。
- 持久性:事务提交之后,保证数据提交到持久化存储设备中例如硬盘。
事务并发问题
- 脏读:读到了一个人正在操作还未提交的数据。
- 不可重复读:两次连续的读取,数据不一致。
- 幻/虚读:往往针对的是整表的操作,比如让你把整个表删除,结果有其他人在你删除的期间,加了一条数据。结果一查,以为是空的,结果还有一条。好像出现了幻觉。
事务隔离级别
- 读未提交:可能出现上面1、2、3的问题
- 读已提交(Oracle默认几倍):可能出现上面2、3的问题
- 可重复读(MySql默认级别):可能出现3的问题
- 串行化:没有问题,但是效率极低。
如何在Hibernate中指定数据库的隔离级别
在项目中如何管理事务
总原则:业务开始之前打开事务,业务执行之后提交事务。执行过程中如果出现异常,回滚事务。
在dao层操作数据库需要用到session对象.在service控制事务也是使用session对象完成. 我们要确保dao层和service层使用的使用同一个session对象。
在hibernate中,确保使用同一个session的问题,hibernate已经帮我们解决了. 我们开发人员只需要调用sf.getCurrentSession()方法即可获得与当前线程绑定的session对象。
注意1:调用getCurrentSession方法必须配合主配置文件中的一段配置
注意2:通过getCurrentSession方法获得的session对象。当事务提交时。session会自动关闭。不要手动调用close关闭,否则会抛异常。
在我们实际的项目中的话,可以这样操作。
service层
dao层
Hibernate中的批量查询
HQL查询-hibernate Query Language(多表查询,但不复杂时使用)
HQL查询,是面向对象的查询,属于Hibernate独家查询语言
基本查询
@Test
/**
* HQL语句基本查询
*/
public void demo6(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//1.书写HQL语句
String hql =" from Customer ";
//2.根据HQL语句创建查询对象
Query query = session.createQuery(hql);
//3.根据查询对象获得查询结果
List<Customer> list = query.list();
System.out.println(list);
tx.commit();
session.close();
}
条件查询
@Test
/**
* HQL语句条件查询
*/
public void demo7(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//1.书写HQL语句
String hql =" from Customer where cust_id = 1";
//2.根据HQL语句创建查询对象
Query query = session.createQuery(hql);
//3.根据查询对象获得查询结果
Customer c = (Customer) query.uniqueResult();
System.out.println(c);
tx.commit();
session.close();
}
@Test
/**
* HQL语句条件查询,通过问号,占位符的方式
*/
public void demo8(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//1.书写HQL语句
String hql =" from Customer where cust_id =?";
//2.根据HQL语句创建查询对象
Query query = session.createQuery(hql);
//3.设置参数
query.setParameter(0, 1l);
//4.根据查询对象获得查询结果
Customer c = (Customer) query.uniqueResult();
System.out.println(c);
tx.commit();
session.close();
}
这里的setParameter方法是相当方便的,它不需要去思考这个的类型,无论是long,int都可以设置。
最后还有一种命名站位符,这种方式的好处是不需要数是第几个占位符。
@Test
/**
* HQL语句条件查询,通过命名占位符的方式
*/
public void demo9(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//1.书写HQL语句
String hql =" from Customer where cust_id = :cust_id";
//2.根据HQL语句创建查询对象
Query query = session.createQuery(hql);
//3.设置参数
query.setLong("cust_id", 1l);
//4.根据查询对象获得查询结果
Customer c = (Customer) query.uniqueResult();
System.out.println(c);
tx.commit();
session.close();
}
分页查询
@Test
/**
* HQL语句分页查询
*/
public void demo10(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//1.书写HQL语句
String hql =" from Customer ";
//2.根据HQL语句创建查询对象
Query query = session.createQuery(hql);
//3.设置参数,limit ?,?
query.setFirstResult(1);//从第几条开始抓
query.setMaxResults(3);//抓多少数据
//4.根据查询对象获得查询结果
List<Customer> list = query.list();
System.out.println(list);
tx.commit();
session.close();
}
Criteria查询(适用于单表条件查询)
Hibernate自创的无语句面向对象查询
基本查询
@Test
/**
* criteria基本查询
*/
public void demo1(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Criteria criteria = session.createCriteria(Customer.class);//查询所有customer对象
List<Customer> list = criteria.list();
System.out.println(list);
tx.commit();
session.close();
}
条件查询
@Test
/**
* criteria条件查询// > gt
// >= ge
// < lt
// <= le
// == eq
// != ne
// in in
// between and between
// like like
// is not null isNotNull
// is null isNull
// or or
// and and
*/
public void demo2(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//创建criteria对象
Criteria criteria = session.createCriteria(Customer.class);//查询所有customer对象
criteria.add(Restrictions.eq("cust_id", 1l));
Customer customer = (Customer) criteria.uniqueResult();
System.out.println(customer);
tx.commit();
session.close();
}
分页查询
@Test
/**
* criteria分页查询
*/
public void demo3(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//创建criteria对象
Criteria criteria = session.createCriteria(Customer.class);//查询所有customer对象
criteria.setFirstResult(0);
criteria.setMaxResults(3);
List<Customer> list = criteria.list();
System.out.println(list);
tx.commit();
session.close();
}
查询总条数
@Test
/**
* criteria查询总条数
*/
public void demo4(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//创建criteria对象
Criteria criteria = session.createCriteria(Customer.class);//查询所有customer对象
criteria.setProjection(Projections.rowCount());
long count = (Long) criteria.uniqueResult();
System.out.println(count);
tx.commit();
session.close();
}
原生SQL查询(适用于复杂业务查询)
基本查询
@Test
/**
* 返回数组List
*/
public void demo1(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//1.写sql语句
String sql = "select* from cst_customer";
//2.创建sql查询对象
SQLQuery query = session.createSQLQuery(sql);
//3.调用方法查询结果
List<Object[]> list = query.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
tx.commit();
session.close();
}
@Test
/**
* 返回对象List
*/
public void demo2(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//1.写sql语句
String sql = "select* from cst_customer";
//2.创建sql查询对象
SQLQuery query = session.createSQLQuery(sql);
//指定将结果集封装到哪个对象中
query.addEntity(Customer.class);
//3.调用方法查询结果
List<Customer> list = query.list();
System.out.println(list);
tx.commit();
session.close();
}
条件查询
@Test
/**
* 条件查询
*/
public void demo3(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//1.写sql语句
String sql = "select* from cst_customer where cust_id = ?";
//2.创建sql查询对象
SQLQuery query = session.createSQLQuery(sql);
query.setParameter(0, 1l);
query.addEntity(Customer.class);
//3.调用方法查询结果
Customer c = (Customer) query.uniqueResult();
System.out.println(c);
tx.commit();
session.close();
}
分页查询
@Test
/**
* 分页查询
*/
public void demo4(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//1.写sql语句
String sql = "select* from cst_customer limit ?,?";
//2.创建sql查询对象
SQLQuery query = session.createSQLQuery(sql);
query.setParameter(0, 0);
query.setParameter(1, 3);
//将指定结果集封装到哪个对象中
query.addEntity(Customer.class);
//3.调用方法查询结果
List<Customer> list = query.list();
System.out.println(list);
tx.commit();
session.close();
}