二、加深理解Hibernate框架
Hibernate_缓存&事务&批量查询概述
一、 Hibernate的持久化类
持久化类是指一个Java类和数据库建立了映射关系,那么这个类就是持久化类。
1. 持久化类的编写规则:
- 持久化类需要有无参的构造方法。
- 因为Hibernate的底层需要使用反射生成类的实例。
- 持久化类的属性需要私有,私有的属性需要提供set和get方法。
- 因为Hibernate底层要将查询到的数据进行封装。
- 持久化类尽量使用包装类的类型。
- 因为包装类与普通类的默认值不同。例:Integer为null,int为0.在数据库表中代表的含义包装类更准确。
- 持久化类需要有一个唯一标识OID与表的主键对应。
- Hibernate不允许在内存中出现两个相同OID的持久化对象。
- 持久化类不能使用final修饰。
- 因为Hibernate中有延迟加载机制,这个机制会产生代理对象。而这个代理对象是使用字节码的增强技术完成的,其实就是产生了一个当前类的子类对象实现的。
2. Hibernate的主键生成策略
- 自然主键
- 把具有业务含义的字段作为主键。
- 代理主键
- 把不具备业务含义的字段作为主键,与对象本身无关,该字段一般取名"ID".
我们在映射文件中设置选择native,根据数据库底层的支持来自动生成策略。
3. 持久化对象的三种状态
- 瞬时态
- 没有OID,与Hibernate的Session没有任何关联
- 持久态
- 有OID,与Session有关联
- 托管态
- 有OID,与Session没有关联
瞬时态:
对象创建时,就是瞬时态。
如: User user = new User();
user.serUname("悟空");
user.setUpassword("123");
这里面的user都是瞬时态,没有OID,与Session没有关联。
持久态:
session.save(user);
这里的user与session产生关联,加入到了Session的缓存中,生成了持久化标识OID,是持久态
此外,get/load/find...等方法获取的对象也是持久态对象
托管态:
session.close();
System.out.println(user);
这里的user有了OID,但是与Session没有了关联,是托管态
持久化对象的三种对象之间的互相转换:
img09.png上图:
1. 一个对象被new出来之后处于瞬时态;当对瞬时态对象执行Session对象的save()/saveOrUpdate()方法后,
该对象会被放入Session的一级缓存,进入持久态;当对持久态对象执行evict()/close()/clear()操作后,
对象进入托管态。
2. Session对象直接执行get()/load()/find()/iterate()等方法从数据库查询对象时,查询到的对象也属于
持久态;
3. 当对托管态对象执行Session的upadte()/saveOrUpadate()方法时,会转为持久态;瞬时态与托管态的对象
不在Session对象的管理范围内,因此一段时间后会被JVM回收。
持久化对象的特点:能够自动更新数据库
持久化对象能够自动更新数据库依赖于Hibernate的一级缓存。
@Test
//Hibernate持久化对象自动更新数据库
public void test() {
//1. 获取Hibernate配置对象加载配置
//2. 获取SessionFactory对象
//3. 获取Session对象
Session session = HibernateUtils.openSession();
//4. 开启事务
Transaction transaction = session.beginTransaction();
//5. 执行业务逻辑
User user = session.get(User.class, 2);//获得持久化对象
user.setUname("外星人");
//无需手动调用session.save(user);就可以执行数据库更新了
//6. 提交事务
transaction.commit();
//7. 关闭资源
session.close();
System.out.println("user:"+user);
}
二、 Hibernate的一级缓存
缓存的概念:介于应用程序与永久性存储的数据源之间,作用是降低应用程序直接读写数据源的频率,从而提高应用的运行性能。缓存中的数据是数据源数据的拷贝。
1. 什么是Hibernate的一级缓存
- Hibernate的一级缓存就是指Session缓存,Session缓存就是一块内存空间。
- 在使用Hibernate查询对象时,首先会拿该对象的OID去Session缓存中查找是否有匹配该OID值的对象。如果有就从Session缓存中取出使用,不再查询数据库。如果没有找到,就会去数据库中查询。
- 当从数据库查询到数据时,也会在Session缓存区放置一份该对象的数据信息,下次再查询就可以在Session缓存中直接查询到。
@Test
//测试一级缓存的存在
public void test() {
//首先获取到一个对象,再重复获取该对象,查看会打印几次sql语句
//1. 获取Hibernate配置对象加载配置
//2. 获取SessionFactory工厂对象
//3. 获取Session对象
Session session = HibernateUtils.openSession();
//4. 开启事务
Transaction transaction = session.beginTransaction();
//5. 实现业务逻辑
User user = session.get(User.class, 1);
System.out.println(user);
User user2 = session.get(User.class, 1);
System.out.println(user2);
User user3 = session.get(User.class, 1);
System.out.println(user3);
//6. 提交事务
transaction.commit();
//7. 释放资源
session.close();
}
结果只执行了一次查询语句,说明Hibernate只去数据库查询了一次获取到了user对象,然后将该对象的数据信息存入了Session缓存中,在这个Session没有关闭前,都可以从缓存中直接取出与该OID匹配的user对象。
img10.pngHibernate的一级缓存(Session缓存)的特点:
-
当应用程序调用Session接口的save()、update()、saveOrUpdate()时,如果缓存区中没有对应的对象,Hibernate会自动的将从数据库查询到的相应的对象信息加入到Session缓存中去。
-
当调用Session接口的load()、get()、以及Query接口的list()方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库。如果缓存中没有要查询的对象,就去数据库中查询并放入Session缓存中。
-
当调用Session对象的close(),Session缓存会被清空。
2. Session缓存的快照区
Hibernate向一级缓存放入数据时,同时会将一份数据放到Hibernate快照中,当事务提交时,
同时会清理Session的一级缓存,这时Hibernate会根据OID比较一级缓存与快照中的对象是否一致,
如果不一致执行update语句。如果一致就不执行。
Hibernate的快照确保了一级缓存中的数据与数据库中数据的一致。
这也是Hibernate中持久化对象自动更新数据库的原理。
三、 Hibernate的事务控制
事务:逻辑上的一组操作,组成这组操作的各个单元,要不全都成功,要不全都失败。
1. 事务的四个特性:(ACID特性)
-
原子性(Automic):表示将事务所做的操作捆绑成一个不可分割的单元。对事务所进行的数据修改等操作,要不全都成功,要不全都失败。
-
一致性(Consistency):表示事务完成后,必须使所有的数据都保持一致状态。
-
隔离性(Isolation):一个事务的执行不能被其他事务干扰,并发执行的事务之间不能互相干扰。
-
持久性(Durability):事务一旦提交,它对数据库中数据的修改是永久性的。提交后的其他操作或者故障都不会有任何故障。
2. 事务的并发问题
-
脏读
一个事务提交到了另一个事务未提交的数据。这是很严重的并发问题,必须解决。 例如: 用户购物,付款尚未成功提交之前,卖家就能读到用户付款的数据,误以为已经付款。
-
不可重复读
一个事务读到了另一个事务已经提交后的update的数据,导致在同一个事务中的多次查询结果不一致。 例如: 小王在刷卡的同时,家人用他的银行卡进行了网上购物,导致小王刷卡前后两次查询的结果不一致。
-
虚读
一个事务都到了另一个事务已经提交的insert的数据,导致在同一个事务中的多次查询结果不一致。
3. 事务的4个隔离级别
为了避免事务并发问题的发生,标准sql规范中定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。
- 读未提交。(Read Uncommitted,1级),一个事务可以访问其他事务未提交的数据。
- 读已提交。(Read Commited,2级),一个事务在执行的过程中,可以访问其他事务成功提交的新插入的数据,也可以访问成功修改的数据。但禁止访问未提交的数据。该隔离级别可以有效的防止脏读。
- 可重复读。(Repeatable Read,4级), 一个事务在执行的过程中,可以访问其他事务成功提交的新插入的数据,但不可以访问成功修改的数据。此隔离级别可以有效的防止脏读与不可重复读。
- 序列化。(Serializable,8级),提供严格的事务隔离,事务只能序列化,一个一个的执行,不能并发执行。此隔离级别可以防止脏读,不可重复读以及虚读。
隔离级别越高,安全性越高,性能越低。
mysql的隔离级别默认是可重复读(Repeatable Read)
oracle的隔离级别默认是可重复读(Read Committed)
4. Hibernate的事务管理
<!--
设置隔离级别:
hibernate.connection.isolation = 4
1-Read uncommitted isolation
2-Read committed isolation
4-Repeatable read isolation
8-Serializable isolation
-->
<property name="hibernate.connection.isolation">4</property>
在使用Hibernate进行事务管理时,service层与dao层要保证Session对象的一致,可以采用三种方式确保Session
对象的一致: Session对象传递,使用ThreadLocal存储与当前线程绑定的Session对象,直接使用Hibernate封装好的getCurrentSession()方法。
在这里,前两种方式就省略了,如果有想了解的,可以翻看我以前写的Connection部分JDBCUtils工具类的封装。
直接使用Hibernate封装好的管理Session的方式
在Hibernate的配置文件中,hibernate.current_session_context_class
属性用于指定Session的管理方式:
- thread : Session对象的生命周期与当前线程绑定
- jta : Session对象的生命周期与JTA事务绑定
- managed : Hibernate委托程序来管理Session的生命周期
在hibernate.cfg.xml
进行如下配置:
<property name="hibernate.current_session_context_class">thread</property>
配置后,Hibernate提供了getCurrentThread()方法来创建一个与当前线程绑定的Session对象。
在HibernateUtils工具类中添加getCurrentSession方法:
// 获得当前线程中的绑定的Session
// 注意:必须配置
// return
public static Session getCurrentSession() {
return sf.getCurrentSession();
}
需要注意的是:必须先在配置中进行配置,而且使用后无需手动关闭Session对象,事务提交后会自动关闭Session对象
Sercice层:
//用户转账的业务
public void transfer(String sender, String receiver, String amount) throws Exception {
Transaction transaction = null;
try {
//调用dao层
TransferDAO dao = new TransferDAOImpl();
//由于这里操作了两条sql语句因此应该开启事务
Session session = HibernateUtils.getCurrentSession();
transaction = session.beginTransaction();
//转出
dao.out(sender,amount);
//转入
dao.in(receiver,amount);
//没有异常就提交
transaction.commit();
} catch (Exception e) {
//回滚事务
transaction.rollback();
e.printStackTrace();
throw e;
}
}
dao 层:
// 转出
public void out(String sender, String amount) throws SQLException {
Session session = HibernateUtils.getCurrentSession();
// 转出
User user = session.get(User.class, 1);
double amount2 = Double.parseDouble(amount);
user.setAcount(user.getAcount() - amount2);
}
@Override
// 转入
public void in(String receiver, String amount) throws SQLException {
Session session = HibernateUtils.getCurrentSession();
// 转入
User user = session.get(User.class, 2);
double amount2 = Double.parseDouble(amount);
user.setAcount(user.getAcount() + amount2);
}
四、 Hibernate的批量查询(概述)
1. HQL查询 : Hibernate Query Language
HQL : Hibernate Query Language。Hibernate
的独家查询语言,属于面向对象的查询语言。
适用于:多表查询,但不是很复杂时使用。
- setXxx : 如果HQL语句中有参数,根据参数的不同类型设置参数的值。
- setParameter : 上面方法的万金油版本。可以根据参数自动选择参数的类型设置。
- uniqueResult : 结果只有一条记录的时候使用,用于获取唯一的结果。
- list : 获取多条记录存储在List集合中。
- setFirstResult : 分页查询设置第一条记录的位置。表示从第几条数据查询,默认从0开始。
- setMaxResult : 分页查询设置结果集的最大记录数。即每页显示的最大数据数量。
使用HQL的步骤:
- 获取Session对象,开启事务
- 编写HQL语句
- 使用Session对象获取查询对象Query
- 如果HQL语句包含参数,使用query.setXxx()设置参数。
- 这里有一个万金油的设置参数的方法. query.setParameter();
- 执行HQL查询语句
HQL查询测试:
/**
* Hibernate的HQL语句练习
*/
public class HibernateTest {
@Test
// HQL基本查询
public void test() {
// 1. 获取Session对象
Session session = HibernateUtils.getCurrentSession();
// 2.开启事务
Transaction transaction = session.beginTransaction();
// -----------------------------------------
// 3. 业务逻辑:HQL的基本查询
// 3.1 创建HQL查询语句。HQL语句格式为:select * from 全类名.
// HQL语句格式为:select * from 全类名.
// 在查询所有字段时可以省略select *
// String hql = "select * from com.itdream.domain.Customer";
String hql = "from Customer";
// 3.2 创建查询对象Query
Query query = session.createQuery(hql);
// 3.3 执行查询
List<Customer> list = query.list();
// 如果确定结果为一条结果,可以使用uniqeResult()
// Customer customer = (Customer) query.uniqueResult();
System.out.println(list);
// -----------------------------------------
// 4.提交事务
transaction.commit();
// getCurrentSession无需手动关闭
}
@Test
// HQL条件查询:问号占位符
public void test2() {
// 1. 获取Session对象
Session session = HibernateUtils.getCurrentSession();
// 2.开启事务
Transaction transaction = session.beginTransaction();
// -----------------------------------------
// 3. 业务逻辑:HQL条件查询:问号占位符
// 3.1 编写HQL查询语句,注意:HQL语句中出现的是类中的属性而不是表中的字段
String hql = "from Customer where cid = ?";
//3.2 获取HQL的查询对象Query
Query query = session.createQuery(hql);
//设置问号占位符处的参数.Hibernate的问号占位付默认从0开始
//query.setInteger(0, 2);
//万金油的设置参数方法
query.setParameter(0, 2);
//3.3 执行查询
Customer customer = (Customer) query.uniqueResult();
System.out.println(customer);
// -----------------------------------------
// 4.提交事务
transaction.commit();
// getCurrentSession无需手动关闭
}
@Test
// HQL条件查询:命名占位符
public void test3() {
// 1. 获取Session对象
Session session = HibernateUtils.getCurrentSession();
// 2.开启事务
Transaction transaction = session.beginTransaction();
// -----------------------------------------
// 3. 业务逻辑:HQL条件查询:命名占位符(冒号:+命名)
// 3.1 编写HQL查询语句,注意:HQL语句中出现的是类中的属性而不是表中的字段
String hql = "from Customer where cid = :cid";
//3.2 获取HQL的查询对象Query
Query query = session.createQuery(hql);
//设置问号占位符处的参数.Hibernate的问号占位付默认从0开始
//万金油的设置参数方法
query.setParameter("cid", 2);
//3.3 执行查询
Customer customer = (Customer) query.uniqueResult();
System.out.println(customer);
// -----------------------------------------
// 4.提交事务
transaction.commit();
// getCurrentSession无需手动关闭
}
@Test
// HQL分页查询
public void test4() {
// 1. 获取Session对象
Session session = HibernateUtils.getCurrentSession();
// 2.开启事务
Transaction transaction = session.beginTransaction();
// -----------------------------------------
// 3. 业务逻辑:HQL条件查询:命名占位符(冒号:+命名)
// 3.1 编写HQL查询语句,注意:HQL语句中出现的是类中的属性而不是表中的字段
String hql = "from Customer";
//3.2 获取查询对象Query
Query query = session.createQuery(hql);
//3.3 设置分页的参数
query.setFirstResult(4);//从第(4+1)条数据开始,Hibernate默认从0开始
query.setMaxResults(2);//每页显示两条数据
//3.4执行hql语句
List<Customer> list = query.list();
System.out.println(list);
// -----------------------------------------
// 4.提交事务
transaction.commit();
// getCurrentSession无需手动关闭
}
}
分页查询结果:
img11.png2. Critiria查询
Criteria查询是Hibernate的核心查询对象。它是完全面向对象的无语句查询。在Criteria查询方式中,是绝对不会出现sql语句的。Criteria查询又称为QBC查询(Query By Criteria)。
适用于单表的条件查询。
常用API
//获取Criteria查询对象
Criteria criteria = session.createCriteria(Customer.class); //传入的参数是持久化类对象
//设置查询条件
// 参数1:持久化类中的属性 。 参数2:对应的值
Criterion eq = Restrictions.eq("cid", 1);
//设置分页参数
criteria.setFirstResult(4);//这里的4代表的是索引,第五条记录开始
criteria.setMaxResults(2);//每页显示2条数据
//查询总记录数
criteria.setProjection(Projections.rowCount());
//执行查询
List<Customer> list = criteria.list();
Customer customer = (Customer) criteria.uniqueResult();
Criteria查询的步骤:
- 获取Session对象,开启事务
- 获取查询对象
Criteria
- 如果有限制条件使用
Restrictions
添加限制条件 - 如果是分页查询使用
criteria.setFirstResult或setMaxResult
方法设置分页参数 - 将
Restrictions
设置的限制条件添加到查询对象Criteria
中 - 执行查询
Criteria查询测试:
@Test
// Criteria基本查询 :查询所有数据
public void test() {
// 1. 获取Session对象
Session session = HibernateUtils.getCurrentSession();
// 2.开启事务
Transaction transaction = session.beginTransaction();
// -----------------------------------------
// 3. 业务逻辑:Criteria的基本查询
// 3.1 获取Criteria查询对象
Criteria criteria = session.createCriteria(Customer.class);
// 3.2 执行查询
List<Customer> list = criteria.list();
// Customer customer = (Customer) criteria.uniqueResult();
System.out.println(list);
// -----------------------------------------
// 4.提交事务
transaction.commit();
// getCurrentSession无需手动关闭
}
@Test
// Criteria条查询
public void test2() {
// 1. 获取Session对象
Session session = HibernateUtils.getCurrentSession();
// 2.开启事务
Transaction transaction = session.beginTransaction();
// -----------------------------------------
// 3. 业务逻辑:Criteria的条件查询
// 3.1 获取Criteria查询对象
Criteria criteria = session.createCriteria(Customer.class);
// 3.2 设置查询限制条件
// 参数1:持久化类中的属性 。 参数2:对应的值
Criterion eq = Restrictions.eq("cid", 1);
Criterion eq2 = Restrictions.eq("cname", "唐嫣");
// 添加限制条件到查询对象中(可以添加多个限制条件)
criteria.add(eq);
criteria.add(eq2);
// 3.3 执行查询操作
// List<Customer> list = criteria.list();
Customer customer = (Customer) criteria.uniqueResult();
System.out.println(customer);
// -----------------------------------------
// 4.提交事务
transaction.commit();
// getCurrentSession无需手动关闭
}
@Test
// Criteria分页查询
public void test3() {
// 1. 获取Session对象
Session session = HibernateUtils.getCurrentSession();
// 2.开启事务
Transaction transaction = session.beginTransaction();
// -----------------------------------------
// 3. 业务逻辑:Criteria的分页查询
// 3.1 获取Criteria查询对象
Criteria criteria = session.createCriteria(Customer.class);
//3.2 设置分页参数
criteria.setFirstResult(4);//这里的4代表的是索引,第五条记录开始
criteria.setMaxResults(2);//每页显示2条数据
//3.3 执行查询
List<Customer> list = criteria.list();
System.out.println(list);
// -----------------------------------------
// 4.提交事务
transaction.commit();
// getCurrentSession无需手动关闭
}
@Test
// Criteria查询总记录数
public void test4() {
// 1. 获取Session对象
Session session = HibernateUtils.getCurrentSession();
// 2.开启事务
Transaction transaction = session.beginTransaction();
// -----------------------------------------
// 3. 业务逻辑:Criteria的分页查询
// 3.1 获取Criteria查询对象
Criteria criteria = session.createCriteria(Customer.class);
// 3.2 获取总记录数
criteria.setProjection(Projections.rowCount());
//3.3 执行查询操作获取总记录数
Long rows = (Long) criteria.uniqueResult();
System.out.println(rows);
// -----------------------------------------
// 4.提交事务
transaction.commit();
// getCurrentSession无需手动关闭
}
附常用Restrictions的静态方法:
常见的Restrictions的静态方法
方法 说明
Restrictions.eq =
Restrictions.allEq 利用Map来进行多个等于的限制
Restrictions.gt >(greater than)
Restrictions.ge >=(greater than or equal)
Restrictions.lt < (less than)
Restrictions.le <=(less than or equal)
Restrictions.between BETWEEN
Restrictions.like LIKE
Restrictions.in in
Restrictions.and and
Restrictions.or or
Restrictions.sqlRestriction 用SQL限定查询
3. SQLQuery查询(原生的SQL语句查询)
适用于复杂的多表业务查询
常用API:
//创建SQLQuery查询对象
SQLQuery query = session.createSQLQuery(sql);
//指定将结果集封装到某个对象中
query.addEntity(Customer.class);
//执行sql语句
List<Customer> list = query.list();
Customer customer = (Long)query.uniqueResult();
SQLQuery查询测试:
@Test
// SQLQuery基本查询 :查询所有数据(返回Object数组的List)
public void test() {
// 1. 获取Session对象
Session session = HibernateUtils.getCurrentSession();
// 2.开启事务
Transaction transaction = session.beginTransaction();
// -----------------------------------------
// 3. 业务逻辑:SQLQuery的基本查询:查询所有数据
// 3.1 编写sql语句
String sql = "select * from customer";
// 3.2 获取SQLQuery查询对象
SQLQuery query = session.createSQLQuery(sql);
// 3.3 执行查询,返回的结果是一个List集合
// 每条记录的查询结果作为Object数组的元素。将所有的记录Object数组存入List集合
List<Object[]> list = query.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
// -----------------------------------------
// 4.提交事务
transaction.commit();
// getCurrentSession无需手动关闭
}
img12.png
@Test
// SQLQuery基本查询 :查询所有数据(返回Customer的List)
public void test2() {
// 1. 获取Session对象
Session session = HibernateUtils.getCurrentSession();
// 2.开启事务
Transaction transaction = session.beginTransaction();
// -----------------------------------------
// 3. 业务逻辑:SQLQuery的基本查询:查询所有数据
// 3.1 编写sql语句
String sql = "select * from customer";
// 3.2 创建SQLQuery查询对象
SQLQuery query = session.createSQLQuery(sql);
// 3.3 指定将结果集封装到某个对象中
query.addEntity(Customer.class);
// 3.4 执行sql语句
List<Customer> list = query.list();
System.out.println(list);
// -----------------------------------------
// 4.提交事务
transaction.commit();
// getCurrentSession无需手动关闭
}
img13.png
@Test
// SQLQuery条件查询
public void test3() {
// 1. 获取Session对象
Session session = HibernateUtils.getCurrentSession();
// 2.开启事务
Transaction transaction = session.beginTransaction();
// -----------------------------------------
// 3. 业务逻辑:SQLQuery的基本查询:查询所有数据
// 3.1 编写sql语句
String sql = "select * from customer where cid = ?";
// 3.2 创建SQLQuery查询对象
SQLQuery query = session.createSQLQuery(sql);
// 设置参数的值。参数1:第几个数据。参数2:对应的值
query.setParameter(0, 1);
// 3.3 指定将结果集封装到某个对象中
query.addEntity(Customer.class);
// 3.4 执行sql语句
Customer customer = (Customer) query.uniqueResult();
System.out.println(customer);
// -----------------------------------------
// 4.提交事务
transaction.commit();
// getCurrentSession无需手动关闭
}
@Test
// SQLQuery分页查询:方式1:传统sql语句
public void test4() {
// 1. 获取Session对象
Session session = HibernateUtils.getCurrentSession();
// 2.开启事务
Transaction transaction = session.beginTransaction();
// -----------------------------------------
// 3. 业务逻辑:SQLQuery的基本查询:查询所有数据
// 3.1 编写sql语句
String sql = "select * from customer limit ?,?";
// 3.2 创建SQLQuery查询对象
SQLQuery query = session.createSQLQuery(sql);
// 设置分页查询的参数
query.setParameter(0, 4);
query.setParameter(1, 2);
// 3.3 指定将结果集封装到某个对象中
query.addEntity(Customer.class);
// 3.4 执行sql语句
List<Customer> list = query.list();
System.out.println(list);
// -----------------------------------------
// 4.提交事务
transaction.commit();
// getCurrentSession无需手动关闭
}
@Test
// SQLQuery分页查询:方式2:setFirstResult()方法
public void test5() {
// 1. 获取Session对象
Session session = HibernateUtils.getCurrentSession();
// 2.开启事务
Transaction transaction = session.beginTransaction();
// -----------------------------------------
// 3. 业务逻辑:SQLQuery的基本查询:查询所有数据
// 3.1 编写sql语句
String sql = "select * from customer";
// 3.2 创建SQLQuery查询对象
SQLQuery query = session.createSQLQuery(sql);
// 设置分页查询的参数
query.setFirstResult(4);// 设置从第几个数据开始查询,默认从0开始
query.setMaxResults(2);// 设置每页显示的数据条数
// 3.3 指定将结果集封装到某个对象中
query.addEntity(Customer.class);
// 3.4 执行sql语句
List<Customer> list = query.list();
System.out.println(list);
// -----------------------------------------
// 4.提交事务
transaction.commit();
// getCurrentSession无需手动关闭
}
4. 简单的CRM查询案例
步骤:
1. 搭建工程环境
1. 创建工程,导入jar包,拷贝页面等文件
2. 创建数据库表cst_customer
3. 创建持久化类Customer
4. 创建持久化类和数据库表的映射文件Customer.hbm.xml
5. 创建Hibernate的核心配置文件hibernate.cfg.xml
6. 完成核心配置文件中c3p0的配置,创建log4j.properties配置文件
7. 编写工具类HibernateUtils,环境搭建完毕
1.1.导入jar包
包含有:
- 数据库驱动
- Hibernate必需包下的所有jar包
- Hibernate的c3p0中的3个jar包
- 日志集成slf4j和log4j的3个jar包
- jstl.jar与standard.jar的jstl标签库jar包
1.2 创建数据库表
CREATE DATABASE hibernate_crm;
USE hibernate_crm;
CREATE TABLE `cst_customer` (
`cust_id` BIGINT(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
`cust_name` VARCHAR(32) NOT NULL COMMENT '客户名称(公司名称)',
`cust_user_id` BIGINT(32) DEFAULT NULL COMMENT '负责人id',
`cust_create_id` BIGINT(32) DEFAULT NULL COMMENT '创建人id',
`cust_source` VARCHAR(32) DEFAULT NULL COMMENT '客户信息来源',
`cust_industry` VARCHAR(32) DEFAULT NULL COMMENT '客户所属行业',
`cust_level` VARCHAR(32) DEFAULT NULL COMMENT '客户级别',
`cust_linkman` VARCHAR(64) DEFAULT NULL COMMENT '联系人',
`cust_phone` VARCHAR(64) DEFAULT NULL COMMENT '固定电话',
`cust_mobile` VARCHAR(16) DEFAULT NULL COMMENT '移动电话',
PRIMARY KEY (`cust_id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO cst_customer(cust_id,cust_name)
VALUES(NULL,'唐嫣'),(NULL,'钟汉良'),(NULL,'林心如'),
(NULL,'霍建华'),(NULL,'杨幂'),(NULL,'刘恺威'),
(NULL,'高圆圆'),(NULL,'赵又廷');
1.3 创建持久化类
与数据库表基本保持一致,按照持久化类的编写规则编写。
img15.png1.4 创建持久化类和数据库表的映射文件Customer.hbm.xml
步骤:
- 在持久化类的同级目录下,创建xml文件,取名类名.hbm.xml。这里名字为:
Customer.hbm.xml
- 导入DTD约束。从jar包中可以找到DTD的约束描述,copy过来
- 创建持久化类与数据库表的映射
- 创建持久化类中标识符OID与数据库表的主键的映射
- 创建类中普通属性与数据库表的字段的映射
<?xml version="1.0" encoding="UTF-8"?>
<!-- 建立持久化类Customer与数据库表customer的映射关系的配置文件 -->
<!-- 引入DTD约束 -->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- 建立持久化类与表的映射关系 -->
<class name="com.itdream.domain.Customer" table="cst_customer">
<!-- 配置持久化类OID与表的主键的映射 -->
<id name="cust_id" column="cust_id">
<!-- 配置主键生成策略 -->
<generator class="native"></generator>
</id>
<!-- 配置普通字段的映射关系 -->
<property name="cust_name" column="cust_name"></property>
</class>
</hibernate-mapping>
1.5 创建Hibernate的核心配置文件hibernate.cfg.xml
步骤:
- 在src目录下创建文件名为
hibernate.cfg.xml
的配置文件 - 导入DTD约束
- 配置Hibernate连接数据库的基本配置
- 配置Session与当前线程绑定
- 配置Hibernate基本属性,包括:
- Hibernate方言,根据这个配置Hiberante使用对应的sql语句,实现了跨数据库
- 显示SQL语句
- 格式化SQL语句
- 配置Hibernate对应数据库表的生成方式
hbm2ddl
- 配置数据库的隔离级别
- c3p0连接池的配置
- 加载映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- 导入Hibernate核心配置文件的DTD约束 -->
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- 配置Hibernate的核心配置文件 -->
<hibernate-configuration>
<session-factory>
<!--1. Hibernate连接数据库的基本配置 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_crm</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<!--2. Session与本地线程绑定 -->
<property name="hibernate.current_session_context_class">thread</property>
<!--3. Hibernate的基本属性配置 -->
<!-- Hibernate的方言配置,Hibernate根据这个配置生成对应的SQL语句,从而实现跨数据库 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 显示SQL语句 -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化SQL语句 -->
<property name="hibernate.format_sql">true</property>
<!-- Hibernate对数据库表的生成方式,hbm2ddl -->
<property name="hibernate.hbm2ddl.auto">none</property>
<!-- 配置隔离级别 :4 读可重复(Repeatable read) -->
<property name="hibernate.connection.isolation">4</property>
<!-- C3P0连接池的配置 -->
<property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
<!-- 最小连接 -->
<property name="hibernate.c3p0.min_size">5</property>
<!-- 最大连接数 -->
<property name="hibernate.c3p0.max_size">20</property>
<!-- 连接超时时长 -->
<property name="hibernate.c3p0.timeout">120</property>
<!-- 每120秒检查空闲连接 -->
<property name="hibernate.c3p0.idle_test_period">120</property>
<!-- 最大statments数量 -->
<property name="hibernate.c3p0.max_statements">120</property>
<!-- 连接用完后,每次增加的连接数 -->
<property name="hibernate.c3p0.acquire_increment">2</property>
<!-- 每次都验证连接是否可用 -->
<property name="hibernate.c3p0.validate">false</property>
<!--4. 加载映射文件 -->
<mapping resource="com/itdream/domain/Customer.hbm.xml" />
</session-factory>
</hibernate-configuration>
1.6 完成核心配置文件中c3p0的配置,创建log4j.properties配置文件
1. c3p0的配置见上一条1.5中核心配置文件.
2. log4j日志集成的配置文件log4j.properties
在src目录下创建log4j.properties:
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=d:\\mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=info, stdout,file
1.7 编写工具类HibernateUtils
/**
* 初始化SessionFactory,提供Session对象的Hibernate工具类
*/
public class HibernateUtils {
private static SessionFactory sf = null;
static {
// 创建Hibernate配置对象,加载src目录下默认的hibernate.cfg.xml配置文件
Configuration configuration = new Configuration().configure();
// 创建SessionFactory工厂对象
sf = configuration.buildSessionFactory();
// 程序关闭销毁SessionFactory对象
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
System.out.println("程序关闭销毁SessionFactory对象");
sf.close();
}
}));
}
/**
* 获取Session对象的方法
*/
public static Session openSession() {
return sf.openSession();
}
/**
* 获取与当前线程绑定的Session对象
*/
public static Session getCurrentSession() {
return sf.getCurrentSession();
}
}
2. 代码实现,使用Hibernate的查询语句查询所有Customer
2.1 mennu.jsp修改a标签跳转
<TD class=menuSmall><A class=style2 href="customer?method=list"target=main>- 客户列表</A></TD>
2.2 创建Servlet,映射url-pattern为servlet
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取传递过来的method参数
String method = request.getParameter("method");
if ("list".equals(method)) { // 如果是list,就查询所有Customer客户
try {
// 调用service层
CustomerService service = new CustomerServiceImpl();
List<Customer> list = service.findAllCustomers();
// 存入request域
request.setAttribute("list", list);
System.out.println(list);
// 转发
request.getRequestDispatcher("/jsp/customer/list.jsp").forward(request, response);;
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2.3 CustomerService层,调用dao层查询所有Customer
@Override
public List<Customer> findAllCustomers() throws SQLException {
//调用dao层查询所有Customer用户
CustomerDAO dao = new CustomerDAOImpl();
return dao.findAllCustomers();
}
2.4 CustomerDAO层,查询所有Customer返回
使用Criteria与HQL两种查询方式,单表查询推荐使用Criteria查询
@Override
// 方式1:显示所有Customer的方法:Criteria查询(单表查询简单)
public List<Customer> findAllCustomers() throws SQLException {
// 1. 获取配置对象加载配置文件
// 2. 获取SessionFactory对象
// 3. 获取Session对象
Session session = HibernateUtils.getCurrentSession();
// 4. 开启事务
Transaction transaction = session.beginTransaction();
// 5. 业务逻辑 :查询所有Customer对象,单表查询使用Criteria较为简单
// 5.1 创建Criteria查询对象
Criteria criteria = session.createCriteria(Customer.class);
// 5.2 如果有条件就添加限制条件Restrictions,然后添加到查询对象中
// 5.3 执行查询
List<Customer> list = criteria.list();
// 6. 提交事务
transaction.commit();
// 7. getCurrentSession()无需手动关闭Session对象
return list;
}
/*@Override
// 方式2:显示所有Customer的方法:HQL查询(常用于不是很复杂的多表查询)
public List<Customer> findAllCustomers() throws SQLException {
// 1. 获取配置对象加载配置文件
// 2. 获取SessionFactory对象
// 3. 获取Session对象
Session session = HibernateUtils.getCurrentSession();
// 4. 开启事务
Transaction transaction = session.beginTransaction();
// 5. 业务逻辑 : 查询所有Customer对象,单表查询使用Criteria较为简单
// 5.1 编写HQL语句,HQL语句中出现的都是持久化类中的属性,而不是数据库表中的字段
String hql = "from Customer";
// 5.2 创建HQL的Query对象
Query query = session.createQuery(hql);
// 5.3 如果hql语句中有参数使用setParameter()设置参数
// 5.4 执行hql语句
List<Customer> list = query.list();
// 6. 提交事务
transaction.commit();
// 7. getCurrentSession()无需手动关闭Session对象
return list;
}*/
2.5 查询结果,list页面解析(这里省略)
查询结果图:
img16.png