Hibernate特性

1 持久化类及编写规则
2 主键生成策略
3 实体类状态
4 一级缓存
5 快照机制
6 绑定Session到当前线程
7 Hibernate标准查询
8 HQL查询

1 持久化类及编写规则

持久化类就是实体类。

实体类的包名:entity(实体)、pojo(简单的Java对象)、domian(域对象)

编写规则:

  1. 属性的声明必须使用私有的private
  2. 通过get,set方法获得设置和获得属性值
  3. 属性的声明不能使用final关键字
  4. 建议实现序列接口Serializable
import java.io.Serializable;

public class Customer implements Serializable {
    private static final long serialVersionUID = 194674031268504970L;
    private  Long custId; // 客户编号(主键)
    private String custName; // 客户名称(公司名称)
    private String custSource; // 客户信息来源
    private String custIndustry; // 客户所属行业
    private String custLevel; // 客户级别

    public Long getCustId() {
        return custId;
    }

    public void setCustId(Long custId) {
        this.custId = custId;
    }

    public String getCustName() {
        return custName;
    }

    public void setCustName(String custName) {
        this.custName = custName;
    }

    public String getCustSource() {
        return custSource;
    }

    public void setCustSource(String custSource) {
        this.custSource = custSource;
    }

    public String getCustIndustry() {
        return custIndustry;
    }

    public void setCustIndustry(String custIndustry) {
        this.custIndustry = custIndustry;
    }

    public String getCustLevel() {
        return custLevel;
    }

    public void setCustLevel(String custLevel) {
        this.custLevel = custLevel;
    }
}

2 主键生成策略

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

2.1 常用的主键生成策略

策略名说明
incrementincrement策略是指,不使用数据库本地的自增长策略,而是由程序产生一个自增长的ID值,赋予数据库。
identityidentity策略,指定使用数据库中的ID自增长策略。只能用于有ID自增长功能的数据库,如:MySQL、SQLServer…不支持没有ID自增长策略的数据库,如:Oracle、DB2…
sequence使用序列的自增长策略,主要用于有序列的数据库,如:Oracle、DB2。如果用于不支持序列的数据库(如:MySQL),该策略会使用一个表模拟序列。
native使用数据库本地的策略,就是数据库里面使用什么策略就用什么策略。Hibernate不做任何的判断。如:MySQL数据库使用了increment_auto自增长策略,使用native表示直接调用数据库里面的increment_auto策略。
uuid就是数据库的主键是使用一个唯一的字符串的来存储。这个唯一的字符串就是UUID
assignedassigned策略就是不使用主键生成策略,由手工输入ID

2.2 increment生成策略

increment策略是指,不使用数据库本地的自增长策略,而是由Hibernate框架产生一个自增长的ID值,赋予数据库的主键。

  • 好处:兼容号,可以支持各种数据库。
  • 缺点:由于主键值由框架生成,所以效率相对低。
  • 应用场景:适合一些需要支持多种数据库的产品型项目。

映射文件中的配置如下:

<id name="custId" column="cust_id">
   <!-- ID 生成策略 -->
   <!-- increment:由框架生成ID自增长列的值 -->
   <generator class="increment"></generator>
</id>

2.3 identity之间生成策略

identity策略,指定使用数据库中的ID自增长策略。只能用于有ID自增长功能的数据库(如:MySQL、SQLServer…),不支持没有ID自增长策略的数据库(如:Oracle、DB2…)

  • 好处:指定使用数据库中的ID自增长策略,效率高。

映射文件中的配置如下:

<id name="custId" column="cust_id">
    <!-- ID 生成策略 -->
    <!-- identity:指定使用数据库中的ID自增长策略 -->
    <generator class="identity"></generator>
</id>

2.4 native生成策略

使用数据库本地的策略,就是数据库里面使用什么策略就用什么策略。Hibernate不做任何的判断。如:MySQL数据库使用了increment_auto自增长策略,使用native表示直接调用数据库里面的increment_auto策略。

映射文件中的配置如下:

<id name="custId" column="cust_id">
   <!-- ID 生成策略 -->
   <generator class="native"></generator>
</id>

native生成策略支持所有的关系型数据库。Oracle使用native生成策略,缺点是:序列名固定为:HIBERNATE_SEQUENCE,并且多个表使用一个序列,会导致序列断号。

2.5 sequence生成策略

使用序列的自增长策略,主要用于有序列的数据库,如:Oracle、DB2。如果用于不支持序列的数据库(如:MySQL),该策略会使用一个表模拟序列。

映射文件中的配置如下:

<id name="custId" column="cust_id">
    <!-- ID生成策略 -->
    <!-- 
        sequence:使用序列策略
        序列是需要序列名的,如果不配置默认为HIBERNATE_SEQUENCE
        通过参数可以配置序列名、补偿、开始位置
        注意:所有策略的字符串都对应着一个类
        如:sequence对应的类就是org.hibernate.id.enhanced.SequenceStyleGenerator
        所有策略对应的类都可以在hibernate-core包下的org.hibernate.id包下找到
    -->
    <!-- <generator class="org.hibernate.id.enhanced.SequenceStyleGenerator"></generator> -->
    <generator class="sequence">
        <!-- 配置参数 -->
        <!-- 参数,序列名 -->
        <param name="sequence_name">seq_customer</param>
        <!-- 参数,开始位置,默认就是1 -->
        <param name="initial_value">1</param>
        <!-- 参数,步长,默认就是1 -->
        <param name="increment_size">1</param>
    </generator>
</id>

2.6 uuid主键生成策略

就是数据库的主键是使用一个唯一的字符串的来存储。这个唯一的字符串就是UUID。

实体类代码:

    // UUID使用字符串存储主键。
    private String custId;
    public String getCustId() {
        return custId;
    }
    public void setCustId(String custId) {
        this.custId = custId;
    }

映射文件中的配置如下:

 <id name="custId" column="cust_id">
     <!-- ID生成策略 -->
     <!-- uuid:使用一个唯一的字符串值作为主键的值 -->
     <generator class="uuid"></generator>
 </id>

2.7 assigned主键生成策略

assigned策略就是不使用主键生成策略,由手工输入ID。

映射文件中的配置如下:

<id name="custId" column="cust_id">
    <!-- ID生成策略 -->
    <!-- assigned -->
    <generator class="assigned"></generator>
</id>

调用代码,必须要有ID值:

    @Test
    public void save(){
        // 1.获得操作对象
        Session session = HibernateUtils.getSession();

        // 2.打开事务,操作都需要打开事务(增删改)
        Transaction transaction = session.beginTransaction();

        // 3.操作
        Customer customer = new Customer();
        // 配置了assigned主键策略,表示ID值不会自动生成,需要手工输入。
        customer.setCustId(1L);
        customer.setCustName("阿里");
        session.save(customer);

        // 4.提交事务
        transaction.commit();

        // 5.关闭操作对象session
        session.close();
    } // 如果调用代码没有ID值,会报异常

3 实体类状态转换

实体类(持久化类)是由状态的。

Hibernate是一个先映射,后操作的框架。所谓的状态就是实体类的对象和数据库是否有关联的情况。

3.1 瞬时态

使用new创建一个对象,这时实体对象和数据库没有任何关系。如:

Customer customer = new Customer();

此时,对象的状态为瞬时态(自由态)。

3.2 持久态

创建的对象被session操作过了。如:

    @Test
    public void save(){
        // 1.获得操作对象
        Session session = HibernateUtils.getSession();

        // 2.打开事务,操作都需要打开事务(增删改)
        Transaction transaction = session.beginTransaction();

        // 3.操作
        Customer customer = new Customer();
        customer.setCustName("百度");
        session.save(customer);
         // 这个时候customer,被session操作过了,和数据库建立了关系。

        // 4.提交事务
        transaction.commit();

        // 5.关闭操作类对象session
        session.close();
    }

将被session操作过的对象的状态称为持久态(托管态)。

3.3 游离态

创建的对象被session操作后,session关闭了。如:

    @Test
    public void get(){
        // 1.获得操作对象
        Session session = HibernateUtils.getSession();

        // 通过OID获得对应的记录
        Customer customer = session.get(Customer.class, 2L);

        session.close();
        // 虽然customer是通过session获得的,但session关闭了,此时为游离态
        System.out.println(customer.getCustName());
    }

被session操作过后,session失效了。此时实体对象的状态为游离态。

3.4 状态转移图

这里写图片描述

重点:要知道如何获得持久态的对象。

4 一级缓存

Hibernate是支持一级缓存的。所谓的一级缓存就是Session级别的缓存。就是说,同一个session执行多次同样的查询(get/load),只会查询一次,之后直接返回缓存的数据。

4.1 Hibernate一级缓存示例

    @Test
    public void get() {
        // 1.获得操作对象
        Session session = HibernateUtils.getSession();
        // get方法通过OID获得对应的记录
        // 如果支持缓存,get四次数据库,只查一次。
        Customer customer01 = session.get(Customer.class, 1L);
        System.out.println(customer01.getCustName());

        Customer customer02 = session.get(Customer.class, 1L);
        System.out.println(customer02.getCustName());

        Customer customer03 = session.get(Customer.class, 1L);
        System.out.println(customer03.getCustName());

        Customer customer04 = session.get(Customer.class, 1L);
        System.out.println(customer04.getCustName());

        session.close();
    } // 四个get方法,只会有一条SQL语句!

4.2 Hibernate清空缓存的方法

    public void get(){
        // 1.获得操作对象
        Session session = HibernateUtils.getSession();

        // get方法通过OID获得对应的记录
        // 如果支持缓存,get四次数据库,只查一次。
        Customer customer1 = session.get(Customer.class, 1L);
        System.out.println(customer1.getCustName());
        // clear()清空同一个session的所有持久化对象。缓存被清空
        session.clear();

        Customer customer2 = session.get(Customer.class, 1L);
        System.out.println(customer2.getCustName());
        // evict()清空指定的持久化对象,该对象的缓存被清空
        session.evict(customer2);

        Customer customer3 = session.get(Customer.class, 1L);
        System.out.println(customer3.getCustName());

        Customer customer4 = session.get(Customer.class, 1L);
        System.out.println(customer4.getCustName());
        // 表示关闭了session。session的所有缓存被清空
        session.close();
    } // 会产生3条SQL语句

5 快照机制

当对象变成持久态对象,和数据库表关联后,在session中会保存两份数据的副本:一份是缓存,一个是快照。

  • 缓存的作用:用于提高查询的效率。
  • 快照的作用:用于更新数据,作对比。

持久态对象支持快照,直接可以通过修改持久化对象的属性更新数据

    @Test
    public void update(){
        // 1.获得操作对象
        Session session = HibernateUtils.getSession();

        // 2.开启事务
        Transaction transaction = session.beginTransaction();

        // 3.获得一个持久态对象。
        Customer customer = session.get(Customer.class,1L);
        // 只要修改了字段,提交事务就可以直接更新到数据库
        customer.setCustSource("互联网");

        // 4.提交事务
        transaction.commit();

        // 5.关闭会话对象
        session.close();
        System.out.println(customer.getCustName()); 
    }

实现原理图:

这里写图片描述

6 绑定Session到当前线程

绑定Session到当前线程,就是在同一条线程中的session是相同的。

配置文件(hibernate.cfg.xml)绑定线程的配置:

<!-- 绑定线程 -->
<property name="hibernate.current_session_context_class">thread</property>

修改SessionFactory获得Session的代码:

public class HibernateUtils {

    // 如果一个程序中有多个连接池,有可能会导致事物处理无法提交,所以要保证程序里只有一个连接池
    public static SessionFactory sessionFactory = HibernateUtils.createSessionFactory();

    // 1.获得一个会话工厂
    private static SessionFactory createSessionFactory() {
        // (1)创建一个Configuration的对象
        Configuration config = new Configuration();

        // (2)读取默认路径下的hibernate.cfg.xml配置文件
        config.configure();

        // (3)构造一个会话工厂
        SessionFactory sessionFactory = config.buildSessionFactory();

        return sessionFactory;
    }

    // 2.获得Session
    public static Session getSession() {
        // return sessionFactory.openSession();
        return sessionFactory.getCurrentSession(); // 配置线程绑定后一定要修改获取session方式
    }
}
public class CustomerDAO {

    public void sava(Customer customer) {
        // 获得操作对象
        Session session = HibernateUtils.getSession();

        // 添加数据
        session.save(customer);
    }
}
public class CustomerDAOTest {
    @Test
    public void save() {
        // 1.获得操作对象
        Session session = HibernateUtils.getSession();

        // 2.打开事务。
        Transaction transaction = session.beginTransaction();

        // 3.操作
        Customer customer = new Customer();
        customer.setCustName("阿里");
        // 将记录保存到数据库中
        CustomerDAO dao = new CustomerDAO();
        dao.sava(customer);

        // 4.提交事务
        transaction.commit();

        // 5.如果session绑定到当前线程,session会随线程的启动而启动,会随线程的结束而结束,不能手动关闭。
        // session.close();
    }

    @Test
    public void get() {
        Session session = HibernateUtils.getSession();

        // 如果使用了线程绑定,操作必须要开启事务,包括查询
        session.beginTransaction();

        Customer customer = session.get(Customer.class, 1L);
        System.out.println(customer.getCustName());
    }
}

7 Hibernate查询对象API

7.1 标准查询

标准查询(QBC:Query By Criteria),就是完全通过Java代码查询数据库。

7.2 查询所有数据

    /**
     * 查询所有的数据(QBC)
     */
    @Test
    public void findAll() {
        // 获得session
        Session session = HibernateUtils.getSession();

        // 获得Criteria接口,必须指定查询的实体类
        Criteria criteria = session.createCriteria(Customer.class);

        // 查询所有的数据,返回一个实体类的集合
        List<Customer> customers = criteria.list();

        // 遍历集合
        for(Customer customer : customers) {
            System.out.println(customer.getCustName());
        }

        // 关闭session
        session.close();
    }

7.3 条件查询

QBC限制条件的说明:

短语含义
Restrictions.eq等于=
Restrictions.allEq使用Map,使用key/value进行多个等于的判断
Restrictions.gt大于>
Restrictions.ge大于等于>=
Restrictions.lt小于<
Restrictions.le小于等于<=
Restrictions.between对应sql的between子句
Restrictions.like对应sql的like子句
Restrictions.in对应sql的in子句
Restrictions.andand关系
Restrictions.oror关系
Restrictions.sqlRestrictionsql限定查询
Restrictions.asc()根据传入的字段进行升序排序
Restrictions.desc()根据传入的字段进行降序排序
    /**
     * 条件查询,通过客户名模糊查询
     */
    @Test
    public void findByName() {
        // 获得session
        Session session = HibernateUtils.getSession();

        // 获得Criteria接口,必须指定查询的实体类
        Criteria criteria = session.createCriteria(Customer.class);

        // 设置查询的条件
        criteria.add(Restrictions.like("custName", "%百%"));

        // 查询所有的数据,返回一个实体类的集合
        List<Customer> customers = criteria.list();

        // 遍历集合
        for(Customer customer : customers) {
            System.out.println(customer.getCustName());
        }

        // 关闭session
        session.close();
    }

7.4 分页查询

    /**
     * 分页查询
     */
    @Test
    public void findByPage() {
        // 获得session
        Session session = HibernateUtils.getSession();

        // 获得Criteria接口,必须指定查询的实体类
        Criteria criteria = session.createCriteria(Customer.class);

        // 设置分页开始的位置。编号是从0开始的。要求取出表中第三条到第五条的数据。
        criteria.setFirstResult(2);
        // 设置取出多少条数据。第三条到第五条总共3条数据
        criteria.setMaxResults(3);

        // 查询所有的数据,返回一个实体类的集合
        List<Customer> customers = criteria.list();

        // 遍历集合
        for(Customer customer : customers) {
            System.out.println(customer.getCustName());
        }

        // 关闭session
        session.close();
    }

8 HQL查询

8.1 什么是HQL

HQL(Hibernate Query Language):Hibernate查询语言。就是一套类似SQL的语言,但是HQL操作的是持久化对象。

8.2 获得所有数据

    /**
     * 通过HQL查询所有数据
     */
    @Test
    public void findAll() {
        // 获得操作对象
        Session session = HibernateUtils.getSession();

        // 获得一个查询HQL的接口Query
        Query query = session.createQuery("from Customer");

        // 通过list方法获得所有数据
        List<Customer> customers = query.list();

        // 遍历
        for(Customer customer : customers) {
            System.out.println(customer.getCustName());
        }

        // 关闭会话对象
        session.close();
    }
    /**
     * 通过HQL查询所有数据(使用select)
     */
    @Test
    public void findAll02() {
        // 获得操作对象
        Session session = HibernateUtils.getSession();

        // 获得一个查询HQL的接口Query
        // 注意:如果需要使用select返回数据,返回的是一个对象,不能使用*
        Query query = session.createQuery("select c from Customer c");

        // 通过list方法获得所有数据
        List<Customer> customers = query.list();

        // 遍历
        for(Customer customer : customers) {
            System.out.println(customer.getCustName());
        }

        // 关闭会话对象
        session.close();
    }

8.3 条件查询

    /**
     * 通过HQL查询名字带“百”的客户(问号?)
     */
    @Test
    public void findByName() {
        // 获得操作对象
        Session session = HibernateUtils.getSession();

        // 获得一个查询HQL的接口Query
        Query query = session.createQuery("from Customer c where c.custName like ?");
        // 设置参数,注意:Hibernate的索引是从0开始的
        query.setString(0, "%百%");

        // 通过list方法获得所有数据
        List<Customer> customers = query.list();

        // 遍历
        for(Customer customer : customers) {
            System.out.println(customer.getCustName());
        }

        // 关闭会话对象
        session.close();
    }
    /**
     * 通过HQL查询名字带“百”的客户(命名参数)
     */
    @Test
    public void findByName02() {
        // 获得操作对象
        Session session = HibernateUtils.getSession();

        // 获得一个查询HQL的接口Query
        Query query = session.createQuery("from Customer c where c.custName like :cname");
        // 设置参数,注意:Hibernate的索引是从0开始的
        query.setString("cname", "%百%");

        // 通过list方法获得所有数据
        List<Customer> customers = query.list();

        // 遍历
        for(Customer customer : customers) {
            System.out.println(customer.getCustName());
        }

        // 关闭会话对象
        session.close();
    }

8.4 统计记录数据

    /**
     * 通过HQL统计所有数据
     */
    @Test
    public void count() {
        // 获得操作对象
        Session session = HibernateUtils.getSession();

        // 获得一个查询HQL的接口Query
        // 注意:count(*)是统计记录数的固定写法,返回Long类型
        Query query = session.createQuery("select count(*) from Customer");

        // 如果返回的只有一个值,使用uniqueResult方法
        Long count = (Long) query.uniqueResult();

        System.out.println(count);

        // 关闭会话对象
        session.close();
    }

8.5 分页查询

    /**
     * 通过HQL分页查询数据
     */
    @Test
    public void findByPage() {
        // 获得操作对象
        Session session = HibernateUtils.getSession();

        // 获得一个查询HQL的接口Query
        Query query = session.createQuery("from Customer");

        // 要在查询之前分页。索引从0开始,所以第三条数据为2
        query.setFirstResult(2);
        // 查询3条数据
        query.setMaxResults(3);

        // 通过list方法获得分页的数据
        List<Customer> customers = query.list();

        // 遍历
        for(Customer customer : customers) {
            System.out.println(customer.getCustName());
        }

        // 关闭会话对象
        session.close();
    }

8.6 投影查询

当查询的记录不是所有字段,而是指定的字段, 如果需要使用一个实体类接收,就需要一个有参数的构造方法。我们将这种有参数的查询,称为投影查询。

    /**
     * 通过HQL查询客户表,只需要查出custName,custSource
     */
    @Test
    public void findCustomer() {
        // 获得操作对象
        Session session = HibernateUtils.getSession();

        // 获得一个查询HQL的接口Query
        Query query = session.createQuery("select custName,custSource from Customer");

        // 通过list方法获得所有数据。返回的是一个Object[]数组的集合。数组中包含两个元素:custName和custSource的值。这种方式很不方便,不推荐!
        List<Object[]> customers = query.list();

        // 遍历
        for(Object[] customer : customers) {
            System.out.println(customer[0]);
            System.out.println(customer[1]);
        }

        // 关闭会话对象
        session.close();
    }
    /**
     * 通过HQL查询客户表,只需要查出custName,custSource(通过有参数的构造函数实现)
     * 实体类中必须要有相应的有参数的构造函数
     */
    @Test
    public void findCustomer02() {
        // 获得操作对象
        Session session = HibernateUtils.getSession();

        // 获得一个查询HQL的接口Query
        Query query = session.createQuery("select new Customer(custName,custSource) from Customer");

        // 通过list方法获得所有数据。
        List<Customer> customers = query.list();

        // 遍历
        for(Customer customer : customers) {
            System.out.println(customer.getCustName());
            System.out.println(customer.getCustSource());
        }

        // 关闭会话对象
        session.close();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值