问题
I know this has been asked a lot of times before, I know it because I've searched for every related question to my problem to try to find a solution, however, none of the proposed solutions are working for me and I'm pretty sure that I have to be missing something.
Person Class:
@Entity
@Table(name = "person", schema = "test")
public class PersonEntity {
@Id
@Column(name = "id")
private long id;
@Basic
@Column(name = "name")
private String name;
@Basic
@Column(name = "age")
private int age;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "personid")
private List<ProjectEntity> projects;
}
Project Class:
@Entity
@Table(name = "project", schema = "test")
public class ProjectEntity {
@Id
@Column(name = "id")
private long id;
@Basic
@Column(name = "name")
private String name;
@Basic
@Column(name = "budget")
private int budget;
@JoinColumn(name = "personid", referencedColumnName = "id")
@ManyToOne(cascade = CascadeType.ALL)
private PersonEntity personid;
}
I have a bidirectional OneToMany/ManyToOne relationship, I have tried changing the cascade type to PERSIST, adding 'optional=false' and way more things but nothing seems to work.
I read that I have to 'join' manually the entities before the persist, and that's what I did:
em = JPAUtility.getEntityManager();
em.getTransaction().begin();
PersonEntity personTest = new PersonEntity();
personTest.setName("Test");
personTest.setAge(23);
ProjectEntity projectTest = new ProjectEntity();
projectTest.setName("hello");
projectTest.setBudget(232);
projectTest.setPersonid(personTest);
List<ProjectEntity> projects = new ArrayList<ProjectEntity>();
projects.add(projectTest);
personTest.setProjects(projects);
em.persist(personTest);
em.getTransaction().commit();
em.close();
return personTest;
But I still get this:
Caused by:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:
Cannot add or update a child row: a foreign key constraint fails
(`test`.`project`, CONSTRAINT `FK_Personid` FOREIGN KEY (`personid`) REFERENCES
`person` (`id`) ON DELETE CASCADE ON UPDATE CASCADE)
I honestly don't know what I'm missing, if anyone has any suggestion I'll be more than happy to try it.
Thank you so much!
SOLUTION
I managed to solve the problem thanks to all the suggestions, basically, I was missing the @GeneratedValue(strategy=GenerationType.AUTO)
annotation which I removed because I thought it didn't work but, it wasn't working because I was missing a property on the persistence.xml:
<property name="hibernate.id.new_generator_mappings" value="false" />
I found this info here
You also need a method to add the relationship in the objects:
public void addToProjects(ProjectEntity project){
project.setPersonid(this);
this.projects.add(project);
}
To make this work you need to initialize the List when you declare the variable:
private List<ProjectEntity> projects = new ArrayList<ProjectEntity>();
And that's it!
This is the final working code in case anyone can find it useful :):
Person Class:
@Entity
@Table(name = "person", schema = "test")
public class PersonEntity {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name = "id")
private long id;
@Basic
@Column(name = "name")
private String name;
@Basic
@Column(name = "age")
private int age;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "personid")
private List<ProjectEntity> projects = new ArrayList<ProjectEntity>();
public void addToProjects(ProjectEntity project) {
project.setPersonid(this);
this.projects.add(project);
}
}
Project Class:
@Entity
@Table(name = "project", schema = "test")
public class ProjectEntity {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name = "id")
private long id;
@Basic
@Column(name = "name")
private String name;
@Basic
@Column(name = "budget")
private int budget;
@JoinColumn(name = "personid", referencedColumnName = "id")
@ManyToOne(cascade = CascadeType.PERSIST)
private PersonEntity personid;
public void setPersonid(PersonEntity personid) {
this.personid = personid;
}
}
Make sure you add the Children to their Parent and vice-versa (addToProjects())
em = JPAUtility.getEntityManager();
em.getTransaction().begin();
PersonEntity personTest = new PersonEntity();
personTest.setName("Butters");
personTest.setAge(10);
ProjectEntity projectTest = new ProjectEntity();
projectTest.setName("Hanks");
projectTest.setBudget(10000);
ProjectEntity projectTest2 = new ProjectEntity();
projectTest2.setName("X");
projectTest2.setBudget(100);
personTest.addToProjects(projectTest);
personTest.addToProjects(projectTest2);
em.persist(personTest);
em.getTransaction().commit();
em.close();
Hope it helps! Thank you so much.
回答1:
The main thing that you will want to watch out for is to define the owning side of the relation correctly. As far as I remember, my takeaway from the (sometimes difficult to understand) official documentation was that the owning side is pretty much the one that will by default trigger cascades and transparent deletions.
For example, in the above, you have defined the owning side as ProjectEntity
, so the most important step for cascaded persistence to work is to add the project to PersonEntity.projects
.
You will then want to call persist
on the owning side of the relation, i.e.
em.persist(projectTest);
If this doesn't help, I would suggest that you enable SQL logging in your JPA provider to find out what statements it is trying to execute, and especially in what order these entities are being insert
ed.
Also try, as per existing comments, to persist person first. If you do this, I believe the correct way is to add the persisted entity to the relationship, i.e:
PersonEntity persistedPerson = em.persist(personTest);
projectTest.setPersonId(persistedPerson);
em.persist(projectTest);
回答2:
A couple of leads I can think of, because I crossed more than once this kind of problems:
Unless you want a cascade operation from Project
to update your Person
, you should remove
@ManyToOne(cascade = CascadeType.ALL)
from your personid
attribute
Try updating your projects
collection instead of creating a new one, because if it's already managed by Hibernate (which doesn't need to be , the persist/merge operation will be executed on the old one and the new one.
Person Class:
private List<ProjectEntity> projects = new ArrayList<>();
your code :
personTest.getProjects().addAll(projects);
I usually prefer merge
instead of persist
, because I find it more 'natural', and sometimes, the output is clearly not the same.
回答3:
I had the same problem. A @ManyToOne
that was not working for no reason and 2 classes. I added @GeneratedValue(strategy=GenerationType.AUTO)
, but it didn't fix my problem.
I also tried to rename the classes, clean, close project, restart, etc., but none worked. At the end, I deleted the files (made a copy before) and recreated them from new and that fixed my problem. I was on Eclipse 4.8, Spring 2.5, Groovy 2.5, Java 1.8
UPDATE: Not really sure what was the problem, anyway (for groovy) check your save method: myUserRepo(new MyUser("username")), check as well your xxxxRepo< MyUser, Integer> and also check that your file is .groovy (last one shouldn't be a problem)
Other UPDATE:
If you're creating a relational between 2 tables and use the save result, be sure to use @Transactional
on a Service and link the relation field, for example:
@Transactional
UserAccount save(UserAccount userAccount) {
User user = userRepo.save(new User(userAccount))
UserAccount.setUser(user)
userAccountRepo.save(userAccount)
}
来源:https://stackoverflow.com/questions/49554459/jpa-onetomany-manytoone-relationship-not-working-what-am-i-missing