2012-02-04 312 views
1

我在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

回答

1

@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

2

首先,在你的兩個班的@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