Java
Java Web框架

Hibernate(7)

简介:Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。

1. HQL详解

1.1. 普通查询

  1. 查询所有
  • @Test
  • public void demo01(){
  • //1 查询所有
  • Session session = factory.openSession();
  • session.beginTransaction();
  • //1 使用简单类名 , 存在自动导包
  • // * Customer.hbm.xml <hibernate-mapping auto-import="true">
  • // Query query = session.createQuery("from Customer");
  • //2 使用全限定类名
  • Query query = session.createQuery("from com.itheima.a_init.Customer");
  • List<Customer> allCustomer = query.list();
  • for (Customer customer : allCustomer) {
  • System.out.println(customer);
  • }
  • session.getTransaction().commit();
  • session.close();
  • }

打印信息:

  • [Customer [id=1, name=tom, orders=[com.coderap.domain.Order@162522b, com.coderap.domain.Order@162522e]]]
  1. 选择查询
  • @Test
  • public void demo02() {
  • // 2 简单条件查询
  • Session session = factory.openSession();
  • session.beginTransaction();
  • // 1 指定数据,cid OID名称
  • // Query query = session.createQuery("from Customer where cid = 1");
  • // 2 如果使用id,也可以(了解)
  • // Query query = session.createQuery("from Customer where id = 1");
  • // 3 对象别名 ,格式: 类 [as] 别名
  • // Query query = session.createQuery("from Customer as c where c.cid = 1");
  • // 4 查询所有项,mysql--> select * from...
  • Query query = session.createQuery("select c.id, c.name from Customer as c where c.cid = 1");
  • List<Object[]> list = query.list();
  • for(Object[] objs : list) {
  • System.out.println(Arrays.toString(objs));
  • }
  • session.getTransaction().commit();
  • session.close();
  • }

打印信息:

  • [1, tom]
  • [2, jerry]
  1. 投影查询
  • @Test
  • public void demo04() {
  • // 4 投影
  • Session session = factory.openSession();
  • session.beginTransaction();
  • // 1 默认
  • // 如果单列 ,select c.cname from,需要List<Object>
  • // 如果多列,select c.cid,c.cname from ,需要List<Object[]> ,list存放每行,Object[]多列
  • // Query query =
  • // session.createQuery("select c.cid,c.cname from Customer c");
  • // 2 将查询部分数据,设置Customer对象中
  • // * 格式:new Customer(c.cid,c.cname)
  • // * 注意:Customer必须提供相应的构造方法。
  • // * 如果投影使用oid,结果脱管态对象。
  • Query query = session.createQuery("select new Customer(c.cid,c.cname) from Customer c");
  • List<Customer> list = query.list();
  • System.out.println(list);
  • session.getTransaction().commit();
  • session.close();
  • }

此时Customer需要有对应参数类型的构造方法。

打印信息:

  • [Customer [id=1, name=tom, orders=[]], Customer [id=2, name=jerry, orders=[]]]
  1. 排序
  • @Test
  • public void demo03() {
  • // 3 排序 ,mysql--> select... order by 字段 [asc]|desc ,....
  • Session session = factory.openSession();
  • session.beginTransaction();
  • Query query = session.createQuery("from Customer c order by c.id desc");
  • List<Customer> list = query.list();
  • System.out.println(list);
  • session.getTransaction().commit();
  • session.close();
  • }

打印信息:

  • [Customer [id=2, name=jerry, orders=[]], Customer [id=1, name=tom, orders=[]]]
  1. 分页
  • @Test
  • public void demo05() {
  • // 分页
  • Session session = factory.openSession();
  • session.beginTransaction();
  • Query query = session.createQuery("from Customer c order by c.id desc");
  • // * limit ?,? setFirstResult, setMaxResults
  • // (当前页数-1)*每页最大记录数
  • // * 从那个索引开始取数据,包含索引本身的记录
  • query.setFirstResult(1);
  • // * 查询多少条数据,pageSize
  • query.setMaxResults(1);
  • List<Customer> list = query.list();
  • System.out.println(list);
  • session.getTransaction().commit();
  • session.close();
  • }

打印信息:

  • [Customer [id=2, name=jerry, orders=[]]]
  1. 绑定参数
  • @Test
  • public void demo06(){
  • /* 6 绑定参数
  • * 方式1:占位符,使用? 在hql语句替换具体参数
  • * 设置参数 query.setXxx(int , object)
  • * 参数1:?位置,从0开始。
  • * 参数2:实际参数
  • * 例如:String --> query.setString(int,String)
  • * 方式2:别名 , 格式 “属性= :别名 ”
  • * 设置参数 query.setXxx(String,object)
  • * 参数1:别名
  • * 参数2:实际参数
  • * 例如:Integer --> query.setInteger(String,Integer)
  • * 提供公共设置方法
  • * setParameter(int|string , Object)
  • */
  • Session session = factory.openSession();
  • session.beginTransaction();
  • Integer cid = 1;
  • // 方式1 Query query = session.createQuery("from Customer where cid = ?");
  • // query.setInteger(0, 2); // 第一个参数为占位符的索引,从0开始,第二个参数为索引赋值
  • // 方式2
  • Query query = session.createQuery("from Customer where cid = :parameterName");
  • // query.setInteger("parameterName", 2);
  • query.setParameter("parameterName", 2);
  • Customer customer = (Customer) query.uniqueResult();
  • System.out.println(customer);
  • session.getTransaction().commit();
  • session.close();
  • }

打印信息:

  • [Customer [id=2, name=jerry, orders=[]]]
  1. 聚合函数和分组
  • @Test
  • public void demo07() {
  • /*
  • * 聚合函数
  • */
  • Session session = factory.openSession();
  • session.beginTransaction();
  • // 1 统计数量
  • // Query query = session.createQuery("select count(*) from Customer");
  • // 2 别名形式
  • // Query query = session.createQuery("select count(c) from Customer c");
  • // 3 oid形式
  • Query query = session.createQuery("select count(cid) from Customer");
  • // 4 平均值
  • // Query query = session.createQuery("select avg(c.id) from Customer c");
  • // 5 总和
  • // Query query = session.createQuery("select sum(c.id) from Customer c");
  • // 5 最大值
  • // Query query = session.createQuery("select max(c.id) from Customer c");
  • // 5 最小值
  • // Query query = session.createQuery("select min(c.id) from Customer c");
  • Long numLong = (Long) query.uniqueResult();
  • int num = numLong.intValue();
  • System.out.println(num);
  • session.getTransaction().commit();
  • session.close();
  • }
  • @Test
  • public void demo8() {
  • /*
  • * 分组 group by .. having..
  • */
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Query query = session.createQuery("select o.customer, count(o) " + " from Order o " + " group by o.customer " + " having count(o) > 2 ");
  • List<Object[]> list = query.list();
  • for (Object[] objs : list) {
  • System.out.println(Arrays.toString(objs));
  • }
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

1.2. 连接查询

  1. 交叉连接,生成笛卡尔积
  • @Test
  • // 开发时要避免出现笛卡尔积
  • public void fun1() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Query query = session.createQuery("from Customer c,Order o");
  • List<Object[]> list = query.list();
  • for (Object[] objs : list) {
  • System.out.println(Arrays.toString(objs));
  • }
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }
  1. 隐式内连接,在笛卡尔积基础上过滤无效数据
  • @Test
  • public void fun2() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Query query = session.createQuery("from Customer c,Order o where o.customer = c");
  • List<Object[]> list = query.list();
  • for (Object[] objs : list) {
  • System.out.println(Arrays.toString(objs));
  • }
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }
  1. 显示内连接
  • 非迫切,即使用inner join,将父与子对象装入数组中分别返回
  • @Test
  • public void fun3() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Query query = session.createQuery("from Customer c inner join c.orders ");
  • List<Object[]> list = query.list();
  • for (Object[] objs : list) {
  • System.out.println(Arrays.toString(objs));
  • }
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

非迫切返回的数据结构类型为:

  • List<Object[]>
  • Object[]中装有[Customer, Order]
  • 迫切,即使用inner join fetch,迫切连接会将子装入父中,组装成一个对象
  • @Test
  • public void fun4() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Query query = session.createQuery("from Customer c inner join fetch c.orders ");
  • List<Object> list = query.list();
  • for (Object obj : list) {
  • System.out.println(obj);
  • }
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

迫切返回的数据结构类型为:

  • List<Customer>
  1. 左外连接,left [outer] join
  • 非迫切
  • @Test
  • public void fun5() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Query query = session.createQuery("from Customer c left outer join c.orders ");
  • List<Object[]> list = query.list();
  • for (Object[] objs : list) {
  • System.out.println(Arrays.toString(objs));
  • }
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }
  • 迫切,left [outer] join fetch
  • @Test
  • public void fun6() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Query query = session.createQuery("from Customer c left outer join fetch c.orders ");
  • List<Object> list = query.list();
  • for (Object obj : list) {
  • System.out.println(obj);
  • }
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }
  1. 右外连接
  • 非迫切,right [outer] join
  • @Test
  • public void fun7() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Query query = session.createQuery("from Customer c right outer join c.orders ");
  • List<Object[]> list = query.list();
  • for (Object[] objs : list) {
  • System.out.println(Arrays.toString(objs));
  • }
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }
  • 非迫切,right [outer] join fetch
  • @Test
  • public void fun8() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Query query = session.createQuery("from Customer c right outer join fetch c.orders ");
  • List<Object> list = query.list();
  • for (Object obj : list) {
  • System.out.println(obj);
  • }
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

2. 命名查询

我们可以将SQL查询语句提取到配置文件中,然后从配置文件中加载并查询。命名查询分为全局配置和局部配置:

  • <?xml version="1.0" encoding="UTF-8"?>
  • <!DOCTYPE hibernate-mapping PUBLIC
  • "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  • "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
  • <hibernate-mapping package="com.coderap.domain">
  • <class name="Customer" table="t_customer">
  • <id name="id" column="id">
  • <generator class="native"></generator>
  • </id>
  • <property name="name" column="name" type="string"></property>
  • <set name="orders" batch-size="2">
  • <key column="cid"></key>
  • <one-to-many class="Order" />
  • </set>
  • <!-- 局部配置 -->
  • <query name="local"><![CDATA[from Order]]></query>
  • </class>
  • <!-- 全局配置 -->
  • <query name="global"><![CDATA[from Customer]]></query>
  • </hibernate-mapping>
  • @Test
  • // 找到局部配置的HQL语句
  • public void fun1() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • // 从com.coderap.domain.Customer的hbm文件中找
  • Query query = session.getNamedQuery("com.coderap.domain.Customer.local");
  • List<Object> list = query.list();
  • System.out.println(list);
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }
  • @Test
  • // 找到全局配置的HQL语句
  • public void fun2() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • // 从com.coderap.domain.Customer的hbm文件中找
  • Query query = session.getNamedQuery("global");
  • List<Object> list = query.list();
  • System.out.println(list);
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

3. QBC详解

QBC查询,即Query By Criteria条件查询,面向对象的查询的方式.

  • QBC简单的查询:
  • // 简单查询
  • List<Customer> list = session.createCriteria(Customer.class).list();
  • for (Customer customer : list) {
  • System.out.println(customer);
  • }
  • QBC分页的查询:
  • Criteria criteria = session.createCriteria(Order.class);
  • criteria.setFirstResult(10);
  • criteria.setMaxResults(10);
  • List<Order> list = criteria.list();
  • QBC排序查询
  • Criteria criteria = session.createCriteria(Customer.class);
  • // criteria.addOrder(org.hibernate.criterion.Order.asc("age"));
  • criteria.addOrder(org.hibernate.criterion.Order.desc("age"));
  • List<Customer> list = criteria.list();
  • QBC条件查询
  • // 按名称查询:
  • /*Criteria criteria = session.createCriteria(Customer.class);
  • criteria.add(Restrictions.eq("cname", "tom"));
  • List<Customer> list = criteria.list();*/
  • // 模糊查询;
  • /*Criteria criteria = session.createCriteria(Customer.class);
  • criteria.add(Restrictions.like("cname", "t%"));
  • List<Customer> list = criteria.list();*/
  • // 条件并列查询
  • Criteria criteria = session.createCriteria(Customer.class);
  • criteria.add(Restrictions.like("cname", "t%"));
  • criteria.add(Restrictions.ge("age", 35));
  • List<Customer> list = criteria.list();
  • 离线查询

DetachedCriteria离线查询对象,不需要使用Session就可以拼凑查询条件。一般使用在Web层或service层拼凑。将此对象传递给DAO层,此时将与session进行绑定执行查询。离线查询条件与QBC是一样的。

  • @Test
  • public void fun1() {
  • // Service层
  • DetachedCriteria dc = DetachedCriteria.forClass(Customer.class);
  • dc.add(Restrictions.eq("id", 1));
  • // -------------------------------------------------
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Criteria c = dc.getExecutableCriteria(session);
  • System.out.println(c.list());
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

4. 常见配置

4.1. 整合C3P0连接池

  1. 导入c3p0-0.9.1.jar
  2. 配置相关参数:

hibernate.cfg.xml文件配置hibernate.connection.provider_class=org.hibernate.connection.C3P0ConnectionProvider

hibernate.cfg.xml:

  • <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>

其支持的参数有:

  • #hibernate.c3p0.max_size 2
  • #hibernate.c3p0.min_size 2
  • #hibernate.c3p0.timeout 5000
  • #hibernate.c3p0.max_statements 100
  • #hibernate.c3p0.idle_test_period 3000
  • #hibernate.c3p0.acquire_increment 2
  • #hibernate.c3p0.validate false

hibernate.cfg.xml:

  • <property name="hibernate.c3p0.max_size">5</property>
  • <property name="hibernate.c3p0.min_size">2</property>

4.2. 事务

  1. 事务:一组业务操作,要么全部成功,要么全部不成功。
  2. 特性:ACID
  • 原子性:整体
  • 一致性:数据
  • 隔离性:并发
  • 持久性:结果
  1. 隔离问题:
  • 脏读:一个事务读到另一个事务未提交的内容
  • 不可重复读:一个事务读到另一个事务已提交的内容(insert)
  • 虚读(幻读):一个事务读到另一个事务已提交的内容(update)
  1. 隔离级别,解决问题:
  • read uncommittd,读未提交。存在3个问题。
  • read committed,读已提交。解决:脏读。存在2个问题。
  • repeatable read ,可重复读。解决:脏读、不可重复读。存在1个问题。
  • serializable,串行化。单事务。没有问题。

4.2.1. hibernate设置隔离级别

在hibernate.cfg.xml配置hibernate.connection.isolation=4

  • <!-- 改变Hibernate连接数据库的事务隔离级别
  • 1:读未提交
  • 2:读已提交
  • 4:可重复读
  • 8:串行化
  • -->
  • <property name="hibernate.connection.isolation">4</property>

5. 锁

5.1. 悲观锁

默认认为一定会发送别人要修改我使用的数据,那就可以为读取的数据加锁。

  • 读锁/共享锁:读锁可被其他线程所共享,如果是读取的话大家都可以用这把锁读到数据。select * from table lock in share mode。(读锁、共享锁);

  • 写锁/排他锁:写锁不能共享,只要有人为数据加入了写锁,其他人就不能为数据加任何锁。select * from table for update。(写锁、排它锁)。

悲观锁的代码实现:

  • @Test
  • // 悲观锁
  • // 写锁
  • public void fun1() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Customer c = (Customer) session.get(Customer.class, 1,
  • LockOptions.UPGRADE);
  • System.out.println(c);
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }
  • @Test
  • // 悲观锁
  • // 读锁
  • public void fun2() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Customer c = (Customer) session.get(Customer.class, 1, LockOptions.READ);
  • System.out.println(c);
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

5.2. 乐观锁

乐观锁的一种实现方法:某条数据中有一个Version字段代表数据的版本,初始化为1,假设现在有A和B两个线程同时修改该条数据,A和B同时取出该数据时,Version都为1,A先修改完数据,会将自己那份数据的Version加1改为2,然后进行提交,A会将自己拥有的这条数据中的Version和数据库中的该条数据的Version进行比对,发现自己拥有的这条数据中的Version值比数据库中的该条数据的Version值大,所以提交成功;B随后修改数据,也会将自己那份数据的Version加1改为2,当B提交修改的数据时,会和数据库中的该条数据的Version进行比对,发现自己拥有的这条数据中的Version不比数据库中的该条数据的Version值大,所以认为提交失败。

5.2.1. Hibernate中乐观锁的配置

  • 步骤一:在PO对象(Javabean)提供字段,表示版本字段。一般Integer

Customer.java:

  • package com.coderap.domain;
  • import java.util.HashSet;
  • import java.util.Set;
  • public class Customer {
  • private Integer id;
  • private String name;
  • private Set<Order> orders = new HashSet<Order>();
  • // 乐观锁版本号
  • private Integer version;
  • public Customer(Integer id, String name) {
  • super();
  • this.id = id;
  • this.name = name;
  • }
  • public Integer getVersion() {
  • return version;
  • }
  • public void setVersion(Integer version) {
  • this.version = version;
  • }
  • public Customer() {
  • }
  • public Integer getId() {
  • return id;
  • }
  • public void setId(Integer id) {
  • this.id = id;
  • }
  • public String getName() {
  • return name;
  • }
  • public void setName(String name) {
  • this.name = name;
  • }
  • public Set<Order> getOrders() {
  • return orders;
  • }
  • public void setOrders(Set<Order> orders) {
  • this.orders = orders;
  • }
  • @Override
  • public String toString() {
  • return "Customer [id=" + id + ", name=" + name + ", orders=" + orders + "]";
  • }
  • }

步骤二:在PO的配置文件中添加版本号配置:

Customer.hbm.xml:

  • <?xml version="1.0" encoding="UTF-8"?>
  • <!DOCTYPE hibernate-mapping PUBLIC
  • "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  • "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
  • <hibernate-mapping package="com.itheima.domain">
  • <class name="Customer" table="t_customer">
  • <id name="id" column="id">
  • <generator class="native"></generator>
  • </id>
  • <!-- 告诉Hibernate乐观锁的版本号的属性名 -->
  • <version name="version"></version>
  • <property name="name" column="name" type="string"></property>
  • <set name="orders" >
  • <key column="cid"></key>
  • <one-to-many class="Order" />
  • </set>
  • </class>
  • </hibernate-mapping>

步骤三:测试

  • @Test
  • public void demo02() {
  • // 1 查询所有
  • Session session = factory.openSession();
  • session.beginTransaction();
  • // Order order = new Order();
  • // order.setPrice(998d);
  • // session.save(order);
  • Order order = (Order) session.get(Order.class, 32);
  • order.setPrice(889d);
  • session.getTransaction().commit();
  • session.close();
  • }