복붙노트

[SPRING] 하나의 Service 메소드는 Spring 트랜잭션을위한 내부 다중 메소드를 호출한다.

SPRING

하나의 Service 메소드는 Spring 트랜잭션을위한 내부 다중 메소드를 호출한다.

package com.bluesky;

public interface FooServiceIface {
    public  void insertA();
    public void insertB();
}
package com.bluesky;

import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class FooServiceImpl extends JdbcDaoSupport implements FooServiceIface {

    public void insertA() {
        this.getJdbcTemplate().execute("insert student(name) values('stuA')");
         insertB();
         int i=10/0;
    }

    public void insertB() {
        this.getJdbcTemplate().execute("insert student(name) values('stuB')");

    }

}
public class Client {

    public static void main(String[] args) {

        ApplicationContext appContxt = new ClassPathXmlApplicationContext("applicationContext.xml");

        FooServiceIface  fService= (FooServiceIface)appContxt.getBean("fooService");

        fService.insertA();

    }
}
<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
       <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
       <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf-8"/>
       <property name="username" value="root"/>
       <property name="password" value="root"/>
    </bean>

    <bean id="fooService" class="com.bluesky.FooServiceImpl">
         <property name="dataSource" ref="dataSource"/>  
    </bean>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
        <property name="dataSource" ref="dataSource"/>  
    </bean>  

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="insertA" propagation="REQUIRED" />
             <tx:method name="insertB" propagation="REQUIRES_NEW" />
        </tx:attributes>
    </tx:advice>

    <aop:config proxy-target-class="true">
        <aop:pointcut id="interceptorPointCuts" expression="execution(* com.bluesky.*Service*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" />       
    </aop:config>     
</beans>

복잡한 코드를 유감으로 여긴다.

클라이언트 블로우 디버그 로그를 실행하면 다음과 같이 표시됩니다.

21:44:19,546 DEBUG TransactionSynchronizationManager:183 - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@ba86ef] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@1b9658e] to thread [main]
21:44:19,546 DEBUG TransactionSynchronizationManager:258 - Initializing transaction synchronization
21:44:19,547 DEBUG TransactionInterceptor:362 - Getting transaction for [com.bluesky.FooServiceImpl.insertA]
21:44:19,547 DEBUG JdbcTemplate:416 - Executing SQL statement [insert student(name) values('stuA')]
21:44:19,592 DEBUG JdbcTemplate:416 - Executing SQL statement [insert student(name) values('stuB')]
21:44:19,594 DEBUG TransactionInterceptor:406 - Completing transaction for [com.bluesky.FooServiceImpl.insertA] after exception: java.lang.ArithmeticException: / by zero
21:44:19,594 DEBUG RuleBasedTransactionAttribute:130 - Applying rules to determine whether transaction should rollback on java.lang.ArithmeticException: / by zero
21:44:19,594 DEBUG RuleBasedTransactionAttribute:147 - Winning rollback rule is: null
21:44:19,595 DEBUG RuleBasedTransactionAttribute:152 - No relevant rollback rule found: applying default rules
21:44:19,595 DEBUG DataSourceTransactionManager:938 - Triggering beforeCompletion synchronization
21:44:19,595 DEBUG DataSourceTransactionManager:843 - Initiating transaction rollback
21:44:19,596 DEBUG DataSourceTransactionManager:279 - Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@167f4bf]
21:44:19,598 DEBUG DataSourceTransactionManager:967 - Triggering afterCompletion synchronization
21:44:19,598 DEBUG TransactionSynchronizationManager:316 - Clearing transaction synchronization

나는 insertA () 메소드를 호출 할 때이 메소드가 트랜잭션을 시작한다는 것을 알았다. 코드가 insertB ()에 도착하면 시작할 트랜잭션이 없다.

내가 설정하지 않았거나 내가 실수 한 부분이 있습니까?

제 의도는 insertA ()가 insertB ()를 호출 할 때 REQUIRES_NEW 트랜잭션 시작입니다.

해결법

  1. ==============================

    1.코드 리팩토링에 대한 KLE의 충고는 돈에 대한 것이지만 작동하지 않는 이유에 대해서는 Spring AOP가 기본적으로 JDK 동적 프록시를 사용하여 AOP를 제공합니다. 즉, 서비스를 무언가에 주입 할 때 실제로 주입되는 것은 서비스 인터페이스를 구현하는 프록시 인스턴스입니다. 이 프록시에서 메소드를 호출하면 실제 서비스 인스턴스에 위임하기 전에 트랜잭션 코드가 실행됩니다. 제어 흐름이 서비스 내부에 있으면, this.foo ()를 통해 다른 메소드를 호출하면 (이는 암시 적 임에도 불구하고) 동일한 인스턴스 (즉, 서비스)에서 메소드를 호출하기 만하면됩니다. 프록시는 트랜잭션에 대해 알고있는 유일한 것입니다. AspectJ로 빌드 또는로드 타임 바이트 코드로 전환했다면, 트랜잭션 호출 코드가 별도의 객체로 살지 않고 서비스 코드로 직접 짜여 졌기 때문에, AspectJ로 짜 넣은 바이트 코드로 바꿀 수있다.

    코드 리팩토링에 대한 KLE의 충고는 돈에 대한 것이지만 작동하지 않는 이유에 대해서는 Spring AOP가 기본적으로 JDK 동적 프록시를 사용하여 AOP를 제공합니다. 즉, 서비스를 무언가에 주입 할 때 실제로 주입되는 것은 서비스 인터페이스를 구현하는 프록시 인스턴스입니다. 이 프록시에서 메소드를 호출하면 실제 서비스 인스턴스에 위임하기 전에 트랜잭션 코드가 실행됩니다. 제어 흐름이 서비스 내부에 있으면, this.foo ()를 통해 다른 메소드를 호출하면 (이는 암시 적 임에도 불구하고) 동일한 인스턴스 (즉, 서비스)에서 메소드를 호출하기 만하면됩니다. 프록시는 트랜잭션에 대해 알고있는 유일한 것입니다. AspectJ로 빌드 또는로드 타임 바이트 코드로 전환했다면, 트랜잭션 호출 코드가 별도의 객체로 살지 않고 서비스 코드로 직접 짜여 졌기 때문에, AspectJ로 짜 넣은 바이트 코드로 바꿀 수있다.

  2. ==============================

    2.네 문제를 이해한다. 기술적으로 복잡한 방법이 있지만 실제로는 가치 있다고 생각하지 않습니다. 간단하고 강력하며 코드를 실제로 향상시키는 또 다른 접근 방식을 제안합니다.

    네 문제를 이해한다. 기술적으로 복잡한 방법이 있지만 실제로는 가치 있다고 생각하지 않습니다. 간단하고 강력하며 코드를 실제로 향상시키는 또 다른 접근 방식을 제안합니다.

    같은 봄 콩에서 먹는 대신 두 개의 다른 콩에서 먹게하십시오. B가 A 안으로 주입됩니다.

    나는 당신의 질문에 답하고 있지 않다는 것을 알았지 만 장점에 대해 생각해 보았습니다.

  3. ==============================

    3.KLE의 답변은 탄탄한 조언입니다. 사진을 완성하기 만하면 해결 방법이 있지만 AOP가 의미하는 모든 내용이 중단됩니다.

    KLE의 답변은 탄탄한 조언입니다. 사진을 완성하기 만하면 해결 방법이 있지만 AOP가 의미하는 모든 내용이 중단됩니다.

    public void insertA() {
        this.getJdbcTemplate().execute("insert student(name) values('stuA')");
        // this works, but... gah!
        ((FooServiceIface) AopContext.currentProxy()).insertB();
        int i=10/0;
    }
    

    참고:

  4. from https://stackoverflow.com/questions/7231220/one-service-method-invoke-inner-multiple-method-for-spring-transaction by cc-by-sa and MIT license