Мне нужно сохранить некоторые данные в базе данных MySQL, используя Jpa 2/Hibernate 3.5.1. По устаревшим причинам таблица, в которой я хочу хранить данные, имеет составной первичный ключ. Первая часть первичного ключа имеет тип INTEGER (автоинкрементное значение), вторая часть имеет тип BIGINT (длинный в Java-коде — задается вручную перед сохранением).
Я реализовал (пример кода под трассировкой стека) комбинированный первичный ключ через аннотацию @IdClass, часть первого ключа также имеет набор стратегий генерации: @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 заставит его работать, но будет генерировать значения idPartOne с шагом ~ 32 000. К сожалению, другое приложение использует ту же самую таблицу базы данных без JPA/Hibernate и хорошо увеличивает эту часть идентификатора с шагом 1.
Требуется, чтобы генерация идентификатора выполнялась одинаково, независимо от того, какое приложение хранит данные в этой таблице (то есть приращение идентификатора на 1). Что было бы лучшим решением для достижения этого? Подсказка, мы не можем изменить другое приложение!
Любая помощь очень ценится.
Спасибо,
Маркус
idPartOne
,idPartTwo
)? Возможно ли, что в эту таблицу вставлена запись, но с дублированным «idPartOne»? - person Mike Lue   schedule 07.03.2012