8

我需要使用 Jpa 2/Hibernate 3.5.1 将一些数据存储在 MySQL 数据库中。由于遗留原因,我要存储数据的表具有复合主键。主键的第一部分是 INTEGER 类型(自动增量值),第二部分是 BIGINT 类型(Java 代码中的 Long - 在持久化之前手动设置)。

我已经通过@IdClass-Annotation 实现(堆栈跟踪下方的示例代码)组合主键,第一个键部分也有一个生成策略集:@GeneratedValue(strategy = GenerationType.IDENTITY)

当试图用这样的代码持久化一个对象时

...
TestData testData = new TestData("data");
testData.setIdPartTwo(2L);

entityManager.getTransaction().begin();
entityManager.persist(testData);
entityManager.getTransaction().commit();

抛出以下异常:

javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: could not set a field value by reflection setter of org.example.persistence.TestDataId.idPartOne
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1235)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1168)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1174)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:674)
at org.example.persistence.PersistenceTest.shouldPersistTestData(PersistenceTest.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.testng.internal.MethodHelper.invokeMethod(MethodHelper.java:640)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:627)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:799)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1103)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:137)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:121)
at org.testng.TestRunner.runWorkers(TestRunner.java:1098)
at org.testng.TestRunner.privateRun(TestRunner.java:727)
at org.testng.TestRunner.run(TestRunner.java:581)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:315)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:310)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:272)
at org.testng.SuiteRunner.run(SuiteRunner.java:221)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:40)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:83)
at org.testng.internal.thread.ThreadUtil$CountDownLatchedRunnable.run(ThreadUtil.java:151)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)


Caused by: org.hibernate.PropertyAccessException: could not set a field value by reflection setter of org.example.persistence.TestDataId.idPartOne
at org.hibernate.property.DirectPropertyAccessor$DirectSetter.set(DirectPropertyAccessor.java:151)
at org.hibernate.mapping.Component$ValueGenerationPlan.execute(Component.java:438)
at org.hibernate.id.CompositeNestedGeneratedValueGenerator.generate(CompositeNestedGeneratedValueGenerator.java:122)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:122)
at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:69)
at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:179)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:800)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:774)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:778)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:668)
... 24 more


Caused by: java.lang.IllegalArgumentException: Can not set java.lang.Integer field org.example.persistence.TestDataId.idPartOne to org.hibernate.id.IdentifierGeneratorHelper$2
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:150)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:63)
at java.lang.reflect.Field.set(Field.java:657)
at org.hibernate.property.DirectPropertyAccessor$DirectSetter.set(DirectPropertyAccessor.java:139)
... 35 more

我的实体类如下所示:

@Entity
@IdClass(TestDataId.class)
public class TestData implements Serializable {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer idPartOne;
  @Id
  private Long idPartTwo;
  private String data;
  public TestData() {}
    // getters and setters
    // hashCode() and equals()
}

组合主键:

public class TestDataId implements Serializable {

  private static final long serialVersionUID = 1L;
  private Integer idPartOne;
  private Long idPartTwo;
  public TestDataId() {}
    // getters and setters
    // hashCode() and equals()
}

测试表是使用以下语句创建的:

CREATE TABLE `testdb`.`testdata` 
(`idPartOne` INTEGER NOT NULL AUTO_INCREMENT,
 `idPartTwo` BIGINT(20) NOT NULL DEFAULT 0,
 `data` VARCHAR(45),
 PRIMARY KEY(`idPartOne`, `idPartTwo`))
ENGINE = InnoDB;

将 GenerationType 更改为 TABLE 将使其工作,但会以 ~32.000 的步长生成 idPartOne 值。不幸的是,另一个应用程序在没有 JPA/Hibernate 的情况下使用了这个完全相同的数据库表,并且很好地以 1 的步长递增了这个 id-part。

要求无论哪个应用程序将数据存储到该表中,都以相同的方式生成 id(即 id 递增 1)。实现这一目标的最佳解决方案是什么?提示,我们不能更改其他应用程序!

非常感谢任何帮助。

谢谢,

马库斯

4

1 回答 1

1

注释嵌入的 id 时是否有效?

@Embeddable
public class TestDataId
{
    @GeneratedValue(strategy = GenerationType.TABLE)
    private Integer idPartOne;
    private Long idPartTwo;
}

@Entity
public class TestData
{
    @EmbeddedId
    private TestDataId key;
    private String data;
}
于 2012-06-07T14:29:36.740 回答