hibernate缓存快照、hibernate事务、hibernate批量查询
hibernnate进阶
hibernate一级缓存
hibernate中的一级缓存是为了提高操作效率,减少不必要的读取
在使用get()方法向数据库中查询数据时,会将返回的ResultSet对象里面的内容封装成实体类对象,并向hibernate一级缓存中放入该对象,在下一次需要从上数据库取数据时如果缓存中已经有了符合要求的对象,那么就不会在从数据库里面取了,而是直接拿缓存里面的对象,这样可以减少访问数据库的次数,提高效率
下面这段程序体现了hibernate缓存的作用:
Session session = HBUtils.getSession(); Transaction t = session.beginTransaction(); // 设置相同的条件拿取对象 Customer customer = session.get(Customer.class, 1L); // 执行完该方法后,缓存里面已经有了id为1的对象了 Customer customer2 = session.get(Customer.class, 1L); // customer == customer2 得到的结果是true,说明两次get()拿到的是同一个对象 // 这是因为hibernate一级缓存里面有满足要求的数据,所以在第二次执行get()方法的时候拿到的是缓存里面的同一个对象 System.out.println(customer == customer2); t.commit(); session.close();
在之前的学习中有提到过对象的三种状态,即瞬时状态,持久化状态,游离|托管状态,对应hibernate缓存即是:
- 瞬时状态:没有id, 没在session缓存中
- 持久化状态:有id, 在session缓存中
- 游离|托管状态:有id,没在session缓存中
hibernate快照
hibernate中的快照是为了减少对数据库的update,减少不必要的修改
在执行commit()方法的时候,hibernate会将缓存里面的对象与快照里面的对象进行对比,如果不一致,那么执行update将新的数据刷新到数据库,如果对象一致,说明用户没有对对象进行更改,那么就不会执行update
下面两段程序体现了hibernate快照的作用:
Session session = HBUtils.getSession(); Transaction t = session.beginTransaction(); Customer customer = session.get(Customer.class, 1L); customer.setCust_name("timo"); // 程序修改了对象的Cust_name属性 session.update(customer); System.out.println("=================="); t.commit(); // 在执行commit()方法的时候会将缓存中的对象与快照中的对象,由于进行了修改,所以会执行update session.close();
上面的程序由于用户修改了对象导致缓存和快照中的数据不一致,所以会执行update
Session session = HBUtils.getSession(); Transaction t = session.beginTransaction(); Customer customer = session.get(Customer.class, 1L); System.out.println("=================="); t.commit(); // 程序并未对对象作出任何的更改,所以缓存和快照中的数据依然一致,不会update到数据库 session.close();
并没有执行update
hibernate事务
事务的特性(ACID):
- 原子性(A):事务的原子性指的是,事务中包含的程序作为数据库的逻辑工作单位,它所做的对数据修改操作要么全部执行,要么完全不执行。
- 一致性(C):事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。这种特性称为事务的一致性。假如数据库的状态满足所有的完整性约束,就说该数据库是一致的。
- 隔离性(I):隔离性指并发的事务是相互隔离的。即一个事务内部的操作及正在操作的数据必须封锁起来,不被其它企图进行修改的事务看到。
- 持久性(D):持久性意味着当系统或介质发生故障时,确保已提交事务的更新不能丢失。即一旦一个事务提交,DBMS保证它对数据库中数据的改变应该是永久性的,耐得住任何系统故障。持久性通过数据库备份和恢复来保证。
事务并发的问题
- 脏读:指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据
- 不可重复读:一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读
- 幻读 | 虚读:是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,之后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样
事务的隔离级别
- 读未提交:不能解决(脏读、不可重复读、幻读)
- 读已提交:不能解决(不可重复读,幻读)
- 可重复读(mysql的默认级别):不能解决(幻读)
- 串行化:都能解决,但是效率低,不常使用
hibernate配置事务级别
在 hibernate-release-5.0.7.Final\project\etc\hibernate.properties 文件中找到隔离级别
## specify a JDBC isolation level #hibernate.connection.isolation 4
在src目录下的hibernate.cfg.xml文件中配置事务级别,即往文件中添加下面一行数据
<property name="hibernate.connection.isolation">4</property>
上面标签体中的数字表示隔离级别,即:
- 0001 --> 1 --> 读未提交
- 0010 --> 2 --> 读已提交
- 0100 --> 4 --> 可重复读
- 1000 --> 8 --> 串行化
上面的4表示选择可重复读
如何在项目中管理事务
在业务开始之前打开事务,业务提交之后提交事务,执行过程中有异常,回滚事务
在Dao层操作数据库需要用到session对象,在service层控制事务也是需要用到session对象完成
要确保dao层和service层使用的是同一个session对象
在hibernate中已经解决了确保使用同一个session的问题(ThreadLocal),开发人员只需要调用sessionFactory.getCurrentSession()方法即可获得与当前线程绑定的session
注意一:调用getCurrentSession()方法必须在主配置文件中配置 <property name="hibernate.current_session_context_class">thread</property>
注意二:通过getCurrentSession方法获得的session对象,当事务提交时,session会自动关闭,切记不要手动调用close关闭
hibernate中的批量查询(概述,先不做深入讲解)
HQL查询(Hibernate Query Language)
hql属于hibernate的独家查询语言,属于面向对象查询语言
初试:
@Test public void test() { // 1. 获取session对象 Session session = HBUtils.getSession(); // 2. 控制事务 Transaction t = session.beginTransaction(); // 3. 执行操作 //------------------------------------- // 3-1. 书写完整的hql语句 (from 对象完整的类名) String hql = "from Customer"; // 如果项目中没有同名的类可以不用完整的类名 // 3-2. 根据HQL语句创建查询对象 Query query = session.createQuery(hql); // 3-3. 根据查询对象查询结果 List<Customer> lists = query.list(); // Customer c = query.uniqueResult(); // 如果查询结果只有一个,可以使用该方法接收 // 3-4. 输出结果 System.out.println(lists); //------------------------------------- // 4. 提交事务 t.commit(); session.close(); }
条件查询:
@Test public void test() { // 1. 获取session对象 Session session = HBUtils.getSession(); // 2. 控制事务 Transaction t = session.beginTransaction(); // 3. 执行操作 //------------------------------------- // 3-1. 书写完整的hql语句 String hql = "from Customer where cust_id = 1"; // 是通过对象的属性名进行筛选,跟数据库的表名和列名没有半毛钱关系!!! // 3-2. 根据HQL语句创建查询对象 Query query = session.createQuery(hql); // 3-3. 根据查询对象查询结果 Customer c = query.uniqueResult(); // 如果查询结果只有一个,可以使用该方法接收 // 3-4. 输出结果 System.out.println(c); //------------------------------------- // 4. 提交事务 t.commit(); session.close(); }
占位符:
// 1. 书写hql语句 from 对象完整的类名 String hql = "from Customer where cust_id = ?"; // 设置占位符 // 2. 根据HQL语句创建查询对象 Query query = session.createQuery(hql); // 2-1. 设置参数 query.setLong(0, 1L); // 在hibernate中开始的索引是0,跟JDBC略有不同 query.setParameter(0, 1L); // 可以设置任何参数 // 3. 根据查询对象查询结果 List<Customer> querys = query.list(); // 接收多条数据 // Customer c = query.uniqueResult(); // 接收唯一的查询结果 System.out.println(querys); // 输出结果
命名占位符:
String hql = "from Customer where cust_id= :cust_id"; // cust_id就是占位符的名字Query query = session.createQuery(hql); query.setParameter("cust_id", 1L); // 通过占位符的名字进行添加
分页查询:
String hql = "from Customer"; Query query = session.createQuery(hql); query.setFirstResult(0); // 起始的地址 query.setMaxResult(1); // 要提取的数据个数
criteria查询
无语句面向对象查询
初试:
// 1. 获取session Session session = HBUtils.openSession(); // 2. 控制事务 Transaction tx = session.beginTransaction(); // 3. 执行操作 //------------------------------- // 创建criteria查询对象 Criteria criteria = session.createCriteria(Customer.class); // 查询所有的对象 List<Customer> list = criteria.list(); // Customer c = (Customer) criteri.uniqueResult(); // 查询结果只有一句 //------------------------------- // 4. 提交事务 tx.commit(); // 5. 关系资源 session.close();
条件查询:
// 创建criteria查询对象 Criteria criteria = session.createCriteria(Customer.class); // 查询所有的对象 // 添加查询条件 criteria.add(Restrictions.eq("cust_id", 1L)); // 查询 Customer c = (Customer) criteria.uniqueResult();
criteria查询方法API
方法 | 说明 |
Restrictions.eq(arg0, arg1) | 即=,即查询出数据库表中 arg0 = arg1 的行 |
gt(arg0, arg1) | 即>,即查询出数据库表中 arg0 > arg1 的行 |
ge(arg0, arg1) | 即>=,即查询出数据库表中 arg0 >= arg1 的行 |
lt(arg0, arg1) | 即<,即查询出数据库表中 arg0 < arg1 的行 |
le(arg0, arg1) | 即<=,即查询出数据库表中 arg0 <= arg1 的行 |
ne(arg0, arg1) | 即!=,即查询出数据库表中 arg0 != arg1 的行 |
in(arg0, Object[]) | 即模糊匹配in,查询在表中匹配Object[]数组中的行,譬如:Restrictions.in("cust_id", new Object[] {19L, 18L}) 表示id中有19L跟18L的行 |
between(arg0, arg1, aarg2) | 即between and, 查询数据库表中 arg1 <= arg0 <= arg2 的行 |
like(arg0, arg1) | 即模糊匹配like, 查询出数据库表某列(arg0)中符合arg1形式的行,譬如:Restrictions.like("cust_name", "_i%")表示查询出数据库表name列中 i 字母在第二个的行 |
isNull(arg0) | 查询出数据库表中某列(arg0)为空的行 |
isNotNull(arg0) | 查询出数据库表中某列(arg0)不为空的行 |
and(Criterion...parameter) | 即逻辑与,符合条件0跟条件1的行,譬如:Restrictions.and(Restrictions.eq("cust_id", 19L), Restrictions.like("cust_name", "Sam"))表示查询出数据库中id等于19而且name等于Sam的行 |
or(Criterioon...prameter) | 逻辑或,符合条件0或条件1的行,用法与and一致 |
not(Criterion...parameter) | 逻辑非,不符合条件0跟条件1的行,用法与and和or一致 |
分页查询:
// 创建criteria查询对象 Criteria criteria = session.createCriteria(Customer.class); // 查询所有的对象 // 设置分页 criteria.setFirstResult(0); // 从0开始查询 criteria.setMaxResults(1); // 一次查询多少条数据 // 执行查询 List<Customer> list = criteria.list();
查询总页数:
// 创建criteria查询对象 Criteria criteria = session.createCriteria(Customer.class); // 查询所有的对象 // 设置查询的聚合函数 =》 总行数 criteria.setProjection(Projections.rowCoount()); // rowCount() 聚合函数计算所有的条数,在除以每一页的行数就可以计算出总页数 // 执行查询 Long count = criteria.uniqueResult();
原生sql查询
初试:
// 1. 获取session Session session = HBUtils.openSession(); // 2. 控制事务 Transaction tx = session.beginTransaction(); // 3. 执行操作 //------------------------------- // 1. 书写sql语句 String sql = "select * from cst_customer"; // 这里是表名,不是对象名了 // 2. 创建sql查询对象 SQLQuery query = session.createSQLQuery(sql); // 3. 查询结果 List<Object[]> list = query.list(); // 每个Object[]数组封装一行数据 // 4. 打印结果 for(Object[] objs : list){ System.out.println(Arrays.toString(objs)); } //-------------------------------
将结果集封装到对象中:
上面的结果虽然能从数据库的表哦中得到数据,但是的得到的是Object数组操作起来显然不太方便,那么怎样把每一个Object数组里面的数据封装到对应的实体类中呢?hibernate很贴心提供了对应的方法
// 1. 获取session Session session = HBUtils.openSession(); // 2. 控制事务 Transaction tx = session.beginTransaction(); // 3. 执行操作 //------------------------------- // 1. 书写sql语句 String sql = "select * from cst_customer"; // 这里是表名,不是对象名了 // 2. 创建sql查询对象 SQLQuery query = session.createSQLQuery(sql); // 3. 指定结果集封装到哪个对象中 query.addEntity(Customer.class); // 4. 查询结果 List<Customer> list = query.list();
条件查询:
// 1. 获取session Session session = HBUtils.openSession(); // 2. 控制事务 Transaction tx = session.beginTransaction(); // 3. 执行操作 //------------------------------- // 1. 书写sql语句 String sql = "select * from cst_customer where cust_id = ?"; // 这里是表名,不是对象名了 // 2. 创建sql查询对象 SQLQuery query = session.createSQLQuery(sql); // 3. 设置占位符 query.setParameter(0, 1L); // 这里起始还是0 // 4. 指定结果集封装到哪个对象中 query.addEntity(Customer.class); // 5. 查询结果 List<Customer> list = query.list(); // ------------------------------- tx.commit(); session.close();
分页查询:
// 1. 获取session Session session = HBUtils.openSession(); // 2. 控制事务 Transaction tx = session.beginTransaction(); // 3. 执行操作 //------------------------------- // 1. 书写sql语句 String sql = "select * from cst_customer limit ?, ?"; // 这里是表名,不是对象名了 // 2. 创建sql查询对象 SQLQuery query = session.createSQLQuery(sql); // 3. 设置占位符 query.setParameter(0, 0L); // 从0开始查询 query.setParameter(1, 10L); // 查询10条数据 // 4. 指定结果集封装到哪个对象中 query.addEntity(Customer.class); // 5. 查询结果 List<Customer> list = query.list(); // -------------------------------- tx.commit(); session.close();