@Version Annotation Example In Hibernate

In this article, we will see @Version Annotation Example In Hibernate Using Spring Boot. We will see the complete example from scratch but before going ahead let’s see below points which demonstrate how @Version behaves in a different scenario.

 

@Version Annotation Example In Hibernate

 

 

 

Hibernate version default value – When we create an entity for the first time default version value is zero(the field which is annotated with @Version), which hibernate takes care.

When does hibernate version increase – When we modify some entity’s field and update that entity the version will increase. As many time we will update the entity version will increase, but at least one field should be changed. If we don’t modify any field and try to update the entity, the version will not update.

What will happen if don’t provide correct version – Suppose we are trying to update an entity and we are not providing the correct version(for example in database version value is 1 and we are providing version value 0 or 2 any number rather than 1) we will get an exception.

“optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction.”

Note – If we don’t provide the latest version it will throw an exception and we will not able to perform the update operation.

How to use @Version annotation with the field – See below sample code.

@Entity
public class Student {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;

	@Column(name = "student_name")
	private String studentName;

	@Column(name = "roll_number")
	private String rollNumber;
	
	@Column(name = "version")
	@Version
	private Long version;
	
}

 

When we create an entity for the first time default version value will be zero(we will see in the example later). Now we want to update this entity(modifying some field) we need to pass the same version number and our version should increase by +1. For example, consider we have an entity called Student which has four field id, studentName, rollNumber and version. If we modified one or more fields(i.e studentName or rollNumber ) of the entity and try to update this entity, the version will increase by +1. The @Version annotation in hibernate is used for Optimistic locking while performing update operation. In the Optimistic Locking approach first, we read a record from the database(this record contains the version number) and the user updates this record which we have just read from the database(suppose the user has modified one field in entity and try to update that entity). In this case, User will only able to update the record if the version hasn’t been changed by another user before you write the record back into the database.

To see how version works( in case of save and update) we will define two REST API as below.

http://localhost:9091/student/save – default version 0.

http://localhost:9091/student/update/1 – version will increase by 1. At least one field must modify else version will remain the same.

Let’s see @Version Annotation Example In Hibernate Using Spring Boot, Eclipse and Oracle from scratch.

Open eclipse and create maven project, Don’t forget to check ‘Create a simple project (skip)’ click on next.  Fill all details(GroupId – versionexample, ArtifactId – versionexample and name – versionexample) and click on finish. Keep packaging as the jar.

Modify pom.xml

<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>versionexample</groupId>
	<artifactId>versionexample</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>versionexample</name>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.2.RELEASE</version>
	</parent>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>

		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

		<dependency>
			<groupId>com.oracle</groupId>
			<artifactId>ojdbc6</artifactId>
			<version>11.2.0.3</version>
		</dependency>

	</dependencies>
	<build>
		<finalName>${project.artifactId}</finalName>
		<plugins>

			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<fork>true</fork>
					<executable>C:\Program Files\Java\jdk1.8.0_131\bin\javac.exe</executable>
				</configuration>
			</plugin>


		</plugins>
	</build>
</project>

Note – In pom.xml we have defined javac.exe path in configuration tag. You need to change accordingly i.e where you have installed JDK.

If you see any error for oracle dependency then follow these steps.

Directory structure –

@Version Annotation Example In Hibernate

 

 

Student.java

package com.javatute.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Version;

@Entity
public class Student {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;

	@Column(name = "student_name")
	private String studentName;

	@Column(name = "roll_number")
	private String rollNumber;
	
	@Column(name = "version")
	@Version
	private Long version;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getStudentName() {
		return studentName;
	}

	public void setStudentName(String studentName) {
		this.studentName = studentName;
	}

	public Long getVersion() {
		return version;
	}

	public void setVersion(Long version) {
		this.version = version;
	}

	public String getRollNumber() {
		return rollNumber;
	}

	public void setRollNumber(String rollNumber) {
		this.rollNumber = rollNumber;
	}
}

StudentController.java

package com.javatute.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.javatute.entity.Student;
import com.javatute.service.StudentService;

@RestController
@RequestMapping(value = "/student")
public class StudentController {

	@Autowired
	private StudentService studentService;

	@RequestMapping(value = "/save", method = RequestMethod.POST)
	@ResponseBody
	public Student saveBook(@RequestBody Student student) {
		Student studentResponse = (Student) studentService.saveStudent(student);
		return studentResponse;
	}
	
	@RequestMapping(value = "/update/{id}", method = RequestMethod.PUT)
	@ResponseBody
	public Student saveBook(@RequestBody Student student, @PathVariable int id) {
		Student existingStudent = studentService.findById(id);
		if(existingStudent == null) {
			throw new RuntimeException("Record not found");
		}
		Student studentResponse = (Student) studentService.updateStudent(student);
		return studentResponse;
	}

}

StudentRepository.java – interface

package com.javatute.repository;

import java.io.Serializable;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.javatute.entity.Student;
@Repository
public interface StudentRepository extends CrudRepository<Student,Serializable> {
	public Student findById(int id);
}

StudentService.java – interface

package com.javatute.service;

import org.springframework.stereotype.Component;

import com.javatute.entity.Student;

@Component
public interface StudentService {
	public Student saveStudent(Student student);
	
	public Student findById(int id);

	public Student updateStudent(Student student);
}

 

StudentServiceImpl.java

package com.javatute.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.javatute.entity.Student;
import com.javatute.repository.StudentRepository;
import com.javatute.service.StudentService;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Service("studentServiceImpl")
public class StudentServiceImpl implements StudentService {

	@Autowired
	private StudentRepository studentRepository;

	@Transactional
	public Student saveStudent(Student student) {
		Student studentresponse = studentRepository.save(student);
		return studentresponse;
	}

	@Transactional
	public Student findById(int id) {
		Student studentresponse = studentRepository.findById(id);
		return studentresponse;
	}

	@Transactional
	public Student updateStudent(Student student) {
		Student studentresponse = studentRepository.save(student);
		return studentresponse;
	}
	
}

SpringMain.java

package com.javatute.main;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = "com.*")
@EntityScan("com.javatute.entity")
public class SpringMain {
	public static void main(String[] args) {

		SpringApplication.run(SpringMain.class, args);
	}

}

 

JpaConfig.java

package com.javatute.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@Configuration
@EnableJpaRepositories(basePackages = "com.javatute.repository")
public class JpaConfig {

}

application.properties

# Connection url for the database
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:XE
spring.datasource.username=SYSTEM
spring.datasource.password=oracle2
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
# Show or not log for each sql query
spring.jpa.show-sql = true
 
 
spring.jpa.hibernate.ddl-auto =create
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.Oracle10gDialect
 
server.port = 9091

Let’s run the SpringMain class(run as java application).

Perform save operation first using below REST API.

http://localhost:9091/student/save

Request Data –

{
"studentName":"rakesh",
"rollNumber":"0126CS01"
}

Response Data –

{
    "id": 1,
    "studentName": "rakesh",
    "rollNumber": "0126CS01",
    "version": 0
}

In response, we have version value is zero. When we create entity first time the default value of the version is zero. Now we will update the entity. Just we will change the name or rollNumber and we will see what does happen.

For update the entity we will use below REST API. Let’s update the rollNumber of the student.

http://localhost:9091/student/update/1

Request Data –

{
    "id": 1,
    "studentName": "rakesh",
    "rollNumber": "0126CS02",
    "version": 0
}

Response Data –

{
    "id": 1,
    "studentName": "rakesh",
    "rollNumber": "0126CS02",
    "version": 1
}

Now version has been increased by +1. This is how @Version works in Hibernate. As many times we will update the student’s name or rollNumber version will increase by 1.

Now, what will happen if we want to update student entity but we don’t provide proper version? For example we want to update name too. Here what would be the version in request data? It would be 1 or 0?  When we are going to update entity for next time version should be current version i.e nothing but 1.

Valid Request Data –

{
    "id": 1,
    "studentName": "rakesh",
    "rollNumber": "0126CS02",
    "version": 1
}

Again the question comes what will happen if we try to update student entity with old version i.e 0(update rollNumber to 0126CS03)

Request data –

{
    "id": 1,
    "studentName": "rakesh",
    "rollNumber": "0126CS03",
    "version": 0
}

 

Response –

{
    "timestamp": "2020-01-11T18:12:33.690+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "Object of class [com.javatute.entity.Student] with identifier [1]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.javatute.entity.Student#1]",
    "path": "/student/update/1"
}

Oops ! We have exception

 

 

Note – When we create a new entity default version assign to zero by hibernate as below.

Versioning.java

private static Object seed(VersionType versionType, SharedSessionContractImplementor session) {
		final Object seed = versionType.seed( session );
		
		return seed;
	}

LongType.java

@Override
public Long seed(SharedSessionContractImplementor session) {
   return ZERO;
}

That’s all about @Version Annotation Example In Hibernate Using Spring Boot.

You may like.

See more about @Version. Here is the docs.