Hibernate_day02
1.今天任务
·Hibernate中的实体规则
·Hibernate中的对象状态
·Hibernate进阶-一级缓存
·Hibernate中的事务
·Hibernate中的批量查询
·案例练习-客户列表
2.相关知识点
2.1 Hibernate中的实体规则
2.1.1 实体类创建的注意事项
1.持久化类提供无参数构造。
2.成员变量私有,提供共有get/set方法访问.需提供属性
3.持久化类中的属性,应尽量使用包装类型
4.持久化类需要提供oid.与数据库中的主键列对应
5.不要用final修饰class (hibernate使用cglib代理生成代理对象.代理对象是继承被代理对象.如果被final修饰.将无法生成代理.)
2.1.2 主键类型
1.自然主键(少见)
表的业务列中,有某业务列符合,必须有,并且不重复的特征时,该列可以作为主键使用.
2.代理主键(常见)
表的业务列中,没有某业务列符合,必须有,并且不重复的特征时,创建一个没有业务意义的列作为主键
2.1.3 主键生成策略
1. 代理主键
identity : 主键自增.由数据库来维护主键值.录入时不需要指定主键.
sequence:Oracle中的主键生成策略.
increment(了解): 主键自增.由hibernate来维护.每次插入前会先查询表中id最大值.+1作为新主键值.
hilo(了解): 高低位算法.主键自增.由hibernate来维护.开发时不使用.
native:hilo+sequence+identity自动三选一策略.
uuid: 产生随机字符串作为主键. 主键类型必须为string类型.
2. 自然主键
assigned:自然主键生成策略. hibernate不会管理主键值.由开发人员自己录入.
2.2 Hibernate中的对象状态
Hibernate管理对象包括三种状态:瞬时状态(Transient)、持久状态(Persistent)、托管/游离状态(Detached),如下图:
1、transient 瞬时状态
new一个新的对象即为瞬时状态,瞬时状态和session没有关联,一个瞬时对象和数据库记录不对应。
瞬时对象--->save或saveOrUpadate---->持久对象。
2、persistent 持久状态
持久状态对象和session有关联,持久对象和数据库记录存在对应,持久对象属性值变更可以持久到数据库中。
执行Get查询可以得到一个持久对象。
持久对象--->delete删除--->瞬时对象
持久对象-->session.close(session关闭)--->托管对象
3、detached 托管状态
托管对象和session没有关联,它和瞬时对象的区别在于托管对象可能会和数据库记录存在对应。
托管对象------>update或saveOrUpdate --->持久对象
2.3 Hibernate进阶-一级缓存
2.3.1 什么是一级缓存
Hibernate框架中共有两级缓存. Session级别的缓存是属于一级缓存,SessionFactory级别的缓存是二级缓存.
缓存:将数据库或硬盘中的数据,存入到内存中.当下次使用的时候可以从内存中获得,减少数据库访问次数.(优化)
一级缓存:生命周期与session一致.
在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存. 只要 Session 实例没有结束生命周期, 存放在它缓存中的对象也不会结束生命周期.
当session的save()方法持久化一个对象时,该对象被载入缓存,以后即使程序中不再引用该对象,只要缓存不清空,该对象仍然处于生命周期中。当试图get()、 load()对象时,会判断缓存中是否存在该对象,有则返回,此时不查询数据库。没有再查询数据库。
2.3.2 测试一级缓存
测试方法1:
@Test
public void f1() {
// 获取session
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
session.close();
}
控制台打印结果:
方法1测试结果可以看到查询了两次只打印一次sql查询语句 由此得知缓存起作用了
缓存原理图如下:
测试方法2:
@Test
public void f2(){
// 获取session
Session session =HibernateUtils.openSession();
// 开启事务
Transaction tx = session.beginTransaction();
// 执行代码
// 获取id为1的客户对象此时该对象为持久化状态 (该id数据库name属性为小石)
Customer c = session.get(Customer.class, 1l);
// 修改客户名字
c.setCust_name("小林");
// 改回客户名字
c.setCust_name("小石");
// 提交事务
tx.commit();
// 关闭session
session.close();
}
控制台打印结果:
从测试结果到控制台只打印了查询语句 没有打印修改语句 原因是hibernate会对比缓存中的对象和快照,如果有变化会同步到数据库中。从而提高效率,减少不必要的修改语句发送。
原理图如下:
2.4 Hibernate中的事务
2.4.1什么是事务?
什么是事务?
当多个操作有这样的要求时:要么都成功要么都失败,这说明这多个操作必须在一个事务中,事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。
事务的四个特性?
A:原子性(Atomicity)
事务中包括的诸操作要么全成功,要么全失败。
B:一致性(Consistency)
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。
也就是说事务执行之前数据库数据处于正确状态,执行之后也处理正确状态,如果有些事务运行中发生故障,这些事务对数据库所做的修改有一部分已写入数据库,这时数据库就处于不正确的状态即不一致的状态。
一致性与原子性是密切相关的,如果事务没有原子性的保证,那么在发生系统故障的情况下,数据库就有可能处于不一致状态。
C:隔离性(Isolation)
一个事务的执行不能被其他事务干扰。解决多个线程并发操作事务情况,每个事务互相不影响
D:持续性/永久性(Durability)
一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
2.4.2 事务隔离级别
ANSI SQL 标准定义了隔离级别,但并不是SQL数据库独有.JTA也定义了同样的隔离级别.级别越高,成本越高。
由事务隔离性引发几种问题?
脏读、 不可重复读 、虚读 、 丢失更新
脏读: 一个事务读取到另一个事务 未提交数据
不可重复读: 一个事务中,先后读取两次,两次读取结果不同,读取到了另一个事务已经提交的数据,此问题针对update更新来说。
虚读: 一个事务,先后读取两次,结果不同,读取到了另一个事务已经提交的数据 ,此问题针对insert插入来说。
丢失更新: 两个事务 同时修改一条数据,后提交事务,覆盖了之前提交事务结果
事务的隔离级别,是由数据库提供的,并不是所有数据库都支持四种隔离级别
MySQL: READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE (默认 REPEATABLE_READ)
Oracle: READ_UNCOMMITTED、READ_COMMITTED、 SERIALIZABLE
(默认 READ_COMMITTED )
在使用数据库时候,隔离级别越高,安全性越高,性能越低
实际开发中,不会选择最高或者最低隔离级别,选择 READ_COMMITTED(oracle 默认)、REPEATABLE_READ (mysql默认)
Hibernate 如何设置隔离级别 ?
每个数据库连接都有默认的隔离级别,通常是读已提交或可重复读.可以通过数据库配置设置,也可在应用程序中设置.例如Hibernate:
hibernate.connection.isolation = 4
1—Read uncommittedisolation
2—Read committedisolation
4—Repeatable readisolation
8—Serializableisolation
在hibernate.cfg.xml中设置隔离级别:
<!--
事务隔离级别
1 读未提交
2 读已提交
4 可重复读
8 串行化
-->
<property name="hibernate.connection.isolation">4</property>
2.4.3 在项目中如何控制管理事务
在业务开始之前打开事务,在业务执行之后提交事务,如果在执行过程中出现异常则回滚事务。
确保service层跟dao层使用的是同一个对象,hibernate已经提供了获取当前线程session的方法 sessionFactory.getCurrentSession();
调用getCurrentSession()方法首先需要在主配置文件中配置以下信息:
<!-- 指定session与当前线程绑定 -->
<property name="hibernate.current_session_context_class">thread</property>
Crm项目中事务控制代码:
Service层
public void save(Customer c) {
//TODOAuto-generated method stub
//开始之前开启事务
Transaction tx = HibernateUtils.getCurrentSession().beginTransaction();
try{
//调用dao层添加客户
customrDao.save(c);
} catch(Exception e) {
//出现异常回滚事务
tx.rollback();
}
//执行完提交事务
tx.commit();
//事务提交后无需调用close方法关闭session hibernate已经帮我们完成了在getCurrentSession的事务提交之后自动关闭session
}
Dao层
public void save(Customer c) {
// TODOAuto-generated method stub
//打开一个会话
Session session =HibernateUtils.getCurrentSession();
//保存到数据库
session.save(c);
}
2.5Hibernate中的批量查询
2.5.1 HQL查询(一般用于不复杂的多表查询)
参考:http://www.cnblogs.com/liuconglin/p/5716624.html
/**
* hql无参查询所有客户
*/
@Test
public void f1(){
//打开一个session
Session session =HibernateUtils.openSession();
//hql语句
String hql = "fromCustomer";
//创建查询
Query query = session.createQuery(hql);
//查询结果会封装到list集合
List<Customer> cList = query.list();
//打印查询结果
System.out.println(cList.toString());
//关闭session
session.close();
}
/**
* 命令占位符
*/
@Test
public void f2(){
//打开一个session
Session session = HibernateUtils.openSession();
//hql语句
String hql = "fromCustomer where cust_id=:cust_id";
//创建查询
Query query = session.createQuery(hql);
//设置参数
//query.setLong("cust_id",1l);
query.setParameter("cust_id", 1l); //使用setParameter可以设置所有类型的参数
//查询结果会封装到list集合
Customer c =(Customer) query.uniqueResult();
//打印查询结果
System.out.println(c.toString());
//关闭session
session.close();
}
/**
* ?占位符
*/
@Test
public void f3(){
//打开一个session
Session session =HibernateUtils.openSession();
//hql语句
String hql = "fromCustomer where cust_id=?";
//创建查询
Query query = session.createQuery(hql);
//设置参数
//query.setLong(0, 1l);
query.setParameter(0, 1l); //使用setParameter可以设置所有类型的参数
//查询结果会封装到list集合
Customer c =(Customer) query.uniqueResult();
//打印查询结果
System.out.println(c.toString());
//关闭session
session.close();
}
/**
* 分页查询
*/
@Test
public void f4(){
//打开一个session
Session session =HibernateUtils.openSession();
//hql语句
String hql = "fromCustomer";
//创建查询
Query query = session.createQuery(hql);
//设置分页参数
query.setFirstResult(0); //相当于limit的第一个参数
query.setMaxResults(2); //相当于limit的第二个参数
//查询结果会封装到list集合
List<Customer> list = query.list();
//打印查询结果
System.out.println(list.toString());
//关闭session
session.close();
}
2.5.2 Criteria查询(一般用于单表查询)
参考:http://www.cnblogs.com/liunanjava/p/4340103.html
/**
* 基本查询
*/
@Test
public void f1(){
Session session =HibernateUtils.openSession();
//创建criteria查询
Criteria criteria = session.createCriteria(Customer.class);
//获取查询结果集合
List<Customer> list = criteria.list();
System.out.println(list.toString());
//关闭session
session.close();
}
/**
* 条件查询
*/
@Test
public void f2(){
Session session =HibernateUtils.openSession();
//创建criteria查询
Criteria criteria = session.createCriteria(Customer.class);
//添加参数
criteria.add(Restrictions.eq("cust_id", 1l));
//得到查询结果
Customer c =(Customer) criteria.uniqueResult();
System.out.println(c.toString());
//关闭session
session.close();
}
/**
* 分页查询
*/
@Test
public void f3(){
Session session =HibernateUtils.openSession();
//创建criteria查询
Criteria criteria = session.createCriteria(Customer.class);
//设置分页参数
criteria.setFirstResult(0);
criteria.setMaxResults(2);
//获取查询结果集合
List<Customer> list = criteria.list();
System.out.println(list.toString());
//关闭session
session.close();
}
/**
* 查询记录总数
*/
@Test
public void f4(){
Session session =HibernateUtils.openSession();
//创建criteria查询
Criteria criteria = session.createCriteria(Customer.class);
criteria.setProjection(Projections.rowCount());
Long count = (Long) criteria.uniqueResult();
System.out.println(count);
//关闭session
session.close();
}
2.5.3原生SQL查询(一般用于复杂的业务查询)
参考:http://www.cnblogs.com/klguang/p/4872605.html
/**
* 基本查询(返回对象集合)
*/
@Test
public void f1(){
//打开session
Session session =HibernateUtils.openSession();
//创建查询
String sql = "select* from cst_customer";
SQLQuery query = session.createSQLQuery(sql);
//添加查询结果的实体对象
query.addEntity(Customer.class);
List<Customer> cList = query.list();
System.out.println(cList);
//关闭session
session.close();
}
/**
* 基本查询(返回数组)
*/
@Test
public void f2(){
//打开session
Session session =HibernateUtils.openSession();
//创建查询
String sql = "select* from cst_customer";
SQLQuery query = session.createSQLQuery(sql);
//添加查询结果的实体对象
List<Object[]> cList = query.list();
for (Object[] objects : cList) {
System.out.println(Arrays.toString(objects));
}
//关闭session
session.close();
}
/**
* 条件查询
*/
@Test
public void f3(){
//打开session
Session session =HibernateUtils.openSession();
//创建查询
String sql = "select* from cst_customer where cust_id=?";
SQLQuery query = session.createSQLQuery(sql);
query.addEntity(Customer.class);
//添加查询条件
query.setParameter(0,1l);
//打印查询结果
List<Customer> list = query.list();
System.out.println(list);
//关闭session
session.close();
}
/**
* 分页查询
*/
@Test
public void f4(){
//打开session
Session session =HibernateUtils.openSession();
//创建查询
String sql = "select* from cst_customer limit ?,?";
SQLQuery query = session.createSQLQuery(sql);
query.addEntity(Customer.class);
//添加查询条件
query.setParameter(0,0);
query.setParameter(1,2);
//打印查询结果
List<Customer> list = query.list();
System.out.println(list);
//关闭session
session.close();
}
3.CRM案例-客户列表
3.1案例需求
CRM系统中客户信息管理模块功能包括:
新增客户信息
客户信息查询
修改客户信息
删除客户信息
本功能要实现查询客户,页面如下:
3.2代码实现
Servlet:
public Stringlist(HttpServletRequest request, HttpServletResponse response) throws Exception{
//获取当期页
int currentPage = 1;
//如果参数为空默认显示第一页
if (request.getParameter("currentPage")!=null) {
currentPage = Integer.parseInt(request.getParameter("currentPage"));
}
//设置每一页显示几条记录
int pageSize = 10;
//调用业务层获取客户列表
PageBean<Customer> pb = customerService.getCustomerByPage(currentPage,pageSize);
request.setAttribute("pb", pb);
return"/jsp/customer/list.jsp";
}
Service层:
public PageBean<Customer> getCustomerByPage(int currentPage, int pageSize) throws Exception{
// TODOAuto-generated method stub
//获取客户列表
List<Customer> list = customrDao.getCustomerList(currentPage,pageSize);
//获取总数量
int count = customrDao.getCount();
//封装到pagebean
PageBean<Customer> pb = newPageBean<>(list, currentPage, pageSize, count);
return pb;
}
Dao层:
@Override
public List<Customer> getCustomerList(int currentPage, int pageSize) throws Exception{
// TODOAuto-generated method stub
// 打开会话
Session session =HibernateUtils.openSession();
// hql语句
String hql = "fromCustomer";
// 创建查询
Query query = session.createQuery(hql);
// 设置分页
query.setFirstResult((currentPage - 1) * pageSize);
query.setMaxResults(pageSize);
// 获取客户列表
List<Customer> list = query.list();
// 关闭session
session.close();
return list;
}
@Override
public int getCount()throws Exception {
// TODOAuto-generated method stub
// 打开会话
Session session =HibernateUtils.openSession();
// 创建查询
Criteria criteria = session.createCriteria(Customer.class);
// 设置参数
criteria.setProjection(Projections.rowCount());
// 总数量
Long count = (Long) criteria.uniqueResult();
// 关闭session
session.close();
return count.intValue();
}
3.3测试