我正在使用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的「事務性」註釋。通過測試失敗,我的意思是斷言失敗,因爲在內部事務中添加的關係對測試方法是不可見的。 –