In this comprehensive tutorial, we will build a Human Resources Management System using Spring Boot 3, Spring Security, Spring Data JPA, and PL/SQL Oracle Procedures to handle operations like registering and updating users, employees, and admins. We’ll cover step-by-step instructions on setting up the project, configuring security, and making database calls using Oracle stored procedures with the @Procedure
annotation.
Table of Contents for This Tutorial
- Â Introduction to Spring Boot and its advantages.
- Â Introduction to REST API and DTO Pattern.
- Create our project using Spring Initializer: Spring Initializer.
- Add dependencies in the POM file such as MapStruct and JWT.
- Create our package structure.
- Configure
application.yml
with our database connection and other important properties. - Create our entity class.
- Create our DTO class.
- Create our mapper class.
- Create our security class with the necessary beans.
- Create our configuration class with the necessary beans.
- Add our beans to the Spring Boot application main class.
- Create our Docker file with the configuration to deploy an image and serve mail.
- Create our service and implementation classes.
- Create our repository class.
- Create our exception class.
- Create our controller class.
- Create our constants.
- Create our email HTML template in resources.
1. Introduction to Spring Boot and its Advantages
Spring Boot simplifies Java development by removing boilerplate configuration and providing a rapid way to create production-ready applications. By using Spring Boot, we benefit from features like:
- Auto-configuration
- Embedded server support (Tomcat, Jetty)
- Easy integration with databases (Spring Data JPA)
- Simplified security (Spring Security)
2. Introduction to REST API and DTO Pattern
- REST API is an architectural style for building APIs that follow the principles of REST (Representational State Transfer). RESTful APIs provide a way for client-server communication using HTTP methods.
- DTO Pattern separates the data that the client interacts with from the internal structure of the system, improving flexibility and security.
3. Setting Up the Project with Spring Initializer
Go to Spring Initializer, and create a new Spring Boot project with the following dependencies:
- Spring Web
- Spring Data JPA
- Spring Security
- Lombok
- Oracle JDBC
- JWT
- Java Mail Sender
- Thymeleaf
- Validation
Generate the project and extract it to your workspace.
4. Adding Dependencies
In your pom.xml
, add the following dependencies:
<?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.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.starlingdiaz</groupId>
<artifactId>human-resources-management</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>human-resources-management</name>
<description>human-resources-management</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
</properties>
<dependencies>
<!--Spring Boot Starter Data JPA-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--Spring Boot Starter Mail-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!--Spring Security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--Validation-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>
<!--Map Struct-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<!--Springdoc OpenAPI-->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc11</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-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
5. Creating the Package Structure
Create the following package structure:

6. Configuring application.yml
Inside the src/main/resources/application.yml
, configure your Oracle database connection and security properties:
spring:
application:
name: Human Resources Management API
datasource:
url: jdbc:oracle:thin:@localhost:1521:xe
username: C##HUMAN_RESOURCE
password: 1234
driver-class-name: oracle.jdbc.OracleDriver
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
dialect: org.hibernate.dialect.OracleDialect
format_sql: true
mail:
host: localhost
port: 1025
username: admin
password: admin
properties:
mail:
smtp:
trust: "*"
auth: true
starttls:
enable: true
connection:
timeout: 5000
writetimeout: 5000
security:
jwt:
secret-key: "RSjUBwnNFcjYzUFqFFDw1pCFbfZed5MC2QQVzs+CWeY="
expiration-time: 86400000
mailing:
frontend:
activation:
activationUrl: http://localhost:4200/activate-account
logging:
level:
org:
springframework:
security: DEBUG
web: DEBUG
server:
port: 8080
7. Creating the Entity Class
We will create the User
, Employee
, and Admin
entities. These classes will map to the corresponding database tables.
User
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.entity;
import jakarta.persistence.*;
import lombok.*;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.security.Principal;
import java.util.Collection;
import java.util.HashSet;
@Entity
@Table(name = "USERS")
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User implements UserDetails, Principal {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String lastName;
private String dni;
private String profession;
private String address;
private String country;
private String email;
private String Token;
@Column(nullable = false)
private String password;
private boolean enabled = false;
public Collection<? extends GrantedAuthority> getAuthorities() {
return new HashSet<>();
}
@Override
public String getUsername() {
return email;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return enabled;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
public String FullName() {
return name + " " + lastName;
}
}
Employee
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.entity;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Table(name = "EMPLOYEES")
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String position;
private String department;
private double salary;
}
Admin
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.entity;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Table(name = "ADMINS")
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Admin {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String role;
private String email;
}
8. Creating the DTO Class
We’ll create DTOs for each entity to handle data transfer between the client and server.
UserDTO.java
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.dto;
import lombok.Data;
@Data
public class UserDTO {
private String name;
private String lastName;
private String dni;
private String profession;
private String address;
private String country;
private String email;
private String password;
private String confirmPassword;
}
AdminDTO.java
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.dto;
import lombok.Data;
@Data
public class AdminDTO {
private String name;
private String role;
private String email;
}
EmployeeDTO.java
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.dto;
import lombok.Data;
@Data
public class EmployeeDTO {
private String name;
private String position;
private String department;
private double salary;
}
9. Creating the Mapper Class
We will use MapStruct to map between entities and DTOs.
UserMapper.java
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.mapper;
import com.starlingdiaz.humanresourcesmanagement.dto.UserDTO;
import com.starlingdiaz.humanresourcesmanagement.entity.User;
import org.mapstruct.Mapper;
@Mapper(componentModel = "spring")
public interface UserMapper {
UserDTO toDTO(User user);
User toEntity(UserDTO userDTO);
}
AdminMapper
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.mapper;
import com.starlingdiaz.humanresourcesmanagement.dto.AdminDTO;
import com.starlingdiaz.humanresourcesmanagement.entity.Admin;
import org.mapstruct.Mapper;
@Mapper(componentModel = "spring")
public interface AdminMapper {
AdminDTO toDTO(Admin admin);
Admin toEntity(AdminDTO adminDTO);
}
EmployeeMapper
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.mapper;
import com.starlingdiaz.humanresourcesmanagement.dto.EmployeeDTO;
import com.starlingdiaz.humanresourcesmanagement.entity.Employee;
import org.mapstruct.Mapper;
@Mapper(componentModel = "spring")
public interface EmployeeMapper {
EmployeeDTO toDTO(Employee employee);
Employee toEntity(EmployeeDTO employeeDTO);
}
10. Create our security class with the necessary beans.
You need to implement a JWT-based authentication. First, create a custom UserDetailsService
Impl that loads user details from the database and a JWT token provider to generate tokens into package Security.
first, we need to create a config with beans
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.config;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.Arrays;
import java.util.Collections;
import static org.springframework.http.HttpHeaders.*;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
@Configuration
@RequiredArgsConstructor
public class BeanConfig {
private final UserDetailsService userDetailsService;
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration AuthConfig) throws Exception {
return AuthConfig.getAuthenticationManager();
}
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedOrigins(Collections.singletonList("http://localhost:4200"));
config.setAllowedHeaders(Arrays.asList(
ORIGIN,
CONTENT_TYPE,
ACCEPT,
AUTHORIZATION
));
config.setAllowedMethods(Arrays.asList(
"GET",
"POST",
"PUT",
"DELETE"
));
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
11. Create our configuration class with the necessary beans.
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfig{
private final JwFilterServiceSecurity jwtAuthFilter;
private final AuthenticationProvider authenticationProvider;
public static String[] PUBLIC_URLS = {
"/v1/api/**" ,
"/v2/api-docs" ,
"/v3/api-docs/**" ,
"/v3/api-docs/swagger-config",
"/swagger-resources/**",
"/swagger-ui.html",
"/h2-console/login.do",
"/h2-console/**",
"/swagger-ui/**",
"/swagger-ui/index.html",
"/api/v1/users/register",
"/api/v1/users/login",
"/api/v1/**",
"/api/auth/**",
"/api/v1/users/activate-account",
};
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.cors(withDefaults())
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(req ->
req.requestMatchers(PUBLIC_URLS)
.permitAll().anyRequest().authenticated()
)
.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authenticationProvider(authenticationProvider)
.addFilterBefore((Filter) jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
http.headers().frameOptions().disable();
return http.build();
}
}
Then create files called JwFilterServiceSecurity and JwTServiceSecurity into Package called Security
JwFilterServiceSecurity
@Service
@RequiredArgsConstructor
public class JwFilterServiceSecurity extends OncePerRequestFilter {
private final JwTServiceSecurity jwTService;
private final UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(
@NotNull HttpServletRequest request,
@NotNull HttpServletResponse response,
@NotNull FilterChain filterChain) throws ServletException, IOException {
if (request.getServletPath().contains("/api/v1/users/login") ||
request.getServletPath().contains("/api/v1/users/register") ||
request.getServletPath().contains("/api/v1/users/activate-account") ||
request.getServletPath().contains("/swagger-ui/") ||
request.getServletPath().contains("/h2-console/**") ||
request.getServletPath().contains("/v3/api-docs")) {
filterChain.doFilter(request, response);
return;
}
final String authorizationHeader = request.getHeader(AUTHORIZATION);
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
String jwt = authorizationHeader.substring(7);
String userEmail = jwTService.extractUsername(jwt);
if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(userEmail);
if (jwTService.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
}
filterChain.doFilter(request, response);
}
}
JwTServiceSecurity
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.security;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import java.security.Key;
import java.sql.Date;
import java.util.HashMap;
import java.util.function.Function;
@Service
public class JwTServiceSecurity{
@Value("${security.jwt.expiration-time}")
private long jwtExpiration;
@Value("${security.jwt.secret-key}")
private String secretkey;
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
}
private Key getSigningKey() {
byte[] keyBytes = Decoders.BASE64.decode(secretkey);
return Keys.hmacShaKeyFor(keyBytes);
}
public String generateToken(HashMap<String, Object> claims, UserDetails userDetails){
return buildToken(claims, userDetails, jwtExpiration);
}
private String buildToken(HashMap<String, Object> extraClaims, UserDetails userDetails, long jwtExpiration) {
var authorities = userDetails.getAuthorities()
.stream()
.map(authority -> authority.getAuthority())
.toList();
return Jwts.builder()
.setClaims(extraClaims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + jwtExpiration))
.claim("authorities", authorities)
.signWith(getSigningKey())
.compact();
}
public boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
private boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date(System.currentTimeMillis()));
}
private Date extractExpiration(String token) {
return (Date) extractClaim(token, Claims::getExpiration);
}
}
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.service.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("User Not Found with Email: " + email));
}
}
12. Create our repository class
UserRepository.
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.repository;
import com.starlingdiaz.humanresourcesmanagement.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.query.Procedure;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long>{
Optional<User> findByEmail(String email);
// This method links to the 'register_user' stored procedure
@Procedure(procedureName = "register_user")
void registerUser(String p_name, String p_lastname, String p_dni, String p_profession, String p_address, String p_country, String p_email, String p_password);
// This method links to the 'update_user' stored procedure
@Procedure(procedureName = "update_user")
void updateUser(Long p_id, String p_name, String p_lastname , String p_dni, String p_profession, String p_address, String p_country);
}
AdminRepository
/**
* @author Starling Diaz on 10/21/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/21/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.repository;
import com.starlingdiaz.humanresourcesmanagement.entity.Admin;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AdminRepository extends JpaRepository<Admin, Long> {
}
EmployeeRepository
/**
* @author Starling Diaz on 10/22/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/22/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.repository;
import com.starlingdiaz.humanresourcesmanagement.entity.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
Oracle Procedure
--Register a user
CREATE OR REPLACE PROCEDURE C##HUMAN_RESOURCE.register_user(
p_name IN VARCHAR2,
p_lastname IN VARCHAR2,
p_dni IN VARCHAR2,
p_profession IN VARCHAR2,
p_address IN VARCHAR2,
p_country IN VARCHAR2,
p_email IN VARCHAR2,
p_password IN VARCHAR2,
p_token IN VARCHAR2,-- nuevo parámetro para el token
p_user_id OUT NUMBER
)
AS
BEGIN
INSERT INTO C##HUMAN_RESOURCE.USERS (name, last_name, dni, profession, address, country, email, password, enabled, token)
VALUES (p_name, p_lastname, p_dni, p_profession, p_address, p_country, p_email, p_password, '0', p_token)
RETURNING id INTO p_user_id;
END;
--UPDATE A USER
CREATE OR REPLACE PROCEDURE C##HUMAN_RESOURCE.update_user(
p_id IN NUMBER,
p_name IN VARCHAR2,
p_lastname IN VARCHAR2,
p_dni IN VARCHAR2,
p_profession IN VARCHAR2,
p_address IN VARCHAR2,
p_country IN VARCHAR2)
AS
BEGIN
UPDATE C##HUMAN_RESOURCE.USERS
SET name = p_name,
last_name = p_lastname,
dni = p_dni,
profession = p_profession,
address = p_address,
country = p_country
WHERE id = p_id;
END;
/
--Register Admin
CREATE OR REPLACE PROCEDURE C##HUMAN_RESOURCE.register_admin(
p_email IN VARCHAR2,
p_name IN VARCHAR2,
p_role IN VARCHAR2
)
AS
BEGIN
INSERT INTO C##HUMAN_RESOURCE.ADMINS (email, name, role)
VALUES (p_email, p_name, p_role);
END;
--UPDATE A ADMIN
CREATE OR REPLACE PROCEDURE C##HUMAN_RESOURCE.update_admin(
p_id IN NUMBER,
p_email IN VARCHAR2,
p_name IN VARCHAR2,
p_role IN VARCHAR2
)
AS
BEGIN
UPDATE C##HUMAN_RESOURCE.ADMINS
SET email = p_email,
name = p_name,
role = p_role
WHERE id = p_id;
END;
/
--Register EMPLOYEES
CREATE OR REPLACE PROCEDURE C##HUMAN_RESOURCE.register_employee(
p_name IN VARCHAR2,
p_position IN VARCHAR2,
p_department IN VARCHAR2,
p_salary IN NUMBER
)
AS
BEGIN
INSERT INTO C##HUMAN_RESOURCE.EMPLOYEES (name, position, department, salary)
VALUES (p_name, p_position, p_department, p_salary);
END;
/
pendiente
--UPDATE A EMPLOYEES
CREATE OR REPLACE PROCEDURE C##HUMAN_RESOURCE.update_employee(
p_id IN NUMBER,
p_name IN VARCHAR2,
p_position IN VARCHAR2,
p_department IN VARCHAR2,
p_salary IN Number
)
AS
BEGIN
UPDATE C##HUMAN_RESOURCE.EMPLOYEES
SET name = p_name,
position = p_position,
department = p_department,
salary = p_salary
WHERE id = p_id;
END;
/
13. Create our service and implementation classes.
UserService
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.service;
import com.starlingdiaz.humanresourcesmanagement.dto.LoginDTO;
import com.starlingdiaz.humanresourcesmanagement.dto.UserDTO;
import com.starlingdiaz.humanresourcesmanagement.dto.response.LoginResponseDTO;
import com.starlingdiaz.humanresourcesmanagement.entity.User;
import com.starlingdiaz.humanresourcesmanagement.exception.TokenExpiredException;
import jakarta.mail.MessagingException;
import org.springframework.stereotype.Service;
@Service
public interface UserService {
UserDTO registerUser(UserDTO userDTO) throws Exception;
void updateUser(Long id, UserDTO userDTO);
String generateAndSaveActivationToken(User user);
String generateActivationCode(int length);
LoginResponseDTO loginAuthenticate(LoginDTO loginDto);
String activateAccount(String token) throws MessagingException, TokenExpiredException;
}
Next, we need to create the UserServiceImpl
class, which implements the UserService
interface. In this class, we can use EntityManager
to interact with the database in a more flexible way, leveraging helpful methods such as those that allow us to invoke stored procedures. The EntityManager
provides a way to handle native queries and stored procedures, which is especially useful when we need to pass specific parameters to these procedures, aligning our business logic with the underlying database operations.
The following code demonstrates how to call the stored procedures register_user
and update_user
using EntityManager
for registering and updating users in the database. We define the necessary input parameters for each procedure and map them to the values from the UserDTO
object.
By using StoredProcedureQuery
, we are able to define and execute the stored procedure, passing the appropriate arguments to handle the user registration and update processes. Below is the full implementation of the service:
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.service.impl;
import com.starlingdiaz.humanresourcesmanagement.constants.EmailTemplateName;
import com.starlingdiaz.humanresourcesmanagement.dto.LoginDTO;
import com.starlingdiaz.humanresourcesmanagement.dto.UserDTO;
import com.starlingdiaz.humanresourcesmanagement.dto.response.LoginResponseDTO;
import com.starlingdiaz.humanresourcesmanagement.entity.Token;
import com.starlingdiaz.humanresourcesmanagement.entity.User;
import com.starlingdiaz.humanresourcesmanagement.exception.TokenExpiredException;
import com.starlingdiaz.humanresourcesmanagement.exception.UserNotFoundException;
import com.starlingdiaz.humanresourcesmanagement.repository.TokenRepository;
import com.starlingdiaz.humanresourcesmanagement.repository.UserRepository;
import com.starlingdiaz.humanresourcesmanagement.security.JwTServiceSecurity;
import com.starlingdiaz.humanresourcesmanagement.service.UserService;
import jakarta.mail.MessagingException;
import jakarta.persistence.*;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
public static final String REGISTER_USER = "register_user";
public static final String UPDATE_USER = "update_user";
@PersistenceContext
private EntityManager entityManager;
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final TokenRepository tokenRepository;
private final EmailServiceImpl emailServiceImpl;
private final AuthenticationManager authenticationManager;
private final JwTServiceSecurity jwTServiceSecurity;
@Value("${mailing.frontend.activation.activationUrl}")
private String activationUrl;
@Override
public UserDTO registerUser(UserDTO userDTO) throws Exception {
String activationToken = generateActivationCode(6);
StoredProcedureQuery query = entityManager.createStoredProcedureQuery(REGISTER_USER);
registerUserParameter(query);
registerUserSetParameter(userDTO, query, activationToken);
query.execute();
// Get ID of User
BigDecimal userIdBigDecimal = (BigDecimal) query.getOutputParameterValue(10);
Long userId = userIdBigDecimal.longValue();
// Search the user created
User newUser = userRepository.findById(userId)
.orElseThrow(() -> new EntityNotFoundException("User not found after creation"));
// save token
saveTokenForUser(newUser, activationToken);
sendVerificationEmail(newUser);
return userDTO;
}
private void registerUserSetParameter(UserDTO userDTO, StoredProcedureQuery query, String activationToken) {
query.setParameter(1, userDTO.getName());
query.setParameter(2, userDTO.getLastName());
query.setParameter(3, userDTO.getDni());
query.setParameter(4, userDTO.getProfession());
query.setParameter(5, userDTO.getAddress());
query.setParameter(6, userDTO.getCountry());
query.setParameter(7, userDTO.getEmail());
query.setParameter(8, passwordEncoder.encode(userDTO.getPassword()));
query.setParameter(9, activationToken);
}
private static void registerUserParameter(StoredProcedureQuery query) {
query.registerStoredProcedureParameter(1, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(2, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(3, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(4, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(5, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(6, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(7, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(8, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(9, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(10, BigDecimal.class, ParameterMode.OUT);
}
private void saveTokenForUser(User user, String tokenValue) {
Token token = Token.builder()
.token(tokenValue)
.createdAt(LocalDateTime.now())
.expiredAt(LocalDateTime.now().plusMinutes(45))
.user(user)
.build();
// Guardar el token en la base de datos
tokenRepository.save(token);
}
private void sendVerificationEmail(User user) throws MessagingException {
emailServiceImpl.sendEmail(
user.getEmail(),
user.FullName(),
EmailTemplateName.ACTIVATE_ACCOUNT,
activationUrl,
user.getToken(),
"Activate your account"
);
}
@Override
public void updateUser(Long id, UserDTO userDTO){
// Call the stored procedure using EntityManager
StoredProcedureQuery query = entityManager.createStoredProcedureQuery(UPDATE_USER);
query.registerStoredProcedureParameter(1, Long.class, ParameterMode.IN);
query.registerStoredProcedureParameter(2, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(3, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(4, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(5, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(6, String.class, ParameterMode.IN);
query.setParameter(1, id);
query.setParameter(2, userDTO.getName());
query.setParameter(3, userDTO.getDni());
query.setParameter(4, userDTO.getProfession());
query.setParameter(5, userDTO.getAddress());
query.setParameter(6, userDTO.getCountry());
query.execute();
}
@Override
public String generateAndSaveActivationToken(User user) {
String generatedToken = generateActivationCode(6);
var token = Token.builder()
.token(generatedToken)
.createdAt(LocalDateTime.now())
.expiredAt(LocalDateTime.now().plusMinutes(45))
.user(user)
.build();
tokenRepository.save(token);
return generatedToken;
}
@Override
public String generateActivationCode(int length) {
String characters = "123456789";
StringBuilder result = new StringBuilder();
SecureRandom random = new SecureRandom();
for (int i = 0; i < length; i++) {
int randomIndex = random.nextInt(characters.length());
result.append(characters.charAt(randomIndex));
}
return result.toString();
}
@Override
public LoginResponseDTO loginAuthenticate(LoginDTO loginDto) {
if(loginDto.getEmail() == null || loginDto.getEmail().isEmpty()){
throw new RuntimeException("Email is required");
}
if(loginDto.getPassword() == null || loginDto.getPassword().isEmpty()){
throw new RuntimeException("Password is required");
}
try {
var auth = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginDto.getEmail(), loginDto.getPassword())
);
var user = (User) auth.getPrincipal();
var claims = new HashMap<String, Object>();
claims.put("name", user.getName());
claims.put("email", user.getEmail());
claims.put("id", user.getId());
var jwtToken = jwTServiceSecurity.generateToken(claims, user);
return LoginResponseDTO.builder().token(jwtToken).build();
} catch (AuthenticationException e){
if(userRepository.findByEmail(loginDto.getEmail()).isPresent()){
throw new RuntimeException("Password is incorrect");
} else {
throw new UserNotFoundException("No user found with the provided email address");
}
}
}
@Override
public String activateAccount(String token) throws MessagingException, TokenExpiredException {
Optional<Token> tokenOptional = Optional.ofNullable(tokenRepository.findByToken(token));
if(!tokenOptional.isPresent()){
throw new TokenExpiredException("Invalid Token");
}
Token savedToken = tokenOptional.get();
if(LocalDateTime.now().isAfter(savedToken.getExpiredAt())){
sendVerificationEmail(savedToken.getUser());
throw new TokenExpiredException("Activation token has expired. A new token has been sent to the email address.");
}
User user = savedToken.getUser();
if (user == null) {
throw new UserNotFoundException("User not found");
}
user.setEnabled(true);
userRepository.save(user);
savedToken.setValidatedAt(LocalDateTime.now());
tokenRepository.save(savedToken);
return token;
}
}
AdminService
/**
* @author Starling Diaz on 10/21/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/21/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.service;
import com.starlingdiaz.humanresourcesmanagement.dto.AdminDTO;
import com.starlingdiaz.humanresourcesmanagement.dto.UserDTO;
public interface AdminService {
void registerAdmin(AdminDTO adminDTO);
void updateAdmin(Long id, UserDTO userDTO);
}
AdminServiceImpl
/**
* @author Starling Diaz on 10/21/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/21/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.service.impl;
import com.starlingdiaz.humanresourcesmanagement.dto.AdminDTO;
import com.starlingdiaz.humanresourcesmanagement.service.AdminService;
import jakarta.persistence.EntityManager;
import jakarta.persistence.ParameterMode;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.StoredProcedureQuery;
import org.springframework.stereotype.Service;
@Service
public class AdminServiceImpl implements AdminService {
public static final String REGISTER_ADMIN = "register_admin";
public static final String UPDATE_ADMIN = "update_admin";
@PersistenceContext
private EntityManager entityManager;
@Override
public void registerAdmin(AdminDTO adminDTO) {
// Call the stored procedure using EntityManager
StoredProcedureQuery query = entityManager.createStoredProcedureQuery(REGISTER_ADMIN);
query.registerStoredProcedureParameter(1, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(2, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(3, String.class, ParameterMode.IN);
query.setParameter(1, adminDTO.getName());
query.setParameter(2, adminDTO.getRole());
query.setParameter(3, adminDTO.getEmail());
query.execute();
}
@Override
public void updateAdmin(Long id, AdminDTO adminDTO) {
// Call the stored procedure using EntityManager
StoredProcedureQuery query = entityManager.createStoredProcedureQuery(UPDATE_ADMIN);
query.registerStoredProcedureParameter(1, Long.class, ParameterMode.IN);
query.registerStoredProcedureParameter(2, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(3, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(4, String.class, ParameterMode.IN);
query.setParameter(1, id);
query.setParameter(2, adminDTO.getName());
query.setParameter(3, adminDTO.getRole());
query.setParameter(4, adminDTO.getEmail());
query.execute();
}
}
EmployeeService
/**
* @author Starling Diaz on 10/22/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/22/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.service;
import com.starlingdiaz.humanresourcesmanagement.dto.EmployeeDTO;
import java.util.List;
public interface EmployeeService {
void registerEmployee(EmployeeDTO employeeDTO);
void updateEmployee(Long id, EmployeeDTO employeeDTO);
void deleteEmployee(Long id);
List<EmployeeDTO> getAllEmployees();
EmployeeDTO getEmployeeById(Long id);
}
EmployeeServiceImpl
/**
* @author Starling Diaz on 10/22/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/22/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.service.impl;
import com.starlingdiaz.humanresourcesmanagement.dto.EmployeeDTO;
import com.starlingdiaz.humanresourcesmanagement.mapper.EmployeeMapper;
import com.starlingdiaz.humanresourcesmanagement.mapper.EmployeeMapperImpl;
import com.starlingdiaz.humanresourcesmanagement.repository.EmployeeRepository;
import com.starlingdiaz.humanresourcesmanagement.service.EmployeeService;
import jakarta.persistence.EntityManager;
import jakarta.persistence.ParameterMode;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.StoredProcedureQuery;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
@AllArgsConstructor
public class EmployeeServiceImpl implements EmployeeService {
public static final String REGISTER_EMPLOYEE = "register_employee";
public static final String UPDATE_EMPLOYEE = "update_employee";
private final EmployeeMapper employeeMapper;
@PersistenceContext
private EntityManager entityManager;
private final EmployeeRepository employeeRepository;
/**
* Register an employee
* @param employeeDTO
* name
* position
* department
* salary
*/
@Override
public void registerEmployee(EmployeeDTO employeeDTO) {
StoredProcedureQuery query = entityManager.createStoredProcedureQuery(REGISTER_EMPLOYEE);
query.registerStoredProcedureParameter(1, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(2, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(3, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(4, Double.class, ParameterMode.IN);
query.setParameter(1, employeeDTO.getName());
query.setParameter(2, employeeDTO.getPosition());
query.setParameter(3, employeeDTO.getDepartment());
query.setParameter(4, employeeDTO.getSalary());
query.execute();
}
@Override
public void updateEmployee(Long id, EmployeeDTO employeeDTO) {
StoredProcedureQuery query = entityManager.createStoredProcedureQuery(UPDATE_EMPLOYEE);
query.registerStoredProcedureParameter(1, Long.class, ParameterMode.IN);
query.registerStoredProcedureParameter(2, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(3, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(4, String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(5, Double.class, ParameterMode.IN);
query.setParameter(1, id);
query.setParameter(2, employeeDTO.getName());
query.setParameter(3, employeeDTO.getPosition());
query.setParameter(4, employeeDTO.getDepartment());
query.setParameter(5, employeeDTO.getSalary());
query.execute();
}
@Override
public void deleteEmployee(Long id) {
this.entityManager.createQuery("DELETE FROM Employee e WHERE e.id = :id")
.setParameter("id", id)
.executeUpdate();
}
@Override
public List<EmployeeDTO> getAllEmployees() {
return employeeRepository.findAll()
.stream().map(employeeMapper::toDTO)
.collect(Collectors.toList());
}
@Override
public EmployeeDTO getEmployeeById(Long id) {
return this.entityManager.find(EmployeeDTO.class, id);
}
}
14. Create our controller class.
UserController
/**
* @author Starling Diaz on 10/20/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/20/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.controller;
import com.starlingdiaz.humanresourcesmanagement.dto.UserDTO;
import com.starlingdiaz.humanresourcesmanagement.service.UserService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping("/register")
public ResponseEntity<String> registerUser(@RequestBody UserDTO userDTO) {
userService.registerUser(userDTO);
return ResponseEntity.ok("User registered successfully.");
}
@PutMapping("/update/{id}")
public ResponseEntity<String> updateUser(@PathVariable Long id, @RequestBody UserDTO userDTO) {
userService.updateUser(id, userDTO);
return ResponseEntity.ok("User updated successfully.");
}
}
AdminController
/**
* @author Starling Diaz on 10/21/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/21/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.controller;
import com.starlingdiaz.humanresourcesmanagement.dto.AdminDTO;
import com.starlingdiaz.humanresourcesmanagement.dto.UserDTO;
import com.starlingdiaz.humanresourcesmanagement.service.AdminService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/admins")
@RequiredArgsConstructor
public class AdminController {
private final AdminService adminService;
@PostMapping("/register-admin")
public ResponseEntity<String> registerUser(@RequestBody AdminDTO adminDTO) {
adminService.registerAdmin(adminDTO);
return ResponseEntity.ok("Admin registered successfully.");
}
@PutMapping("/update-admin/{id}")
public ResponseEntity<String> updateUser(@PathVariable Long id, @RequestBody AdminDTO adminDTO) {
adminService.updateAdmin(id, adminDTO);
return ResponseEntity.ok("Admin updated successfully.");
}
}
EmployeeController
/**
* @author Starling Diaz on 10/22/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/22/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.controller;
import com.starlingdiaz.humanresourcesmanagement.dto.EmployeeDTO;
import com.starlingdiaz.humanresourcesmanagement.service.EmployeeService;
import lombok.AllArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/v1/employees")
@AllArgsConstructor
public class EmployeeController {
private final EmployeeService employeeService;
@PostMapping("/register-employee")
public ResponseEntity<String> registerEmployee(@RequestBody EmployeeDTO employeeDTO) {
employeeService.registerEmployee(employeeDTO);
return ResponseEntity.ok("Employee registered successfully.");
}
@PutMapping("/update-employee/{id}")
public ResponseEntity<String> updateEmployee(@PathVariable Long id, @RequestBody EmployeeDTO employeeDTO) {
employeeService.updateEmployee(id, employeeDTO);
return ResponseEntity.ok("Employee updated successfully.");
}
@DeleteMapping("/delete-employee/{id}")
public ResponseEntity<String> deleteEmployee(@PathVariable Long id) {
employeeService.deleteEmployee(id);
return ResponseEntity.ok("Employee deleted successfully.");
}
@GetMapping("/get-all-employees")
public ResponseEntity<List<EmployeeDTO>> getAllEmployees() {
return ResponseEntity.ok(employeeService.getAllEmployees());
}
}
15. Create our Constans and EmailService and Email HTML Template in resources.
EmailTemplateName
/**
* @author Starling Diaz on 10/22/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/22/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.constants;
import lombok.Getter;
@Getter
public enum EmailTemplateName {
ACTIVATE_ACCOUNT("activate_account"),
RESET_PASSWORD("reset-password");
private final String name;
EmailTemplateName(String name) {
this.name = name;
}
}
EmailService
/**
* @author Starling Diaz on 10/22/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/22/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.service;
import com.starlingdiaz.humanresourcesmanagement.constants.EmailTemplateName;
import jakarta.mail.MessagingException;
public interface EmailService {
void sendEmail(String to, String username, EmailTemplateName emailTemplate, String ConfirmationUrl, String activationConde, String subject) throws MessagingException;
}
EmailServiceImpl
/**
* @author Starling Diaz on 10/22/2024.
* @Github https://github.com/NSTLRD
* @Website https://mentorly.blog/
* @Academy https://www.mentor-ly.com/
* @version human-resources-management 1.0
* @since 10/22/2024.
*/
package com.starlingdiaz.humanresourcesmanagement.service.impl;
import com.starlingdiaz.humanresourcesmanagement.constants.EmailTemplateName;
import com.starlingdiaz.humanresourcesmanagement.service.EmailService;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.AllArgsConstructor;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring6.SpringTemplateEngine;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@Service
@AllArgsConstructor
public class EmailServiceImpl implements EmailService {
private final JavaMailSender javaMailSender;
private final SpringTemplateEngine templateEngine;
@Override
@Async
public void sendEmail(String to, String username, EmailTemplateName emailTemplate, String ConfirmationUrl, String activationConde, String subject) throws MessagingException {
// send email
String templateName = null;
switch (emailTemplate == null ? EmailTemplateName.ACTIVATE_ACCOUNT : emailTemplate) {
case ACTIVATE_ACCOUNT:
templateName = "activate_account";
break;
case RESET_PASSWORD:
templateName = "reset-password";
break;
}
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,
MimeMessageHelper.MULTIPART_MODE_MIXED,
StandardCharsets.UTF_8.name()
);
//add the parameters to the template
Map<String, Object> properties = new HashMap<>();
properties.put("username", username);
properties.put("confirmationUrl", ConfirmationUrl);
properties.put("activation_code", activationConde);
//pass the parameters to the template
Context context = new Context();
context.setVariables(properties);
//create the email body
helper.setFrom("[email protected]");
helper.setTo(to);
helper.setSubject(subject);
String template = templateEngine.process(templateName, context);
helper.setText(template, true);
javaMailSender.send(mimeMessage);
}
}
Conclusion
Thank you for following along with the development of the Human Resources Management project! We hope that this guide has been helpful in understanding the process of building a Spring Boot REST API with best practices. Feel free to explore the full project on GitHub and stay connected through our various platforms to continue learning and improving your skills.
For more projects, tutorials, and resources, check out the links below:
Contact and Social Links
- GitHub: Starling Diaz
- Website: Mentorly Blog
- LinkedIn: LinkedIn
- Mentorly Academy: Mentorly Academy
- YouTube: Mentorly YouTube
Feel free to contribute to the repository, share your feedback, or reach out if you have any questions. We’re looking forward to connecting with you across our platforms. Happy coding!