Hibernate中事务小案例

Posted whcwkw1314

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hibernate中事务小案例相关的知识,希望对你有一定的参考价值。

理论知识:

什么是事务?

指作为单个逻辑工作单位执行的一系列操作,要么完全的执行,要么完全不执行。事务处理可以确保非事务性单元内的所有操作都完全完成,否则永久不会更新面向数据的资源。通过将一组操作组合为一个要么成功要么失败的单元,可以简化错误恢复并使应用程序更加可靠。

并发控制概述
事务是并发控制的基本单位,保证事务ACID的特性是事务处理的重要任务,而并发操作有可能会破坏其ACID特性。
DBMS并发控制机制的责任:
对并发操作进行正确调度,保证事务的隔离性更一般,确保数据库的一致性。

事务并发的问题:

(1)脏读:读到另一个事务未提交前的数据。

(2)不可重复读:同一条记录2次读取到的数据不一样。

(3)幻读(虚读):同一个表2次查询到的数据不一样。

事务的隔离级别:

(1)读,未提交(read uncommited):什么都没解决,性能最好,一般不用。

(2)读已提交数据(read commited):Oracle默认,解决了脏读。 

(3)可重复读(repeatable read):mysql默认,解决了脏读,不可重复读。

(4)串行化(serializable序列化),容易产生死锁。解决所有上述并发问题。  

 

#查询事务隔离级别
SELECT @@tx_isolation;
#修改隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL read UNCOMMITTED ;

 

案例演示:

A向B转账,A金额减少,B金额增加。 若转账中出现问题,则A和B的金额都不变。

技术分享图片
public class User {

    private Integer uid;
   // private String uid;
    private String  name;
    private String  password;
    private Double  balance; //金额

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Double getBalance() {
        return balance;
    }

    public void setBalance(Double balance) {
        this.balance = balance;
    }

    @Override
    public String toString() {
        return "User{" + "uid=" + uid + ", name=‘" + name + ‘\\‘‘ + ", password=‘" + password + ‘\\‘‘ + ", balance=" + balance + ‘}‘;
    }
}
实体类
技术分享图片
 1 <!DOCTYPE hibernate-mapping PUBLIC
 2         "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 3         "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 4 <hibernate-mapping>
 5     <!--
 6     name:User的相对路径
 7     table:数据库表的名称
 8     -->
 9     <class name="a_helloworld.entity.User" table="t_user">
10         <!--
11         一个类标签里面必须有一个id.
12         hibernate要求实体类有一个属性是唯一值。
13         column:数据库列名  可以省略,省略的话默认和name的值一样
14         -->
15         <id name="uid" column="uid">
16             <!--
17             主键的生成策略
18             native:主键自动增长
19             uuid:自动生成一个长度为32的字符串
20             -->
21             <generator class="native"></generator>
22         </id>
23         <!--
24         其他普通属性
25         type:一般不去设置 框架会自动对应
26         not-null:非空
27         -->
28         <property name="name"></property>
29         <property name="password"></property>
30         <property name="balance"></property>
31     </class>
32 </hibernate-mapping>
实体类映射文件
技术分享图片
 1 <!DOCTYPE hibernate-configuration PUBLIC
 2         "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
 3         "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
 4 <hibernate-configuration>
 5     <session-factory>
 6         <!--1 数据库信息-->
 7         <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
 8         <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate001?characterEncording=UTF-8</property>
 9         <property name="hibernate.connection.username">root</property>
10         <property name="hibernate.connection.password">123456</property>
11 
12         <!--2 配置hibernate信息  可选部分-->
13         <!--在控制台输出底层sql语句
14         项目在开发阶段设置为true  项目发布的时候改成false
15         因为日志信息会写在文件中(io操作),浪费额外的资源
16         -->
17         <property name="show_sql">true</property>
18         <!--对输出的sql语句进行格式化-->
19         <property name="format_sql">true</property>
20 
21         <!--hibernate帮助我们创建表的策略需要配置
22           create:每次执行都重新创建表,数据会丢失
23           update:如果已经有表,更新。如果没有就创建(一般使用)
24           create-drop:每次执行都重新创建,数据丢失(开发的时候使用)
25           validate:校验。每次运行校验数据库表是否正确(不会更新或创建表)
26         -->
27         <property name="hbm2ddl.auto">update</property>
28 
29         <!--配置数据库方言
30         告诉hibernate你用的是什么方言
31         Mysql:limit
32         oracle:rownum
33         让hibernate识别不同数据库的自己特有的语句
34         -->
35         <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
36 
37         <!--设置事务隔离级别-->
38         <property name="hibernate.connection.isolation">4</property>
39 
40         <!--当前session绑定本地线程-->
41         <property name="hibernate.current_session_context_class">thread</property>
42 
43         <!--3 把映射文件加载-->
44         <mapping resource="b_Query/entity/User.hbm.xml"></mapping>
45     </session-factory>
46 </hibernate-configuration>
hibernate核心配置文件
技术分享图片
public interface UserService {
    public void transfer(Integer from,Integer to,Double money);
}
service层接口
技术分享图片
 1 public class UserServiceImpl implements UserService {
 2     private UserDAO userDAO = new UserDAOImpl();
 3     @Override
 4     public void transfer(Integer from, Integer to, Double money) {
 5 
 6         Session session = HibernateUtils.getCurrentSession();
 7 
 8       /*  //验证 session是否相同
 9         Session session1 = HibernateUtils.getCurrentSession();
10         System.out.println(session == session1);*/
11 
12         Transaction transaction = session.beginTransaction();
13 
14         try {
15             //from  减少金额
16             userDAO.reduce(from,money);
17 
18             int i =5/0;
19             //to 增加金额
20             userDAO.increase(to,money);
21             transaction.commit();
22         }catch (Exception e){
23             e.printStackTrace();
24             transaction.rollback();
25         }
26 
27     }
28 }
service层接口实现
技术分享图片
public interface UserDAO {
    public void increase(Integer id,Double money);
    public void reduce(Integer id,Double money);
}
DAO层接口
技术分享图片
public class UserDAOImpl implements UserDAO {
    @Override
    public void increase(Integer id, Double money) {
        Session session = HibernateUtils.getCurrentSession();

        User user = session.get(User.class, id);
        user.setBalance(user.getBalance()+money);
        session.update(user);

    }

    @Override
    public void reduce(Integer id, Double money) {
        Session session = HibernateUtils.getCurrentSession();

        User user = session.get(User.class, id);
        user.setBalance(user.getBalance()-money);
        session.update(user);

    }
}
DAO层接口实现
技术分享图片
public class HibernateUtils {
    private static Configuration configure;
    private static SessionFactory sessionFactory;
    static {
        configure = new Configuration().configure();
        sessionFactory = configure.buildSessionFactory();
    }

    public static SessionFactory getsessionFactory(){
        return sessionFactory;
    }

    //获取session
    public static Session getSession(){
        return sessionFactory.openSession();
    }

    //拿到和当前线程绑定的session
    public static Session getCurrentSession(){
        Session currentSession = sessionFactory.getCurrentSession();
        return currentSession;
    }
}
hibernate工具类
技术分享图片
public class BalanceDemo {
    @Test
    public void transfer(){
        UserService userService = new UserServiceImpl();
        userService.transfer(2,1,5.0);
    }
}
测试类

在业务层如果转账过程中出现了问题,事务回滚,可以保证数据库的一致性。

转账前的记录:

技术分享图片

处理业务时产生异常:

技术分享图片

这时后刷新记录发现记录中的balance字段(表示金额)数值不变。业务逻辑实现类中,转账后捕获到了异常事务进行了回滚。

若没有产生异常,则两条记录的金额都发生变化。

 

注意:

1.在hibernate.cfg.xml核心配置文件中可以配置事务隔离级别  4表示repeatable read

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

2.在DAO层操作数据库需要用到session,在service层获取事务也需要session,我们要确保2个session为同一个对象

解决:将session与本地线程绑定。

需要在hibernate.cfg.xml核心配置文件中配置

<!--当前session绑定本地线程-->
<property name="hibernate.current_session_context_class">thread</property>

 





以上是关于Hibernate中事务小案例的主要内容,如果未能解决你的问题,请参考以下文章

hibernate添加数据入门小案例

Hibernate 入门小案例

HIBERNATE 入门小案例

使用反射在外部JAR / CLASS上调用包含Hibernate事务的方法(Java EE)

理解片段事务期间片段的生命周期方法调用

hibernate5小案例讲解