I have a project where every Student
can be tested by a Tester
, and a single tester can test many students.
I need students to know their testers - so this is a unidirectional Many to One.
Tester:
@Entity
public class Tester
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
public Tester(String name)
{
this.name = name;
}
protected Tester(){}
public String getName()
{
return this.name;
}
@Override
public String toString()
{
return String.format("Tester [id=%s, name=%s]", this.id, this.name);
}
}
Student:
@Entity
public class Student
{
@Id
private Long id; //this shouldn't be auto incremented
@Column(nullable = false)
private int age;
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "tester_id")
private Tester tester;
public Student(Long id, int age, Tester tester)
{
this.id = id;
this.age = age;
this.tester = tester;
}
protected Student(){}
public int getAge()
{
return this.age;
}
public Tester getTester()
{
return this.tester;
}
@Override
public String toString()
{
return String.format("Student [id=%s, age=%s, tester=%s]", this.id, this.age, this.tester);
}
}
When I run this code:
Tester tester = new Tester("Tester");
Student student = new Student(1L, 27, tester);
this.studentRepository.save(student);
I expect both entities to be saved due to the cascade, but instead I get an sql error because the tester's name is magically null?!?
If I remove the nullable = false
from the column, the tester entity is saved but with null in the name column...
What the hell did I do wrong?
Please don't recommend saving the Tester first, it's not helping because I want to understand what's wrong with my code.
Answer from comment. As per the Spring docs, the student is assumed to already exist as it has an id:
By default Spring Data JPA inspects first if there is a Version-property of non-primitive type. If there is, the entity is considered new if the value of that property is null. Without such a Version-property Spring Data JPA inspects the identifier property of the given entity. If the identifier property is null, then the entity is assumed to be new. Otherwise, it is assumed to be not new.
This can be fixed by adding a version column or by using a @Transient flag that controls if the entity is new or not (described in more detail in the linked document).
Alternatively, the entity manager's persist method can be used without Spring.