hibernate学习笔记(二)深入了解hibernate

作者:叁念


一、关于sessionFactory 与session的一些优化

  • 上篇文摘讲到:sessionFactory对象有且只有一个(原因:负责保存和使用所有配置信息,消耗内存资源非常大)
  • 因此我们就专门建立一个工具类,以静态代码块的方式创建sessionFactory,以此保证在整个程序运行期间只有一个sessionFactory对象存在,优化程序减少资源的使用
  • HibernateUtil 代码如下(之后的文章如果出现该类,读者运行我的代码会出错(即找不到该类),原因就是缺少我们的自定义类HibernateUtil
package com.sannian.utils;

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

public class HibernateUtil {

    // 会话工厂,以单例方式管理
    private static SessionFactory sessionFactory;
    // 以单例方式管理sessionFactory
    static {
        try {
            sessionFactory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
        } catch (HibernateException e) {
            e.printStackTrace();
            throw new HibernateException("初始化会话工厂失败!");
        }

    }
    // 得到一个单例的会话工厂
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
    // 获取一个新session
    public static Session openSession() {
        return sessionFactory.openSession();
    }
    //获取一个线程绑定的session
    public static Session getCurrentSession() throws HibernateException {
        return sessionFactory.getCurrentSession(); 
    }

}

二、主键生成策略

顾名思义,指的就是数据库中表的主键是由何种策略产生的

1.1 书写方式:
XXX.hbm.xml文件中
<generator class="native"></generator>
1.2 详解:
  • increment:查找主键最大的值,在最大的值得基础上加1,效率比较低。
  • assigned:由程序手动赋值,如xxx.setpid(4l)
  • identity:支持主键自动增长
  • uuid:产生随机字符串作为主键,持久化类中的主键属性必须是String类型
  • native(推荐):Hibernate会根据数据库的不同,选择合适的主键的生成策略,hilo+sequence+identity 自动三选一策略.
  • sequence:是oracle内部特有的内容,相当于uuid,所以是字符串类型
  • hilo(了解):高低位算法.主键自增.由hibernate来维护.开发时不使用.

三、hibernate中的对象状态

2.1 对象三种状态认识:
  • 瞬时状态:没有id,没有在session缓存中
  • 持久化状态 :有id,在session缓存中,持久态对象的改动会同步到数据库
  • 游离|托管状态:有id,没有在session缓存中
  • -
2.2 对象三种状态的转换:

        Session session = HibernateUtil.openSession();//使用上文的自定义工具类获取Session
        Transaction transaction = session.beginTransaction();

        Customer c = new Customer();// 瞬时态,没有id的,跟session没有关联
        c.setCust_name("腾讯");

        session.save(c);        // 持久态,有id,跟session有关联

        transaction.commit();   // 托管|游离态,有id,跟session没有关联
        session.close();

以下是相关的图解,结合以上代码不难理解三种状态是如何进行转化的:

那么处于持久态的对象有何作用呢?聪明的伙伴应该就明白了——我对这个对象操作就可以对数据库进行操作了啊

        Session session = HibernateUtil.openSession();
        Transaction transaction = session.beginTransaction();
        Customer newcustomer = session.get(Customer.class, (long)1);
        newcustomer.setCust_name("我现在是持久态了,现在我修改了名字,你去看看数据库有没有变化吧");//设置该对象属性对应的值后,无需再书写额外的代码,就会同步到数据库表当中去哦,这就是面向对象操控数据库表
        transaction.commit();
        session.close();

四、Hibernate的一级缓存,指的就是session

4.1 缓存的功能

缓存用于提高效率,这里不过多解释
hibernate中的一级缓存也是为了提高操作数据库的效率.

4.12验证Hibernate一级缓存的存在
        //HibernateUtil上文提到的工具类
        Session session = HibernateUtil.openSession();
        Customer customer = session.get(Customer.class, (long)1);

执行如下代码,控制台会出现以下信息,即查询数据库的sql语句信息

为了验证缓存是否存在,我们执行以下代码:

        //HibernateUtil上文提到的工具类
        Session session = HibernateUtil.openSession();
        Customer customer = session.get(Customer.class, (long)1);
        System.out.println(customer);  //打印结果:Customer [cust_id=1, cust_name=我现在是持久态了,现在我修改了名字,你去看看数据库有没有变化吧。。。
        customer.setCust_name("又一次改名");
        System.out.println(customer);//打印结果:Customer [cust_id=1, cust_name=又一次改名,
        Customer customer1 = session.get(Customer.class, (long)1);
        System.out.println(customer1);//打印结果:Customer [cust_id=1, cust_name=又一次改名,
        Customer customer2 = session.get(Customer.class, (long)1);
        Customer customer3 = session.get(Customer.class, (long)1);
        Customer customer4 = session.get(Customer.class, (long)1);

再次查看控制台,查看有几条sql语句?你会发现,咦?似乎还是一条,但是数据都查出来了,这是为什么呢?原因就是hibernate提供了缓存机制,同一个对象的重复查询是不需要重置执行sql语句的,hibernate帮我们减少了不必要的sql语句发送

4.3Hibernate一级缓存过程

五、hibernate中的事务

什么是事务?网上好多人都会举例子,比如银行存钱啊上面的,其实都很老套了,可能大家都明白事务是什么。
但我自己总结的事务其实就是一句话:它是用来保证数据的一致性和完整性的

5.1 事务特性
  • 原子性 Atomicity:
    指事务包含的所有操作要么全部成功,要么全部失败回滚
    因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
  • 一致性 Consistency:
    事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执
    行之后都必须处于一致性状态。
    拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几
    次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
  • 隔离性 Isolation:
    当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被
    其他事务的操作所干扰,多个并发事务之间要相互隔离。即要达到这么一种效果:对于任意两个
    并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,
    这样每个事务都感觉不到有其他事务在并发地执行。
  • 持久性 Durability:
    指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇
    到故障的情况下也不会丢失提交事务的操作。
5.2 事务并发问题(拓展)
  • 脏读

脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。
例如:用户A向用户B转账100元,对应SQL命令如下
update account set money=money+100 where name=’B’; (此时A通知B)
update account set money=money - 100 where name=’A’;
   当只执行第一条SQL时,A通知B查看账户,B发现确实钱已到账(此时即发生了脏读),而之后无论第
二条SQL是否执行,只要该事务不提交,则所有操作都将回滚,那么当B以后再次查看账户时就会发现
钱其实并没有转。

  • 不可重复读

不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
   例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取
该数据就得到了不同的结果,发送了不可重复读。
   不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是
读取了前一事务提交的数据。
   在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主。
但在另一些情况下就有可能发生问题,例如对于同一个数据A和B依次查询就可能不同,A和B就可能打起来了……

  • 幻|虚读

幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改
为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。
而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就
好像产生幻觉一样,这就是发生了幻读。
幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的
都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

5.3 事务的隔离级别

事务的隔离级别——级别越高,执行效率就越低

  • 1 Read uncommitted 读未提交 最低级别,任何情况都无法保证。
  • 2 Read committed 读已提交 可避免脏读的发生。
  • 4 Repeatable read 可重复读(mysql默认级别) 可避免脏读、不可重复读的发生。
  • 8 Serializable 串行化 可避免脏读、不可重复读、幻读的发生。

如何在hibernate中指定数据库的隔离级别?(hibernate.cfg.xml文件中配置如下:)

<property name="hibernate.connection.isolation">4</property>

在项目中如何管理事务?【重要】

  • 业务开始之前打开事务,业务执行之后提交事务. 执行过程中出现异常.回滚事务.
  • 在dao层操作数据库需要用到session对象.在service控制事务也是使用session对象完成. 我们要确保dao层和service层使用的使用同一个session对象(读者可以仔细想想为什么我要在service中进行事务的管理而不是在dao层,原因其实很简单,主要是一个业务可能不止包含一个sql脚本。。。所以在这个业务中多个dao层操作需要确保使用同一个session)
  • 在hibernate中,确保使用同一个session的问题,hibernate已经帮我们解决了. 我们开发人员只需要调用sf.getCurrentSession()方法即可获得与当前线程绑定的session对象

【注意1】: 调用getCurrentSession方法必须配合主配置(hibernate.cfg.xml文件中配置如下:)中的一段配置

            <!-- 把session对象和当前线程绑定 -->
            <property name="hibernate.current_session_context_class">thread</property>

如果你没有添加以上配置,项目会出错,如下信息:

org.hibernate.HibernateException: No CurrentSessionContext configured!

【注意2】:通过getCurrentSession方法获得的session对象.当事务提交时,session会自动关闭.不要手动调用close关闭.

sessionFactory.getCurrentSession(); 

猜你喜欢

转载自blog.csdn.net/qq_36868342/article/details/80609332