JPA optimistic lock exception in Java Development
This post explains the JPA technology and its use in java development. Experts of java development India are explaining the use case of technologies- JPA and Hibernate, MySql database, Maven. Read this post and know what they want to say.
Technology: JPA stands for the Java Persistence API which is the standard from Sun Micro Systems for persistence. ORM stands for Object relational mapping. JPA is the standard for ORM. If a framework has to say ORM then it should implement all the specifications given by JPA. JPA is just specification, it needs persistence provide for CRUD operations. Hibernate, Eclipse link, etc. Are example of persistence providers.
Technologies: JPA and Hibernate, Maven, MySql database
Usecase: If you are working on Java development applications with JPA, hibernate, eclipse link you will be getting below exception often:
javax.persistence.OptimisticLockException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect).
The problem is a JPA persistence provider is trying to update the Object which is not the latest version object. Generally relational databases maintain version whenever a record is updated in the table. If you want to update the record in the table you should take the latest version record from the database and update it. As we are using ORM we should take the last version object and merge it.
So in this document we can see when this error occur and how to resolve it.
1. Project Structure
Project structure will be like the above screenshot.
If you look at the project structure, it’s a maven project pom.xml is the mandatory file it. In this maven file we can configure dependencies for jpa, hibernate, java, database etc. I am using MySQL database for data storage.
Three packages, one for entity, one for Business logic, one for Application testing. If you observe there is one DTO package, if you are working on multi-tier application entities will not be directly exposed to the client layer. The entity will be converted to DTO (Data Transfer Object) which will be mapped to entities.
2. Project Dependencies
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>naveen.examples</groupId> <artifactId>jpa</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>jpa</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> <hibernate.version>4.3.6.Final</hibernate.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- JPA --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>${hibernate.version}</version> </dependency> <!-- For connection pooling --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-c3p0</artifactId> <version>${hibernate.version}</version> </dependency> <!-- Database --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.31</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>
3. JPA Configuration
As this is Hibernate added hibernate dependencies, if it is some other ORM add those dependencies.
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" version="2.1"> <persistence-unit name="jpa-example" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <properties> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost/employee" /> <property name="javax.persistence.jdbc.user" value="root" /> <property name="javax.persistence.jdbc.password" value="root" /> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" /> <property name="hibernate.show_sql" value="true" /> <property name="hibernate.format_sql" value="true" /> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" /> <property name="hibernate.hbm2ddl.auto" value="validate" /> <!-- Configuring Connection Pool --> <property name="hibernate.c3p0.min_size" value="5" /> <property name="hibernate.c3p0.max_size" value="20" /> <property name="hibernate.c3p0.timeout" value="500" /> <property name="hibernate.c3p0.max_statements" value="50" /> <property name="hibernate.c3p0.idle_test_period" value="2000" /> </properties> </persistence-unit> </persistence>
This is the persistence.xml, here we need to mention database credentials, the persistence name, all the required attributes by the entity manager factory.
4. CRUD
This is the entity class which is going to persist in the database.
// Import statements @NamedQueries({ @NamedQuery(name = EmployeeBE.FIND_ALL, query = "SELECT e FROM EmployeeBE e order by e.name "), }) @Entity @Table(name = "T_EMP") public class EmployeeBE implements Serializable{ private static final long serialVersionUID = 1607726899931733607L; public static final String FIND_ALL = "naveen.examples.jpa.entity.EmployeeBE.find_all"; @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; @Column(name = "NAME") private String name; @Column(name = "version_num") @Version private int version; // Getters and setters
// Interface for CRUD operations. package naveen.examples.jpa.business; import java.util.List; import naveen.examples.jpa.entity.EmployeeBE; public interface EmployeeCrud { List<EmployeeBE> findAllEmployeeBECol(); EmployeeBE saveEmployee(EmployeeBE employeeBE); EmployeeBE updateEmployee(EmployeeBE employeeBE); EmployeeBE findById(int id);
// Implementatino class for CRUD operations public class EmployeeCrudImpl implements EmployeeCrud{ public EmployeeBE saveEmployee(EmployeeBE employeeBE) { EntityManager em = EntityManagerUtil.getEntityManager(); em.getTransaction().begin(); em.persist(employeeBE); em.getTransaction().commit(); return employeeBE; public EmployeeBE updateEmployee(EmployeeBE employeeBE) { EntityManager em = EntityManagerUtil.getEntityManager(); em.getTransaction().begin(); em.merge(employeeBE); em.getTransaction().commit(); return employeeBE; @SuppressWarnings("unchecked") public List<EmployeeBE> findAllEmployeeBECol() { EntityManager em = EntityManagerUtil.getEntityManager(); Query query = em.createNamedQuery(EmployeeBE.FIND_ALL); return query.getResultList(); public EmployeeBE findById(int id) { EntityManager em = EntityManagerUtil.getEntityManager(); return em.find(EmployeeBE.class, id);
// Entity Manager public class EntityManagerUtil { private static EntityManager entityManager; private EntityManagerUtil() { public static EntityManager getEntityManager() { if(entityManager==null){ EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("jpa-example"); return emFactory.createEntityManager(); return entityManager;
If you look at the above code snippets we have
- Interface, defines business methods
- InterfaceImpl, implementation of business methods
- EntityManagerUtility, to get the entity manger object.
In the EntityManagerUtil class we are passing jpa-example parameter that we mention in persistence.xml file, so it will connect to that persistence unit.
5. DEMO
Demonstrate how to hit the JPA optimistic lock exception.
public class Application { public static void main(String args[]) { EmployeeCrud employeeCrud = new EmployeeCrudImpl(); List<EmployeeBE> employeeBEs = employeeCrud.findAllEmployeeBECol(); if(!employeeBEs.isEmpty()){ EmployeeBE employeeBE = employeeBEs.get(0); employeeBE.setName("Updated"); employeeCrud.updateEmployee(employeeBE); // Here Optimistic lock exception employeeCrud.updateEmployee(employeeBE); }else{ EmployeeBE employeeBE = new EmployeeBE(); employeeBE.setName("Naveen"); employeeBE = employeeCrud.saveEmployee(employeeBE);
Before running the app there is no data in DB
If you run the application
If you observe there is data in the table with id 16 and version 0.
Now if you run the application in debug mode
If you observe the first update has completed, but there is same employee object in the code, as the first update method completed it’s already committed so the version becomes 1.
If you observe the screenshot the version became 1 but the object is the old one that means with version 0. Now if the second update method executed with old object, then you will get an Optimistic lock exception.
If you continue the execution, then results in an Optimistic lock exception.
6. Solution
To resolve this error we have two ways:
- Get the latest object from the database and set the old object values if you need those values to be persisted to the new object and merge it.
- For the old object set the latest version from Database.
I am following second approach, If you see screenshot I am setting the latest version number to old object
After execution in the console, no error:
This is the SQL query generated for the second update method. So the exception has not occurred.
Experts of Java development India have shared their best knowledge about JPA technology and its use in java project. If you need more details, you can ask the developers who are already applying this technology in their projects.
Conclusion
By using these two ways you can resolve this quite annoying error. As this is a very simple example, you can find it easily, but in real time application you will not find this much simple. So whenever you get this exception run the application in debug mode and go to the approximate code snippet where it’s showing then press F5 in debugging and go deep into the entity manager implementation of corresponding persistence provider somewhere else your entity is getting updated. Then follow eithr4 of these two techniques depending on your requirement. Keep on checking the version number in database. For each change they may or may not increase based on persistence provider implementation.
References
From:一号门
COMMENTS