Search code examples
javaspringspring-bootrestjpa

How to fix UnsatisfiedDependencyException: Error creating bean with name 'employeeController'?


I have created a webapp serving somewhat like a REST catalog of employees stored in embedded database, but something goes wrong. I have an UnsatisfiedDependencyException: Error creating bean with name

'employeeController': Unsatisfied dependency expressed through field 'employeeService': Error creating bean with name 'employeeServiceImpl': Unsatisfied dependency expressed through field 'employeeRepository': Error creating bean with name 'employeeRepository' defined in com.springemployeecatalog.repository.EmployeeRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Not a managed type: class com.springemployeecatalog.entity.Employee.

What am I doing wrong?

Endpoints (all serves GET requests) should be: /employees - list all employees. Supports paging*.

/employees/{employee_id} - single employee. If parameter named full_chain exists and is set to true then full manager chain should be written (include employee`s manager, manager of manager, manager of manager of manager and so on up to the organization head).

/employees/by_manager/{managerId} - list employees who subordinates to the manager. No transitivity. Supports paging*.

/employees/by_department/{departmentId or departmentName} - list employees who is working in the department. Supports paging*.

  • Supports paging - means that you may manage what sublist of employees you want to get by three parameters: page - number of the page (starts with 0); size - amount of entry per page; sort - name of the field for sorting (single value from list [lastName, hired, position, salary], order is ascending).

Entity: Employee class

package com.springemployeecatalog.entity;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.IOException;
import java.io.StringWriter;
import java.time.LocalDate;
import java.util.List;

@Entity
@NoArgsConstructor
@Table(name ="employee")
    public class Employee {
    @Id
    @GeneratedValue(strategy =  GenerationType.IDENTITY)
    @Column(name = "emp_id")
    private Integer id;
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "fullName")
    private FullName fullName;
    @Enumerated(EnumType.ORDINAL)
    @Column(name = "position")
    private Position position;
    @ManyToOne(cascade = {CascadeType.PERSIST })
    @JoinColumn(name = "manager_id")
    private Employee manager;
    @Column(name = "hired")
    private LocalDate hired;
    @Column(name = "salary")
    private Double salary;
    @ManyToOne(cascade ={CascadeType.ALL})
    @JoinColumn(name = "department_id")
    private Department department;
    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "manager_id", referencedColumnName = "emp_id")
    private List<Employee> employeeList;
   // constructors, getters, setters ...

Department class

package com.springemployeecatalog.entity;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;

import lombok.NoArgsConstructor;
import javax.persistence.*;

import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

@Entity
@NoArgsConstructor
@Table(name = "department")
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "dep_id")
    private Integer id;
    @Column(name = "name")
    private String name;
    @Column(name = "location")
    private String location;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "department_id", referencedColumnName = "dep_id" )
    private List <Employee> employeeList;
   // constructors, getters, setters ...

FullName class

package com.springemployeecatalog.entity;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;

import javax.persistence.*;
@Embeddable
public class FullName {
    @Column(name = "firstname")
    private String firstName;
    @Column(name = "lastname")
    private String lastName;
    @Column(name = "middlename")
    private String middleName;
    @OneToOne(mappedBy = "fullNAme", cascade = {CascadeType.PERSIST, CascadeType.REFRESH})
    private Employee employee;
   // constructors, getters, setters ...

Position enum package com.springemployeecatalog.entity;

public enum Position {
    PRESIDENT,
    MANAGER,
    ANALYST,
    CLERK,
    SALESMAN
}

Controller:

package com.springemployeecatalog.controller;

import com.springemployeecatalog.entity.Employee;
import com.springemployeecatalog.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;

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

import java.util.List;

@RestController
public class EmployeeController {
    @Autowired
   private EmployeeService employeeService;

    @GetMapping("/")
    public String homePage() {
        return "Welcome to the Employee Catalog!";
    }
    @GetMapping("/employees")
    public List<Employee> showAllEmployees(@RequestParam(defaultValue = "0") int page,
                                           @RequestParam(defaultValue = "10") int size,
                                           @RequestParam(defaultValue = "id") String sort) {
        return employeeService.getAllEmployees(page, size, sort);
    }

    @GetMapping("/employees/{id}")
    public Employee findEmployee(@PathVariable Integer id,
                                 @RequestParam(defaultValue = "false") boolean fullChain) {
        return employeeService.getEmployee(id, fullChain);
    }

    @GetMapping("/employees/by_manager/{managerId}")
    public List<Employee> findEmployeesByManager(@PathVariable Integer managerId,
                                             @RequestParam(defaultValue = "0") int page,
                                             @RequestParam(defaultValue = "10") int size,
                                             @RequestParam(defaultValue = "id") String sort){     
        return employeeService.getEmployeesByManager(managerId, page, size, sort);
    }

    @GetMapping("/employees/by_department/{departmentId}")
    public List<Employee> findEmployeesByDepartment(@PathVariable Integer departmentId,
                                                @RequestParam(defaultValue = "0") int page,
                                                @RequestParam(defaultValue = "10") int size,
                                                @RequestParam(defaultValue = "id") String sort) {
        return employeeService.getEmployeesByDepartment(departmentId, page, size, sort);
    }
}

Repository:

package com.springemployeecatalog.repository;

import com.springemployeecatalog.entity.Department;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface DepartmentRepository extends JpaRepository<Department, Integer> {
}


package com.springemployeecatalog.repository;

import com.springemployeecatalog.entity.Department;
import com.springemployeecatalog.entity.Employee;
import org.springframework.data.entity.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;
@Repository
public interface EmployeeRepository extends JpaRepository <Employee, Integer> {
    List<Employee> findEmployeesByManager(Employee manager, Pageable pageable);
    List<Employee> findEmployeesByDepartment(Department department, Pageable pageable);
}

Service:

 package com.springemployeecatalog.service;

import com.springemployeecatalog.entity.Employee;

import java.util.List;

public interface EmployeeService {
    List<Employee> getAllEmployees(int page, int size, String sort);
    Employee getEmployee(Integer id, boolean fullChain);
    List<Employee> getEmployeesByManager(Integer managerId, int page, int size, String sort);
    List<Employee> getEmployeesByDepartment(Integer departmentId, int page, int size, String sort);
}


package com.springemployeecatalog.service;

import com.springemployeecatalog.entity.Department;
import com.springemployeecatalog.entity.Employee;
import com.springemployeecatalog.repository.DepartmentRepository;
import com.springemployeecatalog.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class EmployeeServiceImpl implements EmployeeService{
    @Autowired
    private EmployeeRepository employeeRepository;

    @Autowired
    private DepartmentRepository departmentRepository;

    @Override
    public List<Employee> getAllEmployees(int page, int size, String sort) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(sort));
        Page<Employee> employeePage = employeeRepository.findAll(pageable);
        System.out.println("Отримано " + employeePage.getTotalElements() + " записів");

        return employeePage != null ? employeePage.getContent() : Collections.emptyList();
    }

    @Override
    public Employee getEmployee(Integer id, boolean fullChain) {
        Employee employee = employeeRepository.findById(id).orElse(null);
        List<Employee> managerChain = new ArrayList<>();
        if (fullChain && employee != null) {
            Employee manager = employee.getManager();
            while (manager != null) {
                managerChain.add(manager);
                manager = manager.getManager();
            }
        }
        Map<String, Object> response = new HashMap<>();
        response.put("employee", employee);
        response.put("managerChain", managerChain);
        return employee;
    }

    @Override
    public List<Employee> getEmployeesByManager(Integer managerId, int page, int size, String sort) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(sort));
        Employee manager = employeeRepository.findById(managerId).orElse(null);
        if (manager != null) {
            return employeeRepository.findEmployeesByManager(manager, pageable);
        }
        return Collections.emptyList();
    }
    @Override
    public List<Employee> getEmployeesByDepartment(Integer departmentId, int page, int size, String sort) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(sort));
        Department department = departmentRepository.findById(departmentId).orElse(null);
        if (department != null) {
            return employeeRepository.findEmployeesByDepartment(department, pageable);
        }
        return Collections.emptyList();
    }
}

This is application properties:

server.port=8080

spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

spring.h2.console.enabled=true
spring.h2.console.path=/h2

spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.generate-ddl=false
spring.jpa.hibernate.ddl-auto=
spring.jpa.show-sql=true

pom.xml file:

<?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>com.spring</groupId>
    <artifactId>employees-catalogue</artifactId>
    <version>1.0-SNAPSHOT</version>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.0.4</version>
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>19</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>23.0</version>
    </dependency>

    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.9.2</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>


    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>

</dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>

        </plugins>
    </build>
</project>

Solution

  • I think with Spring 3, I've seen some usage of annotations from Jakarta (ex: jakarta.persistence.Entity) instead of javax.persistence to resolve this error.

    Can you test with Jakarta annotations instead of javax.persistence and see if it resolves? You will need to replace Entity, Id, and perhaps more.