Весенняя транзакция - PullRequest
       28

Весенняя транзакция

1 голос
/ 04 февраля 2012

У меня проблемы с транзакциями 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 ]

2 голосов
/ 04 февраля 2012

Прежде всего, аннотации @Transactional в ваших двух классах игнорируются, поскольку вы создаете экземпляры этих классов напрямую (используя new), а не получаете экземпляр из контекста пружины.

Так что на самом деле, он сводится к следующему коду:

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

Блок finally всегда выполняется, даже если в блоке try создается исключение.Таким образом, тест создает человека в person1 и person2.

1 голос
/ 05 февраля 2012

@ Anders Ваш InnerClass с аннотацией @Transactional не является производным от интерфейса, если вы не используете прокси на основе AspectJ или CG-LIB, аспект @Transactional не вступит в силу, поскольку динамические прокси требуют интерфейсаприсутствовать.Быстрое исправление будет состоять в том, чтобы извлечь ваш внутренний класс из интерфейса, определить bean-компонент в конфигурации Spring и последовательно использовать интерфейс для обращения к bean-компоненту.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...