1

我在使用 Spring 事务时遇到问题。我真的需要帮助,因为我不知道为什么 personDao2 没有按应有的方式回滚(请参阅下面用“失败!”评论的断言)。有输入吗?

我的 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());
    }
}
4

2 回答 2

2

首先,@Transactional忽略两个类上的注释,因为您直接(使用new)实例化这些类,而不是从 spring 上下文中获取实例。

所以事实上,它归结为这段代码:

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

finally 块总是被执行,即使在 try 块中抛出了异常。所以测试会在 inperson1和 in 中创建一个人person2

于 2012-02-04T10:30:07.690 回答
1

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

于 2012-02-04T22:05:56.870 回答