Jsh0s
Jsh0s

Reputation: 599

Hibernate - 1:N - The parent is duplicated upon saving the child

GitHub repo if needed, Maven web project (pom.xml), SQL script under resources directory.

I'm aware this is my fault, the problem is I haven't been able to fix it in the entire day, its likely something simple and over my head, also please ignore the relations regarding the table names and columns, it's a sample project to show the problem.


Expected:

Store the new child along its parent relation (the child has a column for it), without storing the parent again.


Error:

CascadeType.ALL causes the parent to duplicate, but attempting to remove it to use the other types throws: java.sql.SQLIntegrityConstraintViolationException: Column 'user_id' cannot be null

Column 'user_id' is the name of the column in the child table that stores the parent relation.


I will skip some annotations among other things so this doesn't become a wall of code


User entity

private Long id;
private String name;

@OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = {
        CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH})
private List<Username> usernameList = new ArrayList<>();

Username entity

private Long id;
private String username;

@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "user_id")
private User user;

Again, using anything but CascadeType.All throws an error for some reason


UsernameDAO (this is the method being used to store the child, username, which duplicates the parent, user)

Session session = factory.getCurrentSession();
session.save(username);

UsernameService

usernameDAO.save(username);

Upvotes: 3

Views: 723

Answers (2)

Jsh0s
Jsh0s

Reputation: 599

Please let me know if it's wrong to answer one's own question, so I just remove this.

The problem was in the front-end, I had the following in my Spring Form:

<form:select path="user" items="${listUsers}" />

When it should of been

<form:select path="user.id" items="${listUsers}" itemValue="id"/>

1) If itemValue is not specified Spring will take the value from toString.

2) Use the id field (binded entity) as the itemValue and the relationship field id as the path

3) You don't necessarily have to remove ClassType.PERSIST in order to avoid duplicates, as long as your entities are binded by their unique identifier (like the id), otherwise hibernate will see it as a new entry.

Upvotes: 1

atish.s
atish.s

Reputation: 1903

I believe the way you are assignment the object User in the object Username is incorrect. If you are doing something like this:

User user = new User();
user.set(... // set your attributes

Username username = new Username();
username.set(user);

Now if you save the username, hibernate will create a entry in the database for user because the way you have created user is by using the new keyword and this will make a new entry in the db.

If you don't want to create a new entry for the user, then you will have to load the entity from the database, so you should add a new method in your service class User which will return you a user given a user id.

e.g:

User user = userService.getUser(10);

Username username = new Username();
username.set(user);

Now when you will save username, this will not create a new entry in the table User. This is the way hibernate works. We have to load the entity, then do our operation on it and save it. The new keyword will create a new entry even if the id of the entity (primary key in db) is the same.

Upvotes: 1

Related Questions