春季交易问题

问题描述:

我在Spring交易中遇到问题。我真的需要帮助,因为我无法弄清楚为什么personsDao2没有按照原样回滚(参见下面的断言评论为“FAILS!”)。任何输入?春季交易问题

我的Eclipse项目可在http://www52.zippyshare.com/v/4142091/file.html下载。所有依赖关系都在那里,所以很容易。

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 2012-02-04 09:29:03

+0

他们似乎指向相同的数据库,它混淆了你为什么认为2不会看到1所做的更改? – Affe 2012-02-04 10:07:55

+0

他们指向相同的分贝,但指向不同的表--- persons1和persons2。 persons1表由内部类使用,而persons2表由外部类使用。 – aandeers 2012-02-04 10:11:26

@Anders YourTransactional注解不带接口,如果你不使用AspectJ编程或基于CG-LIB的代理,@Transactional方面不会生效,因为动态代理需要接口存在。一个快速的解决方法是从接口派生你的内部类,在Spring配置中定义bean,并始终使用接口来引用bean。

+0

我为MyInnerClass和MyOuterClass创建了接口。我尝试在接口和实现上都放置@Transactional注释,但都没有让我的测试通过:S – aandeers 2012-02-05 16:49:11

+0

这很奇怪。内部事务回滚,但不是外部事务。这与我想达到的目的相反。 – aandeers 2012-02-05 17:05:45

+0

问题可能是我正在使用sqlite,并且第一次写入会锁定数据库。 MyInnerClass.method中发生异常,说数据库已被锁定... – aandeers 2012-02-05 17:13:23

首先,在你的两个班的@Transactional忽略注释,因为你实例化这些类直接(使用new),而不是从Spring上下文得到一个实例。

所以,事实上,它boild到这个代码:

try { 
    personDao2.createPerson(); // creates a person in persons2 
    throw new RuntimeException(); 
} 
finally { 
    personDao1.createPerson(); // creates a person in person1 
} 

一个最后总是执行块,即使一个异常在try块抛出。因此测试会在person1person2中创建一个人。

+0

你是对的!我现在从spring上下文中获取对象,但是我的测试仍然失败。我期望person2表回滚但不是。 – aandeers 2012-02-04 14:02:14

+0

然后DAO不参与Spring的交易。向我们展示它的代码。 – 2012-02-04 14:09:47

+0

我已经添加了上面的DAO代码。 – aandeers 2012-02-04 18:32:56