Reputation: 599
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
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
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