Spring MVC app + Hibernate = GenericJDBCException

时间:2011-10-17 21:55:13

标签: hibernate jpa spring-mvc

  

可能重复:
  Hibernate and Spring - entity with multiple members inheriting from same parent causes JDBCException, @Transactional weirdness

我已将此问题重新格式化为SSCCE - See the reformatted question

我的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

我找到了解决这个问题的三种方法,其中没有一个我真正理解:

  1. 只有一个项目在Section实体中扩展为Commentable。从Section中删除“Announcements”onetomany集合(以及从测试中调用它)会使此错误完全消失。显然,我需要有多个不同类型的Commentables的集合,所以这是不可接受的。

  2. 使用@Transactional注释@Test调用。在其他地方使用相同的代码,这完全解决了问题,并且公告和家庭作业都得到了正确的保持而没有错误。

    @Test
    @Transactional
    public void Test() {
        dbTestData.persist();
    }
    
  3. 在与创建该部分的事务不同的事务中添加分配和通知。如果我在afterTest()方法中添加赋值和通知,则所有查询都会成功完成。但是对于Section实体中的集合中的CascadeType = CascadeType.all,我不明白为什么这应该重要

  4. 我对ORM很新,并且一直试图了解这里发生了什么,所以我希望有更多经验的人可以帮助我。使用@Transactional注释Test方法的事实使我认为Hibernate生成的表可以处理这种具有多个实体集合的情况,这些实体扩展了相同的抽象类,但这意味着我不明白@交易方式让我担心。 (我已将Hibernate设置为使用我的供应商适配器bean上的<property name="generateDdl" value="true"/>在我的Spring配置文件中自动生成所有必需的表)

    感谢您的帮助。对不起,这个问题很长,可能是2行答案。

    如果您需要我的代码,请告诉我。

1 个答案:

答案 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);