@Modifying Annotation in Spring JPA

@Modifying annotation provided by the Spring Data JPA allows us to modify queries by updating, deleting, and enhancing the capabilities of the @Query annotation in the JpaRepository. This annotation will allow us to transform data beyond simple data fetch, ensuring transaction integrity and better performance. This @Modifying annotation is used to enhance the @Query annotation so we cannot execute SELECT queries only, but also INSERT, UPDATE, DELETE and DDL queries that allow us to modify the structure of the database table schema using Spring Data JPA. 

modifying_annotation

Use of @Modifying annotation

The @Modifying annotation is used when we need to execute modifying queries such as updates or deletes in the JpaRepository. This annotation is important because by default Spring Data JPA provides query methods considered read-only, optimizing queries but not modifying them.
Improved Performance: It Improves performance by allowing us to change queries to be executed.
Transactional Integrity: It ensures transaction integrity while performing data modifications.
Support for native queries: It allows native queries for data transformation.
Automatic Flush and Clear: It supports automatic flushing and clearing of durability references.

Implementation of @Modifying Annotation

We are going to implement @Modyifying Annotation by creating a restful web service Spring Boot Application using Maven, Spring Web, Spring Data JPA, Lombok and H2 database.

These are the following steps:

  1. Creating a Spring Boot Starter Project
  2. Keep the IDE ready
  3. Maven Dependency
  4. Defining the configuration
  5. Creating a JPA Entity
  6. Creating a JPA Repository
  7. Creating a Service Interface
  8. Creating a Service class
  9. Creating a Rest Controller class
  10. Run the Spring Application and Check

1. Creating a Spring Boot Starter Project

  We are creating a Spring Boot Application from the web tool Spring Initializr or you can create it from the IDE(STS, VS Code etc.) you are using. 
Add the following dependencies:

  • Spring Web
  • Spring Data JPA
  • Lombok
  • H2 Database

2. Keep the IDE ready

      We are importing this created application into our Eclipse IDE or you can import it into another IDE you use. You can refer to this article to create and set up the Spring Boot Project in Eclipse IDE.

Project Structure 

modifying_annotation

3. Maven Dependency

Here is the complete pom.xml file for the Spring Boot Application.
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.6.3</version>
		<relativePath />
		<!-- lookup parent from repository -->
	</parent>
	<groupId>com.springjava</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

4. Defining the configuration

We are configuring the H2 database configuration in the application.properties file.
application.properties

# H2 Database Configuration
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true

5. Creating a JPA Entity

We are creating a JPA entity class User with these properties(id, name, email, active and login_status).
User.java

package com.springjava.entity;

import javax.persistence.Entity;

import javax.persistence.GeneratedValue;

import javax.persistence.GenerationType;

import javax.persistence.Id;

import lombok.Data;

@Data

@Entity

public class User {

  @Id

  @GeneratedValue(strategy = GenerationType.IDENTITY)

  private Long id;

  private String name;

  private String email;

  private boolean active;

  private String login_status;

}
  • This @Data annotation is used for a constructor, setter method, getter method, etc.
  • This @Entity annotation is used to create a table through Java code in the database. 
  • This @Id annotation is used to create a primary key of the entity class.
  • This @GeneratedValue(strategy = GenerationType.IDENTITY) annotation is used to generate increment value of the specified property of entity class

6. Creating a JPA Repository

We are creating a JPA Repository to interact with the JPA Entity class and adding two custom methods[deleteUserActiveFalse() and deleteColumn()].
UserRepository.java

package com.springjava.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import org.springframework.data.jpa.repository.Modifying;

import org.springframework.data.jpa.repository.Query;

import org.springframework.transaction.annotation.Transactional;

import com.springjava.entity.User;

public interface UserRepository extends JpaRepository < User, Long > {

  @Transactional

  @Modifying

  @Query("delete from User u where u.active = false")

  void deleteUserActiveFalse();

  @Transactional

  @Modifying

  @Query(value = "alter table user drop column login_status", nativeQuery = true)

  void deleteColumn();

}
  • We added methods deleteUserFalse() for deleting the user where the active field is false and deleteColumn() for dropping the column from the database table.
  • @Query is used to define a custom query using JPQL and Native SQL.
  • @Modifying is used to modify the query and increase the capabilities of @Query annotation.
  • @Transactional is used to execute the query in the database.
  • We created a JPQL [@Query(“delete from User u where u.active = false”)] for implementing @Modifying annotation in the JpaRepository.
  • We created a native query [@Query(value = “alter table user drop column login_status”, nativeQuery = true)] for implementing @Modifying annotation in the JpaRepository.

7. Creating a Service Interface

We are creating a Service interface with some method declaration[saveAll(List userList), deleteUserActiveFalse() and deleteColumn()]. So the implementation class of this interface overrides these declared methods.
UserService.java

package com.springjava.service;

import java.util.List;

import com.springjava.entity.User;

public interface UserService {

  void saveAll(List < User > userList);

  void deleteUserActiveFalse();

  void deleteColumn();

}

8. Creating a Service class

We are creating a Service class UserServiceImpl and this class is implementing the UserService interface. This class is annotated with @Service annotation to act service. 
UserServiceImpl.java

package com.springjava.service;

import java.util.List;

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

import org.springframework.stereotype.Service;

import com.springjava.entity.User;

import com.springjava.repository.UserRepository;

@Service

public class UserServiceImpl implements UserService {

  @Autowired

  private UserRepository userRepo;

  @Override

  public void saveAll(List < User > userList) {

    userRepo.saveAll(userList);

  }

  @Override

  public void deleteUserActiveFalse() {

    userRepo.deleteUserActiveFalse();

  }

  @Override

  public void deleteColumn() {

    userRepo.deleteColumn();

  }

}
  • We used @Autowired annotation to inject UserRepository in this service class.
  • We used saveAll(), deleteUserActiveFalse() and deleteColumn() methods of that JPA Repository.

9. Creating a Rest Controller class

We are creating a Rest Controller class UserController in which all methods are created for API endpoints for handling requests from the clients. 
UserController.Java

package com.springjava.controller;

import java.util.LinkedHashMap;

import java.util.List;

import java.util.Map;

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

import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.DeleteMapping;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.PutMapping;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import com.springjava.entity.User;

import com.springjava.service.UserService;

@RestController

@RequestMapping("/api/user")

public class UserController {

  @Autowired

  private UserService userService;

  @PostMapping("/save")

  public ResponseEntity < ? > save(@RequestBody List < User > userList) {

    Map < String, Object > respUser = new LinkedHashMap < String, Object > ();

    userService.saveAll(userList);

    respUser.put("status", 1);

    respUser.put("message", "Record is Saved Successfully!");

    return new ResponseEntity < > (respUser, HttpStatus.CREATED);

  }

  @DeleteMapping("/delete")

  public ResponseEntity < ? > deleteUserActiveFalse() {

    Map < String, Object > respUser = new LinkedHashMap < String, Object > ();

    userService.deleteUserActiveFalse();

    respUser.put("status", 1);

    respUser.put("data", "Successfully Deleted!");

    return new ResponseEntity < > (respUser, HttpStatus.OK);

  }

  @PutMapping("/delete-column")

  public ResponseEntity < ? > deleteColumn() {

    Map < String, Object > respUser = new LinkedHashMap < String, Object > ();

    userService.deleteColumn();

    respUser.put("status", 1);

    respUser.put("data", "Successfully Column Deleted!");

    return new ResponseEntity < > (respUser, HttpStatus.OK);

  }

}
  • This class is annotated with @RestController annotation to make this class act as a Rest Controller for giving responses in JSON form.
  • We used @RequestMapping annotation to define the base URL for the application.
  • We used @PostMapping, @DeleteMapping and @PutMapping annotations to handle HTTP requests from the client.
  • We used ResponseEntity to represent the entire HTTP response.
  • We used @Autowired  annotation to inject UserService in the class.
  • We used @RequestBody annotation to take a JSON array in the save() method as the List of User class parameter.
  • We have created two restful web services handling methods[save(), deleteUserActiveFalse() and deleteColumn()].
  • save(): This saves the list of user records into the database.
  • deleteUserActiveFalse(): This method is used to delete users where the active field is false from the database table.
  • deleteColumn(): This method is used to delete the column from the database table.

10. Run the Spring Boot Application and Check

Right-click this Spring Boot application on the DemoApplication.java, then click Run As and select Java Application. 

Check H2 Database

Check the H2 database console and browse this URL “http://localhost:8080/h2-console”.

modifying_annotation

See the below table here:

modifying_annotation

JSON Array:

We created a JSON array to pass on the API as a request for the testing POST mapping API in the Postman tool.

[
  {
    "name": "Test",
    "email": "test@gmail.com",
    "active": true
  },
  {
    "name": "ABC",
    "email": "abc@gmail.com",
    "active": false
  },
  {
    "name": "XYZ",
    "email": "xyz@gmail.com",
    "active": false
  }
]

Testing API on the Postman

Saving the user data

POST: http://localhost:8080/api/user/save

modifying_annotation

Check the table:

modifying_annotation

After this API hits Spring Data JPA (internally uses Hibernate as a JPA provider) generated SQL statement in the console below here:

Hibernate: 
insert into user (id, active, email, login_status, name) 
values 
(null, ?, ?, ?, ?)

Hibernate: 
insert into user (id, active, email, login_status, name) 
values 
(null, ?, ?, ?, ?)

Hibernate: 
insert into user (id, active, email, login_status, name) 
values 
(null, ?, ?, ?, ?)

Deleting the user from the database table whose active field is false

DELETE: http://localhost:8080/api/user/delete

modifying_annotation

After this API hits Spring Data JPA (internally uses Hibernate as a JPA provider) generated SQL statement in the console below here:

Hibernate: 
delete from 
user 
where 
active=false

Check the table:

modifying_annotation

Drop the column from the database table

PUT: http://localhost:8080/api/user/delete-column

modifying_annotation

After this API hit Spring Data JPA (internally uses Hibernate as a JPA provider) generated SQL statement in the console below here:

Hibernate: 
alter table user drop column login_status

Check the table:

modifying_annotation

Conclusion

In this topic, we learnt how to implement @Modifying annotation for deleting and dropping a column using Spring Boot restful application.

Leave a Comment