程序员技术干货程序猿阵线联盟-汇总各类技术干货

SSH框架之旅-hibernate(2)

2017-09-23  本文已影响134人  Wizey
hibernate

1.主键生成策略


1.1 主键的两种类型

注意:在开发中,建议使用代理主键。

1.2 hibernate 中主键的生成策略

2.持久化类


2.1 持久化类的编写规则

实体类经过 hibernate 操作转换成持久化类,下面还是使用实体类说明规则。

2.2 持久化类的三种状态

关于这三种状态的理解,可以结合下面的 curd 操作和一级缓存来理解。

3.curd 操作


实体类的代码

package cc.wenshixin.entity;

public class Notice {

    private int id; // 公告序号
    private String title; // 公告标题
    private String content; // 公告内容
    private String people; // 发布人
    private String date; // 发布日期

    public Notice()
    {
        
    }
    
    public Notice(String title, String content, String people, String date) {
        super();
        this.title = title;
        this.content = content;
        this.people = people;
        this.date = date;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getPeople() {
        return people;
    }

    public void setPeople(String people) {
        this.people = people;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    @Override
    public String toString() {
        return "Notice [id=" + id + ", title=" + title + ", content=" + content + ", people=" + people + ", date=" + date
                + "]";
    }

}

hibernate 自定义的工具类,方便操作 hibernate。

package cc.wenshixin.utility;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtility {
    private static Configuration cfg = null;
    private static SessionFactory sessionFactory = null;
    
    //静态代码块
    static
    {
        //加载核心配置文件
        cfg = new Configuration().configure();
        sessionFactory = cfg.buildSessionFactory();
    }
    
    /*提供方法返回sessionFactory*/
    public static SessionFactory getSessionFactory()
    {
        return sessionFactory;
    }
    
    /*提供于本地线程绑定的session方法*/
    public static Session getSession()
    {
        return sessionFactory.getCurrentSession();
    }
}

下面的操作都是使用JUnit测试工具测试的代码。

3.1 增加操作

增加操作让持久化类从瞬时态变为持久态。

@Test
    public void testSave()
    {
        //得到sessionFactory对象
        SessionFactory sessionFactory = HibernateUtility.getSessionFactory();
        //得session对象
        Session session = sessionFactory.openSession();
        //开启事务
        Transaction tx = session.beginTransaction();
        
        /*执行curd操作*/
        
        Notice notice = new Notice("实验室开放", "同学们课外可自由选择实验", "admin", "2017-10-1");
        session.save(notice);
    
        //执行事务
        tx.commit();
        //关闭session和sessionFactory
        session.close();
        sessionFactory.close();
    }

3.2 查询操作

hibernate 的删改操作都是基于查询操作实现的。

@Test
    public void testGet()
    {
        //得到sessionFactory对象
        SessionFactory sessionFactory = HibernateUtility.getSessionFactory();
        //得到session
        Session session = sessionFactory.openSession();
        //开启事务
        Transaction tx = session.beginTransaction();
        
        //执行查询操作
        Notice notice = session.get(Notice.class, 2);
        //这里要先重写toString()方法
        System.out.println(notice.toString());
        
        //提交事务
        tx.commit();
        
        //关闭session和sessionFactory
        session.close();
        sessionFactory.close();
    }

3.3 删除操作

下面展示了两种方式来删除一条记录,但建议使用第一种,先查询后删除的方式,应该避免第二种直接设置主键对应属性值的方式。

@Test
    public void testDelete()
    {
        //得到sessionFactory对象
        SessionFactory sessionFactory = HibernateUtility.getSessionFactory();
        //得到session对象
        Session session = sessionFactory.openSession();
        //开启事务
        Transaction tx = session.beginTransaction();
        
        //执行删除操作
        //第一种方法
        //Notice notice = session.get(Notice.class, 3);
        
        //第二种方法
        //Notice notice = new Notice();
        //notice.setId(2);

        session.delete(notice);
        
        //提交事务
        tx.commit();
        
        //关闭session和sessionFactory
        session.close();
        sessionFactory.close();
    }

3.4 修改操作

先得到持久态的对象,再对这个对象进行操作。

@Test
    public void testUpdate()
    {
        //得到sessionFactory对象
        SessionFactory sessionFactory = HibernateUtility.getSessionFactory();
        //得到session对象
        Session session = sessionFactory.openSession();
        //开启事务
        Transaction tx = session.beginTransaction();
        
        //执行更新操作
        Notice notice = session.get(Notice.class, 2);
        notice.setTitle("我改变了");
        session.update(notice);
        
        //执行事务
        tx.commit();
        
        //关闭session和sessionFactory
        session.close();
        sessionFactory.close();
    }

3.5 增加或更新操作

saveOrUpdate()方法是更具持久化对象的状态来做增加或者更新操作的,对象如果是瞬时态,那么执行事务就做增加操作,如果对象是托管态,那么执行事务就做更新操作,但此时要注意,更新操作要把持久化类的所有属性都设置值,否则没有设置属性值的字段为null,下面的代码就会产生这种情况,所以不推荐使用托管态修改数据表种的记录。

@Test
    public void testSaveOrUpdate()
    {
        //得到sessionFactory对象
        SessionFactory sessionFactory = HibernateUtility.getSessionFactory();
        //得到session对象
        Session session = sessionFactory.openSession();
        //开启事务
        Transaction tx = session.beginTransaction();
        
        //执行增加或更新操作
        //Notice notice = new Notice("新的公告", "公告内容", "admin", "2017-10-9");
        Notice notice = new Notice();
        notice.setId(4);
        notice.setPeople("admin");
        session.saveOrUpdate(notice);
        
        //提交事务
        tx.commit();
        
        //关闭session和sessionFactory
        session.close();
        sessionFactory.close(); 
    }

3.6 持久化类状态之间的转化

Notice notice = new Notice(); //瞬时态
notice。setId(2); //托管态

注意:由于持久化态对象的值改变,其实不用调用 update()方法或者 saveOrUpdate()方法,在执行完事务后就可以自动更新数据库的(在一级缓存中会解释自动更新),但是还是建议把方法加上,便于阅读代码。

4.一级缓存


4.1 什么是一级缓存

首先我们要明白什么是缓存,数据库本身其实就是一个文件系统,并且我们知道使用流的方式操作文件效率不高,所以我们把数据放到内存里面,这样就可以直接读取内存里面的数据,提高读取的效率。

hibernate 框架提供了很多的优化方式,一级缓冲就是优化方式之一。hibernate 还有二级缓存,但现在已经不适用了,使用 redis技术来代替了。

hibernate 的一级缓存就是指 session 缓存,session 缓冲就是一块内存空间,用来存放相互管理的 java 对象,在使用 hibernate 查询对象时,先根据对象的 OID(唯一标识符)去一级缓存中查找,如果找到就直接从一级缓存中取出使用,不用再去数据库查询了,这样就提高了查询效率,如果一级缓存中没有,就要去数据库中查询,然后把查到的数据信息放到一级缓存中。hibernate 的一级缓存的作用就是减少对数据库的访问。

4.2 一级缓存的特点

4.3 验证一级缓存的存在

    Notice notice1 = session.get(Notice.class, 1);
    System.out.println(notice1);
    Notice notice2 = session.get(Notice.class, 1);
    System.out.println(notice2);
    //比较的是对象的指向的地址是否一样
    System.out.println(notice1==notice2);

连续执行查询操作,观察控制台的输出,发现只出现了一次查询的 sql 语句,这就说明第二次的查询不是在数据库中查询得到的,而是直接从 hibernate 的一级缓存中取的,并且比较两个对象引用的地址也是true。

验证一级缓存

4.4 解释持久化类自动更新

在前面我们说持久化类改变属性值后,不需使用 update()方法就可以自动更新数据库里面的记录,我们需要指导 hibernate 一级缓存的内部结构。在执行完查询操作后,把查询到的数据放到缓冲区,并且复制一份数据到快照区,直接通过 set 方法改变持久化对象的属性值,也会改变缓冲区里面的内容,在提交事务时比较缓冲区和快照区里面的数据是否一致,如果不一致,就更新数据库中的记录,并更新快照区中的数据。快照区的作用就是确保一级缓存中的数据和数据库中的数据一致。

持久化类自动更新

5.事务操作


hibernate 是 jdbc 的轻量级封装,hibernate 的事务处理就是数据库的事务处理。

5.1 什么是事务

在数据库操作上,一项事务是由一条或多条操作数据库的 sql 语句组成的一个不可分割的工作单元。只有当事务中的所有操作都正常完成,整个事务才会被提交到数据库中。如果事务中由一项操作没有完成,则整个事务就会被回滚。事务简单理解起来就是,一组逻辑上的操作,组成这组操作的各个单元,要么一起成功,要么一起失败,具有统一性。

5.2 事务的四个特性详解

事务有很严格的定义,需要同时满足下面的四个特性,这四个特性通常称之为 ACID 特性。

5.3 事务的并发问题

在实际应用中,数据库是要被多个用户共同访问的,在多个事务同时使用相同的数据时,可能会发生并发的问题。

5.4 事务的隔离级别

为了避免上面所说的事务并发问题发生,所以在标准的 SQL 规范中,定义了四个事务隔离级别,不同的隔离级别对事务的处理是不同的。

事务的隔离级别是由数据库提供的,但并不是所有数据库都支持四种隔离级别的。在使用数据库时,隔离级别越高,安全性越高,性能越低。在实际的开发中,不会选择最高或者最低的隔离级别,使用数据库默认的即可。

5.5 hibernate 事务规范代码

在 hibernate 中,可以通过代码来操作管理事务,如通过 Transaction tx = session.beginTransaction(); 开启一个事务,持久化操作后,通过 tx.commit(); 提交事务,如果事务出现异常,要通过 tx.rollback(); 操作来撤销事务(回滚事务)。

除了在代码中对事务开启,提交和回滚操作外,还可以在 hibernate 的配置文件中对事务进行配置。在配置文件中,可以设置事务的隔离级别。其具体的配置方法是在 hibernate.cfg.xml 文件中的 property 标签中进行的。配置方法:<property name="hibernate.connection.isolation">4</property> ,并且我们在进行正真的事务管理时,需要考虑到事务的应用场景,事务的控制不应该放在 DAO 层,而应该放在 Service 层调用多个 DAO 实现一个业务逻辑的操作。其实最主要的是如何保证在 Service 中开启事务时使用的 Session 对象和 DAO 中多个操作使用的是同一个 Session 对象。

事务处理的层

下面有两种解决办法。

第二种方式时最优的方案,而且具体的实现,hibernate 已经在内部完成了,我们只需要配置一下。hibernate5 种提供了三种管理 Session 对象的方法。

在 hibernate 的配置文件中,hibernate.current_session_context_class 属性用于指定 Session 管理方式,可选值有:1. tread,Session 对象的生命周期与本地线程绑定;2. jta,Session 对象的生命周期与 JTA 事务绑定;managed,hibernate 委托程序来管理 Session 对象的生命周期。在这里我们选择 tread 值,在 hibernate.cfg.xml 中进行配置:<property name="hibernate.current_session_context_class">thread</property>,并且在 hibernate 中提供了 getCurrentSession()方法来创建一个 Session 和本地线程 TreadLocal 绑定的方法。

    /*提供于本地线程绑定的session方法*/
    public static Session getSession()
    {
        return sessionFactory.getCurrentSession();
    }

hibernate 提供的这个与本地线程绑定的 Session 可以不用关闭,当线程执行结束后,就会自动关闭了。

下面给出事务操作的规范代码写法。

代码结构如下:

try {
  开启事务
  提交事务
}catch() {
  回滚事务
}finally {
  关闭
}
    @Test
    public void testTx1()
    {
        Session session = null;
        Transaction tx = null;
        
        try{
            //得到与本地线程绑定的 Session
            session = HibernateUtility.getSession();
            //开启事务
            tx = session.beginTransaction();
            
            //添加操作
            Notice notice = new Notice("本地线程绑定", "规范操作", "admin", "2017-10-8");
            session.save(notice);
            
            //提交事务
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            //回滚事务
            tx.rollback();
        } finally {
            //不需要关闭session
        }
    }
    
    //下面的代码只是上面代码的对比
    @Test
    public void testTx2()
    {
        SessionFactory sessionFactory = null;
        Session session = null;
        Transaction tx = null;
        
        try{
            sessionFactory = HibernateUtility.getSessionFactory();
            //不是与本地线程绑定的 Session,类似于单例模式。
            session = sessionFactory.openSession();
            //开启事务
            tx = session.beginTransaction();
            
            //添加操作
            Notice notice = new Notice("本地线程绑定", "规范操作", "admin", "2017-10-8");
            session.save(notice);
            
            //提交事务
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            //回滚事务
            tx.rollback();
        } finally {
            //需要关闭session
            session.close();
            sessionFactory.close();
        }
    }

6.hibernate 查询相关API的简单介绍


在前面,我们只进行了简单的 curd 操作,对于查询操作,hibernate 还有几种不同的 API 可以选择使用,在这里先简单介绍一下,在后面还会详细叙述。

6.1 Query 对象

使用 query 对象,不需要写 sql 语句,但要写简单的 hql(hibernate query language,hibernate 的查询语言) 语句。

hql 和 sql 语句的区别:

hql语句的写法:from 实体类的名称

Query 对象的使用:

示例代码如下:

    @Test
    //查询表中所有数据
    public void testQuery1()
    {
        Session session = HibernateUtility.getSession();
        Transaction tx = session.beginTransaction();
        
        Query<Notice> query = session.createQuery("from Notice");
        List<Notice> list = query.list();
        for(Notice notice : list)
        {
            System.out.println(notice);
        }
    }
    
    @Test
    //有条件的查询
    public void testQuery2()
    {
        Session session = HibernateUtility.getSession();
        Transaction tx = session.beginTransaction();
        
        Query<Notice> query = session.createQuery("from Notice where title=?");
        query.setString(0, "实验室开放");
        List<Notice> list = query.list();
        for(Notice notice : list)
        {
            System.out.println(notice);
        }
    }

6.2 Criteria 对象

使用 criteria 对象,不需要写语句,直接调用方法来实现。

criteria 对象的使用:

示例代码如下:

    @Test
    //查询表中所有数据
    public void testCriteria1()
    {
        Session session = HibernateUtility.getSession();
        Transaction tx = session.beginTransaction();
        
        Criteria criteria = session.createCriteria(Notice.class);
        List<Notice> list = criteria.list();
        for(Notice notice : list)
        {
            System.out.println(notice);
        }   
    }
    
    @Test
    //有条件的查询
    public void testCriterial2()
    {
        Session session = HibernateUtility.getSession();
        Transaction tx = session.beginTransaction();
        
        Criteria criteria = session.createCriteria(Notice.class);
        criteria.add(Restrictions.eq("title", "实验室开放"));
        List<Notice> list = criteria.list();
        for(Notice notice : list)
        {
            System.out.println(notice);
        }
    }

6.3 SQLQuery 对象

从名字就可以看出是和 sql 有关的,直接写 sql 语句,底层 hibernate 调用的是 sql 语句实现的。

SQLQuery 对象

示例代码如下:

@Test
    public void testSQLQuery()
    {
        Session session = HibernateUtility.getSession();
        Transaction tx = session.beginTransaction();
        
        SQLQuery sqlQuery = session.createSQLQuery("SELECT * FROM notice_content");
        List<Object[]> list = sqlQuery.list();
        for(Object[] objects : list)
        {
            System.out.println(Arrays.toString(objects));
        }
    }
上一篇 下一篇

猜你喜欢

热点阅读