【问题标题】:Spring Boot Hibernate - Use of @OneToMany targeting an unmapped classSpring Boot Hibernate - 使用@OneToMany 定位未映射的类
【发布时间】:2018-06-22 20:14:04
【问题描述】:

意识到已经有很多关于这个的问题,我看不出我的具体案例的问题。我的应用程序中有另一个实例(工作正常),据我所知,我正在镜像配置。事实上,当我使用mvn: spring-boot:run 运行应用程序时,一切正常,所有数据都按预期找到。但是,当我尝试为应用程序运行测试时,任何使用

@RunWith(SpringRunner.class)
@DataJpaTest
public class TestClass {
    @Autowired
    private TestEntityManager em;
    ...
}

产生这个错误:

java.lang.IllegalStateException: 无法加载 ApplicationContext 引起:org.springframework.beans.factory.BeanCreationException: 创建类中定义的名称为“entityManagerFactory”的bean时出错 路径资源 [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: 调用 init 方法失败;嵌套异常是 org.hibernate.AnnotationException:使用@OneToMany 或@ManyToMany 针对未映射的类: com.utilities.domain.manufacturing.Machine.operators[com.humanresources.domain.MachineOperator] 引起:org.hibernate.AnnotationException:使用@OneToMany 或 @ManyToMany 针对未映射的类: com.utilities.domain.manufacturing.Machine.operators[com.humanresources.domain.MachineOperator]

诚然,我对配置不是很了解,但我不明白为什么一组类可以工作,但事实并非如此。以下是类(仅包含相关部分):

员工

@Entity
@Table(name="humanresources.employees")
public class Employee implements Serializable {
    private static final long serialVersionUID = 1L;

    private int employeeID;
    ...
    private List<MachineOperator> machines = new ArrayList<>();

    public Employee() {}

    @Id
    @Column(name="pk_employeeid")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JsonView(View.SimpleEmployeeView.class)
    public int getEmployeeID() {
        return employeeID;
    }

    public void setEmployeeID(int employeeID) {
        this.employeeID = employeeID;
    }

    ...    
    @OneToMany(mappedBy="employee",cascade=CascadeType.ALL,orphanRemoval=true)
    @JsonView(View.EmployeeView.class)
    public List<MachineOperator> getMachines() {
        return machines;
    }

    public void setMachines(List<MachineOperator> machines) {
        this.machines = machines;
    }

    public void addMachine(Machine machine) {
        MachineOperator machineOperator = new MachineOperator(this, machine);
        this.machines.add(machineOperator);
        machine.getOperators().add(machineOperator);
    }

    public void removeCompany(Machine machine) {
        for (Iterator<MachineOperator> iterator = machines.iterator(); iterator.hasNext(); ) {
            MachineOperator machineOperator = iterator.next();

            if (machineOperator.getEmployee().equals(this) &&
                    machineOperator.getMachine().equals(machine)) {
                iterator.remove();
                machineOperator.getMachine().getOperators().remove(machineOperator);
                machineOperator.setEmployee(null);
                machineOperator.setMachine(null);
            }
        }
    }
}

机器

@Entity
@Table(name="utilities.mnfg_machines")
public class Machine implements Serializable {
    private static final long serialVersionUID = 1L;

    private int machineID;
    ...
    private List<MachineOperator> operators = new ArrayList<>();

    public Machine() {}

    @Id
    @Column(name="pk_machineid")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JsonView({View.MachineView.class,View.DefaultMachineView.class})
    public int getMachineID() {
        return machineID;
    }

    public void setMachineID(int machineID) {
        this.machineID = machineID;
    }

    ...

    @OneToMany(mappedBy="machine",orphanRemoval=true)
    @JsonView({View.MachineView.class,View.DefaultMachineView.class})
    public List<MachineOperator> getOperators() {
        return operators;
    }

    public void setOperators(List<MachineOperator> operators) {
        this.operators = operators;
    }
}

机器操作员

@Entity
@Table(name="humanresources.employee_machineoperators")
@IdClass(MachineOperatorID.class)
public class MachineOperator implements Serializable {
    private static final long serialVersionUID = 1L;

    private Employee employee;
    private Machine machine;
    private SkillLevel skillLevel;

    public MachineOperator() {}

    public MachineOperator(Employee employee, Machine machine) {
        this.employee = employee;
        this.machine = machine;
    }

    public MachineOperator(Employee employee, Machine machine, SkillLevel skillLevel) {
        this.employee = employee;
        this.machine = machine;
        this.skillLevel = skillLevel;
    }

    @Id
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="fk_employeeid")
    @JsonView(View.SimpleEmployeeView.class)
    public Employee getEmployee() {
        return employee;
    }

    public void setEmployee(Employee employee) {
        this.employee = employee;
    }

    @Id
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="fk_machineid")
    public Machine getMachine() {
        return machine;
    }

    public void setMachine(Machine machine) {
        this.machine = machine;
    }

    @ManyToOne
    @JoinColumn(name="fk_skilllevelid")
    public SkillLevel getSkillLevel() {
        return skillLevel;
    }

    public void setSkillLevel(SkillLevel skillLevel) {
        this.skillLevel = skillLevel;
    }

    @Override
    public int hashCode() {
        return Objects.hash(machine, employee);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;

        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }

        MachineOperator other = (MachineOperator) obj;
        return Objects.equals(machine, other.getMachine()) && Objects.equals(employee, other.getEmployee());
    }
}

机器操作员ID

public class MachineOperatorID implements Serializable {
    private static final long serialVersionUID = 1L;

    private Employee employee;
    private Machine machine;

    public MachineOperatorID() {}

    public MachineOperatorID(Employee employee, Machine machine) {
        this.employee = employee;
        this.machine = machine;
    }

    public Employee getEmployee() {
        return employee;
    }

    public void setEmployee(Employee employee) {
        this.employee = employee;
    }

    public Machine getMachine() {
        return machine;
    }

    public void setMachine(Machine machine) {
        this.machine = machine;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 83 * hash + Objects.hashCode(this.machine);
        hash = 83 * hash + Objects.hashCode(this.employee);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final MachineOperatorID other = (MachineOperatorID) obj;
        if (!Objects.equals(this.machine, other.machine)) {
            return false;
        }
        if (!Objects.equals(this.employee, other.employee)) {
            return false;
        }
        return true;
    }
}

任何人都知道出了什么问题,或者有没有更好的方法来获得相同的结果? (我希望能够查看一个员工并查看他们可以操作的机器,或者查看一台机器并查看所有可以操作它的员工。)我使用的是 Spring Boot 2.0.3。谢谢!

【问题讨论】:

标签: hibernate spring-boot spring-data-jpa


【解决方案1】:

如果您的javax.persistence@Entity 没有注释或者实体没有被扫描,通常会发生这种情况。如果您在 cmets 中尝试过给定的选项,请按如下方式调试:

  • 在 application.yaml/properties 或 log4j2.xml 或任何日志记录配置中启用 DEBUG org.hibernate 日志记录。

寻找以下或类似的日志:
o.hibernate.jpa.internal.util.LogHelper : PersistenceUnitInfo . . . Managed classes names [ . . ]

由此,您将了解已注册的实体。检查是否从路径扫描了您的实体。

  • 其次,如果您的实体已加载,请将您的IdClass MachineOperatorID 更改为仅具有特定的列字段而不是对象。从休眠规范,我引用:

将多个属性映射为@Id 属性并声明一个外部 类作为标识符类型。这个类,需要 可序列化,通过 @IdClass 注释在实体上声明。 标识符类型必须包含与标识符相同的属性 实体的属性:每个属性名称必须相同,其 如果实体属性是基本属性,类型也必须相同 type,它的类型必须是关联的主键的类型 如果实体属性是关联(@OneToOne 或 @ManyToOne)。

  • 如果这是可更改的代码,请简化您的关联。将@Id 用于各个列,并在不同的字段上分别使用@ManyToOne 关联。绝对可以大大简化你们的关系。

【讨论】:

  • 查找Managed classes names 的提示非常有用。事实证明,它只是从一个包中获取实体。我会认为一个包对于给定的测试来说已经很好了,但显然TestEntityManager 引用/需要的不仅仅是测试中的内容。我在测试中添加了@EntityScan("com.companyname")。之后,所有内容都显示在托管类列表中,并且所有测试现在都通过了。这对我来说并不完全有意义,但它确实有效。
  • 那太好了! :) 我在测试中所做的是 Autowired entityManager 并自己创建了 TestEntityManager。 new TestEntityManager(entityManager.getEntityManagerFactory())。这允许原始实体管理器拥有的扫描包。如果您操纵交易,则不会污染并需要@DirtiesContext
【解决方案2】:

从 machineoperator 类中删除员工和机器对象,并将其替换为其中的 MachineOperatorID 实例。并在 MachineOperatorID 类中创建机器 ID 和操作员 ID,并使用 @ID 注释实例,这将解决您的问题,这是在休眠中做事的正确方法。

【讨论】:

    【解决方案3】:

    首先,您需要通过将 ID 更改为原始类型来编辑 MachineOperatorID 类:

    public class MachineOperatorID implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private int employee;
    private int machine; 
    
    ...
    }
    

    看看能不能解决问题。或者,也许您应该从文档中考虑这种方法:

    如果您希望加载完整的应用程序配置,但 使用嵌入式数据库,你应该考虑结合@SpringBootTest 使用@AutoConfigureTestDatabase 而不是这个注解。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-24
      • 2016-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-03
      相关资源
      最近更新 更多