Spring Boot 3
Spring boot 3 Features :
- Spring Boot 3.0 will require Java 17 or later
- Jakarta EE 9 a new top-level jakarta package, replacing EE 8’s javax top-level package. It will also be the first version of Spring Boot that makes use of Jakarta EE 9 APIs (jakarta.*) instead of EE 8 (javax.*).
- Since Spring Boot 2.4 changed the way that application.properties and application.yaml files were loaded.
Hibernate ORM 6.0
Hibernate  Compatibility :
- Java 11, 17 or later
- Jakarta Persistence 3.1 and 3.0
There are several layers of working with persistent data in Java/Spring:
Demo
Spring boot 3 with hibernate 6, MySQL and YAML file configuration.
Configuration Spring Boot project
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>3.2.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.henry</groupId> <artifactId>spring3-hibernate6</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring3-hibernate6</name> <description>Spring boot 3 with hibernate 6 configuration</description> <properties> <java.version>21</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <!-- POSTGRESQL database driver --> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>6.4.1.Final</version> <type>pom</type> </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>
application.yml
server:
  port: 9000
  servlet:
    context-path: /
spring:
  datasource:
    url: jdbc:postgresql:postgre_test
    username: postgre_test
    password: postgre_test
    driver-class-name: org.postgresql.Driver
    initialization-mode: always
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: create-dropModel
package com.henry.model;
import jakarta.persistence.*;
import lombok.*;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "first_name")
    private String firstName;
    @Column(name = "last_name")
    private String lastName;
}Repository
- UserRepositoryCustom
package com.henry.repository;
public interface UserRepositoryCustom {
    public Integer getSum(int a, int b);
}
- UserRepository
package com.henry.repository;
import com.henry.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User,Long>, UserRepositoryCustom {
    Optional<User> findByFirstName(String firstName);
}
- UserRepositoryCustomImpl
package com.henry.repository.impl;
import com.henry.repository.UserRepositoryCustom;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.hibernate.Session;
import org.hibernate.jdbc.ReturningWork;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Types;
public class UserRepositoryCustomImpl implements UserRepositoryCustom {
    @PersistenceContext
    private EntityManager entityManager;
    @Override
    public Integer getSum(int a, int b) {
        Session session = entityManager.unwrap(Session.class);
        int result = session.doReturningWork(new ReturningWork<Integer>() {
            @Override
            public Integer execute(Connection connection) throws SQLException {
                CallableStatement call = connection.prepareCall("{ ? = call get_sum(?,?) }");
                call.registerOutParameter(1, Types.INTEGER); // or whatever it is
                call.setInt(2, a);
                call.setInt(3, b);
                call.execute();
                return call.getInt(1); // propagate this back to enclosing class
            }
        });
        return  result;
    }
}
Service
- UserService
package com.henry.service;
import com.henry.model.User;
import java.util.List;
public interface UserService {
    User save(User user);
    List<User> findAll();
    Integer getSum(int a, int b);
}
- UserServiceImpl
package com.henry.service.impl;
import com.henry.model.User;
import com.henry.repository.UserRepository;
import com.henry.service.UserService;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
    private final UserRepository userRepository;
    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    @Override
    public User save(User user) {
        return userRepository.save(user);
    }
    @Override
    public List<User> findAll() {
        return userRepository.findAll();
    }
    @Override
    public Integer getSum(int a, int b) {
        return userRepository.getSum(a,b);
    }
}
Controller
- UserController
package com.henry.controller;
import com.henry.model.User;
import com.henry.service.UserService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/users")
public class UserController {
    private final UserService userService;
    public UserController(UserService userService) {
        this.userService = userService;
    }
    @GetMapping("/hello")
    public String helloWorld() {
        return  """
                Hello World, 
                multi-line,
                text block.
                """;
    }
    @GetMapping
    public List<User> findAll() {
        return userService.findAll();
    }
    @GetMapping("/sum/{a}/{b}")
    public Integer getSum(@PathVariable int a, @PathVariable int b) {
        return userService.getSum(a, b);
    }
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public User addUser(@RequestBody User user) {
        return userService.save(user);
    }
}
Test Class
package com.henry;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.henry.model.User;
import com.henry.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.CoreMatchers.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest
public class UserControllerTests {
    @Autowired
    private MockMvc mockMvc;
    @MockBean
    private UserService userService;
    @Autowired
    private ObjectMapper objectMapper;
    @Test
    public void givenUserObject_whenCreateUser_thenReturnSavedUser() throws Exception{
        // given - precondition or setup
        User user = User.builder()
                .firstName("Henry")
                .lastName("Xiloj")
                .build();
        given(userService.save(any(User.class)))
                .willAnswer((invocation)-> invocation.getArgument(0));
        // when - action or behaviour that we are going test
        ResultActions response = mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(user)));
        // then - verify the result or output using assert statements
        response.andDo(print()).
                andExpect(status().isCreated())
                .andExpect(jsonPath("$.firstName",
                        is(user.getFirstName())))
                .andExpect(jsonPath("$.lastName",
                        is(user.getLastName())));
    }
    // JUnit test for Get All users REST API
    @Test
    public void givenListOfUsers_whenGetAllUsers_thenReturnUsersList() throws Exception{
        // given - precondition or setup
        List<User> listOfUsers = new ArrayList<>();
        listOfUsers.add(User.builder().firstName("User1").lastName("User1").build());
        listOfUsers.add(User.builder().firstName("User2").lastName("User2").build());
        given(userService.findAll()).willReturn(listOfUsers);
        // when -  action or the behaviour that we are going test
        ResultActions response = mockMvc.perform(get("/api/users"));
        // then - verify the output
        response.andExpect(status().isOk())
                .andDo(print())
                .andExpect(jsonPath("$.size()",
                        is(listOfUsers.size())));
    }
    @Test
    public void givenEmployeeId_whenGetUsersFunSum_thenReturnUsersObject() throws Exception{
        // given - precondition or setup
        given(userService.getSum(1,2)).willReturn(3);
        // when -  action or the behaviour that we are going test
        ResultActions response = mockMvc.perform(get("/api/users/sum/{a}/{b}", 1,2));
        // then - verify the output
        ResultActions resultActions = response.andExpect(status().isOk())
                .andDo(print());
    }
}
Environment Setup with Docker Compose
To simplify the setup of your application's environment, you can use Docker Compose. Below is a docker-compose-postgresql.yml file that defines services  PostgreSQL database:
version: '3'
services:
  postgres:
    image: postgres:14.1
    container_name: postgre_test
    environment:
      POSTGRES_USER: postgre_test
      POSTGRES_PASSWORD: postgre_test
      POSTGRES_DB: postgre_test
    ports:
      - "5432:5432"
Run command:
docker-compose -f docker-compose-postgresql.yml up -dRun & Test
Run Spring Boot application with command: mvn test -Dtest=UserControllerTests. by console, IntelliJ etc.
https://spring.io/blog/2022/05/24/preparing-for-spring-boot-3-0
https://hibernate.org/orm/releases/6.0/
https://jakarta.ee/specifications/persistence/
https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/core/JdbcTemplate.html
https://docs.oracle.com/javaee/7/api/javax/persistence/EntityManager.html
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
https://docs.oracle.com/en/java/javase/17/
 
.png) 
 
No comments:
Post a Comment