1

Spring トランザクションに問題があります。personDao2 が本来のようにロールバックされない理由がわからないので、本当に助けが必要です (「FAILS!」とコメントされた以下の assert を参照してください)。入力はありますか?

私の 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

まず、Spring コンテキストからインスタンスを取得するのではなく、@Transactionalこれらのクラスを ( を使用して) 直接インスタンス化するため、2 つのクラスの注釈は無視されます。new

したがって、実際には、次のコードに要約されます。

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

try ブロックで例外がスローされた場合でも、finally ブロックは常に実行されます。したがって、このテストはperson1と で人物を作成しperson2ます。

于 2012-02-04T10:30:07.690 に答える
1

@Anders @Transactional アノテーションを持つ InnerClass はインターフェイスから派生しません。AspectJ ウィービングまたは CG-LIB ベースのプロキシを使用していない場合、@Transactional アスペクトは有効になりません。動的プロキシにはインターフェイスが存在する必要があるためです。 . 簡単な修正は、インターフェイスから内部クラスを派生させ、Spring 構成で Bean を定義し、一貫してインターフェイスを使用して Bean を参照することです。

于 2012-02-04T22:05:56.870 に答える