Spring中使用atomikos+druid实现经典分布式事务的方法

经典分布式事务,是相对互联网中的柔性分布式事务而言,其特性为ACID原则,包括原子性(Atomictiy)、一致性(Consistency)、隔离性(Isolation)、持久性(Durabilit):

  • 原子性:事务是一个包含一系列操作的原子操作。事务的原子性确保这些操作全部完成或者全部失败。
  • 一致性:一旦事务的所有操作结束,事务就被提交。然后你的数据和资源将处于遵循业务规则的一直状态。
  • 隔离性:因为同时在相同数据集上可能有许多事务处理,每个事务应该与其他事务隔离,避免数据破坏。
  • 持久性:一旦事务完成,他的结果应该能够承受任何系统错误(想象一下在事务提交过程中机器的电源被切断的情况)。通常,事务的结果被写入持续性存储。

XA是啥?

XA是由X/Open组织提出的分布式事务的架构(或者叫协议)。XA架构主要定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接口。XA接口是双向的系统接口,在事务管理器(Transaction Manager)以及一个或多个资源管理器(Resource Manager)之间形成通信桥梁。也就是说,在基于XA的一个事务中,我们可以针对多个资源进行事务管理,例如一个系统访问多个数据库,或即访问数据库、又访问像消息中间件这样的资源。这样我们就能够实现在多个数据库和消息中间件直接实现全部提交、或全部取消的事务。XA规范不是java的规范,而是一种通用的规范,

目前各种数据库、以及很多消息中间件都支持XA规范。

JTA是满足XA规范的、用于Java开发的规范。所以,当我们说,使用JTA实现分布式事务的时候,其实就是说,使用JTA规范,实现系统内多个数据库、消息中间件等资源的事务。

JTA(Java Transaction API),是J2EE的编程接口规范,它是XA协议的JAVA实现。它主要定义了:

  • 一个事务管理器的接口javax.transaction.TransactionManager,定义了有关事务的开始、提交、撤回等>操作。
  • 一个满足XA规范的资源定义接口javax.transaction.xa.XAResource,一种资源如果要支持JTA事务,就需要让它的资源实现该XAResource接口,并实现该接口定义的两阶段提交相关的接口。如果我们有一个应用,它使用JTA接口实现事务,应用在运行的时候,就需要一个实现JTA的容器,一般情况下,这是一个J2EE容器,像JBoss,Websphere等应用服务器。但是,也有一些独立的框架实现了JTA,例如 Atomikos, bitronix 都提供了jar包方式的JTA实现框架。这样我们就能够在Tomcat或者Jetty之类的服务器上运行使用JTA实现事务的应用系统。在上面的本地事务和外部事务的区别中说到,JTA事务是外部事务,可以用来实现对多个资源的事务性。它正是通过每个资源实现的XAResource来进行两阶段提交的控制。感兴趣的同学可以看看这个接口的方法,除了commit, rollback等方法以外,还有end(), forget(), isSameRM(), prepare()等等。光从这些接口就能够想象JTA在实现两阶段事务的复杂性。

本篇以Spring MVC+Maven+Atomikos+Druid+MyBatis演示分布式事务的实现。

Mave 的pom.xml

<properties>
 <jdk.version>1.8</jdk.version>
 <!-- 注mysql的版本和druid的版本一定要搭配,否则会有问题,目前这两个版本是搭配好的 -->
 <mysql.version>8.0.11</mysql.version>
 <druid.version>1.1.17</druid.version>
 <spring.version>5.1.8.RELEASE</spring.version>
 <cglib.version>3.2.12</cglib.version>
 <atomikos.version>5.0.0</atomikos.version>
 <aspectjweaver.version>1.9.4</aspectjweaver.version>
 <aspectjrt.version>1.5.4</aspectjrt.version>
 <jta.version>1.1</jta.version>
 <mybatise.version>3.2.0</mybatise.version>
 <mybatis.spring>1.2.0</mybatis.spring>
 <log4j.version>1.2.17</log4j.version>
 <junit.version>4.12</junit.version>
 <cglib.version>3.2.4</cglib.version>
</properties>
<dependencies>
 <dependency>
 <groupId>org.mybatis</groupId>
 <artifactId>mybatis</artifactId>
 <version>${mybatise.version}</version>
 </dependency>
 <dependency>
 <groupId>org.mybatis</groupId>
 <artifactId>mybatis-spring</artifactId>
 <version>${mybatis.spring}</version>
 </dependency>
 <dependency>
 <groupId>com.atomikos</groupId>
 <artifactId>atomikos-util</artifactId>
 <version>${atomikos.version}</version>
 </dependency>
 <dependency>
 <groupId>com.atomikos</groupId>
 <artifactId>transactions</artifactId>
 <version>${atomikos.version}</version>
 </dependency>
 <dependency>
 <groupId>com.atomikos</groupId>
 <artifactId>transactions-jta</artifactId>
 <version>${atomikos.version}</version>
 </dependency>
 <dependency>
 <groupId>com.atomikos</groupId>
 <artifactId>transactions-jdbc</artifactId>
 <version>${atomikos.version}</version>
 </dependency>
 <dependency>
 <groupId>com.atomikos</groupId>
 <artifactId>transactions-api</artifactId>
 <version>${atomikos.version}</version>
 </dependency>
 <dependency>
 <groupId>javax.transaction</groupId>
 <artifactId>jta</artifactId>
 <version>${jta.version}</version>
 </dependency>
 <dependency>
 <groupId>cglib</groupId>
 <artifactId>cglib-nodep</artifactId>
 <version>${cglib.version}</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-test</artifactId>
 <version>${spring.version}</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-web</artifactId>
 <version>${spring.version}</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-tx</artifactId>
 <version>${spring.version}</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-beans</artifactId>
 <version>${spring.version}</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-jdbc</artifactId>
 <version>${spring.version}</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-webmvc</artifactId>
 <version>${spring.version}</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-orm</artifactId>
 <version>${spring.version}</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-context-support</artifactId>
 <version>${spring.version}</version>
 </dependency>
 <!-- Spring Aop依赖jar -->
 <dependency>
 <groupId>org.aspectj</groupId>
 <artifactId>aspectjweaver</artifactId>
 <version>${aspectjweaver.version}</version>
 </dependency>

 <dependency>
 <groupId>aspectj</groupId>
 <artifactId>aspectjrt</artifactId>
 <version>${aspectjrt.version}</version>
 </dependency>
 <!-- CGLIB -->
 <dependency>
 <groupId>cglib</groupId>
 <artifactId>cglib</artifactId>
 <version>${cglib.version}</version>
 </dependency>
 <dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
 <version>${mysql.version}</version>
 </dependency>
 <dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>druid</artifactId>
 <version>${druid.version}</version>
 </dependency>
 <dependency>
 <groupId>junit</groupId>
 <artifactId>junit</artifactId>
 <version>${junit.version}</version>
 <scopte>test</scope>
 </dependency>
</dependencies>

spring-application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:mvc="http://www.springframework.org/schema/mvc"
  xmlns:task="http://www.springframework.org/schema/task"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  http://www.springframework.org/schema/mvc
  http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-3.1.xsd
  http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">

   <!-- 1. 数据源配置 -->
  <context:property-placeholder location="classpath*:*.properties" file-encoding="utf8" />
 <bean id="utf8" class="java.lang.String">
 <constructor-arg value="utf-8"></constructor-arg>
 </bean>
  <!-- 开启异步任务(同时开启定时器注解扫描) -->
  <task:annotation-driven />
  <!-- 使用@AspectJ风格的切面声明 -->
  <!-- <aop:aspectj-autoproxy/> -->
  <!-- 使用Annotation自动注册Bean -->
  <!-- 在主容器中不扫描@Controller注解,在SpringMvc中只扫描@Controller注解 -->
  <context:component-scan base-package="net.xiake6"><!-- base-package 如果多个,用“,”分隔 -->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
  </context:component-scan>
  <!-- 引入Mybatis配置 -->
  <!-- <import resource="spring-mybatis-atomikos-druid.xml"/> -->
  <import resource="spring-mybatis-atomikos-druid.xml"/>
</beans>

spring-mybatis-atomikos-druid.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:jee="http://www.springframework.org/schema/jee"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/tx
  http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
  http://www.springframework.org/schema/jee
  http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
  http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-3.0.xsd"
  default-lazy-init="true">  

 <context:annotation-config />
 <!-- 使用Druid使为XA数据源 -->
  <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close" abstract="true">
    <property name="xaDataSourceClassName" value="com.alibaba.druid.pool.xa.DruidXADataSource"/>
    <property name="xaProperties">
      <props>
       <prop key="driverClassName">${jdbc.driverClassName}</prop>
       <!-- 配置初始化大小、最小、最大 -->
  <prop key="initialSize">10</prop>
  <prop key="minIdle">3</prop>
  <prop key="maxActive">100</prop>
  <!-- 配置获取连接等待超时的时间 -->
  <prop key="maxWait">60000</prop>
  <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
  <prop key="timeBetweenEvictionRunsMillis">60000</prop>
  <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
  <prop key="minEvictableIdleTimeMillis">300000</prop>
  <prop key="validationQuery">SELECT 'x'</prop>
  <prop key="testWhileIdle">true</prop>
  <prop key="testOnBorrow">false</prop>
  <prop key="testOnReturn">false</prop>
  <!-- 配置监控统计拦截的filters -->
  <prop key="filters">stat</prop>
      </props>
    </property>
  </bean>
  <!-- 配置数据源一 -->
  <bean id="dataSourceOne" parent="abstractXADataSource">
    <property name="uniqueResourceName">
      <value>dataSourceOne</value>
    </property>
    <property name="xaProperties">
      <props>
        <prop key="url">${jdbc.url}</prop>
        <prop key="username">${jdbc.username}</prop>
        <prop key="password">${jdbc.password}</prop>
      </props>
    </property>
  </bean>
  <!--配置数据源二-->
  <bean id="dataSourceTwo" parent="abstractXADataSource">
    <property name="uniqueResourceName">
      <value>dataSourceTwo</value>
    </property>
    <property name="xaProperties">
      <props>
        <prop key="url">${jdbc.two.url}</prop>
        <prop key="username">${jdbc.two.username}</prop>
        <prop key="password">${jdbc.two.password}</prop>
      </props>
    </property>
  </bean>

  <!--mybatis的相关配置-->
  <bean id="sqlSessionFactoryOne" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSourceOne"/>
    <property name="mapperLocations" value="classpath*:mapping/ds1/*.xml"/>
  </bean>

  <bean id="sqlSessionFactoryTwo" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSourceTwo"/>
    <property name="mapperLocations" value="classpath*:mapping/ds2/*.xml"/>
  </bean>

  <!--配置mybatis映射文件自动扫描-->
  <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="net.xiake6.dao.ds1"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryOne"/>
  </bean>

  <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="net.xiake6.dao.ds2"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryTwo"/>
  </bean>

  <!--配置分布式事务-->
  <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
    <property name="forceShutdown" value="false"/>
  </bean>
  <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
    <property name="transactionTimeout" value="3000"/>
  </bean>
  <!--JTA事务管理器-->
  <bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="transactionManager">
      <ref bean="atomikosTransactionManager"/>
    </property>
    <property name="userTransaction">
      <ref bean="atomikosUserTransaction"/>
    </property>
    <property name="allowCustomIsolationLevels" value="true"/>
  </bean>

  <aop:config proxy-target-class="true">
    <aop:advisor pointcut="execution(* *net.xiake6.service..*(..))" advice-ref="txAdvice" />
  </aop:config>
  <tx:advice id="txAdvice" transaction-manager="jtaTransactionManager">
    <tx:attributes>
      <tx:method name="insert*" propagation="REQUIRED" read-only="false" rollback-for="*Exception"/>
      <tx:method name="add*" propagation="REQUIRED" read-only="false" rollback-for="*Exception" />
      <tx:method name="save*" propagation="REQUIRED" read-only="false" rollback-for="*Exception" />
      <tx:method name="delete*" propagation="REQUIRED" read-only="false" rollback-for="*Exception" />
      <tx:method name="del*" propagation="REQUIRED" read-only="false" rollback-for="*Exception" />
      <tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="*Exception" />
      <tx:method name="select*" propagation="REQUIRED" read-only="true" />
      <tx:method name="query" propagation="REQUIRED" read-only="true" />
    </tx:attributes>
  </tx:advice>

  <!-- 配置事务管理 -->
  <tx:annotation-driven transaction-manager="jtaTransactionManager" />
</beans>

jdbc.properties

#mysql 6.*以上
jdbc.driverClassName = com.mysql.cj.jdbc.Driver
jdbc.url = jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&pinGlobalTxToPhysicalConnection=true&useSSL=false
jdbc.username =root
jdbc.password =root

jdbc.two.url = jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&pinGlobalTxToPhysicalConnection=true&useSSL=false
jdbc.two.username =root
jdbc.two.password =root

jta.properties

com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
com.atomikos.icatch.console_file_name=tm.release.out
com.atomikos.icatch.log_base_name=tm.releaselog
com.atomikos.icatch.tm_unique_name=com.atomikos.spring.jdbc.tm.release
com.atomikos.icatch.console_log_level=INFO

TestInsert.java

@ContextConfiguration(value = {"classpath:spring-application-context.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class TestInsert {
 private Logger logger = LoggerFactory.getLogger(TestInsert.class);
 @Autowired
 private BatchInsertService batchInsertService;

 @Test
 public void insert(){

 long startTime = System.currentTimeMillis();
 User user = new User();
 user.setName("User_"+(int)(Math.random()*100));
 user.setAge((int)(Math.random()*100));

 CustInfo info = new CustInfo();
 info.setPhone("123456789"+(int)(Math.random()*100));
 batchInsertService.insert(user,info);

 long endTime = System.currentTimeMillis();
 logger.info("共耗时:{}毫秒",endTime -startTime);
 }
}

BatchInsertService.java

@Service
public class BatchInsertService {
 private Logger logger = LoggerFactory.getLogger(BatchInsertService.class);
 @Autowired
 private UserService userService;
 @Autowired
 private CustInfoService custInfoService;
 @Transactional(rollbackFor= {Exception.class,RuntimeException.class})
 public void insert(User user,CustInfo custInfo) {
 int insertUser = userService.insert(user);
 logger.info("insertUser={}",insertUser);
 int insertCustInfo = custInfoService.insert(custInfo);
 logger.info("insertCustInfo={}",insertCustInfo);
 }
}

UserService.java

@Service
public class UserService {
 @Autowired
 private UserMapper userMapper;

 public int insert(User record) {
 int result = userMapper.insert(record);
 return result;
 }

}

CustInfoService.java

@Service
public class CustInfoService {
 @Autowired
 private CustInfoMapper custInfoMapper;

 public int insert(CustInfo record) {
 int result = custInfoMapper.insert(record);
 long now = System.currentTimeMillis();
 // 模拟一个异常
 if (now % 2 == 0) {
  throw new RuntimeException("CustInfoMapper throws test insert exception");
 }
 return result;
 }
}

Mapper和Bean等就不列出来了,完成的示例工程在github: https://github.com/fenglibin/DruidWithAtomikos

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Springboot-dubbo-fescar 阿里分布式事务的实现方法

    大家可以自行百度下阿里分布式事务,在这里我就不啰嗦了.下面是阿里分布式事务开源框架的一些资料,本文是springboot+dubbo+fescar的集成. 快速开始 https://github.com/alibaba/fescar/wiki/Quick-Start GIT地址 https://github.com/alibaba/fescar 1.sql CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `br

  • spring整合atomikos实现分布式事务的方法示例

    前言 Atomikos 是一个为Java平台提供增值服务的并且开源类事务管理器,主要用于处理跨数据库事务,比如某个指令在A库和B库都有写操作,业务上要求A库和B库的写操作要具有原子性,这时候就可以用到atomikos.笔者这里整合了一个spring和atomikos的demo,并且通过案例演示说明atomikos的作用. 准备工作 开发工具:idea 数据库:mysql , oracle 正文 源码地址: https://github.com/qw870602/atomikos 演示原理:通过在

  • 详解Spring Boot微服务如何集成fescar解决分布式事务问题

    什么是fescar? 关于fescar的详细介绍,请参阅fescar wiki. 传统的2PC提交协议,会持有一个全局性的锁,所有局部事务预提交成功后一起提交,或有一个局部事务预提交失败后一起回滚,最后释放全局锁.锁持有的时间较长,会对并发造成较大的影响,死锁的风险也较高. fescar的创新之处在于,每个局部事务执行完立即提交,释放本地锁:它会去解析你代码中的sql,从数据库中获得事务提交前的事务资源即数据,存放到undo_log中,全局事务协调器在回滚的时候直接使用undo_log中的数据覆

  • Spring中使用atomikos+druid实现经典分布式事务的方法

    经典分布式事务,是相对互联网中的柔性分布式事务而言,其特性为ACID原则,包括原子性(Atomictiy).一致性(Consistency).隔离性(Isolation).持久性(Durabilit): 原子性:事务是一个包含一系列操作的原子操作.事务的原子性确保这些操作全部完成或者全部失败. 一致性:一旦事务的所有操作结束,事务就被提交.然后你的数据和资源将处于遵循业务规则的一直状态. 隔离性:因为同时在相同数据集上可能有许多事务处理,每个事务应该与其他事务隔离,避免数据破坏. 持久性:一旦事

  • Spring中为bean指定InitMethod和DestroyMethod的执行方法

    1.创建一个类 /** * @author: zhaobin * @date: 2021/11/25 10:16 * @description: */ public class Cat { public Cat(){ System.out.println("先初始化构造器"); } public void start(){ System.out.println("start方法"); } public void destroy(){ System.out.print

  • 带你用Python实现Saga 分布式事务的方法

    目录 分布式事务 SAGA SAGA实践 处理网络异常 处理回滚 小结 银行跨行转账业务是一个典型分布式事务场景,假设 A 需要跨行转账给 B,那么就涉及两个银行的数据,无法通过一个数据库的本地事务保证转账的 ACID,只能够通过分布式事务来解决. 分布式事务 分布式事务在分布式环境下,为了满足可用性.性能与降级服务的需要,降低一致性与隔离性的要求,一方面遵循 BASE 理论: 基本业务可用性( Basic Availability ) 柔性状态( Soft state ) 最终一致性( Eve

  • 关于spring中定时器的使用教程

    前言 在很多实际的web应用中,都有需要定时实现的服务,如每天12点推送个新闻,每隔一个小时提醒用户休息一下眼睛,隔一段时间检测用户是否离线等等. spring框架提供了对定时器的支持,通过配置文件就可以很好的实现定时器,只需要应用启动,就自动启动定时器.下面介绍一下具体做法. 第一种,使用XML配置的方法 前期工作,配置spring的开发环境(这里用到了spring的web应用包,需要导入) 首先创建定时器的任务类,定时器要做什么工作,就在这里写什么方法. package org.time;

  • Spring中循环依赖的解决方法详析

    前言 说起Spring中循环依赖的解决办法,相信很多园友们都或多或少的知道一些,但当真的要详细说明的时候,可能又没法一下将它讲清楚.本文就试着尽自己所能,对此做出一个较详细的解读.另,需注意一点,下文中会出现类的实例化跟类的初始化两个短语,为怕园友迷惑,事先声明一下,本文的实例化是指刚执行完构造器将一个对象new出来,但还未填充属性值的状态,而初始化是指完成了属性的依赖注入. 一.先说说Spring解决的循环依赖是什么 Java中的循环依赖分两种,一种是构造器的循环依赖,另一种是属性的循环依赖.

  • Spring中IOC和AOP的深入讲解

    前言 Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来.它是为了解决企业应用开发的复杂性而创建的.Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情.然而,Spring的用途不仅限于服务器端的开发.从简单性.可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益

  • Spring中@Validated和@Valid区别浅析

    目录 基本概念 @Valid和@Validated 批注 主要区别 什么是嵌套验证? 总结 基本概念 Spring Validation 验证框架对参数的验证机制提供了@Validated(Spring's JSR-303规范,是标准JSR-303的一个变种),javax提供了@Valid(标准JSR-303规范),配合BindingResult可以直接提供参数验证结果.其中对于字段的特定验证注解,比如@NotNull. @Valid和@Validated 批注 在Spring中,我们使用JSR

  • java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源)

    目录 前言 一.项目依赖 二.数据源配置 三.数据源的注册 四.配置数据源对应的sqlSessionFactory 五.测试接口 六.建立JtaTestContoller.java 七.在test.ftl中增加一个按钮来测试 八.启动服务,验证结果 前言 首先,到底啥是分布式事务呢,比如我们在执行一个业务逻辑的时候有两步分别操作A数据源和B数据源,当我们在A数据源执行数据更改后,在B数据源执行时出现运行时异常,那么我们必须要让B数据源的操作回滚,并回滚对A数据源的操作:这种情况在支付业务时常常出

  • 详解SpringBoot基于Dubbo和Seata的分布式事务解决方案

    1. 分布式事务初探 一般来说,目前市面上的数据库都支持本地事务,也就是在你的应用程序中,在一个数据库连接下的操作,可以很容易的实现事务的操作. 但是目前,基于SOA的思想,大部分项目都采用微服务架构后,就会出现了跨服务间的事务需求,这就称为分布式事务. 本文假设你已经了解了事务的运行机制,如果你不了解事务,那么我建议先去看下事务相关的文章,再来阅读本文. 1.1 什么是分布式事务 对于传统的单体应用而言,实现本地事务可以依赖Spring的@Transactional注解标识方法,实现事务非常简

随机推荐