I have a table
CREATE TABLE `SomeEntity` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`subid` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`,`subid`),
I have a entity class with an auto increment field in it.I want to read auto increment id assigned to it when it gets persisted
Annotations on getter are as below
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;
}
I want to have entities as
id 1 subid 0
id 1 subid 1
id 1 subid 2
id 2 subid 0
subid is default 0 in database and i am incrementing it programmatically on updates to that row.
I tried the solution as in this SO post
JPA - Returning an auto generated id after persist()
@Transactional
@Override
public void daoSaveEntity(SomeEntity entity) {
entityManager.persist(entity);
}
Now outside this transaction I am trying to get the auto increment id assigned
@Override
public long serviceSaveEntity(SomeEntity entity) {
dao.daoSaveEntity(entity);
return entity.getId();
}
I am calling this from a web service
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response createEntity(SomeEntity entity) {
The update method is as below
@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);
}
i send N concurrent updates from N threads for entity with Id 1 and few other properties as below
SomeEnity entity = new SomeEntity();
entity.setId(1);
long num = Thread.currentThread().getId();
entity.setFieldOne("FieldOne" + num);
entity.setFieldTwo("" + i);
entity.setFieldThree("" + j);
i++;
j++;
Case 1 with With @Id
on id and @Id
annotation on subid and `entityManager.persist' in update
When i ran with 300 threads some failed with connection exception "too many connections" The databse state is
id 1 subid 0
id 1 subid 1
id 1 subid 2
.. ....
id 1 subid 150
the subid is always incremental ,the race condition is only that which one will be ACTIVE is undefined because of race condition
Case 2 with With @Id
on id and @Id
annotation on subid and `entityManager.merge' in update
id 1 subid 0
id 1 subid 0
id 2 subid 0
.. ....
id 151 subid 0 (Perhaps just a co-incidence that one more thread than case 1 was successful? )
Case 3 With @GeneratedValue
and @Id
on id and **NO @Id
annotation on subid and entityManager.persist in update**
exception -- Detached entity passed to persist
Case 3 With @GeneratedValue
and @Id
on id and **NO @Id
annotation on subid and entityManager.merge in update**
if update is run sequentially the database state is
id 1 subid 0
after next update
id 1 subid 1
after each update same row is updated (leading to only one row at a time)
id 1 subid 2
Case 4 same as Case 3 with Concurrent updates
if run concurrently(with 300 threads) i get the below exception
org.hibernate.HibernateException: More than one row with the given identifier was found: 1
database state is
id 1 subid 2 (Only one thread would have been successful but because of race condition updated subid from 0 to 2 )
Case 5 With @GeneratedValue
and @Id
on id and @Id
annotation on subid
Create also fails with subid org.hibernate.PropertyAccessException: IllegalArgumentException occurred while calling setter of SomeEntity.id
Please explain the causes.From the javadoc of methods i know that
persist - Make an instance managed and persistent.
merge - Merge the state of the given entity into the current persistence context.
My question is more towards how hibernate manages the annotations.
Why is there be a detached entity exception in case 3 when the session is not closed yet?
Why is there a IllegalArgumentException in case 5 ?
I am using hibernate 3.6 mysql 5 and Spring 4
Also please suggest a way achieve such incremental id and subid.(Using custom SelectGenerator ,with a demo implementation or any other way without doing a column concat)
See Question&Answers more detail:
os