У меня есть таблица
CREATE TABLE `SomeEntity` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`subid` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`,`subid`),
У меня есть класс сущности с полем автоматического увеличения в нем. Я хочу прочитать идентификатор автоматического увеличения, назначенный ему, когда он будет сохранен
Аннотации к геттеру приведены ниже
private long id;
private int subid;
@Id
@GeneratedValue **//How do i correct this to have multiple rows with same id and different subid**
@Column(name = "id")
public long getId() {
return id;
}
@Id
@Column(name = "subid")
public int getSubid() {
return subid;
}
Я хочу иметь объекты как
id 1 subid 0
id 1 subid 1
id 1 subid 2
id 2 subid 0
subid по умолчанию 0 в базе данных, и я постепенно увеличиваю его на обновления в этой строке. Я попробовал решение, как в этом сообщении SO JPA - возврат автоматически сгенерированного идентификатора после сохранения()
@Transactional
@Override
public void daoSaveEntity(SomeEntity entity) {
entityManager.persist(entity);
}
Теперь за пределами этой транзакции я пытаюсь получить идентификатор auto increment id
@Override
public long serviceSaveEntity(SomeEntity entity) {
dao.daoSaveEntity(entity);
return entity.getId();
}
Я вызываю это из веб-службы
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response createEntity(SomeEntity entity) {
Метод обновления приведен ниже
@Transactional
public void updateReportJob(SomeEntity someEntity) {
Query query =
entityManager
.createQuery("UPDATE SomeEntity SET state=:newState WHERE id = :id");
query.setParameter("newState","PASSIVE");
query.setParameter("id", id);
query.executeUpdate();
double rand = Math.random();
int i = (int) (rand * 300);
try {
Thread.sleep(i); //only to simulate concurrency issues
} catch (InterruptedException e) {
e.printStackTrace();
}
List<Integer> resList =
entityManager.createQuery("select max(subid) from SomeEntity WHERE id = :id")
.setParameter("id", jobId).getResultList();
// Increment old subid by 1
int subid = resList.get(0);
SomeEntity.setsubid(subid + 1);
SomeEntity.setState("ACTIVE");
// entityManager.merge(SomeEntity);
entityManager.persist(SomeEntity);
}
я отправляю N одновременных обновлений из N потоков для объекта с Id 1 и несколькими другими свойствами, как показано ниже
SomeEnity entity = new SomeEntity();
entity.setId(1);
long num = Thread.currentThread().getId();
entity.setFieldOne("FieldOne" + num);
entity.setFieldTwo("" + i);
entity.setFieldThree("" + j);
i++;
j++;
Случай 1 с помощью @Id
в id и @Id
аннотации к субиду и `entityManager.persist 'в обновлении
Когда я работал с 300 потоками, некоторые с ошибкой подключения "слишком много соединений" Состояние базы данных
id 1 subid 0
id 1 subid 1
id 1 subid 2
.. ....
id 1 subid 150
субад всегда инкрементален, условие гонки - только то, что будет ACTIVE, undefined из-за состояния гонки
Случай 2 с @Id
в id и @Id
аннотации в субиде и `entityManager.merge 'в обновлении
id 1 субад 0 id 1 subid 0 id 2 subid 0 ...... id 151 subid 0 (Возможно, только совпадение, что еще один поток, чем случай 1, был успешным?)
Случай 3 С @GeneratedValue
и @Id
в id и ** NO @Id
аннотация по субиду и entityManager.persist в обновлении **
exception - Отдельный объект, переданный для сохранения
Случай 3 С @GeneratedValue
и @Id
в id и ** NO @Id
аннотация по субиду и entityManager.merge в обновлении **
если обновление выполняется последовательно, состояние базы данных
id 1 subid 0
после следующего обновления
id 1 subid 1
после обновления каждой новой версии обновления (что приводит только к одной строке за раз)
id 1 subid 2
Случай 4, аналогичный случаю 3 с параллельными обновлениямиесли запустить одновременно (с 300 потоками), я получаю следующее исключение
org.hibernate.HibernateException: More than one row with the given identifier was found: 1
Состояние базы данных id 1 subid 2 (Только один поток был бы успешным, но из-за обновленного состояния ранга от 0 до 2)
Случай 5 С @GeneratedValue
и @Id
на id и @Id
аннотации на субад
Create также терпит неудачу с subid org.hibernate.PropertyAccessException: IllegalArgumentException произошло при вызове setter of SomeEntity.id
Пожалуйста, объясните причины. Из javadoc методов я знаю, что
persist - сделать экземпляр управляемым и постоянным.
merge - объединить состояние данного объекта в текущий контекст сохранения.
Мой вопрос больше связан с тем, как hibernate управляет аннотациями. Почему в случае 3 существует исключение отдельного объекта, если сеанс еще не закрыт? Почему в случае 5 существует исключение IllegalArgumentException?
Я использую hibernate 3.6 mysql 5 и Spring 4 Также попробуйте указать такой инкрементный id и субад. (Использование пользовательского SelectGenerator с демонстрационной реализацией или любым другим способом без выполнения столбца concat)