Spring 트랜잭션 관리에서 주의할 점

대부분의 사람들은 데이터베이스 트랜잭션을 처음 접할 때 혼란스러워할 것이며, 그들의 혼란스러운 표정은 지식에 대한 갈증을 드러냅니다. 여기서는 대중적인 과학 지식을 알려 드리겠습니다. :

  • 거래 개념
  • 거래특성
  • MySQL 데이터베이스 스토리지 엔진 지원
  • MySQL 데이터베이스 스토리지 엔진 수정
  • 스프링 구성 트랜잭션
  • 테스트 코드
  • 알아채다

거래 개념

트랜잭션은 동시성 제어의 단위이자 사용자가 정의한 작업 순서입니다. 이러한 작업은 모두 완료되거나 아무것도 수행되지 않으며 이는 통합 작업 단위입니다. 트랜잭션은 서버가 데이터 무결성을 유지할 수 있도록 논리적으로 관련된 작업 집합을 함께 바인딩합니다.
COMMIT: 제출, 즉 트랜잭션을 커밋하는 모든 작업을 나타냅니다. 특히 트랜잭션의 데이터베이스에 대한 모든 업데이트는 디스크의 물리적 데이터베이스에 다시 기록되고 트랜잭션은 정상적으로 종료됩니다.
ROLLBACK은 롤백(rollback) 즉, 트랜잭션이 실행되는 동안 어떤 종류의 실패가 발생하여 트랜잭션을 계속할 수 없게 되는 것을 의미하며, 시스템은 트랜잭션 내 데이터베이스에 대해 완료된 작업을 모두 취소하고 트랜잭션이 시작된 상태로 롤백한다.

예: A가 B에게 온라인으로 돈을 이체하기 위해 온라인 뱅킹을 사용 <이것은 시나리오입니다>
첫 번째 사례: A 사용자의 금액이 감소하고 B 사용자의 금액이 증가 <일반적인 경우>
두 번째 사례: A 사용자의 금액이 감소하고 B 사용자의 도중에 예외가 발생합니다. 금액 증가 없음 <비정상적 상황>
세 번째 상황: 사용자 A의 금액은 감소하지 않지만 사용자 B의 금액은 증가합니다 <비정상적 상황>
. . .
첫 번째 경우를 제외한 나머지는 예외로, 이 경우 트랜잭션을 사용해야 합니다. 하든 안하든 하십시오 . 즉, 계정 A가 감소하면 계정 B도 증가해야 하며, 그렇지 않으면 계정 A는 감소하지 않습니다.

거래특성

트랜잭션의 네 가지 특성(줄여서 ACID): 원자성, 일관성, 격리성, 내구성

원자성: 트랜잭션의 모든 작업은 데이터베이스에서 분할될 수 없습니다. 모두 완료되거나 아무것도 실행되지 않습니다.

일관성: 병렬로 실행되는 여러 트랜잭션의 실행 결과는 특정 순서의 직렬 실행 결과와 일치해야 합니다.

격리(Isolation): 트랜잭션 실행은 다른 트랜잭션의 방해를 받지 않으며, 트랜잭션 실행의 중간 결과는 다른 트랜잭션에 투명해야 합니다.

내구성: 커밋된 트랜잭션의 경우 시스템은 데이터베이스가 실패하더라도 트랜잭션에 의해 수행된 데이터베이스 변경 사항이 손실되지 않도록 보장해야 합니다.

MySQL 데이터베이스 스토리지 엔진 지원

Mysql에는 MyIsAm, InnoDB, MEMORY 및 MERGE의 네 가지 스토리지 엔진이 있습니다. 네 가지 각각 고유한 장점이 있으며, 기술 선택은 다양한 요구에 따라 다양한 방법을 선택할 수 있습니다. 나의IsAm
MyISAM은 MySQL의 기본 스토리지 엔진입니다. MyISAM은 트랜잭션이나 외래 키를 지원하지 않지만 액세스 속도가 빠르고 트랜잭션 무결성에 대한 요구 사항이 없습니다.
MyISAM 테이블은 3가지 다른 저장 형식도 지원합니다.
정적 테이블
정적 테이블은 기본 저장 형식입니다. 정적 테이블의 필드는 가변 길이 필드입니다. 장점은
저장이 매우 빠르고 캐시하기 쉽고 오류 발생 시 복구하기 쉽습니다. 단점은
다음과 같습니다. 동적 테이블보다 더 많은 공간을 차지합니다. (참고: 저장 시 열 너비가 부족할 경우 공백을 사용하여 보충합니다. 해당 공백은 접근 시 획득되지 않습니다.)
동적 테이블
동적 테이블의 필드는 가변 길이로 상대적으로 공간을 적게 차지한다는 장점이 있지만, 레코드를 자주 업데이트하거나 삭제하면 조각화가 발생하므로 정기적으로 성능을 개선해야 하며, 장애 발생 시 복구가 상대적으로 적습니다
. 어려운.
압축 테이블
압축된 테이블은 작은 디스크 공간을 차지하고 각 레코드가 개별적으로 압축되므로 액세스 오버헤드가 거의 없습니다.
InnoDB
InnoDB 스토리지 엔진은 커밋, 롤백 및 충돌 복구 기능을 통해 트랜잭션 안전성을 제공합니다. 그러나 MyISAM 스토리지 엔진에 비해
InnoDB는 쓰기 효율성이 낮고 데이터와 인덱스를 유지하기 위해 더 많은 디스크 공간을 차지합니다.
또한, MySQL은 외래키 저장 엔진으로 InnoDB만을 지원하는데, 외래키 생성 시
첨부된 테이블에 해당 인덱스가 있어야 하며, 하위 테이블에서는 외래키 생성 시 해당 인덱스가 자동으로 생성된다. (관련 테이블의 외래 키는 관련 테이블의 기본 키여야 합니다.)
InnoDB는 동시성이 높고 업데이트 작업이 많은 테이블에 사용하기에 이상적입니다. 트랜잭션을 사용해야 하는 테이블. 자동화된 재해 복구에 대한 요구 사항이 포함된 표입니다.
메모리
MEMORY 스토리지 엔진은 메모리에 저장된 콘텐츠를 사용하여 테이블을 생성합니다.
각 MEMORY 테이블은 실제로 하나의 디스크 파일에만 해당합니다. MEMORY 유형의 테이블 접근은 데이터가 메모리에 저장되고 기본적으로 HASH 인덱스가 사용되기 때문에 매우 빠릅니다.
그러나 서비스가 종료되면 테이블의 데이터는 손실됩니다. 빠른 속도가 요구되고 임시 데이터가 필요한 상황에서는 메모리 저장 엔진이 사용됩니다.
병합
병합 스토리지 엔진은 MyISAM 테이블 그룹의 조합입니다. 이러한 MyISAM 테이블의 구조는 정확히 동일해야 합니다. MERGE 테이블에는 데이터가 없습니다. MERGE 유형 테이블은 쿼리, 업데이트
및 삭제가 가능합니다. 실제로 내부 MyISAM 테이블에서 수행되어 작동됩니다. MERGE 테이블에 대한 삽입 작업의 경우 삽입된 테이블은 INSERT_METHOD 절에 따라 정의되며 세 가지 다른 값을 가질 수 있습니다. 첫 번째 값과 마지막 값에 따라 첫 번째 또는 마지막 테이블에 삽입 작업이 수행됩니다. 이 절을 정의합니다. 또는 NO로 설정하여
MERGE 테이블을 삽입할 수 없음을 나타냅니다. MERGE 테이블에 대해 삭제 작업을 수행할 수 있습니다. 이 작업은 MERGE 테이블의 정의만 삭제하고 내부 테이블에는 영향을 주지 않습니다. MERGE는 디스크에 MERGE 테이블 이름으로 시작하는 두 개의 파일을 유지합니다. .frm 파일은 테이블 정의를 저장하고, .MRG 파일은 MERGE 테이블이 구성되는 테이블과 MERGE 테이블의 기초를 포함하여 결합된 테이블에 대한 정보를 포함합니다. 데이터를 삽입하는 중입니다.
MERGE 테이블은 .MRG 파일을 수정하여 수정할 수 있지만, 수정 후에는 테이블을 플러시하여 새로 고쳐야 합니다.

Mysql의 주류 엔진 방식은 이 4가지이며 조건에 따라 해당하는 방식을 선택할 수 있습니다.

MySQL 데이터베이스 스토리지 엔진 수정

show engines; #显示数据库是否支持InnoDB
변경 방법 1: my.cnf 구성 파일 수정
my.cnf를 열고 [mysqld] 끝에 default-storage-engine=InnoDB를 추가한 후 데이터베이스 서비스를 다시 시작하고 데이터베이스의 기본 엔진을 InnoDB로 변경합니다.
변경 방법 2: 테이블 생성 시 지정
create table tableName( id int primary key, name varchar(50) )type=InnoDB;
변경 방법 3: 테이블 생성 후 수정
alter table tableName ENGINE=InnoDB; #mysql5.0以后用这种方式
alter table tableName type = InnoDB; #mysql5.0之前用这种方式

스프링 구성 트랜잭션

여기에는 두 가지 방법이 있는데 하나는 AOP 자동 주입 방법이고 다른 하나는 Annotation 기반 방법이다.

자동 주입 방식
AOP 크로스커팅 중에 규칙에 따라 필요한 곳에 트랜잭션이 주입됩니다. 이로 인해 불필요한 부위에도 주사를 하게 될 수 있습니다. 자원낭비를 초래합니다. 잊어버릴 필요가 없고, 거래로 관리되지 않는 곳이 있는지 걱정하지 않아도 된다는 장점이 있습니다.
    <!-- 配置数据源 使用的是Druid数据源 -->
    <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />

        <!-- 初始化连接大小 -->
        <property name="initialSize" value="0" />
        <!-- 连接池最大使用连接数量 -->
        <property name="maxActive" value="20" />

        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="0" />
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="60000" />
        <property name="poolPreparedStatements" value="true" />
        <property name="maxPoolPreparedStatementPerConnectionSize"
            value="33" />
        <!-- 用来检测有效sql -->
        <property name="validationQuery" value="${validationQuery}" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <property name="testWhileIdle" value="true" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="25200000" />
        <!-- 打开removeAbandoned功能 -->
        <property name="removeAbandoned" value="true" />
        <!-- 1800秒,也就是30分钟 -->
        <property name="removeAbandonedTimeout" value="1800" />
        <!-- 关闭abanded连接时输出错误日志 -->
        <property name="logAbandoned" value="true" />
        <!-- 监控数据库 -->
        <property name="filters" value="mergeStat" />
    </bean>
    <!-- myBatis文件 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- 添加mybatis的配置 -->
        <property name="configLocation" value="classpath:mybatis.xml"/>
        <!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
        <property name="mapperLocations" value="classpath:com/tanrice/dao/impl/*.xml" />
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.tanrice.dao" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 配置事物 -->
    <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="append*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="insert*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="update*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="modify*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="edit*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="delete*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="remove*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="repair" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="delAndRepair" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="get*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="find*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="load*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="search*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="datagrid*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
        </tx:attributes>
    </tx:advice>

    <!-- Spring aop事务管理 -->
    <aop:config>
        <aop:pointcut id="transactionPointcut" expression="execution(* com.projectname.service..*Impl.*(..))" />
        <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
    </aop:config>
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype">
         <constructor-arg index="0" ref="sqlSessionFactory" /> 
    </bean>
주석 기반 접근 방식
Annotation 기반 메소드는 사용해야 하는 클래스나 메소드에 @Transactional Annotation을 추가하는 것이다. 이 방법의 장점은 자원 낭비를 줄이는 것입니다. 단점은 때때로 잊어버린다는 것입니다. 일단 잊어버리면 일부 메소드에는 트랜잭션이 없습니다.
    <!-- 配置数据源 使用的是Druid数据源 -->
    <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />

        <!-- 初始化连接大小 -->
        <property name="initialSize" value="0" />
        <!-- 连接池最大使用连接数量 -->
        <property name="maxActive" value="20" />

        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="0" />
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="60000" />
        <property name="poolPreparedStatements" value="true" />
        <property name="maxPoolPreparedStatementPerConnectionSize"
            value="33" />
        <!-- 用来检测有效sql -->
        <property name="validationQuery" value="${validationQuery}" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <property name="testWhileIdle" value="true" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="25200000" />
        <!-- 打开removeAbandoned功能 -->
        <property name="removeAbandoned" value="true" />
        <!-- 1800秒,也就是30分钟 -->
        <property name="removeAbandonedTimeout" value="1800" />
        <!-- 关闭abanded连接时输出错误日志 -->
        <property name="logAbandoned" value="true" />
        <!-- 监控数据库 -->
        <property name="filters" value="mergeStat" />
    </bean>

    <!-- myBatis文件 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- 添加mybatis的配置 -->
        <property name="configLocation" value="classpath:mybatis.xml"/>
        <!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
        <property name="mapperLocations" value="classpath:com/tanrice/dao/impl/*.xml" />
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.tanrice.dao" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>


    <!-- 注解的方式配置事务 -->
    <!-- 在需要的地方配置 -->
    <tx:annotation-driven transaction-manager="transactionManager"  proxy-target-class="true"/>

    <!-- 注解方式配置事物 -->
    <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="append*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="insert*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="update*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="modify*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="edit*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="delete*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="remove*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="repair" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="delAndRepair" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="get*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="find*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="load*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="search*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="datagrid*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
        </tx:attributes>
    </tx:advice>
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype">
         <constructor-arg index="0" ref="sqlSessionFactory" /> 
    </bean>
@Service
@Transactional
public class UserTaskServiceImpl implements UserTaskService{
    
    

    @Autowired
    private UserTaskDao userTaskDao;

    @Autowired
    private TaskDao taskDao;

    public boolean updateUserTask(UserTask userTask,int id) {
        boolean flag = false;
        try{
            flag = userTaskDao.saveUserTask(userTask)>0?true:false;
            System.out.println("插入:"+flag);
            flag = taskDao.updateTaskLeftcountById(id)>0?true:false;
            System.out.println("修改:"+flag);
        }catch(Exception e){
            throw new RuntimeException();
        }
        return flag;
    }
}

위 두 가지 방법을 직접 사용해 보시는 것도 좋을 것 같은데, 주석은 xml 구성 파일에 좀 더 자세히 적어두었기 때문에 차근차근 설명하지 않겠습니다.

테스트 코드

서비스 방법이 작성되었습니다. 가장 중요한 것은 mybatis의 코드를 붙여넣는 것입니다. mybatis에서 작성한 SQL 문을 의도적으로 잘못 작성했습니다. 실행 중 오류가 발생했습니다.

    <update id="updateTaskLeftcountById">
        <!-- leftcount为int类型,所以加'aas'的时候会出错 -->
        update t_tm_task_table set leftcount = leftcount+aas where id = #{id}
    </update>
    @org.junit.Test
    public void testTrx(){
        UserTask userTask = new UserTask();
        userTask.setFullname("wanda");
        userTask.setUserid(10071);
        userTask.setTaskid(10026);
        userTask.setGettime(new Date().getTime());
        userTask.setState(5);

        System.out.println("结果是:"+userTaskService.updateUserTask(userTask, 10026));
    }

알아채다

트랜잭션을 사용할 때 주의해야 할 몇 가지 사항이 있습니다.

  • 다오 레이어에서 거래를 설정해 보는 것은 어떨까요?
    • 가장 근본적인 이유는 DAO 레이어가 데이터베이스를 직접 운영하기 때문에 단일 책임이 있고 롤백할 필요가 없다는 점이다.성공은 성공이고, 실패는 실패이다.
  • 트랜잭션 격리 수준
    • 커밋되지 않은 읽기: 더티 읽기라고 부르는 두 개의 동시 트랜잭션, "트랜잭션 A: 리더가 singo에 급여를 지불합니다", "트랜잭션 B: singo가 급여 계정을 쿼리합니다", 트랜잭션 B는 트랜잭션 A 데이터의 커밋되지 않은 데이터를 읽습니다.
    • 커밋된 읽기: 반복 불가능 읽기라고 부르는 두 개의 동시 트랜잭션, "트랜잭션 A: singo 소비", "트랜잭션 B: singo의 아내 온라인 전송", 트랜잭션 A는 미리 데이터를 읽었고, 트랜잭션 B는 즉시 데이터를 읽었습니다. 업데이트되고 트랜잭션이 커밋되었습니다. 트랜잭션 A가 데이터를 다시 읽으면 데이터가 변경되었습니다.
    • 반복 읽기 반복 읽기: 반복 불가능한 읽기를 방지할 수 있습니다. singo가 급여 카드를 가져와 소비할 때 시스템이 급여 카드 정보를 읽기 시작하면(즉, 거래가 시작됨) singo의 아내는 기록을 수정할 수 없습니다. 즉, singo의 아내는 이때 돈을 이체할 수 없습니다. MySQL의 기본 격리 수준은 반복 읽기입니다.
    • 직렬화 가능 직렬화: 직렬화 가능은 가장 높은 트랜잭션 격리 수준입니다. 또한 가장 비싸고 성능이 매우 낮습니다. 일반적으로 거의 사용되지 않습니다. 이 수준에서는 트랜잭션이 순차적으로 실행되어 더티 읽기 및 반복 불가능한 읽기를 방지할 뿐만 아니라 하지만 팬텀도 피합니다.
    <!-- 注解方式配置事物 -->
    <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="append*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="insert*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="update*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="modify*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="edit*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="delete*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="remove*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="repair" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="delAndRepair" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="get*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="find*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="load*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="search*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="datagrid*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
        </tx:attributes>
    </tx:advice>

위에서 구성한 트랜잭션 격리입니다. <tx:method name="repair" propagation="REQUIRED" isolation="READ_COMMITTED" />격리 구성 항목을 보면 구성된 격리 수준입니다.

추천

출처blog.csdn.net/my_God_sky/article/details/53291138