春季交易问题

时间:2012-02-04 09:25:54

标签: java spring transactions rollback transactional

我遇到了Spring事务的问题。我真的需要帮助,因为我无法弄清楚为什么PersonDao2没有回滚的原因(请参阅下面的断言评论为“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 个答案:

答案 0 :(得分:2)

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

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

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

即使在try块中抛出异常,也始终执行finally块。因此,该测试会在person1person2中创建一个人。

答案 1 :(得分:1)