Build REST CRUD APIs with Micronaut and Hibernate
Hello everyone, today we will learn how to build RESTful CRUD APIs using Micronaut and Hibernate with the help of one simple example.
Related topic,
Micronaut is a modern, JVM-predicated, full-stack Java framework designed for building modular, facilely testable JVM applications with support for Java, Kotlin and the Groovy language.
Micronaut provides:
- Dependency Injection and Inversion of Control (IoC)
- Aspect-Oriented Programming (AOP)
- Sensible Defaults and Auto-ConfigurationMessage-Driven Applications
- HTTP Routing
- Client-Side Load Balancing
- Command-Line Applications
- HTTP ServersDistributed Configuration
- Service Discovery
- Fast startup time
- Reduced memory footprint
- Minimal use of reflection
- Minimal use of proxies
- No runtime bytecode generation
- Easy Unit Testing
Technologies used:
- Java 11
- Micronaut 3.1.1
- Micronaut-Hibernate-JPA 3.1.1
- H2DB
- Micronaut-Hikari-Cp 3.1.1
- Maven 3.6.3
Final Project Structure:
Dependency management - Maven - Pom.xml
A Project Object Model or POM is the fundamental unit of work in Maven. It is an XML file that contains information about the project and configuration details utilized by Maven to build the project. It contains default values for most projects. Some of the configurations that can be designated in the POM is the project dependencies, the plugins or goals that can be executed, the build profiles, and so on. Other information such as the project version, description, developers, mailing lists, and such can withal be designated.
<?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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.knf.dev.demo</groupId>
<artifactId>micronaut-hibernate-crud</artifactId>
<version>0.1</version>
<packaging>${packaging}</packaging>
<parent>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-parent</artifactId>
<version>3.1.1</version>
</parent>
<properties>
<packaging>jar</packaging>
<jdk.version>11</jdk.version>
<release.version>11</release.version>
<micronaut.version>3.1.1</micronaut.version>
<exec.mainClass>org.knf.dev.demo.Application</exec.mainClass>
<micronaut.runtime>netty</micronaut.runtime>
</properties>
<repositories>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-validation</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.micronaut.test</groupId>
<artifactId>micronaut-test-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-http-client</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-http-server-netty</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-runtime</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut.sql</groupId>
<artifactId>micronaut-hibernate-jpa</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut.sql</groupId>
<artifactId>micronaut-jdbc-hikari</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.micronaut.build</groupId>
<artifactId>micronaut-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!-- Uncomment to enable incremental compilation -->
<!-- <useIncrementalCompilation>false</useIncrementalCompilation> -->
<annotationProcessorPaths
combine.children="append">
<path>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-http-validation</artifactId>
<version>${micronaut.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-Amicronaut.processing.group=org.knf.dev.demo</arg>
<arg>-Amicronaut.processing.module=micronaut-hibernate-crud</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
Data source & JPA configuration in src/main/resources/application.yml
micronaut:
application:
name: micronautHibernateCrud
datasources:
default:
url: jdbc:h2:mem:devDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
driverClassName: org.h2.Driver
username: sa
password: ''
jpa.default.properties.hibernate.hbm2ddl.auto: update
jpa:
default:
properties:
hibernate:
hbm2ddl:
auto: update
show_sql: true
Entity class
package org.knf.dev.demo.entity;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "users")
public class User implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String emailId;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
public User(Long id, String firstName, String lastName, String emailId) {
super();
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.emailId = emailId;
}
public User() {
super();
}
}
UserRepository
package org.knf.dev.demo.repository;
import java.util.List;
import java.util.Optional;
import org.knf.dev.demo.entity.User;
public interface UserRepository {
Optional<User> findById(Long id);
User save(User user);
void deleteById(Long id);
List<User> findAll();
void update(Long id, User user);
}
UserRepositoryImpl
package org.knf.dev.demo.repository.impl;
import java.util.List;
import java.util.Optional;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import org.knf.dev.demo.entity.User;
import org.knf.dev.demo.repository.UserRepository;
import io.micronaut.transaction.annotation.ReadOnly;
import io.micronaut.transaction.annotation.TransactionalAdvice;
import jakarta.inject.Singleton;
@Singleton
public class UserRepositoryImpl implements UserRepository {
private final EntityManager entityManager;
public UserRepositoryImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}
@Override
@ReadOnly
public Optional<User> findById(Long id) {
return Optional.ofNullable(entityManager.find(User.class, id));
}
@Override
@TransactionalAdvice
@Transactional
public User save(User user) {
entityManager.persist(user);
return user;
}
@Override
@TransactionalAdvice
@Transactional
public void deleteById(Long id) {
findById(id).ifPresent(entityManager::remove);
}
@ReadOnly
public List<User> findAll() {
return entityManager.
createQuery("SELECT c FROM User c").
getResultList();
}
@Override
@Transactional
@TransactionalAdvice
public void update(Long id, User user) {
User userToUpdate = entityManager.find(User.class, id);
if (null != userToUpdate) {
userToUpdate.setFirstName(user.getFirstName());
userToUpdate.setLastName(user.getLastName());
userToUpdate.setEmailId(user.getEmailId());
} else {
throw new RuntimeException("No such user available");
}
}
}
UserController
package org.knf.dev.demo.controller;
import java.util.List;
import java.util.Optional;
import org.knf.dev.demo.entity.User;
import org.knf.dev.demo.repository.UserRepository;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Delete;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import io.micronaut.http.annotation.Put;
import io.micronaut.scheduling.TaskExecutors;
import io.micronaut.scheduling.annotation.ExecuteOn;
@ExecuteOn(TaskExecutors.IO)
@Controller("/users")
public class UserController {
protected final UserRepository userRepository;
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Get
public List<User> getUsers() {
return userRepository.findAll();
}
@Get("/{id}")
public Optional<User> getUser(Long id) {
return userRepository.findById(id);
}
@Put("/{id}")
public void updateUser(Long id, @Body User user) {
userRepository.update(id, user);
}
@Post
public User addUser(@Body User user) {
return userRepository.save(user);
}
@Delete("/{id}")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
Main Class
package org.knf.dev.demo;
import io.micronaut.runtime.Micronaut;
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class, args);
}
}
Verify REST APIs
Run as Java Application
or
mvn mn:run