Reputation: 1685
Recognizing that there are a number of questions about this already, I can't see the problem for my specific case. I have another instance of this in my application (which is working fine), and as far as I can tell I am mirroring the configuration. In fact, when I run the application using mvn: spring-boot:run
, everything works and all the data is found as expected. However, when I try to run the tests for the application, any test that uses
@RunWith(SpringRunner.class)
@DataJpaTest
public class TestClass {
@Autowired
private TestEntityManager em;
...
}
produces this error:
java.lang.IllegalStateException: Failed to load ApplicationContext Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: Use of @OneToMany or @ManyToMany targeting an unmapped class: com.utilities.domain.manufacturing.Machine.operators[com.humanresources.domain.MachineOperator] Caused by: org.hibernate.AnnotationException: Use of @OneToMany or @ManyToMany targeting an unmapped class: com.utilities.domain.manufacturing.Machine.operators[com.humanresources.domain.MachineOperator]
Admittedly, I don't have a great understanding of the configuration, but it doesn't make sense to me why one set of classes works but this does not. Here are the classes (with just the pertinent parts):
Employee
@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);
}
}
}
}
Machine
@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;
}
}
MachineOperator
@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());
}
}
MachineOperatorID
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;
}
}
Anyone have any ideas what is wrong, or is there a better way to get the same result? (I want to be able to view an employee and see the machines they can operate, or view a machine and see all the employees which can operate it.) I am using Spring Boot 2.0.3. Thanks!
Upvotes: 0
Views: 1875
Reputation: 413
First you need to edit your MachineOperatorID Class by changing the IDs to primitive types for example :
public class MachineOperatorID implements Serializable {
private static final long serialVersionUID = 1L;
private int employee;
private int machine;
...
}
and see if it solves the problem. Or maybe you should consider this approach from docs:
If you are looking to load your full application configuration, but use an embedded database, you should consider @SpringBootTest combined with @AutoConfigureTestDatabase rather than this annotation.
Upvotes: 0
Reputation: 5786
Usually this occurs if your javax.persistence
's @Entity
is not annotated or if the entities or not scanned. If you have tried the given options in comments, then please debug as follows:
DEBUG
logging of org.hibernate in application.yaml/properties or log4j2.xml or any logging configuration.Look for following or similar log :
o.hibernate.jpa.internal.util.LogHelper : PersistenceUnitInfo
.
.
.
Managed classes names [
.
.
]
By this, you will know the registered entities. Check if your entity is scanned from the path.
IdClass
MachineOperatorID to only have specific column fields and not object. From the hibernate specs, I quote:map multiple properties as @Id properties and declare an external class to be the identifier type. This class, which needs to be Serializable, is declared on the entity via the @IdClass annotation. The identifier type must contain the same properties as the identifier properties of the entity: each property name must be the same, its type must be the same as well if the entity property is of a basic type, its type must be the type of the primary key of the associated entity if the entity property is an association (either a @OneToOne or a @ManyToOne).
@Id
for individual columns and also use @ManyToOne
associations separately on different field. Definitely your relationship can be simplified a lot.Upvotes: 4
Reputation: 763
Remove the employee and machine objects from machineoperator class and replace it with MachineOperatorID instance in it. and create machine id and operator id as well with in MachineOperatorID class and annotate the instance with @ID that will resolve your problem and thats the correct way of doing things in hibernate.
Upvotes: 0