JPA many to many - do i have to remove\add from both collection sets? - spring

I have a User entity,
And a Department Entity
i want to have #manyToMany relationship between them :
many users can have many departments.
in my User entity:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "UserDepartments", joinColumns = { #JoinColumn(name = "user_id", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "department_id", referencedColumnName = "id") })
private Set<Department> departments;
and my Department entity has a SET as well of users..
my question is:
if i need to implement the method :
public void removeUserFromDepartment(User user, Department department) {
//bla bla
}
do i have to call
department.getUserCollection.remove(user);
AND
user.getDepartmentCollection.remove(department);
Or is there a way to maintain this logic by only removing one of them ?
If i have to save both its pretty hard to maintain especially for someone who doesn't know about the many to many relation of the two entities..

When a OneToMany or ManyToMany relationship exists in JPA the client code is responsible for managing the relationship. This means that you must explicitly remove the object from both sides of the relationship.
So lets say you have a User instance and need to remove a department.
User user = dao.findUser(1L); //Find a user, psuedo code
Department dept = user.getDepartments().get(0);
dept.getUsers().remove(user);
user.getDepartments().remove(user);
dao.update(user); //once again psuedo code
To use the code above you should add a cascade type to the relationship in the user entity:
#ManyToMany(fetch = FetchType.LAZY, cascade=CascadeType.ALL)
#JoinTable(name = "UserDepartments", joinColumns = { #JoinColumn(name = "user_id", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "department_id", referencedColumnName = "id") })
private Set<Department> departments;
This will cause saves on the User entity to be cascaded to the Departments entity. Just a reminder save is psuedo code, it will boil down to a call on the EntityManager such as persist or merge.

Related

Query result Infinite Recursion on ManyToMany relationship on hibernate

I have a entity mapping like this:
As you can see it is the bidirectional relationship and the team_users table has it own primary key and extra column - active.
Key code in team entity:
#OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
private Set<TeamUsers> team_users = new HashSet<TeamUsers>();
Key code in user entity:
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<TeamUsers> team_users = new HashSet<TeamUsers>();
Key code in team_user entity:
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "TEAM_ID")
private Team team;
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
I have a API which will return all team information, then I have service class like:
#Autowired
private TeamRepository teamRepo;
public List<Team> listAll() {
return (List<Team>) teamRepo.findAll();
}
Then the chrome log me that I have a "undefined" value, and when I test it by postman it shows me the infinite loop value:
I want to figure out what is the best approach to fetch the data?
I want to all information that tells me the team situation, including team... users...active status , almost everthing.
Any suggestions?
update
I tried to use #JsonIgnore on intermidate table :
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "TEAM_ID")
private Team team;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
but I won't get user information in that case and it avoid infinite loop:
What else I can do to get all information for teams?

Hibernate mapping user relation to entities

Let's se we have Hibernate entity User with basic fields such as username, password, roles etc..
Now we have an entity such as Car.
User has a OneToOne relationship with Car, cause he can own a car. But he also has besides this a OneToMany relationship to Car, because he also owns the cars of his children. But in the frontend I want to know which cars he owns for himself and which cars he owns for his children. The same applies to the relationship between User and motorbike (his own, his childrens, etc...)
How would the User entity class look like? Is it good to have the relationships mapped in an "Helper" entity such as UserData:
#Entity
#Data
#Table( name = "users",
uniqueConstraints = {
#UniqueConstraint(columnNames = "username")
})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Size(max = 150)
private String username;
#NotBlank
#Size(max = 120)
private String password;
#OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinColumn(name = "USER_DATA_ID")
private UserData userData;
UserData:
#Entity
#Data
#Table( name = "user_data")
public class UserData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinColumn(name = "OWN_CAR_ID")
private Car ownCar;
#OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinColumn(name = "PARTNER_CAR_ID")
private Car partnerCar;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable( name = "user_children_cars",
joinColumns = #JoinColumn(name = "user_data_id"),
inverseJoinColumns = #JoinColumn(name = "car_id"))
private Set<Car> childrenCars = new HashSet<>();
public boolean addToChildrenCarSet(Car c) {
return childrenCars.add(c);
}
public UserData() {
}
}
As you ask for an opinion, I would say it gets unnecessary complicated if you use the intermediate entity user_data. :-) There is no real drawback to add more fields and keys into the user class - performance is probably also better then using the EAGER fetching. If performance is an issue, better optimize querys later on then splitting the table now.
Also the #ManyToMany I would avoid - better create the intermediate table and relations yourself. You can check out https://bootify.io and create your database schema there. There is no EAGER fetching and also no CascadeType.ALL (both only good ideas in special cases), you would probably add more problems with that then actual helping in any way.
So the addToChildrenCarSet method would end up in a #Service class, in a method with #Transactional, in my proposal.

Found shared references to a collection many to many relation

I have this method:
#Override
public Movie createMovie(Movie movie) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
JwtUser user = (JwtUser)authentication.getPrincipal();
User current_user = userRepository.findOne(user.getId());
movieRepository.save(movie);
userRepository.save(new HashSet<User>(){{
add(new User(current_user, new HashSet<Movie>(){{
add(movie);
}}));
}});
return movieRepository.save(movie);
}
When I run my application and call that function I get this error:
Found shared references to a collection: com.movieseat.model.security.User.movies
In my User model I have:
#ManyToMany
#JoinTable(name = "user_movie",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "movie_id", referencedColumnName = "id")
)
private Set<Movie> movies;
And in my Movie model I have:
#ManyToMany(mappedBy = "movies", cascade = CascadeType.ALL)
private Set<User> users = new HashSet<>();
public Set<User> getUsers() {
return users;
}
What produces the error?
As I understand your code, you're trying to create a Movie in database and bind it to the current User. Correct me if I'm wrong.
At first, as you may learn from Hibernate User Guide, bidirectional #ManyToMany association should be defined and used differently.
A bidirectional #ManyToMany association has an owning and a mappedBy side. To preserve synchronicity between both sides, it’s good practice to provide helper methods for adding or removing child entities.
Secondly, you should not use CascadeType.ALL on #ManyToMany associations:
For #ManyToMany associations, the REMOVE entity state transition
doesn’t make sense to be cascaded because it will propagate beyond the
link table. Since the other side might be referenced by other entities
on the parent-side, the automatic removal might end up in a
ConstraintViolationException.
For example, if #ManyToMany(cascade = CascadeType.ALL) was defined and
the first person would be deleted, Hibernate would throw an exception
because another person is still associated with the address that’s
being deleted.
So, we should move cascade to the owning side, change cascade type, provide helper methods to the User and update only the owning side (User) of the association in our business logic. Let's change the code.
User model:
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(name = "user_movie",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "movie_id", referencedColumnName = "id")
)
private Set<Movie> movies = new HashSet<>();
public void addMovie(Movie movie) {
movies.add(movie);
movie.getUsers().add(this);
}
public void removeMovie(Movie movie) {
movies.remove(movie);
movie.getUsers().remove(this);
}
Movie model:
#ManyToMany(mappedBy = "movies")
private Set<User> users = new HashSet<>();
And business logic:
#Override
public Movie createMovie(Movie movie) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
JwtUser user = (JwtUser)authentication.getPrincipal();
User current_user = userRepository.findOne(user.getId());
current_user.addMovie(movie);
userRepository.save(current_user);
return movie;
}

Spring Hibernate Relation Mapping

I'm looking for help in one of my project.
I'm having Company Class and Bank Class.
Company Class and Bank Class are to be mapped using Many to Many Relation using Hibernate.
How Do I start? I'm done creating Company Module which is inserting data into table and same for the Bank. But How to show the mapping between both?
Flow Goes like this -
Add Company -> Edit/Update -> Add Bank to the previous Company Detail -> Bank Also Add/Update -> View All, which is needed to show The list of Companies and their respective banks.
I assume you use junction table so i would done it like this:
Company.class
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "nameOfJunctionTable", catalog = "yourDatabaseName", joinColumns = {
#JoinColumn(name = "companyId", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "bankId",
nullable = false, updatable = false) })
private Collection<Bank> banks;
Bank.class
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "banks")
private Collection<Company> companies;

Spring Crud on Nested Property

I have a spring crud repository:
#Repository
public interface CrudCVVacancyMatchRepository extends CrudRepository<CVVacancyMatchEntity, Long> {
The CVVacancyMatchEntity object has an Vacancy property. I'm trying to write a query method on properties of the Vacancy property.
The following query method works fine (Vacancy has a string property called name):
Iterable<CVVacancyMatchEntity> findByVacancyName(String name);
But Vacancy also has properties as a set:
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "vacancy_industries", joinColumns = { #JoinColumn(name = "fk_vacancy", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "fk_industry", referencedColumnName = "id") })
private final Set<IndustryEntity> industries = Sets.newHashSet();
This doesn't work:
Iterable<CVVacancyMatchEntity> findByVacancyIndustries(Set<IndustryEntity> industryEntities);
I get ERROR SqlExceptionHelper:146 - Syntax error in SQL statement
select cvvacancym0_.id as id1_19_, cvvacancym0_.comment as comment2_19_, cvvacancym0_.cv_id as cv_id4_19_, cvvacancym0_.rating as rating3_19_, cvvacancym0_.user_id as user_id5_19_, cvvacancym0_.vacancy_id as vacancy_6_19_ from cv_vacancy_match cvvacancym0_ cross join vacancy vacancyent1_ cross join vacancy_industries industries2_, industry industryen3_ where cvvacancym0_.vacancy_id=vacancyent1_.id and vacancyent1_.id=industries2_.fk_vacancy and industries2_.fk_industry=industryen3_.id and .=? [42001-182]
Any idea?

Resources