我正在使用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>
上面给出的形式。如果我修改ServiceImpl
类中的FooifyBar
方法并删除对getFoo
和getBar
的调用,则测试用例成功无错。这意味着如果getFoo
发生在fooifyBar
之前,则fooifyBar
对测试方法不可见。这是为什么?
我不是说嵌套事务,修正了标题。正如你所说,这两个是单独的交易,所以内部交易的变化必须对外部交易可见。更奇怪的是,如果我将调用移动到insertFooRelationAndUpdateBar中的getFoo,则测试用例会成功。 –
什么是隔离级别 - 事务启动后对数据库所做的更改是否可见取决于隔离级别。在你的例子中,还不清楚你测试失败的意思 - 哪些是内部和外部事务? – gkamal
隔离不变,即它是默认值。外部事务从测试方法开始,内部事务以insertFooRelationAndUpdateBar方法开始,该方法具有传播= REQUIRES_NEW的“事务性”注释。通过测试失败,我的意思是断言失败,因为在内部事务中添加的关系对测试方法是不可见的。 –