version attributes behavior in bulk update - jpa

An application uses optimistic locking by defining version attributes in its entity classes. The application performs a bulk update of the entities using a JPQL query.In this case which of below statement is correct?
A.The persistence provider will ensure that the version value in each table is updated.
B.The value of the Version attributes of the updated entitles should be also be explicitly updated by the query.
As per JPA specification
"Bulk update maps directly to a database update operation, bypassing optimistic locking checks. Portable
applications must manually update the value of the version column, if desired, and/or manually validate
the value of the version column."
So as per my understanding option B is correct answer. But some of my colleague are saying other-way. Can you please tell me the correct behavior?

You are right, what it comes to specification A is definitely wrong answer.
Specific implementations of specification are still aloud to update value of #Version when JPQL bulk update is done. How certain implementation behaves is hopefully documented. When not, following can be used to check does bulk update affect version:
#Entity
public class SomeEntity {
#Id private int id;
private String someValue;
#Version private int version;
//getters, setters
}
//creating entity
tx.begin();
SomeEntity se = new SomeEntity();
se.setId(1);
em.persist(se);
tx.commit();
String versionJpql = "SELECT se.version FROM SomeEntity se WHERE se.id = 1";
//original version
Integer version = em.createQuery(versionJpql,Integer.class).getSingleResult();
//bulk update
tx.begin();
em.createQuery(
"UPDATE SomeEntity se SET se.someValue='some' WHERE se.id = 1"
).executeUpdate();
tx.commit();
Integer versionAfterUpdate = em.createQuery(versionJpql, Integer.class)
.getSingleResult();
//has version been changed:
assertEquals(version, versionAfterUpdate);

Related

How to properly use Locking or Transactions to prevent duplicates using Spring Data

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.

jpa repository save method returns different id from the one inserted into database

I'm using spring data (jpaRepository) + Oracle 11g Database.
Here's the code of my JUnit test:
#Test
public void testAjoutUtilisateur() {
Utilisateur utilisateur = new Utilisateur();
(...)
utilisateur=repository.save(utilisateur);
Utilisateur dbutilisateur = repository.findOne(utilisateur.getIdutilisateur());
assertNotNull(dbutilisateur);
When I debug I find that "utilisateur" object returned by repository.save method has an id like "2100" while the corresponding inserted line in the database have an id like "43".
I have an Oracle database with a sequence and a trigger to have the auto incremented property for the id for my "Utilisateur" table.
Here is the id definition in my Utilisateur entity:
#Entity
#NamedQuery(name="Utilisateur.findAll", query="SELECT u FROM Utilisateur u")
#SequenceGenerator(sequenceName="ID_UTILISATEUR_SEQ", name="ID_UTILISATEUR_SEQ")
public class Utilisateur implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ID_UTILISATEUR_SEQ")
private Long idutilisateur;
Where is the problem? Is it within the save method?
Thank you.
Edit:
I figured out that the problem was already solved by the solution of #jhadesdev and the data lines I was talking about were inserted when the triggers were actives.
Finally, I have to mention that by default the JUnit test seems to not insert data in the database (it inserts then rollback). In order to invalidate this behaviour we have to specify the #TransactionConfiguration(defaultRollback=false) annotation in the test class.
For example (in my case):
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:context/dao-context.xml" })
#TransactionConfiguration(defaultRollback=false)
#Transactional
public class UtilisateurRepositoryTest {
Hope it can help someone.
The problem is that two separate mechanisms are in place to generate the key:
one at Hibernate level which is to call a sequence and use the value to populate an Id column and send it to the database as the insert key
and another mechanism at the database that Hibernate does not know about: the column is incremented via a trigger.
Hibernate thinks that the insert was made with the value of the sequence, but in the database something else occurred. The simplest solution would probably be to remove the trigger mechanism, and let Hibernate populate the key based on the sequence only.

JPA #PrePersist & LockModeType.OPTIMISTIC_FORCE_INCREMENT

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.

Tracking last change to an object with #Version annotation in EclipseLink

Using JPA with EclipseLink, I would like to track the timestamp of the last update made to an entity instance. Assuming that this would be easy to combine with optimistic locking, I defined the entity as follows:
import javax.persistence.Version;
[...]
#Entity
public class Foo {
#Id int id;
#Version Timestamp lastChange;
[...]
}
Updating a changed object is done with the following code:
EntityManager em = Persistence.createEntityManagerFactory("myConfiguration");
em.getTransaction().begin();
em.merge(foo);
em.getTransaction().commit();
I would expect that foo.lastChange would be set to the new timestamp each time an update to a changed instance is committed. However, while the field LASTCHANGE is updated in the database, it is not updated in the object itself. A second attempt to save the same object again thus fails with an OptimisticLockException. I know that EclipseLink allows to choose between storing the version-field in cache or directly in the object and I made sure that the configuration is set to IN_OBJECT.
The obvious question is: How to get the foo.lastChange field set to the updated timestamp value when saving to the database? Would
foo = em.find(Foo.class, foo.id);
be the only option? I suspect there must be a simpler way to this.
merge does not modify its argument. It copies the state from its argument to the attached version of its argument, and returns the attached version. You should thus use
foo = em.merge(foo);
// ...
return foo;

openjpa throws optimisticklockexception

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...