Spring Data JPA @Modifying Annotation Example

In this post, we will see about Spring Data JPA @Modifying Annotation Example using Spring Boot and Oracle.

We will see how @Modifying annotation helps to execute DML (INSERT, UPDATE, DELETE, etc) named parameter queries that we write using @Query annotations.

Introduction.

Generally, we use CrudRepository’s save() method to create or update an entity in Spring Data JPA. in this tutorial, we are going to see how to insert/update/get/delete records using @Query & @Modifying annotations.

Spring Data JPA provides @Modifying annotation, using this annotation we can write named parameters query using @Query annotation and we can insert, update, or delete an entity.

We will have four APIs to test our implementation as below. We will use postman for testing.

Request MethodRest APIs/End Points
POSThttp://localhost:9091/student/insert
PUThttp://localhost:9091/student/update
GEThttp://localhost:9091/student/{id}
DELETEhttp://localhost:9091/student/delete

Note – The method where we use @Modifying annotation, return type should be int or void. Otherwise, it will throw an exception(we will see later in detail). Insert, update, and delete an entity using Spring Data JPA @Modifying Annotation.

Consider we have an entity called Student.java.

package com.javatute.entity;

@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 = "university")
	String university;
}

Insert an entity using Spring Data JPA @Modifying annotation.

Repository code.

@Modifying
@Query(value = "insert into Student (id,student_name,roll_number, university) "
        + "VALUES(:id,:studentName,:rollNumber,:university)", nativeQuery = true)
public void insertStudentUsingQueryAnnotation(@Param("id") int id, @Param("studentName") String studentName,
        @Param("rollNumber") String rollNumber, @Param("university") String university);

Calling repository method.

@Override
@Transactional
public String insertStudent(Student student) {
    int id = student.getId();
    String studentName = student.getStudentName();
    String rollNumber = student.getRollNumber();
    String university = student.getUniversity();
    studentRepository.insertStudentUsingQueryAnnotation(id, studentName, rollNumber, university);
    return "Record inserted successfully using @Modifiying and @query Named Parameter";
}

Update an entity using Spring Data JPA @Modifying annotation.

Repository code.

@Modifying
@Query("update Student s SET s.studentName = :studentName WHERE s.id = :id")
public void updateStudentUsingQueryAnnotation(@Param("studentName") String studentName, @Param("id") int id);

Calling repository method to update entity using @Modifying.

@Transactional
public String updateStudent(Student student) {
    studentRepository.updateStudentUsingQueryAnnotation(student.getStudentName(), student.getId());
    return "Record updated successfully using @Modifiying and @query Named Parameter";
}

Delete an entity using Spring Data JPA @Modifying annotation.

@Modifying
@Query("delete from Student s where s.id = :id")
public void deleteStudentUsingQueryAnnotation(@Param("id") int id);
@Override
@Transactional
public String deleteStudent(Student student) {
    studentRepository.deleteStudentUsingQueryAnnotation(student.getId());
    return "Record deleted successfully using @Modifiying and @query Named Parameter";
}

Retrieve an entity(No need to use @Modifying annotation).

@Query("select s from Student s where s.id = :id")
public Student findById(@Param("id") int id);
@Transactional
public Student findById(int id) {
    Student studentresponse = studentRepository.findById(id);       
    return studentresponse;
}

Spring Data JPA @Modifying Annotation Example using Spring Boot 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 – modifying, ArtifactId – modifying, and name – modifying) 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>modifying</groupId>
	<artifactId>modifying</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>modifying</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>
</project>
  

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

Directory structure.

Spring Data JPA @Modifying Annotation Example

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;

@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 = "university")
	String university;

	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 String getRollNumber() {
		return rollNumber;
	}

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

	public String getUniversity() {
		return university;
	}

	public void setUniversity(String university) {
		this.university = university;
	}
}

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 = "/insert", method = RequestMethod.POST)
	@ResponseBody
	public String insertStudent(@RequestBody Student student) {
		String response = studentService.insertStudent(student);
		return response;
	}

	@RequestMapping(value = "/update", method = RequestMethod.PUT)
	@ResponseBody
	public String updateStudent(@RequestBody Student student) {
		String response = studentService.updateStudent(student);
		return response;
	}

	@RequestMapping(value = "/{id}", method = RequestMethod.GET)
	@ResponseBody
	public Student getStudent(@PathVariable int id) {
		Student student = studentService.findById(id);
		return student;
	}

	@RequestMapping(value = "/delete", method = RequestMethod.DELETE)
	@ResponseBody
	public String deleteStudent(@RequestBody Student student) {
		String response = studentService.deleteStudent(student);
		return response;
	}
}

StudentRepository.java – interface

package com.javatute.repository;

import java.io.Serializable;

import javax.transaction.Transactional;

import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import com.javatute.entity.Student;

@Repository
public interface StudentRepository extends CrudRepository<Student, Serializable> {

	@Modifying
	@Transactional
	@Query(value = "insert into Student (id,student_name,roll_number, university) "
			+ "VALUES(:id,:studentName,:rollNumber,:university)", nativeQuery = true)
	public void insertStudentUsingQueryAnnotation(@Param("id") int id, @Param("studentName") String studentName,
			@Param("rollNumber") String rollNumber, @Param("university") String university);

	@Modifying
	@Transactional
	@Query("update Student s SET s.studentName = :studentName WHERE s.id = :id")
	public void updateStudentUsingQueryAnnotation(@Param("studentName") String studentName, @Param("id") int id);

	@Modifying
	@Transactional
	@Query("delete from Student s where s.id = :id")
	public void deleteStudentUsingQueryAnnotation(@Param("id") int id);

	@Query("select s from Student s where s.id = :id")
	public Student findById(@Param("id") int id);
}

StudentService.java – interface

package com.javatute.service;

import org.springframework.stereotype.Component;

import com.javatute.entity.Student;

@Component
public interface StudentService {
	public String insertStudent(Student student);

	public String updateStudent(Student student);

	public String deleteStudent(Student student);

	public Student findById(int id);

}

StudentServiceImpl.java

package com.javatute.impl;

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

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

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

	@Autowired
	private StudentRepository studentRepository;

	@Override
	@Transactional
	public String insertStudent(Student student) {
		int id = student.getId();
		String studentName = student.getStudentName();
		String rollNumber = student.getRollNumber();
		String university = student.getUniversity();
		studentRepository.insertStudentUsingQueryAnnotation(id, studentName, rollNumber, university);
		return "Record inserted successfully using @Modifiying and @query Named Parameter";
	}

	@Transactional
	public String updateStudent(Student student) {
		studentRepository.updateStudentUsingQueryAnnotation(student.getStudentName(), student.getId());
		return "Record updated successfully using @Modifiying and @query Named Parameter";
	}

	@Override
	@Transactional
	public String deleteStudent(Student student) {
		studentRepository.deleteStudentUsingQueryAnnotation(student.getId());
		return "Record deleted successfully using @Modifiying and @query Named Parameter";
	}
	
	@Transactional
	public Student findById(int id) {
		Student studentresponse = studentRepository.findById(id);
		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.javatute.*")
@EntityScan("com.javatute.*")
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=oracle
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
# Show or not log for each sql query
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.format_sql=true 
 
spring.jpa.hibernate.ddl-auto =create
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.Oracle10gDialect
 
server.port = 9091

#show sql values
#logging.level.org.hibernate.type.descriptor.sql=trace

hibernate.show_sql = true
#spring.jpa.hibernate.logging.level.sql =FINE
#show sql statement
#logging.level.org.hibernate.SQL=debug

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

Testing of example using Postman.

Perform insert operation first using below REST API.

POST http://localhost:9091/student/insert

Request Data –

{
    "id": 1,
    "studentName": "john",
    "rollNumber": "0126CS01",
    "university": "rgtu"
}

Response data.

Spring Data JPA @Modifying Annotation Example using Spring Boot and Oracle

Perform update operation using below REST API.

PUT – http://localhost:9091/student/update

Request Data –

{
    "id": 1,
    "studentName": "john_updated_name",
    "rollNumber": "0126CS01",
    "university": "rgtu"
}

Response Data.

Spring Data JPA @Modifying Annotation Example using Spring Boot and Oracle

Perform Get operation using below REST API.

GET – http://localhost:9091/student/{id}

Perform Delete operation using below REST API.

DELETE- http://localhost:9091/student/delete

Generated query.

Hibernate: 
    insert 
    into
        Student
        (id,student_name,roll_number, university) 
    VALUES
        (?,?,?,?)
Hibernate: 
    update
        student 
    set
        student_name=? 
    where
        id=?
Hibernate: 
    select
        student0_.id as id1_0_,
        student0_.roll_number as roll_number2_0_,
        student0_.student_name as student_name3_0_,
        student0_.university as university4_0_ 
    from
        student student0_ 
    where
        student0_.id=?
Hibernate: 
    delete 
    from
        student 
    where
        id=?

Understanding difference while updating an entity using CrudRepository’s save() method and @Modifying annotation.

we are going to update the name field of the Student entity using save() method and @Modifying annotation. We will see what is the difference in both cases.

In StudentServiceImpl.java

	@Transactional
	public String updateStudent(Student student) {
		//studentRepository.updateStudentUsingQueryAnnotation(student.getStudentName(), student.getId());
		studentRepository.save(student);
		return "Record updated successfully using save() method";
	}

The query generated while updating the student using the save() method.

Hibernate: 
    select
        student0_.id as id1_0_0_,
        student0_.roll_number as roll_number2_0_0_,
        student0_.student_name as student_name3_0_0_,
        student0_.university as university4_0_0_ 
    from
        student student0_ 
    where
        student0_.id=?
Hibernate: 
    update
        student 
    set
        roll_number=?,
        student_name=?,
        university=? 
    where
        id=?

Now we will update the student name using @Modifying annotation.

The query generated while updating the student using the @Modifying annotation.

Hibernate: 
    update
        student 
    set
        student_name=? 
    where
        id=?

In the case of save() method, first it will retrieve the entity then the update statement will execute. In the case of @Modifying annotation, it will only execute an update statement.

Note – The same thing applies to delete operation. If we delete an entity using Repository delete() method first select statement will get executed then the delete statement will get executed. But if we try to delete @Modifying then only delete statement.

Overriding @Transactional(readOnly = true) behavior using @Modifying Annotation.

When we use @Transactional (readOnly = true) then we will not able to perform update or delete operation. But we can override readOnly behavior using @Modifying annotation. For example, suppose @Transactional annotation has been used with class level or interface level as below and we want to override readOnly behavior for one method(we don’t want to apply readOnly true for updateStudentUsingQueryAnnotation() method).

@Repository
@Transactional(readOnly = true)
public interface StudentRepository extends CrudRepository<Student,Serializable> {

  List<Book> findByBookName(String bookName);
	@Modifying
	@Query("update Student s SET s.studentName = :studentName WHERE s.id = :id")
	public void updateStudentUsingQueryAnnotation(@Param("studentName") String studentName, @Param("id") int id);
		
}

Some Basic Points about @Modifying Annotation.

The @Modifying annotation is available in org.springframework.data.jpa.repository and can be used with class and method.

The @Modifying annotation contains two elements flushAutomatically and clearAutomatically. Both have default values as false.

Using flushAutomatically and clearAutomatically with @Modifying annotation example.

@Modifying(flushAutomatically = true, clearAutomatically = true)
@Query("update Student s SET s.studentName = :studentName WHERE s.id = :id")
public void updateStudentUsingQueryAnnotation(@Param("studentName") String studentName, @Param("id") int id);

If we define flushAutomatically = true that means, the underlying persistence context should flush before executing the modifying query.

When we define clearAutomatically = true that means, clear the underlying persistence context after executing the modifying query.

Note – Here modifying query means @Query("update Student s SET s.studentName = :studentName WHERE s.id = :id").

If we define some different return types than void or int or Integer with the method which is annotated with @Modifying then it will throw java.lang.IllegalArgumentException: Modifying queries can only use void or int/Integer as return type exception. For example, the below code snippet will throw an exception.

@Modifying
@Query("UPDATE Student s SET s.studentName = :studentName WHERE s.id = :id")
public Student updateStudentUsingQueryAnnotation(@Param("studentName") String studentName, @Param("id") int id);

java.lang.IllegalArgumentException: Modifying queries can only use void or int/Integer as return type!
at org.springframework.util.Assert.isTrue(Assert.java:116) ~[spring-core-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.data.jpa.repository.query.JpaQueryExecution$ModifyingExecution.<init>(JpaQueryExecution.java:242) ~[spring-data-jpa-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.getExecution(AbstractJpaQuery.java:157) ~[spring-data-jpa-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:125) ~[spring-data-jpa-2.0.7.RELEASE.jar:2.0.7.RELEASE]

That’s all about Spring Data JPA @Modifying Annotation Example Using Spring Boot and Oracle. In case of any doubt or query please leave a comment.

Download the source code from github.

You may like.

Hibernate/JPA association mapping example using Spring Boot.

Spring Data JPA @Modifying docs.