【问题标题】:Spring/JPA app searching for non-existent database tableSpring/JPA 应用程序搜索不存在的数据库表
【发布时间】:2019-01-23 19:52:59
【问题描述】:

我有一个使用 JPA 作为 ORM 的 Spring 应用程序,我认为这是正确的,并且 Swagger 为我的端点创建了一个漂亮的 UI。

当尝试使用已保存在数据库中的数据发出 POST 或 PUT 请求时,我收到一个错误:

Servlet.service() for servlet [dispatcher] in context with path [/api/orgchart] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not execute statement] with root cause

java.sql.SQLSyntaxErrorException: Table 'orgchart_api.employee_department' doesn't exist

很明显,错误来自 JPA 正在尝试更新 employee_department 表上的此数据并且此表不存在这一事实。我遇到的问题是弄清楚 为什么 JPA 正在尝试访问该表。

数据流:

Swagger UI -> 使用 JSON 的 POST 请求

  {
    "id": 104,
    "isActive": true,
    "manager": null,
    "firstName": "string",
    "middleInitial": null,
    "lastName": "string",
    "department": {
      "id": 104,
      "isActive": true,
      "parentDepartment": {
        "id": 101,
        "isActive": true,
        "parentDepartment": null,
        "manager": null,
        "name": "Marketing"
      },
      "manager": null,
      "name": "Americas"
    },
    "jobTitle": {
      "id": 1001,
      "isActive": true,
      "name": "Jr. Developer"
    },
    "email": "e",
    "skypeName": "e",
    "isManager": false
  }

-> 员工控制器

package com.orgchart.web.controller;

import com.orgchart.model.Employee;
import com.orgchart.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/emps")
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    @RequestMapping(method = {RequestMethod.POST, RequestMethod.PUT})
    public Employee createOrUpdateEmployee(@RequestBody Employee employee) {
        return employeeService.storeOrUpdate(employee);
    }

}

-> 员工服务

package com.nexient.orgchart.service;

import com.nexient.orgchart.data.entity.DepartmentEntity;
import com.nexient.orgchart.data.entity.EmployeeEntity;
import com.nexient.orgchart.data.repository.EmployeeRepository;
import com.nexient.orgchart.mapper.EmployeeMapper;
import com.nexient.orgchart.model.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@Service
public class EmployeeService {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Autowired
    EmployeeMapper employeeMapper;

    public Employee storeOrUpdate(Employee employee) {
        EmployeeEntity employeeEntity = employeeMapper.modelToEntity(employee);
        EmployeeEntity savedEmployeeEntity = employeeRepository.save(employeeEntity);
        Employee employeeModel = employeeMapper.entityToModel(savedEmployeeEntity);
        return employeeModel;
    }

-> 员工映射器

package com.nexient.orgchart.mapper;

import com.nexient.orgchart.data.entity.DepartmentEntity;
import com.nexient.orgchart.data.entity.EmployeeEntity;
import com.nexient.orgchart.data.entity.JobTitleEntity;
import com.nexient.orgchart.model.Department;
import com.nexient.orgchart.model.Employee;
import com.nexient.orgchart.model.JobTitle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

@Component
public class EmployeeMapper implements EntityModelMapper<EmployeeEntity, Employee> {

    @Autowired
    private DepartmentMapper departmentMapper;

    @Autowired
    private JobTitleMapper jobTitleMapper;

    @Override
    public EmployeeEntity modelToEntity(Employee employee) {
        Assert.notNull(employee, "Employee model cannot be null.");

        EmployeeEntity employeeEntity = new EmployeeEntity();
        DepartmentEntity departmentEntity = departmentMapper.modelToEntity(employee.getDepartment());
        JobTitleEntity jobTitleEntity = jobTitleMapper.modelToEntity(employee.getJobTitle());
        Employee employeeManager = employee.getManager();

        if (employeeManager != null) {
            EmployeeEntity employeeManagerEntity = modelToEntity(employeeManager);
            employeeEntity.setManager(employeeManagerEntity);
        }

        employeeEntity.setId(employee.getId());
        employeeEntity.setEmail(employee.getEmail());
        employeeEntity.setFirstName(employee.getFirstName());
        employeeEntity.setMiddleInitial(employee.getMiddleInitial());
        employeeEntity.setLastName(employee.getLastName());
        employeeEntity.setDepartment(departmentEntity);
        employeeEntity.setJobTitle(jobTitleEntity);
        employeeEntity.setIsManager(employee.getIsManager());
        employeeEntity.setSkypeName(employee.getSkypeName());
        employeeEntity.setIsActive(employee.getIsActive());

        return employeeEntity;
    }

从这里它只是通过另一个 &lt;Model&gt;.modelToEntity()jobTitledepartment 并最终从 EmployeeService 文件中调用 employeeRepository.save(employeeEntity),这是错误的来源。

重申一下,为什么我的 POST 请求尝试访问 employee_department 表,而我没有在我的任何实体中将它指定为表名?

实体

员工实体

package com.nexient.orgchart.data.entity;

import org.hibernate.validator.constraints.NotEmpty;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Set;

@Entity
@Table(name = "employee")
public class EmployeeEntity extends BaseEntity {

    @Column(name = "first_name")
    @NotEmpty
    @Size(min = 1)
    private String firstName;

    @Column(name = "middle_initial")
    private Character middleInitial;

    @Column(name = "last_name")
    @NotEmpty
    @Size(min = 1)
    private String lastName;

    @Column(name = "email")
    @NotEmpty
    @Size(min = 1)
    private String email;

    @Column(name = "skype_name")
    @NotEmpty
    @Size(min = 1)
    private String skypeName;

    @ManyToOne
    @JoinColumn(name = "job_title_id")
    private JobTitleEntity jobTitle;

    @ManyToOne
    @JoinColumn(name = "manager_id")
    private EmployeeEntity manager;

    @ManyToOne
    @JoinColumn(name = "department_id")
    private DepartmentEntity department;

    @OneToMany(mappedBy = "manager")
    private Set<EmployeeEntity> ManagedEmployees;

    @OneToMany
    private Set<DepartmentEntity> ManagedDepartments;

    @Column(name = "is_manager")
    @NotNull
    private boolean isManager;

    ... Getters and Setters ...

职位名称实体

package com.nexient.orgchart.data.entity;

import org.hibernate.validator.constraints.NotEmpty;

import javax.persistence.*;
import java.util.Set;

@Entity
@Table(name = "job_title")
public class JobTitleEntity extends BaseEntity {

    @Column(name = "name", unique = true)
    @NotEmpty
    private String name;

    @OneToMany
    private Set<EmployeeEntity> titleEmployees;

    ... Getters and Setters ...

部门实体

package com.nexient.orgchart.data.entity;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.NotEmpty;

import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "department")
public class DepartmentEntity extends BaseEntity {

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "manager_id")
    private EmployeeEntity manager;

    @Column(name = "name", nullable = false, length = 50, unique = true)
    @NotNull
    @NotEmpty
    @Size(min = 1, max = 45)
    private String name;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "parent_department_id", referencedColumnName = "ID")
    private DepartmentEntity parentDepartment;

    @OneToMany(mappedBy = "parentDepartment")
    private Set<DepartmentEntity> departments = new HashSet<>(0);

    @OneToMany(mappedBy = "department")
    private Set<EmployeeEntity> employees = new HashSet<>(0);

    ... Getters and Setters ...

【问题讨论】:

    标签: java spring jpa


    【解决方案1】:

    发生这种情况是因为您有 N 员工与 1 部门关系:

    @ManyToOne
    @JoinColumn(name = "department_id")
    private DepartmentEntity department;
    

    您的 JPA 实现选择使用专用表而不是可为空的列来支持此关系。它显然已经从所涉及实体的表名中自动派生了 employee_department 名称。

    使用配置为生成 DDL 语句并记录所有 SQL 语句的 JPA 实现来运行应用程序可能会非常有启发性。确切的配置将取决于您选择的 JPA 提供程序,但由于您使用的是 Spring,您可能需要 spring.jpa.generate-ddl=truespring.jpa.show-sql=true。见the documentation

    【讨论】:

    • 您知道如何防止这种情况发生吗?还是让创建此表更好?我的 POST 似乎工作了一会儿,然后在几次请求后出现了这个错误。到目前为止,我能够修复它的唯一方法是重置我的数据库和服务器。
    • 要“防止”这种情况发生,您必须有效地放弃这一点并手动定义一个 Entity 来替换原始关系/映射。这也意味着对其余代码的重大更改:您将需要额外的存储库来追溯关系。与您的 JPA 提供者抗争以改变其行为/期望绝对不是一个好主意。最好进行调整,但是您设置数据库以包含关系所需的表。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-02
    • 1970-01-01
    • 2021-08-10
    • 2021-12-09
    • 2017-05-26
    相关资源
    最近更新 更多