I am trying openjpa and jpa. All I have is one entity class as corresponding table in the database. one of the attributes of the entity is username and corresponding row in the db table has varchar2(20). and in my main method what i tried to persist and instance of the entity with username longer than 20.
All I am doing is
em.getTransaction().begin();
em.persist(entity); //entity here is the instance with the username longer than 20
em.getTransaction().commit();
I tried this, hoping to get some other kind of exception, but I don't why I am getting optimisticklockexception.
I do not have any locking setting. I mean I am using default values for locking property.
Does anybody know what's happening here?
Not sure why this happens...I have noticed that the OptimisticLockException can be thrown in weird cases...
Adding a version field to your table and entity can often make OpenJPA work better with locking...
In your entity bean add this (also add the column named VERSION to your table):
private Long version;
#Version
#Column(name="VERSION")
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
Hope this helps...
Related
What is the best way to check if a record exists and if it doesn't, create it (avoiding duplicates)?
Keep in mind that this is a distributed application running across many application servers.
I'm trying to avoid these:
Race Conditions
TOCTOU
A simple example:
Person.java
#Entity
public class Person {
#Id
#GeneratedValue
private long id;
private String firstName;
private String lastName;
//Getters and Setters Omitted
}
PersonRepository.java
public interface PersonRepository extends CrudRepository<Person, Long>{
public Person findByFirstName(String firstName);
}
Some Method
public void someMethod() {
Person john = new Person();
john.setFirstName("John");
john.setLastName("Doe");
if(personRepo.findByFirstName(john.getFirstName()) == null){
personRepo.save(john);
}else{
//Don't Save Person
}
}
Clearly as the code currently stands, there is a chance that the Person could be inserted in the database in between the time I checked if it already exists and when I insert it myself. Thus a duplicate would be created.
How should I avoid this?
Based on my initial research, perhaps a combination of
#Transactional
#Lock
But the exact configuration is what I'm unsure of. Any guidance would be greatly appreciated. To reiterate, this application will be distributed across multiple servers so this must still work in a highly-available, distributed environment.
For Inserts: if you want to prevent same recordsto be persisted, than you may want to take some precoutions on DB side. In your example, if firstname should be unique, then define a unique index on that column, or a agroup of colunsd that should be unique, and let the DB handle the check, you just insert & get exception if you're inserting a record that's already inserted.
For updates: use #Version (javax.persistence.Version) annotation like this:
#Version
private long version;
Define a version column in tables, Hibernate or any other ORM will automatically populate the value & also verison to where clause when entity updated. So if someone try to update the old entity, it prevent this. Be careful, this doesn't throw exception, just return update count as 0, so you may want to check this.
I've an Entity (with a primary key that is not generated by a sequence) like this in a Spring Data JPA/Eclipselink environment :
#Entity
#Table(name="MY_ENTITY")
public class MyEntity implements Serializable {
#Id
#Column(insertable=true, updatable=true, nullable=false)
private String propertyid;
\\other columns
}
and I'm trying to delete a row from the table and reinsert it (with the same primary key).
My approach is to call deleteAll() to clean the table and then save() the new Entity :
#Transactional
public void deleteAndSave(MyEntity entity) {
propertyInfoRepository.deleteAll();
propertyInfoRepository.flush(); // <- having it or not, nothing changes
propertyInfoRepository.save(entity);
}
but this gives me this error :
Caused by: java.lang.IllegalArgumentException: Cannot merge an entity that has been removed: com.xxx.MyEntity#1f28c51
at org.eclipse.persistence.internal.sessions.MergeManager.registerObjectForMergeCloneIntoWorkingCopy(MergeManager.java:912)
at org.eclipse.persistence.internal.sessions.MergeManager.mergeChangesOfCloneIntoWorkingCopy(MergeManager.java:494)
at org.eclipse.persistence.internal.sessions.MergeManager.mergeChanges(MergeManager.java:271)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.mergeCloneWithReferences(UnitOfWorkImpl.java:3495)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.mergeCloneWithReferences(RepeatableWriteUnitOfWork.java:378)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.mergeCloneWithReferences(UnitOfWorkImpl.java:3455)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.mergeInternal(EntityManagerImpl.java:486)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.merge(EntityManagerImpl.java:463)
....
What am I doing wrong?
I do not understand why it is trying to merge the entity instead of simply reinsert it after its deletion.
Thanks for your help!
Directly to answer your question:
The problem is that the entity that you try to save has already a persistent identity, i.e an ID, which is why your repository will try to merge, and not to persist the entity.
If you see this question it seems that it is triggered (at least) on the level of the Spring Repository, so you might consider overriding the save method of the repository and test whether the problem is still there.
JPA EntityManager keeps track of the state of each managed entity. In your case, you delete the entity and then try to merge it, which raises the exception. I can't tell if your approach is correct (seems weird to delete and then merge) since you don't provide the whole picture but you can try the following:
Assuming em is your EntityManager and entity your entity:
em.remove(entity); //This will perform the delete
MyEntity detachedEntity = em.detach(entity); //Gets a detached copy of the entity, EM will not operated on this unless told to do so (see below)
detachedEntity.setId(null) // Avoid duplicate key violations; Optional since you are deleting the original entity
em.persist(detachedEntity); // This will perform the required insert
I came up with interesting situation that I already know how to work around, but I was wondering if there is some elegant solution for this.
I have an Entity, which can not have a #Versio field since it is based on a legacy database, and the table has no column to have this kind of value.
Basically it is something like this:
#Entity
public class MyEntity {
#Id
private int id;
#Temporal(TemporalType.DATE)
private java.util.Date lastUpdated;
}
This is basically just for EULA (End User License Agreement) checking.
I want the Date to be updated when the eula has to be re-accepted (The new eula date is got from other place).
For that I was planning to use:
#PrePersist
#PreUpdate
protected void setPersistTime() {
this.lastUpdated = new Date();
}
The #PrePersist is called correctly when the entity is stored for the first time, but on the subsequent times the JPA seems to think that the entity is the same as before and the #PreUpdate won't be called as there is nothing to change.
I was planning to use
em.refresh(myEntity, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
But that won't work without the #Version which I cannot use due to the legacy db. (no version field I could use and the Date is of wrong type for it).
Btw. Using EclipseLink.
Unfortunately I'm getting an OptimisticLockException in my code and I'm not sure why. Perhaps there is someone who can help me with an answer to a general question.
Following scenario:
#Entity
public class MyEntity {
#Id
#GeneratedValue
private Integer id;
#Version
private int version;
private String value;
}
#Singleton
#TransactionManagement(TransactionManagementType.CONTAINER)
public class MyBean {
#PersistenceContext
private EntityManager em;
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void test() {
MyEntity myEntity = em.find(MyEntity.class, 1);
}
}
CMT are used. Method test() requires a new transaction.
Now my question: Can method test() throw an OptimisticLockException if there is another thread in another bean using the same persistence context changing my entity before commit although I only use find and don't update anything in my method test()?
from this blog
JPA Optimistic locking allows anyone to read and update an entity, however a version check is made upon commit and an exception is thrown if the version was updated in the database since the entity was read
So there is no need to do an update to get an OptimisticLockingException. Assume myEntity.getVersion()==1 when you read it. You will have an OptimisticLockingException if, at commit (i.e. when your test() method ends), the actual value in the version column is != 1.
It means that someone updated the entity (in the mean time between the READ and the transaction COMMIT) and so the values you have just read are no more valid at commit time.
PostgreSQL 9.1
Glassfish 3.1.2.2
Firefox 10.0.0.7
Linux
I am using JPA to persist entities in PostgreSQL. However, there is one table (named Position) which is populated by triggers and I use an entity of that table which acts as a read only. The entity never persist data into this table as it's loaded and manipulated from the triggers. I can load the data into my managed bean and view the Position table data fine.
The problem I have is that once the database (Position table) has been modified by the triggers, when the Position table is queried again, the values are still the same. The Position entity still contains the old values and have not reloaded.
Here is the bean which handles the Position entity. I added em.flush() which didn't help and wasn't sure how to use em.refresh() in this way. And actually, how would syncing help anyways since it doesn't know what I want to sync to without a query.
Any help much appreciated.
The EJB ...
#Stateless
public class PositionBean implements IPosition {
#PersistenceContext(unitName="positionbean-pu")
private EntityManager em;
public Position getPositionById(Integer posId) {
Position pos = null;
try {
pos = (Position) em.createNamedQuery("findPosition")
.setParameter("posId", posId).getSingleResult();
}
catch (Exception e) {
throw new EJBException(e.getMessage());
}
return pos;
}
The entity bean ...
#Entity
#SequenceGenerator(name="posIdGen", initialValue=10000,
sequenceName="pos_seq", allocationSize=1)
#NamedQuery(name="findPosition",
query="SELECT p FROM Position p WHERE p.posId = :posId")
#Table(name="Position")
public class Position implements Serializable {
// ...
persistence.xml
<persistence-unit name="positionbean-pu" transaction-type="JTA">
<jta-data-source>jdbc/postgreSQLPool</jta-data-source>
</persistence-unit>
In case anyone runs into this, I learned how to use refresh() and this fixed my problem.
pos = (Position) em.createNamedQuery("findPosition")
.setParameter("posId", posId).getSingleResult();
em.refresh(pos);
In my opinion the correct way to create a read only entity that is updated "externally" is as follows:
Do use field based annotation for the entity class by setting annotation #Access to AccessType.FIELD (or remove the annotation because it is the default access type) as stated out in this answer. Also propose public getters only as stated out in this answer. This prevents the entity from beeing modified within the application.
Enable selective shared cache mode in persistence.xml file and add annotation #Cacheable(false) to the entity class as stated out in this answer. This forces JPA to always load the entity from the persistence layer when you call EntityManager#find(...).