我的spring webmvc项目有问题,它通过JPA使用Hibernate。该应用程序模拟大学课程,学生,作业,公告和学生对这些作业和公告的评论(以及更多,但这些是此问题的相关实体)
部门实体:
@Entity
public class Section {
@Id
@GeneratedValue
long id;
@OneToMany(cascade = CascadeType.ALL)
private Set<Assignment> assignments = new HashSet<Assignment>();
@OneToMany(cascade = CascadeType.ALL)
private Set<Announcement> announcements = new HashSet<Announcement>();
... more properties, getters/setters, etc ...
分配和公告共享一个基类Commentable,它只有一个自动生成的Id。问题是,当我尝试在事务方法中创建一个节和一个赋值时,我得到一个“org.hibernate.exception.GenericJDBCException:无法执行JDBC批量更新”异常。
以下是测试用例:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath*:WEB-INF/spring-data.xml")
public class EntityPersistenceTest {
@PersistenceContext
EntityManager entityManager;
@Autowired
SectionService sectionService;
@Autowired
DbTestData dbTestData;
@Test
public void Test() {
dbTestData.persistTestData();
}
@After
public void afterTest() {
Section art = sectionService.byCourseSchoolIdYearSeason("art101", 2011, Semester.Season.fall);
Assert.assertTrue(art.getAssignments().size() > 0);
Assert.assertTrue(art.getAnnouncements().size() > 0);
System.out.println("Test completed successfully.");
}
}
这是persistTestData,来自DbTestData(用Component注释)
@Transactional
public void persistTestData(){
Course art = new Course("art101", "Art 101 - Basic Art", "General art class focusing on finger painting");
Professor ron = new Professor("Ron", "Bier", "ron@school.edu", "ron", "password");
Semester fall11 = new Semester(2011, Semester.Season.fall);
Section section = new Section(art, fall11, "51024");
entityManager.persist(art);
entityManager.persist(ron);
entityManager.persist(fall11);
entityManager.persist(section);
Date sevenDaysAway = new Date(System.currentTimeMillis() + (1000 * 60 * 60 * 24 * 7));
section.addAssignment(new Homework(ron, new Date(), sevenDaysAway, true, "Art project 1"));
section.addAnnouncement(new Announcement(ron, new Date(), "Announcement Text"));
}
以下是来自Hibernate的完整调试日志以及调用persistTestData时发生的错误代码:
Hibernate: insert into Course (id, description, schoolId, title) values (null, ?, ?, ?)
Hibernate: call identity()
Hibernate: insert into Person (id, email, enabled, first, last, passwordHash, username, DTYPE) values (null, ?, ?, ?, ?, ?, ?, 'Professor')
Hibernate: call identity()
Hibernate: insert into Semester (id, season, year) values (null, ?, ?)
Hibernate: call identity()
Hibernate: insert into Section (id, active, course_id, sectionId, semester_id) values (null, ?, ?, ?, ?)
Hibernate: call identity()
DEBUG: com.whiteboard.wb.data.entity.Assignment - Created a new Assignment with params
Hibernate: insert into Commentable (id, author_id, postDate, active, due, abstrct, DTYPE) values (null, ?, ?, ?, ?, ?, 'Homework')
Hibernate: call identity()
Hibernate: insert into Person_authorities (Person_id, authorities) values (?, ?)
Hibernate: insert into Section_Commentable (Section_id, assignments_id) values (?, ?)
WARN : org.hibernate.util.JDBCExceptionReporter - SQL Error: 0, SQLState: null
ERROR: org.hibernate.util.JDBCExceptionReporter - failed batch
ERROR: org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session
org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:140)
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:128)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:262)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:182)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1206)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:375)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:76)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:467)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:621)
at com.whiteboard.wb.data.sample.DbTestData$$EnhancerByCGLIB$$ca8fb7f2.persist(<generated>)
at entity.BigTest.Test(BigTest.java:45)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:71)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:199)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:62)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.sql.BatchUpdateException: failed batch
at org.hsqldb.jdbc.jdbcStatement.executeBatch(Unknown Source)
at org.hsqldb.jdbc.jdbcPreparedStatement.executeBatch(Unknown Source)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
... 49 more
我找到了解决这个问题的三种方法,其中没有一个我真正理解:
只有一个项目在Section实体中扩展为Commentable。从Section中删除“Announcements”onetomany集合(以及从测试中调用它)会使此错误完全消失。显然,我需要有多个不同类型的Commentables的集合,所以这是不可接受的。
使用@Transactional注释@Test调用。在其他地方使用相同的代码,这完全解决了问题,并且公告和家庭作业都得到了正确的保持而没有错误。
@Test
@Transactional
public void Test() {
dbTestData.persist();
}
在与创建该部分的事务不同的事务中添加分配和通知。如果我在afterTest()方法中添加赋值和通知,则所有查询都会成功完成。但是对于Section实体中的集合中的CascadeType = CascadeType.all,我不明白为什么这应该重要
我对ORM很新,并且一直试图了解这里发生了什么,所以我希望有更多经验的人可以帮助我。使用@Transactional注释Test方法的事实使我认为Hibernate生成的表可以处理这种具有多个实体集合的情况,这些实体扩展了相同的抽象类,但这意味着我不明白@交易方式让我担心。 (我已将Hibernate设置为使用我的供应商适配器bean上的<property name="generateDdl" value="true"/>
在我的Spring配置文件中自动生成所有必需的表)
感谢您的帮助。对不起,这个问题很长,可能是2行答案。
如果您需要我的代码,请告诉我。
答案 0 :(得分:0)
在持久化Section对象后,您将添加一个新的(和未显示的)Assignment和Announcement实例。您可以显式保留对象,也可以在保留节实例之前添加它们。
Date sevenDaysAway = new Date(System.currentTimeMillis() + (1000 * 60 * 60 * 24 * 7));
section.addAssignment(new Homework(ron, new Date(), sevenDaysAway, true, "Art project 1"));
section.addAnnouncement(new Announcement(ron, new Date(), "Announcement Text"));
entityManager.persist(art);
entityManager.persist(ron);
entityManager.persist(fall11);
entityManager.persist(section);