You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot报错Not a managed type,禁用实体类注解如何解决?

问题描述

我是Spring框架新手,正在学习核心概念,开发一个基于嵌入式数据库的员工REST目录Web应用。实现/employees端点以JSON返回所有员工列表时,遇到错误:Not a managed type: class springemployeecatalog.domain.Employee。要求绝对不能在领域类(如Employee、Department)上使用@Entity等注解。

我理解的应用请求处理流程如下,请帮忙纠正:

  1. 客户端发起请求;
  2. 控制器处理请求并从服务层获取数据;
  3. 服务层通过Repository从数据库获取数据,返回领域对象;
  4. 服务层通过转换器将领域对象转为DTO;
  5. DTO返回给控制器;
  6. 控制器将DTO作为响应返回给客户端。

现有代码

Employee.java

public class Employee {
    private final Long id;
    private final FullName fullName;
    private final Position position;
    private final LocalDate hired;
    private final BigDecimal salary;
    private final Employee manager;
    private final Department department;

    @JsonCreator
    public Employee(@JsonProperty("id") final Long id,
                    @JsonProperty("fullName") final FullName fullName,
                    @JsonProperty("position") final Position position,
                    @JsonProperty("hired") final LocalDate hired,
                    @JsonProperty("salary") final BigDecimal salary,
                    @JsonProperty("manager") final Employee manager,
                    @JsonProperty("department") final Department department) {
        this.id = id;
        this.fullName = fullName;
        this.position = position;
        this.hired = hired;
        this.salary = salary.setScale(5, RoundingMode.HALF_UP);
        this.manager = manager;
        this.department = department;
    }
//getters
}

Department.java

public class Department {

    private final Long id;
    private final String name;
    private final String location;

    @JsonCreator
    public Department(@JsonProperty("id") final Long id,
                    @JsonProperty("name") final String name,
                    @JsonProperty("location") final String location) {
        this.id = id;
        this.name = name;
        this.location = location;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getLocation() {
        return location;
    }
//getters
}

EmployeeDTO.java

public class EmployeeDTO {
    private Long id;
    private FullName fullName;
    private Position position;
    private LocalDate hireDate;
    private BigDecimal salary;
    private EmployeeDTO manager;
    private DepartmentDTO department;
    public Long getId() {
        return id;
    }

    public FullName getFullName() {
        return fullName;
    }

    public Position getPosition() {
        return position;
    }

    public LocalDate getHired() {
        return hireDate;
    }

    public BigDecimal getSalary() {
        return salary;
    }

    public EmployeeDTO getManager() {
        return manager;
    }

    public DepartmentDTO getDepartment() {
        return department;
    }
    public EmployeeDTO(Long id, FullName fullName, Position position, LocalDate hireDate, BigDecimal salary, EmployeeDTO manager, DepartmentDTO department) {
        this.id = id;
        this.fullName = fullName;
        this.position = position;
        this.hireDate = hireDate;
        this.salary = salary;
        this.manager = manager;
        this.department = department;
    }

}

DepartmentDTO.java

public class DepartmentDTO {
    private Long id;
    private String name;
    private String location;

    public DepartmentDTO(Long id, String name, String location) {
        this.id = id;
        this.name = name;
        this.location = location;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getLocation() {
        return location;
    }
}

EmployeeRepository.java

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}

DepartmentRepository.java

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

EmployeeService.java

@Service
public class EmployeeService {
    private final EmployeeRepository employeeRepository;
    private final DepartmentRepository departmentRepository;

    @Autowired
    public EmployeeService(EmployeeRepository employeeRepository, DepartmentRepository departmentRepository) {
        this.employeeRepository = employeeRepository;
        this.departmentRepository = departmentRepository;
    }

    public List<EmployeeDTO> getAllEmployees() {
        return employeeRepository.findAll().stream()
                .map(this::convertToEmployeeDto)
                .collect(Collectors.toList());
    }

    public List<DepartmentDTO> getAllDepartments() {
        return departmentRepository.findAll().stream()
                .map(this::convertToDepartmentDto)
                .collect(Collectors.toList());
    }
    private EmployeeDTO convertToEmployeeDto(Employee employee) {
        Department department = employee.getDepartment();
        Employee manager = employee.getManager();
    
        return new EmployeeDTO(
                employee.getId(),
                employee.getFullName(),
                employee.getPosition(),
                employee.getHired(),
                employee.getSalary(),
                manager != null ? convertToEmployeeDto(manager) : null,
                department != null ? convertToDepartmentDto(department) : null
        );
    }

    private DepartmentDTO convertToDepartmentDto(Department department) {
        return new DepartmentDTO(
                department.getId(),
                department.getName(),
                department.getLocation()
        );
    }
}

EmployeeController.java

@RestController
public class EmployeeController {
    private final EmployeeService employeeDepartmentService;

    @Autowired
    public EmployeeController(EmployeeService employeeDepartmentService) {
        this.employeeDepartmentService = employeeDepartmentService;
    }

    @GetMapping("/employees")
    public List<EmployeeDTO> getAllEmployees() {
        return employeeDepartmentService.getAllEmployees();
    }
}

数据库Schema

CREATE TABLE DEPARTMENT
(
    ID       INTEGER PRIMARY KEY,
    NAME     VARCHAR(14),
    LOCATION VARCHAR(13)
);

CREATE TABLE EMPLOYEE
(
    ID         INTEGER PRIMARY KEY,
    FIRSTNAME  VARCHAR(10),
    LASTNAME   VARCHAR(10),
    MIDDLENAME VARCHAR(10),
    POSITION   VARCHAR(9),
    MANAGER    INTEGER,
    HIREDATE   DATE,
    SALARY     DOUBLE,
    DEPARTMENT INTEGER,
    CONSTRAINT FK_DEPTNO FOREIGN KEY (DEPARTMENT) REFERENCES DEPARTMENT (ID),
    CONSTRAINT FK_EMPNO FOREIGN KEY (MANAGER) REFERENCES EMPLOYEE (ID)
);

COMMIT;

问题分析与解决

错误原因

Not a managed type错误的核心原因是:你使用了JpaRepository,但JPA要求实体类必须被标注为@Entity或通过元数据配置为受管类型。但你要求不能在领域类上加@Entity,因此需要替换为不需要该注解的数据访问方案。

可行方案:使用Spring Data JDBC替代JPA

Spring Data JDBC无需在领域类上添加@Entity,仅需简单注解配置映射关系,完全符合你的要求。

步骤1:替换依赖

如果使用Maven,移除Spring Data JPA依赖,添加Spring Data JDBC:

<!-- 移除原有JPA依赖 -->
<!-- <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency> -->

<!-- 添加Spring Data JDBC依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

步骤2:修改Repository接口

将JpaRepository替换为Spring Data JDBC提供的CrudRepository:

@Repository
public interface EmployeeRepository extends CrudRepository<Employee, Long> {
}
@Repository
public interface DepartmentRepository extends CrudRepository<Department, Long> {
}

步骤3:调整领域类映射配置

添加Spring Data JDBC所需的映射注解,无需@Entity:

修改Employee.java

import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;

@Table("EMPLOYEE")
public class Employee {
    @Id
    private final Long id;
    @Column("FIRSTNAME")
    private final String firstName;
    @Column("LASTNAME")
    private final String lastName;
    @Column("MIDDLENAME")
    private final String middleName;
    private final Position position;
    @Column("HIREDATE")
    private final LocalDate hired;
    private final BigDecimal salary;
    @Column("MANAGER")
    private final Long managerId;
    @Column("DEPARTMENT")
    private final Long departmentId;

    @JsonCreator
    public Employee(@JsonProperty("id") final Long id,
                    @JsonProperty("firstName") final String firstName,
                    @JsonProperty("lastName") final String lastName,
                    @JsonProperty("middleName") final String middleName,
                    @JsonProperty("position") final Position position,
                    @JsonProperty("hired") final LocalDate hired,
                    @JsonProperty("salary") final BigDecimal salary,
                    @JsonProperty("managerId") final Long managerId,
                    @JsonProperty("departmentId") final Long departmentId) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.middleName = middleName;
        this.position = position;
        this.hired = hired;
        this.salary = salary.setScale(5, RoundingMode.HALF_UP);
        this.managerId = managerId;
        this.departmentId = departmentId;
    }

    // 新增getter方法
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public String getMiddleName() { return middleName; }
    public Long getManagerId() { return managerId; }
    public Long getDepartmentId() { return departmentId; }

    // 原有其他getter方法
}

修改Department.java

import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;

@Table("DEPARTMENT")
public class Department {

    @Id
    private final Long id;
    private final String name;
    private final String location;

    @JsonCreator
    public Department(@JsonProperty("id") final Long id,
                    @JsonProperty("name") final String name,
                    @JsonProperty("location") final String location) {
        this.id = id;
        this.name = name;
        this.location = location;
    }

    // 原有getter方法
}

步骤4:调整Service层关联逻辑

由于数据库存储的是关联ID而非对象,需要手动加载并关联manager和department:

@Service
public class EmployeeService {
    private final EmployeeRepository employeeRepository;
    private final DepartmentRepository departmentRepository;

    public EmployeeService(EmployeeRepository employeeRepository, DepartmentRepository departmentRepository) {
        this.employeeRepository = employeeRepository;
        this.departmentRepository = departmentRepository;
    }

    public List<EmployeeDTO> getAllEmployees() {
        List<Employee> employees = (List<Employee>) employeeRepository.findAll();
        Map<Long, Department> departmentMap = StreamSupport.stream(departmentRepository.findAll().spliterator(), false)
                .collect(Collectors.toMap(Department::getId, d -> d));
        Map<Long, Employee> employeeMap = employees.stream()
                .collect(Collectors.toMap(Employee::getId, e -> e));

        return employees.stream()
                .map(employee -> convertToEmployeeDto(employee, employeeMap, departmentMap))
                .collect(Collectors.toList());
    }

    private EmployeeDTO convertToEmployeeDto(Employee employee, Map<Long, Employee> employeeMap, Map<Long, Department> departmentMap) {
        Employee manager = employee.getManagerId() != null ? employeeMap.get(employee.getManagerId()) : null;
        Department department = employee.getDepartmentId() != null ? departmentMap.get(employee.getDepartmentId()) : null;

        return new EmployeeDTO(
                employee.getId(),
                new FullName(employee.getFirstName(), employee.getLastName(), employee.getMiddleName()),
                employee.getPosition(),
                employee.getHired(),
                employee.getSalary(),
                manager != null ? convertToEmployeeDto(manager, employeeMap, departmentMap) : null,
                department != null ? convertToDepartmentDto(department) : null
        );
    }

    private DepartmentDTO convertToDepartmentDto(Department department) {
        return new DepartmentDTO(
                department.getId(),
                department.getName(),
                department.getLocation()
        );
    }
}

请求处理流程纠正

修正后的完整流程:

  1. 客户端发起HTTP GET请求到/employees;
  2. EmployeeController接收请求,调用EmployeeService的getAllEmployees()方法;
  3. EmployeeService调用EmployeeRepository和DepartmentRepository从数据库加载所有员工、部门数据;
  4. EmployeeService将加载的数据转换为DTO(含manager的递归关联转换);
  5. 转换后的List<EmployeeDTO>返回给EmployeeController;
  6. EmployeeController将DTO序列化为JSON,作为HTTP响应返回给客户端。

内容的提问来源于stack exchange,提问作者Leon_idas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 21:54:57