Spring Transaction 事务模拟

事务,是描述一组操作的抽象,比如对数据库的一组操作,要么全部成功,要么全部失败。

事务有四个特性:

Atomicity(原子性),Consistency(一致性),Isolation(隔离性),Durability(持久性)

Spring对事务的支持很强大,但是从本质上来说,事务是否生效取决于数据库底层是否支持(MySQL的MyISAM引擎不支持事务),

同时,一个事务的多个操作需要在同一个connection上。下面手写一个Demo分析Spring事务底层实现。

  • 工程结构

  • connection部分

Spring在配置多个数据源DataSource时,需要通过DataSource来得到操作数据库的管道Connection。

import java.sql.Connection;

import java.sql.SQLException;

import java.util.HashMap;

import java.util.Map;

import javax.sql.DataSource;

public class ConnectionHolder {

//map存放的数据源与连接管道的映射

private Map<DataSource, Connection> map=  new HashMap<DataSource, Connection>();

//根据dataSource获取Connection

public Connection getConnectionByDataSource(DataSource datasource) throws SQLException{

Connection connection = map.get(datasource);

if(connection == null || connection.isClosed()){

connection = datasource.getConnection();

map.put(datasource, connection);

}

return connection;

}

}

ConnectionHolder在多线程的情况下,map是线程不安全的,可能会存在链接在使用的同时,另一线程在关闭的情况。

另一种想法是使用线程安全的ConcurrentHashMap。但是我们真正要做的是保证在一个线程下,一个事务的多个操作拿到的是一个Connection,使用ConcurrentHashMap并不能保证

这个问题。

故考虑使用ThreadLocal类型,ThreadLocal类型是线程共享变量,属线程内的全局变量。且ThreadLocal在多个线程使用的情况下,会为每个线程创建一个副本,线程对ThreadLocal变量的操作,只会影响本线程内的副本。

import java.sql.Connection;

import java.sql.SQLException;

import javax.sql.DataSource;

public class SingleThreadConnectionHolder {

//ThreadLocal封装Map为线程共享变量

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

private static ConnectionHolder getConnectionHolder() {

ConnectionHolder connectionHolder = threadLocal.get();

if(connectionHolder == null) {

connectionHolder = new ConnectionHolder();

threadLocal.set(connectionHolder);

}

return connectionHolder;

}

public static Connection getConnection(DataSource dataSource) throws SQLException{

return getConnectionHolder().getConnectionByDataSource(dataSource);

}

}

通过ThreadLocal封装后,即可保证一个线程内一个DataSource获取到的Connection是唯一的。

  • manage部分

新建TransactionManage,用于事务控制

import java.sql.Connection;

import java.sql.SQLException;

import javax.sql.DataSource;

import resource.connection.SingleThreadConnectionHolder;

public class TransactionManager {

private DataSource dataSource;

public TransactionManager(DataSource dataSource) {

// TODO Auto-generated constructor stub

this.dataSource = dataSource;

}

private Connection getConnection() throws SQLException{

return SingleThreadConnectionHolder.getConnection(dataSource);

}

//开启事务

public void start() throws SQLException{

Connection connection = getConnection();

connection.setAutoCommit(false);

}

//回滚事务

public void rollback() {

Connection connection = null;

try {

connection = getConnection();

connection.rollback();

}catch(SQLException e) {

e.printStackTrace();

}

}

//关闭事务

public void close() throws SQLException{

Connection connection = getConnection();

connection.commit();

connection.setAutoCommit(false);

connection.close();

}

}

  • DAO层 ,用于操作数据库链接,包括UserAcountDao,UserOrderDao

用户购买操作Dao

import java.sql.Connection;

import java.sql.SQLException;

import javax.sql.DataSource;

import resource.connection.SingleThreadConnectionHolder;

public class UserAcountDao {

private DataSource dataSource;

public UserAcountDao(DataSource dataSource){

this.dataSource = dataSource;

}

public void buy() throws SQLException {

Connection connection = SingleThreadConnectionHolder.getConnection(dataSource);

//进行业务操作

//。。。。

System.out.println("当前用户购买线程:" + Thread.currentThread().getName() +

",使用管道 (hashcode):" + connection.hashCode());

}

}

用户订单操作Dao

import java.sql.Connection;

import java.sql.SQLException;

import javax.sql.DataSource;

import resource.connection.SingleThreadConnectionHolder;

public class UserOrderDao {

private DataSource dataSource;

public UserOrderDao(DataSource dataSource){

this.dataSource = dataSource;

}

public void order() throws SQLException {

Connection connection = SingleThreadConnectionHolder.getConnection(dataSource);

//进行业务操作

//。。。。

System.out.println("当前用户订单线程:" + Thread.currentThread().getName() +

",使用管理(hashcode):" + connection.hashCode());

}

}

  • service层,用户进行业务操作

import javax.sql.DataSource;

import resource.dao.UserAcountDao;

import resource.dao.UserOrderDao;

import resource.manage.TransactionManager;

public class UserService {

private UserAcountDao userAcountDao;

private UserOrderDao userOrderDao;

private TransactionManager transactionManager;

public UserService(DataSource dataSource) {

userAcountDao = new UserAcountDao(dataSource);

userOrderDao = new UserOrderDao(dataSource);

transactionManager = new TransactionManager(dataSource);

}

public void action() {

try {

//进行购买,下单操作

transactionManager.start();

userAcountDao.buy();

userOrderDao.order();

transactionManager.close();

}catch(Exception e) {

//发生异常,则事务回滚

e.printStackTrace();

transactionManager.rollback();

}

}

}

  • Test测试程序

import org.apache.commons.dbcp2.BasicDataSource;

import resource.service.UserService;

public class TestTransaction {

public static final String jdbcDriver = "com.mysql.jdbc.Driver";

public static final String jdbcURL = "jdbc:mysql://localhost:3306/my_web?useSSL=false";

public static final String jdbcUsername = "******";//mysql用户名

public static final String jdbcPassword = "******";//密码

public static void main(String[] args) {

BasicDataSource basicDataSource = new BasicDataSource();

basicDataSource.setDriverClassName(jdbcDriver);

basicDataSource.setUsername(jdbcUsername);

basicDataSource.setPassword(jdbcPassword);

basicDataSource.setUrl(jdbcURL);

final UserService userService = new UserService(basicDataSource);

//模拟用户并发请求

for(int i = 0; i < 10; i++) {

new Thread((Runnable)()-> {userService.action();}).start();

}

try {

Thread.sleep(10000);

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}

通过测试程序,并发的模拟用户请求,总计10个线程,每个线程均会调用DAO层进行数据操作。操作结果如下

当前用户购买线程:Thread-10,使用管道 (hashcode):1671328438

当前用户订单线程:Thread-10,使用管道(hashcode):1671328438

当前用户购买线程:Thread-5,使用管道 (hashcode):1172249069

当前用户订单线程:Thread-5,使用管道(hashcode):1172249069

当前用户购买线程:Thread-1,使用管道 (hashcode):863698743

当前用户订单线程:Thread-1,使用管道(hashcode):863698743

当前用户购买线程:Thread-7,使用管道 (hashcode):1206124853

当前用户订单线程:Thread-7,使用管道(hashcode):1206124853

当前用户购买线程:Thread-2,使用管道 (hashcode):1861628772

当前用户购买线程:Thread-6,使用管道 (hashcode):1394656535

当前用户订单线程:Thread-2,使用管道(hashcode):1861628772

当前用户订单线程:Thread-6,使用管道(hashcode):1394656535

当前用户购买线程:Thread-8,使用管道 (hashcode):1883267477

当前用户订单线程:Thread-8,使用管道(hashcode):1883267477

当前用户购买线程:Thread-9,使用管道 (hashcode):1475410105

当前用户订单线程:Thread-9,使用管道(hashcode):1475410105

当前用户购买线程:Thread-3,使用管道 (hashcode):1472283137

当前用户订单线程:Thread-3,使用管道(hashcode):1472283137

当前用户购买线程:Thread-4,使用管道 (hashcode):678585609

当前用户订单线程:Thread-4,使用管道(hashcode):678585609

根据结果可以看出,同一线程获取的connection管道是一样的,若存在多个数据源,则同一线程的同一数据源所获取的管道是一致的。

Spring Transaction 事务模拟

原文地址:https://www.cnblogs.com/ytcs8121/p/11331941.html

时间: 2024-10-14 14:47:34

Spring Transaction 事务模拟的相关文章

Spring transaction事务之roll back回滚

转自:http://blog.csdn.net/lovejavaydj/article/details/7635848 试验方法: 写一个单元测试,调用一个service层方法(发生对数据库进行写操作的方法--insert.update.delete)即可. 试验过程: 定义一个service方法如下: public SMSTiming createSMSTiming(SMSTiming smsTiming){ SMSTiming s= this.getSmsTimingDAO().create

Spring Transaction + MyBatis SqlSession事务管理机制研究学习

线上的系统中,使用的是Spring+Mybatis+Mysql搭建的框架,由于客户需要,最近一直在对性能提升部分进行考虑,主要是涉及Mysql的一些重要参数的配置学习,以及Spring事务管理机制的学习,因为通过观察服务器日志,发现在这两部分的时候耗时比较严重,特别是进行mysql事务提交的时候,项目源码中使用了Spring的声明式事务,即通过@Transactional注解来控制事务的开启与提交,这两天看了一些关于Spring Transaction事务的一些文章,也debug了源码,总算有点

spring transaction源码分析--事务架构

1. 引言  事务特性 事务是并发控制的单元,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位.通过事务将逻辑相关的一组操作绑定在一起,以便服务器 保持数据的完整性.事务通常是以begin transaction开始,以commit或rollback结束.Commint表示提交,即提交事务的所有操作.具体地说就是将事务中所有对数据的更新写回到磁盘上的物理数据库中去,事务正常结束.Rollback表示回滚,即在事务运行的过程中发生了某种故障,事务不能继续进行,系统将

Spring框架事务支持模型的优势

全局事务 全局事务支持对多个事务性资源的操作,通常是关系型数据库和消息队列.应用服务器通过JTA管理全局性事务,API非常烦琐.UserTransaction通常需要从JNDI获取,意味着需要与JNDI绑定在一起,且JTA一般只在应用服务器可用,降低了应用代码的可重用性. 本地事务 本地事务面向具体的资源,例如与JDBC连接关联的事务.本地事务易于使用,但不能跨多个事务性资源.使用JDBC管理事务的代码不能在全局JTA事务中运行,因此不能确保跨多个资源的正确性.且本地事务侵入了编程模型. Spr

Spring 并发事务的探究

前言 在目前的软件架构中,不仅存在单独的数据库操作(一条SQL以内,还存在逻辑性的一组操作.而互联网软件系统最少不了的就是对共享资源的操作.比如热闹的集市,抢购的人群对同见商品的抢购由一位售货员来处理,这样虽然能保证买卖的正确进行,但是牺牲了效率,饱和的销售过程并不能高效处理所有的购买请求,最后打烊了部分顾客悻悻而归.而电脑的发明是让人类解放于这种低效的工作中,提高销售性能,比如抢购系统,秒杀系统等.而这种销售过程必然包含了检查库存.秒杀排队.校对商品信息.下单等一系列的组合操作,而一个交易过程

Spring Transaction

spring 推荐使用 public void save(Good good) {         this.sessionFactory.getCurrentSession().save(good);     } 以上编写dao代码,推荐使用hibernateTemplate,避免框架的入侵.这种方式如果不配置事务管理 <!--<aop:config>-->         <!--<aop:pointcut id="goodServiceMethods&q

Spring的事务机制

JAVA EE传统事务机制 通常有两种事务策略:全局事务和局部事务.全局事务可以跨多个事务性资源(即数据源,典型的是数据库和消息队列),通常都需要J2EE应用服务器的管理,其底层需要服务器的JTA支持.而局部事务则与底层采用的持久化技术有关,如果底层直接使用JDBC,需要用Connection对象来操事务.如果采用Hibernate持久化技术,则需要使用session对象来操作事务. 通常的,使用JTA事务,JDBC事务及Hibernate事务的编程流程大致如下, 上图也可以看出,采用传统事务编

Spring的事务管理

事务 事务:是逻辑上一组操作,要么全都成功,要么全都失败. 事务特性(ACID) 原子性:事务不可分割 一致性:事务执行的前后,数据完整性保持一致 隔离性:一个事务执行的时候,不应该受到其他事务的打扰 持久性:一旦结束,数据就永久的保存到数据库 如果不考虑隔离性 脏读:一个事务读到另一个事务未提交数据 不可重复读:一个事务读到另一个事务已经提交数据(update)导致一个事务多次查询结果不一致 虚读:一个事务读到另一个事务已经提交数据(insert)导致一个事务多次查询结果不一致 事务的隔离级别

Spring中事务管理

1.什么是事务? 事务是逻辑上的一组操作,这组操作要么全部成功,要么全部失败 2.事务具有四大特性ACID 1)原子性(Atomicity):即不可分割性,事务要么全部被执行,要么就全部不被执行.如果事务的所有子事务全部提交成功,则所有的数据库操作被提交,数据库状态发生转换:如果有子事务失败,则其他子事务的数据库操作被回滚,即数据库回到事务执行前的状态,不会发生状态转换. 2)一致性(Consistency):事务的执行使得数据库从一种正确状态转换成另一种正确状态.例如对于银行转账事务,不管事务