Изменения внутренней транзакции не видны внешней транзакции - PullRequest
0 голосов
/ 06 октября 2011

Я использую Spring3 + JPA + Hibernate.Я попытался сохранить структуру примера похожей на мою структуру кода.Пожалуйста, прокрутите вниз до фактического вопроса.Заархивированный проект maven можно загрузить с www.esnips.com/nsdoc/da7a09c0-ce5a-4dbf-80a2-f414ea3bf333/?action=forceDL

Ниже приводится тестируемый класс.

public class ServiceImpl implements Service {

@Autowired
private DataAccessor dataAccessor;

@Autowired
private ServiceTransactions serviceTransactions;

public Foo getFoo(long id) {
    return dataAccessor.getFoo(id);
}

public Foo createFoo(Foo foo) {
    return dataAccessor.createFoo(foo);
}

public Bar createBar(Bar bar) {
    return dataAccessor.createBar(bar);
}

@SuppressWarnings("unused")
public Foo FooifyBar(long fooId, long barId) {
    Foo foo = dataAccessor.getFoo(fooId);
    Bar bar = dataAccessor.getBar(barId);
    return serviceTransactions.fooifyBar(fooId, barId, "Error");
}

}

Ниже приведен класс ServiceTransactions.

public class ServiceTransactions {
    @Autowired
    private DataAccessor dataAccessor;

    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public Foo fooifyBar(long fooId, long barId, String error) {
    Foo foo = dataAccessor.getFoo(fooId);
    Bar bar = dataAccessor.getBar(barId);
    return dataAccessor.fooifyBar(foo, bar, error);
    }
}

Ниже приводится реализация используемого DataAccessor.

public class DataAccessorImpl implements DataAccessor {

@Autowired
private DBController controller;

@Transactional
public Foo getFoo(long id) {
    FooDao food = controller.getFoo(id);
    return convertFoodToFoo(food);
}

@Transactional
public Foo createFoo(Foo foo) {
    FooDao food = new FooDao();
    food.setName(foo.getName());
    return convertFoodToFoo(controller.createFoo(food));
}

@Transactional
public Bar getBar(long id) {
    return convertBardToBar(controller.getBar(id));
}

@Transactional
public Bar createBar(Bar bar) {
    BarDao bard = new BarDao();
    bard.setName(bar.getName());
    return convertBardToBar(controller.createBar(bard));
}

@Transactional
public Foo fooifyBar(Foo foo, Bar bar, String error) {
    return convertFoodToFoo(controller.fooBar(foo.getId(), bar.getId(), error));
}

Ниже приведена реализация DBController

public class DBControllerImpl implements DBController {

@PersistenceContext 
private EntityManager em;

public FooDao getFoo(long id) {
    return em.find(FooDao.class, id);
}

public FooDao createFoo(FooDao foo) {
    em.persist(foo);
    return foo;
}

public BarDao getBar(long id) {
    return em.find(BarDao.class, id);
}

public BarDao createBar(BarDao bar) {
    em.persist(bar);
    return bar;
}

public FooDao fooBar(long fooId, long barId, String error) {
    FooDao foo = em.find(FooDao.class, fooId);
    FooedBarDao fb = new FooedBarDao();
    fb.setFoo(foo);
    fb.setBar(em.find(BarDao.class, barId));
    fb.setError(error);
    em.persist(fb);

    foo.getFooedBars().add(fb);

    em.merge(foo);
    return foo;
}

И, наконец, тестовый класс

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="/testContext.xml")
public class TestFooBar {

@Autowired
private Service service;
Foo foo;
Bar bar;

@BeforeTransaction
public void before() {
    foo = new Foo();
    foo.setName("foo");
    foo = service.createFoo(foo);
    bar = new Bar();
    bar.setName("bar");
    bar = service.createBar(bar);
}

@Test
@Transactional
public void testFooingBar() {
    service.FooifyBar(foo.getId(), bar.getId());
    Foo foo2 = service.getFoo(foo.getId());
    Assert.assertEquals(1, foo2.getFooedBars().size());
}

Теперь вопрос : тестовый случай завершается с ошибкой testFooingBar(com.test.sscce.server.TestFooBar): expected:<1> but was:<0> в приведенной выше форме.Если я изменю метод FooifyBar в классе ServiceImpl и удаляю вызовы getFoo и getBar, тестовый сценарий пройдет без ошибок.Это означает, что изменения, сделанные с помощью fooifyBar, не видны в методе тестирования, если getFoo происходит до fooifyBar.Почему это так?

Ответы [ 2 ]

1 голос
/ 06 октября 2011

Вы спрашиваете, почему изменения, сделанные в одной транзакции, не видны во второй транзакции. Это основная причина использования транзакций: сохранять изменения изолированными до фиксации. Таким образом, вы задаетесь вопросом, почему реляционные базы данных работают так, как они работают.

1 голос
/ 06 октября 2011

REQUIRES_NEW не означает вложенную транзакцию, Spring запускает другую транзакцию, приостановив ту, которая в данный момент активна.Что касается БД, то это две независимые транзакции.

Если вам нужна вложенная транзакция, вы должны использовать атрибут NESTED.Чтобы это работало, база данных и драйвер должны поддерживать определенные функции, которые, как мне кажется, не поддерживаются.

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