内部事务更改对外部事务不可见

时间:2011-10-06 12:08:43

标签: hibernate spring jpa transactions

我正在使用Spring3 + JPA + Hibernate。我试图保持示例的结构类似于我的实际代码结构。请滚动到底部以查看实际问题。 Zipped 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方法并删除对getFoogetBar的调用,则测试用例会成功而不会出错。这意味着,如果在fooifyBar之前发生getFoo,则fooifyBar所做的更改对测试方法不可见。那是为什么?

2 个答案:

答案 0 :(得分:1)

REQUIRES_NEW并不意味着嵌套事务,spring启动另一个事务暂停当前活动的事务。就数据库而言,它们是两个独立的交易。

如果您需要嵌套事务,则应使用NESTED属性。为此,数据库和驱动程序需要支持某些功能 - 我认为这些功能得不到广泛支持。

答案 1 :(得分:1)

您问的是,为什么在一个事务中所做的更改在第二个事务中不可见。这是使用事务的主要原因:到keep changes isolated直到提交。所以你有点问为什么关系数据库会像他们那样工作。