I'm trying using Spring Data Rest in a Spring Boot backend.
I have an entity named Request with some fields and another entity named RequestData.
Now, RequestData's mapped table has the foreign key referencing request_id (and nullable=false)
Then, I need to insert a request along with request data.
The problem here is when I try to create a new request along with its data: I can't set the request inside request data because I still have no request Id, but the request_id is not nullable so i need to insert it.
a body like this:
{
type: "localhost:8080/..../1",
state: "localhost:8080/.../1",
...
data: [
{
field: "foo",
value: "bar",
}
]
}
throws an exception because it needs the request inside the data record.
This is the Request Entity
#Entity
#Table(name="requests")
public class Request {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private long id;
#ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name="request_type_id")
private RequestType type;
#ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name="request_state_id")
private RequestState state;
#ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name="citizen_id")
private User citizen;
#ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name="request_by")
private User requestedBy;
#ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name="operator_id")
private User operator;
#Column(name="creation_date")
private Instant creationDate;
#Column(name="last_change_date")
private Instant lastChangeDate;
#Column(name="closing_date")
private Instant closingDate;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name="request_id")
private List<RequestData> data;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name="request_id")
private List<RequestPost> posts;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name="request_id")
private List<RequestAttachment> attachments;
// constructors, getters, setters, ...
This is the RequestData Entity
#Entity
#Table(name="request_data",
uniqueConstraints = {#UniqueConstraint(columnNames = {"request_id", "field"})})
public class RequestData {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id")
private long id;
#Column(name="field", length=25, nullable = false)
private String field;
#Column(name="value")
private String Value;
#ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name="request_id", nullable = false)
private Request request;
// constructors, getters and setters...
I created a RequestRepository extending JPARepository but not a RequestDataRepository because request data should always go along with the request.
Removing the not nullable constraint on request_data.request_id would let me to do the post call without problems and checking the request data record over the table I find the request_id correctly set.
But I don't want to remove that constraint. Is there any method to specify that request Id will be automatically populated with the new request's id?
Thanks in advance
Related
I am working on a springboot application. I have 2 entity classes, Group and User. I also have #ManyToMany relationship defined in the Group class (Owning entity), and also in the User class, so that I can fetch all the groups a user belongs to. Unfortunately, I can't create a new group or a new user due to the following error;
{
"timestamp": "2022-09-09T20:29:22.606+00:00",
"status": 415,
"error": "Unsupported Media Type",
"message": "Content type 'application/json;charset=UTF-8' not supported"
}
When I try to fetch all groups a user belongs to by calling user.get().getGroups(); I get a a stack overflow error
Note: Currently I have #JsonManagedReference and #JsonBackReference in Group and User classes respectively. I also tried adding #JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") on both classes, but this did not work either. Adding value parameter to #JsonManagedReference and #JsonBackReference as demonstrated below did not work either. What am I doing wrong? What am I missing?
This is my Group entity class
#Table(name = "`group`") // <- group is a reserved keyword in SQL
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#JsonView(Views.Public.class)
private String name;
private Integer maximumMembers;
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
#JoinTable(name = "group_user", joinColumns = #JoinColumn(name = "group_id"), inverseJoinColumns = #JoinColumn(name = "user_id"))
#JsonView(Views.Public.class)
#JsonManagedReference(value = "group-member")
private Set<User> groupMembers;
}
This is my User entity class
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView(Views.Public.class)
private Long id;
#JsonView(Views.Public.class)
private String nickname;
#JsonView(Views.Public.class)
private String username; // <- Unique user's phone number
private String password;
#ElementCollection(targetClass = ApplicationUserRole.class)
#CollectionTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"))
#Enumerated(EnumType.STRING)
#Column(name = "role")
private Set<ApplicationUserRole> roles;
#ManyToMany(mappedBy = "groupMembers", fetch = FetchType.LAZY, targetEntity = Group.class)
#JsonBackReference(value = "user-group")
private Set<Group> groups;
}
Minimal, Reproducible Example https://github.com/Java-Techie-jt/JPA-ManyToMany
I found a permanent solution for this problem. For anyone else facing a similar problem, This is what I found. First, my entity classes had #Data Lombok annotation. I removed this because the #Data annotation has a tendency of almost always loading collections even if you have FetchType.LAZY.
You can read more about why you should't annotate your entity class with #Data here https://www.jpa-buddy.com/blog/lombok-and-jpa-what-may-go-wrong/
After removing this annotation, I removed #JsonManagedReference and #JsonBackReference from both sides of the relationship(both entities). I then added #Jsonignore to the referencing side only(User class). This solves 2 things
Creating a group with a list of users works fine
Adding a list of users to a group works fine.
After this, we are left with one last problem. When we try to read a user from the api, we get a user without the associated list of groups they belong to, because we have #JsonIgnore on the user list. To solve this, I made the controller return a new object. So after fetching the user from my service, I map it to a new data transfer object, the I return this object in the controller.
From here I used #JsonView to filter my responses.
This is how my classes look, notice there is no #Data in annotations.
Group
#Builder
#Entity
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Getter
#Setter
#Table(name = "`group`") // <- group is a reserved keyword in SQL
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private Integer maximumMembers;
#ManyToMany(fetch = FetchType.EAGER,
cascade = {CascadeType.MERGE, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinTable(name = "group_user",
joinColumns = #JoinColumn(name = "group_id"),
inverseJoinColumns = #JoinColumn(name = "user_id"))
#JsonView(UserViews.PublicUserDetails.class)
private Set<User> groupMembers = new HashSet<>();
}
User
#Builder
#Entity
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Getter
#Setter
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView(UserViews.PublicUserDetails.class)
private Long id;
#JsonView(UserViews.PublicUserDetails.class)
private String nickname;
#JsonView(UserViews.PublicUserDetails.class)
private String username; // <- Unique user's phone number
private String password;
#ElementCollection(targetClass = ApplicationUserRole.class)
#CollectionTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"))
#Enumerated(EnumType.STRING)
#Column(name = "role")
#JsonView(UserViews.PublicUserDetails.class)
private Set<ApplicationUserRole> roles;
#JsonIgnore
#ManyToMany(mappedBy = "groupMembers", fetch = FetchType.LAZY, targetEntity = Group.class)
private Set<Group> groups = new HashSet<>();
}
Method fetching user in user controller
#GetMapping("/get-groups")
public ResponseEntity<UserRequestResponseDTO> getWithGroups(#RequestParam(name = "userId") Long userId) {
User user = userService.getWithGroups(userId);
UserRequestResponseDTO response = UserRequestResponseDTO.builder()
.nickname(user.getNickname())
.username(user.getUsername())
.groups(user.getGroups())
.build();
return ResponseEntity.ok().body(response);
}
Hopefully this helps someone💁
I'm triying to save a user class with company and areas selected. User has a many to many relation with company and many to many to areas.
It's giving me the error : detached entity passed to persist:
I'm not sure what is the problem
USER:
#Entity
#Table(name = "NPRO_USUARIOS")
public class User implements Serializable {
private static final long serialVersionUID = -1330075515340995797L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="user_seq_gen")
#SequenceGenerator(name="user_seq_gen", sequenceName="TELCO_NPRO_USER_SEQ")
#NotNull
private int id_usuario;
#NotNull
private String nombre_usuario;
#ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.REMOVE, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
#JoinTable(name = "NPRO_USUARIOS_SOCIEDADES_AREAS", joinColumns = #JoinColumn(name = "id_usuario"), inverseJoinColumns = #JoinColumn(name = "id_sociedad"))
private Set<Sociedad> listaSociedad;
#Transient
private String sociedades;
// Si el area es nula, el usuario estara asignado a todas las areas
#ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.REMOVE, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
#JoinTable(name = "NPRO_USUARIOS_SOCIEDADES_AREAS", joinColumns = #JoinColumn(name = "id_usuario"), inverseJoinColumns = #JoinColumn(name = "id_area"))
private Set<Area> listAreas;
#Transient
private String areas;
#NotNull
private String matricula_usuario;
#NotNull
private String email_usuario;
#ManyToMany(cascade = { CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
#JoinTable(name = "NPRO_PERFILES_USUARIOS", joinColumns = #JoinColumn(name = "id_usuario"), inverseJoinColumns = #JoinColumn(name = "id_rol"))
private Set<Role> listaRoles;
#ManyToMany(cascade = { CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
#JoinTable(name = "NPRO_PERFILES_USUARIOS", joinColumns = #JoinColumn(name = "id_usuario"), inverseJoinColumns = #JoinColumn(name = "id_pantalla"))
private Set<Pantalla> listaPantallas;
private LocalDateTime fecha_ultimo_acceso;
private String observaciones;
#JsonIgnore
#ManyToOne
#JoinColumn(name = "usuario_modif")
private User usuario_modif;
}
Compnay:
#Entity
#Table(name = "NPRO_MAESTRO_SOCIEDADES")
public class Sociedad implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#NotNull
private int id_sociedad;
#NotNull
private String cod_sociedad;
#NotNull
private String cod_sociedad_gl;
#NotNull
private String nombre_sociedad;
#NotNull
private String cif_sociedad;
private String observaciones;
#JsonIgnore
#ManyToOne
#JoinColumn(name = "usuario_modif")
private User usuario_modif;
private String activo;
#JsonIgnore
#ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.REMOVE, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
#JoinTable(name = "NPRO_USUARIOS_SOCIEDADES_AREAS", joinColumns = #JoinColumn(name = "id_sociedad"), inverseJoinColumns = #JoinColumn(name = "id_usuario"))
private Set<User> listaUsuarios;
}
Area:
#Entity
#Table(name = "NPRO_MAESTRO_AREAS")
public class Area implements Serializable {
private static final long serialVersionUID = -1330075515340995797L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="area_seq_gen")
#SequenceGenerator(name="area_seq_gen", sequenceName="TELCO_NPRO_AREAS_SEQ")
#NotNull
private int id_area;
#NotNull
private String nombre_area;
private LocalDateTime fecha_modif;
private String observaciones;
#JsonIgnore
#ManyToOne
#JoinColumn(name = "usuario_modif")
private User usuario_modif;
#NotNull
private String activo;
#ManyToOne
#JoinColumn(name="id_sociedad")
private Sociedad sociedad;
#JsonIgnore
#ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.REMOVE, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
#JoinTable(name = "NPRO_USUARIOS_SOCIEDADES_AREAS", joinColumns = #JoinColumn(name = "id_area"), inverseJoinColumns = #JoinColumn(name = "id_usuario"))
private Set<User> listaUsuarios;
}
I'm using springboot jpa repository save method
#Override
public User save(User user) {
return userRepository.save(user);
}
And this is the complete error :
2020-06-09 15:49:02.371 [nio-8080-exec-4] .m.m.a.ExceptionHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.telefonica.npro.model.Area; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.telefonica.npro.model.Area
Thanks in advance
EDIT :
I'm reading about the eror in this page
http://knowledgespleasure.blogspot.com/2015/06/understand-detached-entity-passed-to.html
And I guess my problem is the last one :
On the other hand, if requirement is never to add a new child if its not alredy in DB then CascadeType.PERSIST should be removed and cascade={CascadeType.MERGE,CascadeType.REFRESH} should be used
User is always related with the company and areas, and they already exist, they are not going to be new.
But if I remove PERSIST, it's triying to insert in an id null in the commun table
NPRO_USUARIOS_SOCIEDADES_AREAS
Any help ?
I will explain your problem for the #ManyToMany bidirectional relationship between User and Area entities.
A bidirectional #ManyToMany association should have an owning and a mappedBy side. The CascadeType should be present only on one side of this association.
As explained in this article, you need to have both sides in sync as otherwise, you break the Domain Model relationship consistency, and the entity state transitions are not guaranteed to work unless both sides are properly synchronized.
For this reason, the User entity defines the addArea and removeArea entity state synchronization methods.
So, you should correct your User - Area #ManyToMany mapping in this way:
#Entity
#Table(name = "NPRO_USUARIOS")
public class User implements Serializable {
// ...
#ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.REMOVE, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
#JoinTable(name = "NPRO_USUARIOS_SOCIEDADES_AREAS", joinColumns = #JoinColumn(name = "id_usuario"), inverseJoinColumns = #JoinColumn(name = "id_area"))
private Set<Area> listAreas;
public User()
{
listAreas = new HashSet<>();
}
public void addArea(Area area) {
listAreas.add(area);
area.getUsers().add(this);
}
public void removeArea(Area area) {
listAreas.remove(area);
area.getUsers().remove(this);
}
}
#Entity
#Table(name = "NPRO_MAESTRO_AREAS")
public class Area implements Serializable {
// ...
#JsonIgnore
#ManyToMany(mappedBy = "listAreas")
private Set<User> listaUsuarios;
}
And then you can save a new user in this way:
User user = new User();
// ...
Area area1 = new Area();
// ...
user.addArea(area1);
Area area2 = new Area();
// ...
user.addArea(area2);
userRepository.save(user);
The similar correction should be done for the User - Sociedad relationship.
I have an Invoice entity which contains another entity - Counterparty and a list of another entities - Items. While setting up relations between list of items and invoice, there were no problems. However, when I try to set up a similar relation between invoice entity and counterparty entity, I get an error:
#OneToOne or #ManyToOne on pl.coderstrust.model.Invoice.counterparty references an unknown entity: pl.coderstrust.model.counterparty.Counterparty
This is my invoice, which expects to contain only one counterparty and a list of items.
#Entity
#Table(name = "invoices")
public class Invoice implements Comparable<Invoice>, Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "invoice_id")
private int id;
#Column(name = "date")
private LocalDate date = LocalDate.now();
#ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST,
CascadeType.REFRESH})
#JoinColumn(name = "nip")
private Counterparty counterparty;
#OneToMany(mappedBy = "invoice", cascade = {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH})
#JsonBackReference
private List<InvoiceItem> invoiceItems = new ArrayList<>();
This is my item entity, which can be related to one invoice:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id")
private int id;
private String description;
private int numberOfItems;
private BigDecimal amount;
private BigDecimal vatAmount;
#JoinColumn(name = "vat_code")
#Enumerated(EnumType.ORDINAL)
private Vat vat;
#OneToOne(mappedBy = "invoice_id", fetch = FetchType.EAGER, cascade = {CascadeType.DETACH,
CascadeType.MERGE, CascadeType.PERSIST,
CascadeType.REFRESH})
#JsonManagedReference
private Invoice invoice;
This is my counterparty, which is supposed to be related to many invoices:
#Entity
#Table(name = "counterparties")
public class Counterparty implements Serializable {
#Id
#Column(name = "nip")
private String nip;
private String companyName;
private String phoneNumber;
private String bankName;
private String bankNumber;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "counterparty", cascade = CascadeType.ALL)
private Address address;
#OneToMany(mappedBy = "counterparty", fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH})
private List<Invoice> invoices;
What is wrong with invoice-counterparty relations?
That Hibernate error is usually thrown when the class is not added to the hibernate configuration. Hibernate needs to be told all of the classes that serve as entities before it can use them.
How do you make your classes known to Hibernate? I.e. either by adding the class to the Configuration object:
configuration.addClass(Counterparty.class);
or by adding the class into a package that is scanned for entities when you are using Spring?
On another note: there seems to be something odd with the Item class perhaps? It specifies a OneToOne relation to Invoice; should this not be a ManyToOne (meaning that a single invoice can have 0 or more Items)?
I have an issue with a delete to a Many side of a ManyToOne relationship. I've already removed all CascadeTypes from the relationship but the issue still remains. The entry won't be removed (only selects are executed and no delete query). I'm trying to delete it through a CRUD repository call to delete. It calls the method and executes successfully but nothing happens.
The relationship goes as follows: an Activity has an assigned Course, a course can have many activities assigned to it. An Activity has a specific ActivityType.
The classes are as below.
Activity
public class Activity implements Item, Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
#ManyToOne
#JoinColumn(name = "type_id", nullable = false)
private ActivityType type;
#ManyToOne
#JoinColumn(name = "course_id", nullable = false)
#JsonSerialize(using = CustomCourseSerializer.class)
private Course course;
...
}
Course
public class Course implements Item, Serializable {
...
#OneToMany(mappedBy = "course", fetch = FetchType.EAGER, targetEntity = Activity.class) //cascade = { CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH, CascadeType.REMOVE}
#Fetch(value = FetchMode.SUBSELECT)
private List<Activity> activities;
...
}
Activity Type (has no reference to Activity)
public class ActivityType implements Item, Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name", nullable = false, unique = true)
private String name;
...
}
Any ideas how can I solve this issue or at least debug it? Thank you.
Add orphanRemoval = true attribute in the #OneToMany annotation in your Course entity.
public class Course implements Item, Serializable {
...
#OneToMany(mappedBy = "course", fetch = FetchType.EAGER, targetEntity = Activity.class, orphanRemoval = true, cascade = CascadeType.REMOVE )
#Fetch(value = FetchMode.SUBSELECT)
private List<Activity> activities;
...
}
Try to delete reference to Activity from Course. It seems unnecessary to me
I have three separate entities in my Spring JPA application - User, Department, Role
I have a single join table in my database to relate each of these Entities: USER_DEPARTMENT_ROLE
My question is, how can I define this relation in my entity classes? Do I have to define a #ManyToMany relationship in each of the separate entities? I know how to define this relationship between two tables, but for more than two I'm not sure where to start.
Any help is appreciated!
If you have more than two relations mapped in your join table then i would suggest creating a separate entity which would be used for mapping that particular table.
The question is whether you can have a distinct id column which would serve as an artificial primary key or you have to stick with the composite primary key build from the three foreign keys.
If you can add that artificial id (which is the modern way of designing your database) then your mapping should look something like the following:
Option 1
class User {
#OneToMany(mappedBy = "user", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<UserDepartmentRoleLink> userDepartmentRoleLinks;
}
class Department{
#OneToMany(mappedBy = "department", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<UserDepartmentRoleLink> userDepartmentRoleLinks;
}
class Role{
#OneToMany(mappedBy = "role", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<UserDepartmentRoleLink> userDepartmentRoleLinks;
}
class UserDepartmentRoleLink {
#Id
#GeneratedValue
private Long id;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "department_id")
private Department department;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "role_id")
private Role role;
}
Regarding setting the cascade types for the many to many relatioship is tricky and for many to many involving three tables is even trickier as every entity can play a role of parent or child depending on the circumstances.. i would suggest sticking only with the cascade = {CascadeType.PERSIST, CascadeType.MERGE} and handling other operations manually.
If you have to stay with the composite primary key then you should add additional Id class and change the link entity to the following:
Option 2
class User {
#OneToMany(mappedBy = "linkPk.user", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<UserDepartmentRoleLink> userDepartmentRoleLinks;
}
class Department{
#OneToMany(mappedBy = "linkPk.department", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<UserDepartmentRoleLink> userDepartmentRoleLinks;
}
class Role{
#OneToMany(mappedBy = "linkPk.role", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<UserDepartmentRoleLink> userDepartmentRoleLinks;
}
Linkage table
class UserDepartmentRoleLink {
#EmbeddedId
private UserDepartmentRoleLinkId linkPk
= new UserDepartmentRoleLinkId();
#Transient
public User getUser() {
return getLinkPk().getUser();
}
#Transient
public User getDepartment() {
return getLinkPk().getDepartment();
}
#Transient
public User getRole() {
return getLinkPk().getRole();
}
}
#Embeddable
public class UserDepartmentRoleLinkId implements java.io.Serializable {
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "department_id")
private Department department;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "role_id")
private Role role;
The bottom line is that you can use Many To Many here like outlined in this post -> example. But in my opinion you would save yourself a lot of headache if you map that link table as above. In the end the call is yours..