2012-02-04 4 views
1

Spring 트랜잭션에 문제가 있습니다. 내가 실제로 personsDao2가 롤백되지 않는 이유를 알아낼 수 없기 때문에 도움이 필요합니다 (아래의 assert에 "FAILS!"주석이 있음). 어떤 입력?Spring 트랜잭션 문제

내 Eclipse 프로젝트는 http://www52.zippyshare.com/v/4142091/file.html에서 다운로드 할 수 있습니다. 모든 의존성이 있으므로 쉽게 갈 수 있습니다. 오히려 봄 컨텍스트에서 인스턴스를 얻는 것보다 직접 (new를 사용하여) 이러한 클래스를 인스턴스화 이후

import org.springframework.transaction.annotation.Propagation; 
import org.springframework.transaction.annotation.Transactional; 
import com.google.common.collect.Lists; 

public class MyInnerClass { 
    private PersonsDao personsDao; 

    public MyInnerClass() { 
    } 

    public PersonsDao getPersonsDao() { 
     return personsDao; 
    } 

    public void setPersonsDao(PersonsDao personsDao) { 
     this.personsDao = personsDao; 
    } 

    @Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor = Exception.class) 
    public void method() { 
     personsDao.createPersons(Lists.newArrayList(new Person("Eva"))); 
    } 
} 

import org.springframework.transaction.annotation.Propagation; 
import org.springframework.transaction.annotation.Transactional; 
import com.google.common.collect.Lists; 

public class MyOuterClass { 
    private MyInnerClass myInnerClass; 
    private PersonsDao personsDao; 

    public MyInnerClass getMyInnerClass() { 
     return myInnerClass; 
    } 

    public void setMyInnerClass(MyInnerClass myInnerClass) { 
     this.myInnerClass = myInnerClass; 
    } 

    public void setMyInnerClass() { 
    } 

    public PersonsDao getPersonsDao() { 
     return personsDao; 
    } 

    public void setPersonsDao(PersonsDao personsDao) { 
     this.personsDao = personsDao; 
    } 

    public MyOuterClass() { 
    } 

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor=Exception.class) 
    public void method() { 
     try { 
      personsDao.createPersons(Lists.newArrayList(new Person("Adam"))); 
      throw new RuntimeException("Forced rollback"); 
     } finally { 
      myInnerClass.method(); 
     } 
    } 
} 

public class Person { 
    public Person(String name) { 
     this.name = name; 
    } 

    public String getName() { 
     return name; 
    } 

    @Override 
    public String toString() { 
     return "Customer [name=" + name + "]"; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (this == obj) 
      return true; 
     if (obj == null) 
      return false; 
     if (getClass() != obj.getClass()) 
      return false; 
     Person other = (Person) obj; 
     if (name == null) { 
      if (other.name != null) 
       return false; 
     } else if (!name.equals(other.name)) 
      return false; 
     return true; 
    } 

    private String name; 
} 

import java.sql.ResultSet; 
import java.sql.SQLException; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 
import javax.sql.DataSource; 
import org.springframework.jdbc.core.RowMapper; 
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 
import org.springframework.jdbc.core.namedparam.SqlParameterSource; 
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils; 

public class PersonsDao { 
    public PersonsDao(DataSource dataSource, String tableName) { 
     namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); 
     this.tableName = tableName; 
    } 

    public List<Person> getPersons() { 
     Map<String, Object> namedParameters = new HashMap<String, Object>(); 
     String getCustomers = "SELECT name FROM " + tableName + " ORDER BY name ASC"; 
     return namedParameterJdbcTemplate.query(getCustomers, namedParameters, getRowMapper()); 
    } 

    public void createPersons(List<Person> customers) { 
     SqlParameterSource[] params = SqlParameterSourceUtils.createBatch(customers.toArray()); 
     String createCustomer = "INSERT INTO " + tableName + " VALUES(:name)"; 
     namedParameterJdbcTemplate.batchUpdate(createCustomer, params); 
    } 

    public void deleteCustomers() { 
     Map<String, Object> namedParameters = new HashMap<String, Object>(); 
     String deleteCustomers = "DELETE FROM " + tableName; 
     namedParameterJdbcTemplate.update(deleteCustomers, namedParameters); 
    } 

    private static RowMapper<Person> getRowMapper() { 
     return new RowMapper<Person>() { 
      @Override 
      public Person mapRow(ResultSet arg0, int arg1) throws SQLException { 
       return new Person(arg0.getString("name")); 
      } 
     }; 
    } 

    private NamedParameterJdbcTemplate namedParameterJdbcTemplate; 
    private String tableName; 
} 

import static org.junit.Assert.*; 
import javax.annotation.Resource; 
import org.junit.After; 
import org.junit.Before; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.test.context.ContextConfiguration; 
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 
import org.springframework.transaction.annotation.Transactional; 

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations = "/beans.xml") 
@Transactional(rollbackFor = Exception.class) 
public class PersonsDaoTest { 
    @Resource 
    private MyInnerClass myInnerClass; 
    @Resource 
    private MyOuterClass myOuterClass; 

    @Test(expected = Exception.class) 
    public void test() { 
     myOuterClass.method(); 
     fail(); 
    } 

    @After 
    public void after() { 
     assertEquals(1, myInnerClass.getPersonsDao().getPersons().size()); 
     assertEquals(0, myOuterClass.getPersonsDao().getPersons().size()); 
    } 

    @Before 
    public void before() { 
     myInnerClass.getPersonsDao().deleteCustomers(); 
     myOuterClass.getPersonsDao().deleteCustomers(); 
     assertEquals(0, myInnerClass.getPersonsDao().getPersons().size()); 
     assertEquals(0, myOuterClass.getPersonsDao().getPersons().size()); 
    } 
} 
+2

여기에서 문제를 설명하고 코드를 설명하면 훨씬 좋을 것입니다. – beerbajay

+0

그들은 동일한 데이터베이스를 가리키고있는 것처럼 보입니다. 왜 2가 1에 의한 변경을 보지 못한다고 생각하는지 혼란 스럽습니까? – Affe

+0

그들은 같은 db이지만 다른 테이블 --- persons1과 persons2를 가리 킵니다. persons1 테이블은 내부 클래스에서 사용하고 persons2 테이블은 외부 클래스에서 사용합니다. – aandeers

답변

1

@Anders @Transactional 주석이있는 InnerClass가 인터페이스에서 파생되지 않습니다. AspectJ 직조 또는 CG-LIB 기반 프록시를 사용하지 않는 경우 @Transactional aspect가 적용되지 않습니다. 동적 프록시는 인터페이스가 있어야합니다. 빠른 수정은 인터페이스에서 내부 클래스를 파생시키고, Spring 구성에서 빈을 정의하고 빈을 참조하기위한 인터페이스를 일관되게 사용하는 것입니다.

+0

MyInnerClass와 MyOuterClass에 대한 인터페이스를 만들었습니다. 나는 @Transactional 어노테이션을 인터페이스와 구현 모두에 넣으려고했지만 둘 다 내 테스트를 통과하지 못한다 : S – aandeers

+0

이상하다. 내부 트랜잭션은 롤백되지만 외부 트랜잭션은 롤백되지 않습니다. 그것은 내가 성취하고자하는 것과는 정반대입니다. – aandeers

+0

문제는 내가 sqlite를 사용하고 있으며 첫 번째 쓰기가 db를 잠그고있는 것일 수 있습니다. MyInnerClass.method에서 데이터베이스가 잠겼다는 예외가 발생했습니다 ... – aandeers

2

첫째, 당신의 두 클래스에 @Transactional 주석이 무시됩니다.

그래서 사실,이 코드를 아래로 boild :

finally 블록은 항상 실행
try { 
    personDao2.createPerson(); // creates a person in persons2 
    throw new RuntimeException(); 
} 
finally { 
    personDao1.createPerson(); // creates a person in person1 
} 

, 예외가 try 블록에서 발생합니다 경우에도 마찬가지입니다. 따라서 시험은 person1person2에 사람을 만듭니다.

+0

당신은 절대적으로 옳습니다! 이제 스프링 컨텍스트에서 객체를 가져 오지만 내 테스트는 여전히 실패합니다. 나는 person2 테이블이 롤백 되길 기대하지만 그렇지 않습니다. – aandeers

+0

그러면 DAO는 Spring의 트랜잭션에 참여하지 않습니다. 코드를 보여주세요. –

+0

위의 DAO 코드를 추가했습니다. – aandeers

관련 문제