EclipseLink:"不允许更新"在本机查询INSERT INTO

时间:2014-12-15 12:30:19

标签: mysql jpa eclipselink

从EL 2.1升级到EclipseLink 2.5后,我们在以前运行的代码上遇到PersistenceException。 风景非常简单:我们有两个相同的表,唯一的区别是一个是另一个的“历史”版本。基本上,当我们确定一行不再更改时,我们将行移动到历史表。这意味着当在第一个表上生成id时,它将在历史表上“继承”。以下是实体:

@Entity
@Table(name = "DOCUMENT")
public class Document implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;
    // Some other fields
}

@Entity
@Table(name = "DOCUMENT_HISTORY")
public class DocumentHistory implements Serializable {
    @Id
    private long id;
    // Some other fields
}

为了将行移动到历史表中,我们使用本机查询(因为某些列未映射为实体类中的字段):

String query = "INSERT INTO DOCUMENT_HISTORY SELECT * FROM DOCUMENT t WHERE t.id=?1";
Query updateQuery = entityManager.createNativeQuery(query);
updateQuery.setParameter(1, document.getId());
updateQuery.executeUpdate();

执行此查询时,有时会抛出此异常:

javax.persistence.PersistenceException: Exception [EclipseLink-7251] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): 
org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [id] of class [org.myc.entities.jpa.company.DocumentHistory] is mapped to a primary key column in the database. Updates are not allowed.
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:868)
    at org.eclipse.persistence.internal.jpa.QueryImpl.performPreQueryFlush(QueryImpl.java:963)
    at org.eclipse.persistence.internal.jpa.QueryImpl.executeUpdate(QueryImpl.java:296)
    at org.myc.utility.jpa.user.DocumentManager.toDocumentHistory(DocumentManager.java:141)
    at java.lang.Thread.run(Thread.java:662)
Caused by: Exception [EclipseLink-7251] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [id] of class [org.myc.entities.jpa.company.DocumentHistory] is mapped to a primary key column in the database. Updates are not allowed.
    at org.eclipse.persistence.exceptions.ValidationException.primaryKeyUpdateDisallowed(ValidationException.java:2548)
    at org.eclipse.persistence.mappings.foundation.AbstractDirectMapping.writeFromObjectIntoRowWithChangeRecord(AbstractDirectMapping.java:1257)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildRowForUpdateWithChangeSet(ObjectBuilder.java:1768)
    at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.updateObjectForWriteWithChangeSet(DatabaseQueryMechanism.java:1030)
    at org.eclipse.persistence.queries.UpdateObjectQuery.executeCommitWithChangeSet(UpdateObjectQuery.java:84)
    at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:301)
    at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:899)
    at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:798)
    at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)
    at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1793)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1775)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1726)
    at org.eclipse.persistence.internal.sessions.CommitManager.commitChangedObjectsForClassWithChangeSet(CommitManager.java:267)
    at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsForClassWithChangeSet(CommitManager.java:192)
    at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:138)
    at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4196)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1441)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithPreBuiltChangeSet(UnitOfWorkImpl.java:1587)
    at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.writeChanges(RepeatableWriteUnitOfWork.java:452)
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:863)
    ... 5 more

有没有人有同样的问题?为什么你认为这不会发生在所有行上,而只发生在其中的一些行中(没有明显的特定特征)? 提前致谢, LUC

P.S。:EL对本机查询执行映射/查询验证是否正确?

2 个答案:

答案 0 :(得分:0)

如JPA 2.0规范中所述,executeUpdate()执行更新或删除语句,因此不支持insert。

您可以通过使用EntityManager(在事务中)持久化并刷新实体,将实体插入到基础数据库中。在您的特定情况下,虚拟示例可能如下所示:

// transaction starts
Document doc = em.find(Document.class, id);
DocumentHistory docHistory = new DocumentHistory(doc);
em.persist(docHistory);
// transaction ends

注意:您可以考虑使用EclipseLink's auditing功能代替专有解决方案(这很好):

  

EclipseLink还支持完整的历史记录支持,它允许在镜像历史记录表中跟踪对数据库所做的所有更改的完整历史记录。

答案 1 :(得分:0)

好的,我发现了问题并且它与该查询完全没有关系,所以我仍然不知道为什么会抛出异常。 问题是,我们没有使用entityManager.find()来获取新插入的历史记录行,而是创建了一个DocumentHistory实例(具有相同的ID),该实例未附加到持久化上下文,然后使用该实例执行其他操作。 希望它可以帮到某人。