Hibernate QBC查询

Hibernate day_02

  1. 持久化类以及编写规则.

持久化类就是我们说的实体类(符合 JavaBean 的规则)。
以后分包:
entity(实体),pojo(简单的 Java 对象),domian(域对象)
1.
属性的声明必须使用私有的 private
2.
通过 get,set 方法获得设置和获得属性值
3. 属性的声明不能使用 final 关键字
4. [可选]建议实现一个序列接口 Serializable.如果需要将对象缓存到本地文件,必须加上

  1. 主键生成策略

所谓的主键生成策略,就是 Hibernate 提供了多种生成主键值的方法

常用的策略有:increment identity sequence native uuid assigned

Hibernate QBC查询

    1.   increment策略

不使用数据库的本地的增长策略,而是由程序(Hibernate框架)产生一个自增长的ID 值,赋予数据库.

<generator class="increment"></generator>

这种策略的好处:

由于ID自增长的值由程序生成,忽略了各种数据库ID自增长的差异,所以兼容性好

坏处:

因为每次增加一个ID值,都需要调用max()函数,效率不高。

应用场景:

  1. 是用在一些需要支持多种数据库的产品型项目。!!
    1. identity策略(只能用于有 ID 自增长类型的数据库 如  mysql sqlserver ,对不支持自增长策略的数据库  如  oracle db2)在配置前  一定要设置相应表格的  id 自增长

这种策略的好处:效率高。因为直接认为数据库就是使用ID自增长的!!

缺点:兼容性差,只支持有ID自增长的数据库。

    1. native 策略

根据数据库的本地策略生成主键值,如果数据库支持ID自增长策略的,使用ID自增长。如果数据库使用的是序列来生成ID值的,那么就是序列!!

--native效率高于increment,低于identitysequence.由于不上不下的效率,使用不多!

    1. uuid 策略(注意  对应的字段必须为字符串)

所谓的uuid就是一个唯一的字符串。一般是32位。

那么什么时候使用UUID呢?

应用场景:
有几个开发人员同时开发一个项目。前提每个开发人员使用的都是自己电脑的数据库。
如果使用
ID 自增长作为 ID 列的值,就会导致每个人的数据的 ID 冲突。
但是如果大家使用
UUID。那么冲突的概率,极小

UUID 用于存储基础数据的表。所谓的基础数据,就是系统必须依赖的数据。

    1.  assigned策略

HIbernate不使用任何的数据库策略,由调用方手工输入。

手工输入使用的概率不多。一般使用手工输入ID的策略用于一对一的情况

    1. sequence

序列策略:一般用于有序列的数据库。如果像MySQL数据库这种没有序列的数据库使用sequence策略,会使用的一个表来模拟序列。ID值生成规则存放在这个模拟表里面!

  1. 实体类状态转换(难点) 会获得持久态对象

实体类(持久化类)对象是有状态的。
为什么实体类对象会有状态?
答:由于
HIbernate 框架是一个先映射,后操作的框架。所谓的状态就是实体类的对象和数据库是否
有关联的情况。

男孩比作一个对象

女孩子比作一个数据库对象

Hibernate 的持久化类有三种状态:
1. 瞬时态(*态):与数据库的表没有任何关联关系的实体对象

Customer customer=new Customer();

2. 持久态:正在与数据库保持连接的关系。

Customer customer=session.get(Customer.class,2L);
3. 游离态:曾经被 session 操作过,但 session 失效了。关闭,清除

    1. 瞬时态:

Customer c=new Customer();

    1. 持久态:
  1. 创建的对象被 sesssion 操作过了 ,操作不包括删除!!

    @Test

    public void save(){

       //1.获得数据库的操作对象,session

       Session session = HibernateUtils.getSession();

       //2.Hibernate默认必须先启动事务,才可以操作(增删改)

       Transaction transaction = session.beginTransaction();

       //3.封装一个有数据的实体类对象

       Customer c=new Customer();

       c.setCustName("阿里巴巴");

       //4.保存

        session.save(c);

       //5.提交

       transaction.commit();

//这个时候 c, session 操作过了。就和数据库建立关系。

       //6.关闭

       session.close();

    }

    1. 游离态: 创建的对象被 sesssion 操作过了,session 关闭了。

瞬时态和游离态,这个两个状态都是和数据库没有关联了。他们区别是:是否曾经被操作过!!

对象状态转移图如下:

Hibernate QBC查询

注意:根据状态转移图,我们学会如何获得持久态对象就可以(会使用get方法就可以了)了!!!

为什么需要了解如何获得持久态对象呢?

答:因为持久态对象是正在和数据库关联的状态的对象。所以支持以下数据库的操作。

  1. 快照机制支持
  2. 缓存机制的支持
  3. 导航查询的支持。

 

  1. 一级缓存  

HIbernate 持久态对象是支持一级缓存。所谓的一级缓存就是 Session 级别的缓存。
意思就是说,
同一个 session 查询同样的数据,只查询一次数据库。如果出现同多次同样的查询(get/load
直接返回缓存的数据

 

问题:如何清除一级缓存?

答:关闭session。和清空session。

 . session.close()  session.clear()

注意,close,clear,evit 清空缓存只是将持久态转成游离态,清空的是数据和数据库的关联,而不是清空数据。对象的属性和数据依然存在.

清除缓存的应用场景:秒杀,团购。遇到数据的数据不断更新,而查询的session又不能不断关闭。所以每次查询数据库表之前,需要清空清除一个缓存!!

  1. 快照机制

当对象变成持久态对象(调用get方法时候)的时候,和数据库表关联后。在 session 会保存两份数据的副本
一份是缓存,一个是快照。
缓存的作用:用于提高查询的效率
快照的作用:用于更新数据,作对比使用。

快照的支持就是持久态对象直接可以通过修改属性更新数据,不需要 update 方法

这里修改持久态对象的属性  直接对该对象数据库中的信息进行更新.

    @Test

    public void update() {

       // 1.获得数据库的操作对象,session

       Session session = HibernateUtils.getSession();

       //2.更新需要启动事务

       Transaction transaction = session.beginTransaction();

       //获得持久态对象

       Customer customer = session.get(Customer.class, 1L);

       //修改属性

       customer.setCustName("alibaba");

       //提交

       transaction.commit();

 

       // .关闭

       session.close();

 

    }

 

执行流程图:

Hibernate QBC查询

  1. 线程绑定(比进程更小的运行单位)  web项目理解为多线程  每一次请求都是一个线程(Session绑定)

所谓的线程绑定,就是将session的对象绑定到当前的线程变量里面。这样确保了在同一条线程中使用的session对象是相同的!!!

 

为什么需要线程绑定呢?

答:如果不使用线程绑定,要处理同时对数据库两个操作的业务,需要通过参数传递的方式来确保session的唯一的。

 

为什么同时操作两个业务的需求,需要session唯一呢?

答:因为数据库事务处理的前提,必须是同一个连接(同一个session)

实现线程绑定的方式:

方式一:

    1. 使用HIbernate的内置实现

Hibernate框架内置支持将对象绑定到当前线程

<property name="hibernate.current_session_context_class">thread</property>

这个时候获得  session 的时候不能够使用  openSession() 方法  要使用 getCurrentSession()方法  获得当前Session 对象.

注意标注红色字体部分.

public class HibernateUtils {

    //通过一个静态变量,确保整个项目里面只有一个SessionFactory对象

    //为什么建议一个项目只有一个SessionFactory对象?

    //答:如果一个项目有多个连接池,可以导致事务不同步!!!

    public static SessionFactory sessionFactory=HibernateUtils.createSessionFactory();

   

    //获得会话工厂

    private static SessionFactory createSessionFactory(){

       //1.获得Configuration对象

       Configuration config=new Configuration();

       //2.读取配置文件,默认读取的就是classpath根目录的hibernate.cfg.xml

       config.configure();

       //3.构建会话工厂

       SessionFactory sessionFactory = config.buildSessionFactory();

       return sessionFactory;

    }

   

    //获得会话,就是一个操作对象

    public static Session getSession(){

       //1.每次都需要在当前线程获得session对象

       //注意:getCurrentSession必须要先配置线程绑定才可以使用

        return sessionFactory.getCurrentSession();

    }

   

    //测试

    public static void main(String[] args) {

       System.out.println(HibernateUtils.getSession());

    }

 

}

 

注意事项:

//注意:hibernate内置实现的线程绑定,已经实现随线程启动而启动,随线程关闭而关闭,所以session不能手工关闭

//session.close();

//注意:实现了内置线程绑定后,必须要先启动事务,才可以查询。所以增删改查都需要开启事务!!!

    1. 第二:使用自定义的方式实现 线程绑定.实现代码:

public class HibernateUtils {

    //通过一个静态变量,确保整个项目里面只有一个SessionFactory对象

    //为什么建议一个项目只有一个SessionFactory对象?

    //答:如果一个项目有多个连接池,可以导致事务不同步!!!

    public static SessionFactory sessionFactory=HibernateUtils.createSessionFactory();

    //声明一个线程变量,作用就是确保存在在线程变量里面的对象,同一条线程是相同的。

    private static ThreadLocal<Session> threadLocal=new ThreadLocal<>();

   

    //获得会话工厂

    private static SessionFactory createSessionFactory(){

       //1.获得Configuration对象

       Configuration config=new Configuration();

       //2.读取配置文件,默认读取的就是classpath根目录的hibernate.cfg.xml

       config.configure();

       //3.构建会话工厂

       SessionFactory sessionFactory = config.buildSessionFactory();

       return sessionFactory;

    }

   

    //获得会话,就是一个操作对象

    public static Session getSession(){

       //判断,线程变量是否存在session

       if(threadLocal.get()==null){

           Session session = sessionFactory.openSession();

           //session对象放在线程变量里面

           threadLocal.set(session);

       }

       return threadLocal.get();

    }

   

    //实现一个关闭的方法,在关闭session后,要移除线程变量里面的session对象

    public static void closeSession(){

       if(threadLocal.get()!=null){

           Session session = threadLocal.get();

           //关闭只是session和数据库断开了,但对象还在!!

           session.close();

           //关闭session后,必须要将线程变量里面的session移除

           threadLocal.remove();

       }

    }

 

 

  1. Hibernate查询 API:  get(类字节码,id)
    1. QBC查询:Query By Criteria。(通过Criteria查询API查询)
      1. 示例代码(查询所有客户):

//需求:查询所有的客户,使用Criteria实现

    @Test

    public void findAll(){

       //1.获得操作对象,session

       Session session = HibernateUtils.getSession();

       //2.获得Criteria查询对象

       Criteria criteria = session.createCriteria(Customer.class);

       //3.通过criteria对象,查询数据,返回多条数据,使用list

       List<Customer> customers = criteria.list();

       for(Customer c:customers){

           System.out.println(c.getCustName());

       }

       session.close();

    }

      1. 模糊查询:

/**

     * 需求:查询客户名有""的客户

     */

    @Test

    public void findByName(){

       //1.获得操作对象,session

       Session session = HibernateUtils.getSession();

       //2.获得Criteria查询对象

       Criteria criteria = session.createCriteria(Customer.class);

       //设置条件

       criteria.add(Restrictions.like("custName", "%%"));

       //3.通过criteria对象,查询数据,返回多条数据,使用list

       List<Customer> customers = criteria.list();

       for(Customer c:customers){

           System.out.println(c.getCustName());

       }

       session.close();

    }

 

      1. 分页查询:

//需求:查询第 3 条开始,取 4 条件数据

    @Test

    public void findByPage(){

       //1.获得操作对象,session

       Session session = HibernateUtils.getSession();

       //2.获得Criteria查询对象

       Criteria criteria = session.createCriteria(Customer.class);

       //设置分页

       //1)设置开始的位置,开始位置从0开始,第三条数据的下标为2

       criteria.setFirstResult(2);

       //(2)设置每页的记录数,每页返回的数据时4条,设置为4

       criteria.setMaxResults(4);

       //3.通过criteria对象,查询数据,返回多条数据,使用list

       List<Customer> customers = criteria.list();

       for(Customer c:customers){

           System.out.println(c.getCustName());

 

    1. HQL查询(实际开发中较常用)
      1. Hql(hibernate query language)查询所有客户:

package com.bdqn.test;

import java.util.List;

import org.hibernate.Query;

import org.hibernate.Session;

import org.junit.Test;

import com.bdqn.entity.Customer;

import com.bdqn.utils.HibernateUtils;

public class CustomerDAOTest {

    // 需求:查询所有的客户,使用hql实现

    @Test

    public void findAll() {

       // 1.获得操作对象,session

       Session session = HibernateUtils.getSession();

       // 2.获得hql查询对象

       Query query = session.createQuery("from Customer");

 

       // 3.通过criteria对象,查询数据,返回多条数据,使用list

       List<Customer> customers = query.list();

       for (Customer c : customers) {

           System.out.println(c.getCustName());

       }

       session.close();

    }

 

    @Test

    public void findAll1() {

       // 1.获得操作对象,session

       Session session = HibernateUtils.getSession();

       // 2.获得hql查询对象

       // 注意,select返回不能是*,必须是一个属性或者对象的别名

       Query query = session.createQuery("select c from Customer c");

 

       // 3.通过criteria对象,查询数据,返回多条数据,使用list

       List<Customer> customers = query.list();

       for (Customer c : customers) {

           System.out.println(c.getCustName());

       }

       session.close();

    }

 

   

 

      1. Hql模糊查询

/**

     * 需求:查询客户名有""的客户

     */

    @Test

    public void findByName() {

       // 1.获得操作对象,session

       Session session = HibernateUtils.getSession();

       // 2.获得Criteria查询对象

       Query query = session.createQuery("from Customer c where c.custName like ?");

       // 设置条件,注意,设置的下标0,为hql的第一个?的值

       query.setString(0, "%%");

       // 3.通过criteria对象,查询数据,返回多条数据,使用list

       List<Customer> customers = query.list();

       for (Customer c : customers) {

           System.out.println(c.getCustName());

       }

       session.close();

    }

 

      1. Hql分页查询:

    // 需求:查询第 3 条开始,取 4 条件数据

    @Test

    public void findByPage() {

       // 1.获得操作对象,session

       Session session = HibernateUtils.getSession();

       // 2.获得hql查询对象

       Query query = session.createQuery("from Customer");

       // 设置分页条件

       // (1)设置开始位置,从0开始,第三条数据下标为2

       query.setFirstResult(2);

       // (2)设置每页的记录数据,为4

       query.setMaxResults(4);

 

       // 3.通过criteria对象,查询数据,返回多条数据,使用list

       List<Customer> customers = query.list();

       for (Customer c : customers) {

           System.out.println(c.getCustName());

       }

       session.close();

    }

 

 

 

      1. Hql查询总记录:

// 需求:统计记录数据

    @Test

    public void count() {

       // 1.获得操作对象,session

       Session session = HibernateUtils.getSession();

       // 2.获得hql查询对象

       // 注意,select返回不能是*,必须是一个属性或者对象的别名

       Query query = session.createQuery("select count(c) from Customer c");

       // 3.返回一条数据,使用uniqueResult

       // 注意:如果返回的数据不确定,随便设置一个类型让它报错。通过错误信息分析返回的类型

       Long uniqueResult = (Long) query.uniqueResult();

       System.out.println(uniqueResult);

       session.close();

    }

 

 

      1. Hql:投影查询(查询部分属性)  了解

// 需求:查询客户的信息,返回custNamecustSource

    @SuppressWarnings("unchecked")

    @Test

    public void findAll2() {

       // 1.获得操作对象,session

       Session session = HibernateUtils.getSession();

       // 2.获得hql查询对象

       // 注意,select返回不能是*,必须是一个属性或者对象的别名

       Query query = session.createQuery("select c.custName,c.custSource from Customer c");

 

       // 3.通过criteria对象,查询数据,返回多条数据,使用list

       List<Object[]> customers = query.list();

       for (Object[] object : customers) {

           System.out.println("客户名:" + object[0] + ",客户来源:" + object[1]);

       }

       session.close();

    }

 

    // 需求:查询客户的信息,返回custNamecustSource,但是必须使用一个customer对象接收

    /**

     *

     * 当查询的记录不是所有字段。而是指定的字段。

     * 如果需要使用一个实体类接收。那么需要一个有参数的构造方法。我们将这种,有构造方法参数的查询,称为投影查询

     */

    @Test

    public void findAll3() {

       // 1.获得操作对象,session

       Session session = HibernateUtils.getSession();

       // 2.获得hql查询对象

       // 注意,select返回不能是*,必须是一个属性或者对象的别名

       Query query = session.createQuery("select new Customer(c.custName,c.custSource) from Customer c");

 

       // 3.通过criteria对象,查询数据,返回多条数据,使用list

       List<Customer> customers = query.list();

       for (Customer c : customers) {

           System.out.println("客户名:" + c.getCustName() + ",客户来源:" + c.getCustSource());

       }

       session.close();

    }

 

}

 

 

    1. HQL操作

所谓的HQL操作,就是使用HQL实现数据库的删改。

      1. Hql删除:

注意:HQL是不支持增加的!!!

 

public class CustomerDAOTest {

 

    //需求:删除客户名有""的客户

    @Test

    public void delete(){

       //1.获得操作对象

       Session session = HibernateUtils.getSession();

       //2.操作需要开启事务

       Transaction transaction = session.beginTransaction();

       //3.获得hql操作对象

        Query query = session.createQuery("delete from Customer c where c.custName like ?");

       //4.设置删除的条件

       query.setString(0, "%%");

      

       //5.执行hql,返回的是影响行数

       int count = query.executeUpdate();

       System.out.println(count);

       transaction.commit();

       session.close();

    }

   

    //需求:删除客户名有""的客户,使用命名参数实现

    //所谓的命名参数,就是使用一个自定义的名字代替原来的?

    @Test

    public void delete1(){

       //1.获得操作对象

       Session session = HibernateUtils.getSession();

       //2.操作需要开启事务

       Transaction transaction = session.beginTransaction();

       //3.获得hql操作对象

       //注意:命名参数声明的时候,使用有 :(冒号的)

       Query query = session.createQuery("delete from Customer c where c.custName like :custName");

       //4.设置删除的条件,设置条件的时候,命名参数是没有冒号的

       query.setString("custName", "%%");

      

       //5.执行hql,返回的是影响行数

       int count = query.executeUpdate();

       System.out.println(count);

       transaction.commit();

       session.close();

    }

   

   

   

 

}

      1. Hql更新:

//需求:通过hql实现,更新客户名名,有“百”的客户来源,为互联网

    @Test

    public void update(){

       //1.获得操作对象

       Session session = HibernateUtils.getSession();

       //2.操作需要开启事务

       Transaction transaction = session.beginTransaction();

       //3.获得hql操作对象

       //注意:命名参数声明的时候,使用有 :(冒号的)

       Query query = session.createQuery("update Customer c set c.custSource = ? where c.custName like  ?");

       //4.设置删除的条件,设置条件的时候,命名参数是没有冒号的

       query.setString(0, "互联网");

       query.setString(1, "%%");

      

       //5.执行hql,返回的是影响行数

       int count = query.executeUpdate();

       System.out.println(count);

       transaction.commit();

       session.close();

    }

 

相对于SQL,使用HQL的好处是什么?

答:HQL操作的是对象,不是数据库的表。所以所有的数据库的语法是一样的。屏蔽了不同数据库的方言的差异!!!!

由于Criteria查找接口比较笨重,所以建议使用HQL

  1. 总结

1今天学习了各种HIbernate的组件。

 

ID生成策略

  1. identity

 

必须要知道如何获得持久态对象。

  1. 通过查询可以获得  get(类字节码,id)
  2. 可以通过更新获得   update(object)
  3. 可以通过插入获得   save(object)

 

持久态对象有什么用

  1. 支持缓存
  2. 支持快照
  3. 支持导航查询

 

 

线程绑定:目的是为了不用传递参数,在同一条线程操作上,任何位置获得的session是相同的。

 

为什么有这个需求

原因因为事务处理必须是同一个session才可以实现!!!!!

 

三种实现线程绑定的方式,会内置配置方式和自定义配置方式。

查找的API。

使用Criteria查找接口。就是使用纯Java对象。查询数据库。(了解)

使用HQL查询接口,通过HQL实现数据库的操作(重点)

相对于SQL,使用HQL的好处是什么?

答:HQL操作的是对象,不是数据库的表。所以所有的数据库的语法是一样的。屏蔽了不同数据库的方言的差异!!!!