I am trying to left join a second table but it doesn't show up. It just gives me all users instead of all users along with the FinishedExams.
This is the method in my repository:
public interface IUserRepository extends CrudRepository<User, Integer> {
#Query("SELECT u FROM User u LEFT JOIN FinishedExam f ON u.id = f.user")
List<User> getAllWithExams();
}
In my FinishedExamController:
#Autowired
private IFinishedExamRepository finishedExamRepository;
#Autowired
private IUserRepository userRepository;
#GetMapping("/allUsersWithExams")
#Fetch(FetchMode.SELECT)
public Iterable<User> getAllUsersWithTheirExams()
{
return userRepository.getAllWithExams();
}
My User model:
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
#GeneratedValue()
#Column(name = "id")
private int id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "role_id", nullable = false)
public Role role;
public String getStudentNumber() {
return studentNumber;
}
public void setStudentNumber(String studentNumber) {
this.studentNumber = studentNumber;
}
#Column(name = "student_number", nullable = true)
private String studentNumber;
#Column(name = "first_name", nullable = true)
private String firstName;
#Column(name = "last_name", nullable = true)
private String lastName;
#Column(name = "email", nullable = true, unique = true)
private String email;
#OneToOne(fetch = FetchType.EAGER, mappedBy = "user")
private FinishedExam finishedExam;
#JsonIgnore
#Column(name = "password", nullable = true)
private String password;
#Column(name = "created_at")
private LocalDateTime createdAt;
#Column(name = "updated_at")
private LocalDateTime updatedAt;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "event_users",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "event_id", referencedColumnName = "id")
)
#PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
#PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
updatedAt = createdAt;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
public String getRole() {
return role.getName();
}
public void setRole(Role role) {
this.role = role;
}
}
Finished exams model:
#Entity
#Table(name = "finished_exams")
public class FinishedExam implements Serializable {
#Id
#GeneratedValue()
#Column(name = "id")
private int id;
#OneToOne(fetch = FetchType.EAGER)
#Fetch(FetchMode.SELECT)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "exam_id")
private Exam exam;
#Column(name = "finishedExam", nullable = false)
private String finishedExam;
#Column(name = "created_at")
private LocalDateTime createdAt;
#Column(name = "updated_at")
private LocalDateTime updatedAt;
#PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
updatedAt = createdAt;
}
#PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
public int getId() {
return id;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Exam getExam() {
return exam;
}
public void setExam(Exam exam) {
this.exam = exam;
}
public String getFinishedExam() {
return finishedExam;
}
public void setFinishedExam(String finishedExam) {
this.finishedExam = finishedExam;
}
public void setId(int id) {
this.id = id;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
}
I can put whatever I want in the ON clause but nothing changes.
You need to provide a path from your entity to target entity to join:
Replace the query
#Query("SELECT u FROM User u LEFT JOIN FinishedExam f ON u.id = f.user")
with
#Query("SELECT u FROM User u LEFT JOIN u.finishedExam f ON u.id = f.user.id")
I can put whatever I want in the ON clause but nothing changes.
Of course nothing changes. You are performing a left join across a 1:1 relationship, selecting (only) the left entity, and not placing any filter criteria on that entity. The join criteria (and indeed the join itself) don't matter: your query is equivalent to SELECT u FROM User u.
Did you perhaps mean to perform an inner join instead?
Related
I have tables User, Roles, Groups and a join table GroupRoles. A user can have many Roles (some of which are not group specific), and a Group can have many roles. Since Roles can be group related I need to associate a groupID with them and hence GroupRoles has a unique key of userID, groupID and roleID. I need userID as part of the key as I need to know what Roles are associated to a user. In hibernate is mapping a unique key like this the same as mapping a composite key, where I would have something along the lines of
#Embeddable
public class GroupRoleKey implements Serializable {
#Column(name="userID")
private Long userID;
#Column(name="groupID")
private Long groupID;
#Column(name="roleID")
private Long roleID;
protected GroupRoleKey(){}
public GroupRoleKey(Long userID, Long roleID, Long groupID) {
this.userID = userID;
this.roleID = roleID;
this.groupID = groupID
}
Honestly I'm not sure if this is even the best way to represent the User - Role - Group relation, any advice would be appreciated. I need to display a Users Roles within each group they belong to, for example I may want to display Bob's Roles Admin and Teacher for Group 1 and role Group Admin for group 2. I need to know which roles correspond to what group and what roles correspond to what user.
EDIT:
Group Entity:
#Table(name="FocusGroups")
#Entity
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "groupID")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long groupID;
private String groupName;
#ManyToMany
#JoinTable(name = "GroupMembers",
joinColumns = #JoinColumn(
name = "groupID"),
inverseJoinColumns = #JoinColumn(
name = "userID"))
private Set<User> groupMembers = new HashSet<>();
#ManyToMany
#JoinTable(name = "GroupRoles",
joinColumns =
#JoinColumn(
name = "groupID"),
inverseJoinColumns = #JoinColumn(
name = "roleID"))
private Set<Role> roles = new HashSet<>();
#ManyToOne(fetch = FetchType.EAGER, optional = true)
#JoinColumn(name="frameworkID", nullable = true)
private Framework framework;
public Group(){}
public Group(String groupName, Set<User> groupMembers, Framework framework) {
this.groupName = groupName;
this.groupMembers = groupMembers;
this.framework = framework;
}
public Long getGroupID() {
return groupID;
}
public void setGroupID(Long groupID) {
this.groupID = groupID;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public Set<User> getGroupMembers() {
return groupMembers;
}
public void setGroupMembers(Set<User> groupMembers) {
this.groupMembers = groupMembers;
}
public void addMembers(Set<User> groupMembers){
this.groupMembers.addAll(groupMembers);
}
public void addMember(User groupMember){
this.groupMembers.add(groupMember);
}
public String groupMembersToString(){
String out = "";
int count = 0;
if(groupMembers.size() > 0){
for(User user: groupMembers){
if(count >= 1){
out += ", ";
}
out += user.getUsername();
count++;
}
}else{
out = "No members";
}
return out;
}
public boolean hasMember(String groupMemberName) {
for (User member : this.groupMembers) {
if (member.getUsername().equals(groupMemberName)) {
return true;
}
}
return false;
}
public User getGroupMember(String groupMemberName){
for(User member: this.groupMembers){
if(member.getUsername().equals(groupMemberName)){
return member;
}
}
return null;
}
public Framework getFramework() {
return framework;
}
public void setFramework(Framework framework) {
this.framework = framework;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
Role:
#Entity
#Table(name = "Roles")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long roleID;
private String roleName;
#ManyToMany
#JoinTable(name = "GroupRoles",
joinColumns =
#JoinColumn(
name = "roleID"),
inverseJoinColumns = #JoinColumn(
name = "groupID", nullable = true))
private Set<Group> groups = new HashSet<>();
protected Role(){}
public Role(String roleName){
this.roleName = roleName;
}
public Long getId() {
return roleID;
}
public void setId(Long id) {
this.roleID = id;
}
public String getName() {
return roleName;
}
public void setName(String roleName) {
this.roleName = roleName;
}
}
User:
#Table(name="Users")
#Entity
#NamedStoredProcedureQueries({
#NamedStoredProcedureQuery(
name = "userRating",
procedureName = "CalculateUserRating",
parameters = {
#StoredProcedureParameter(
name = "userID",
type = Long.class,
mode = ParameterMode.IN),
#StoredProcedureParameter(
name = "focusID",
type = Long.class,
mode = ParameterMode.IN),
#StoredProcedureParameter(
name = "userRating",
type = BigDecimal.class,
mode = ParameterMode.OUT)
})
})
//store a base rating?
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long userID;
#Column(name = "userHashedPassword")
private String password;
#Column(name = "userName")
private String userName;
#Column(name = "userEmail")
private String email;
#ManyToMany
#JoinTable(name = "GroupMembers",
joinColumns = #JoinColumn(
name = "userID"),
inverseJoinColumns = #JoinColumn(
name = "groupID"))
private Set<Group> usersGroups = new HashSet<>();
#ManyToMany
#JoinTable(name = "UserRoles",
joinColumns = #JoinColumn(
name = "userID"),
inverseJoinColumns = #JoinColumn(
name = "roleID"))
private Set<Role> roles = new HashSet<>();
#OneToMany(mappedBy = "user")
private Set<Rating> ratings;
protected User(){}
public User(String userHashedPassword, String userName, String email, Set<Role> roles){
this.password = userHashedPassword;
this.userName = userName;
this.email = email;
this.roles = roles;
}
public User(String userName, String userHashedPassword){
this.userName = userName;
this.password = userHashedPassword;
}
public Long getUserId() {
return userID;
}
public void setId(Long userID) {
this.userID = userID;
}
public String getPassword(){
return password;
}
public void setPassword(String password){
this.password = password;
}
public String getUsername() {
return userName;
}
public void setUsername(String name) {
this.userName = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
public Set<Rating> getRatings() {
return ratings;
}
public void setRatings(Set<Rating> ratings) {
this.ratings = ratings;
}
public String rolesToString(){
String outputRoles = "";
int count = 0;
for(Role role: roles){
if(count >= 1){
outputRoles += ", ";
}
outputRoles += role.getName();
count++;
}
return outputRoles;
}
public void removeRole(Role role){
this.roles.remove(role);
}
public Set<Group> getGroups() {
return usersGroups;
}
public void addGroup(Group group) {
this.usersGroups.add(group);
}
public void addGroups(Set<Group> groups) {
this.usersGroups.addAll(groups);
}
public Set<Group> getUsersGroups() {
return usersGroups;
}
public void setUsersGroups(Set<Group> usersGroups) {
this.usersGroups = usersGroups;
}
public String groupsToString(){
String outputGroups = "";
int count = 0;
if(usersGroups.size() > 0){
for(Group group: usersGroups){
if(count >= 1){
outputGroups += ", ";
}
outputGroups += group.getGroupName();
count++;
}
}else{
outputGroups = "None";
}
return outputGroups;
}
}
Here is my User entity
package org.scd.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.scd.model.security.Role;
import javax.persistence.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
#Entity
#Table(name = "USERS")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "FIRST_NAME", nullable = false, length = 45)
private String firstName;
#Column(name = "LAST_NAME", nullable = false, length = 45)
private String lastName;
#Column(name = "EMAIL", nullable = false, unique = true, length = 45)
private String email;
#Column(name = "PASSWORD", nullable = false, length = 256)
private String password;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>(0);
#OneToMany(mappedBy = "user",
fetch = FetchType.EAGER
)
private List<Position> positions;
public User() {
}
public User(String firstName, String lastName, String email, String password) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.password = password;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public void setPassword(String password) {
this.password = password;
}
public List<Position> getPositions() {
return positions;
}
public void setPositions(List<Position> positions) {
this.positions = positions;
}
#JsonIgnore
public String getPassword() {
return password;
}
#JsonProperty("password")
public String getHiddenPassword() {
return "****";
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
Here is my Position entity
package org.scd.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.format.annotation.DateTimeFormat;
import javax.persistence.*;
import java.time.LocalDate;
import java.util.Date;
#Entity
#Table(name = "POSITIONS")
public class Position {
#Id
#GeneratedValue(strategy =GenerationType.IDENTITY)
private Long id;
#Column(name = "START_DATE", nullable = false)
private LocalDate creationDate;
#Column(name = "LATITUDE", nullable = false, length = 45)
private String latitude;
#Column(name = "LONGITUDE", nullable = false, length = 45)
private String longitude;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id", nullable = false, updatable = false)
#JsonIgnore
private User user;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Position )) return false;
return id != null && id.equals(((Position) o).getId());
}
#Override
public int hashCode() {
return 31;
}
public Position() {
}
public Position(LocalDate creationDate, String latitude, String longitude, User user) {
this.creationDate = creationDate;
this.latitude = latitude;
this.longitude = longitude;
this.user = user;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public LocalDate getCreationDate() {
return creationDate;
}
public void setCreationDate(LocalDate creationDate) {
this.creationDate = creationDate;
}
public String getLatitude() {
return latitude;
}
public void setLatitude(String latitude) {
this.latitude = latitude;
}
public String getLongitude() {
return longitude;
}
public void setLongitude(String longitude) {
this.longitude = longitude;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
Here is the GET request that doesn't work. I can only send the startDate and endDate as a String, but I need them to be in Date format.
#GetMapping(path = "/byUserIdAndTimePeriod/{userId}/{startDate}/{endDate}")
public ResponseEntity<List<Position>> getPositionByUserAndTimePeriod(#PathVariable Long userId, #PathVariable Date startDate, #PathVariable Date endDate) {
return new ResponseEntity<>(new ArrayList<Position>(positionService.getPositionByUserAndTimePeriod(userId,startDate,endDate)),HttpStatus.OK);
}
This one is the updated GET request that works
#GetMapping(path = "/byUserIdAndTimePeriod/{userId}/{startDate}/{endDate}")
public ResponseEntity<List<Position>> getPositionByUserAndTimePeriod(#PathVariable Long userId,
#PathVariable #DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
#PathVariable #DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {
return new ResponseEntity<>(new ArrayList<Position>(positionService.getPositionByUserAndTimePeriod(userId,startDate,endDate)),HttpStatus.OK);
}
Here is the position service
#Override
public List<Position> getPositionByUserAndTimePeriod(Long userId, LocalDate startDate, LocalDate endDate) {
User user = new User();
user.setId(userId);
return positionRepository.findAllByUserAndCreationDateBetween(user,startDate,endDate);
}
Here is the position repository
List<Position> findAllByUserAndCreationDateBetween(User user, LocalDate startDate, LocalDate endDate);
Do you have any ideas as to how I should resolve this problem?
How should the GET request look? Should I modify some things in the service or other places?
Thanks for your time.
You have to add the #DateTimeFormat(pattern="yyy-MM-dd") annotation to the parameter:
#GetMapping(path = "/byUserIdAndTimePeriod/{userId}/{startDate}/{endDate}")
public ResponseEntity<List<Position>> getPositionByUserAndTimePeriod(#PathVariable Long userId,
#PathVariable Date startDate,
#PathVariable #DateTimeFormat(pattern="yyy-MM-dd") Date endDate) {
return new ResponseEntity<>(new ArrayList<Position>(positionService.getPositionByUserAndTimePeriod(userId,startDate,endDate)),HttpStatus.OK);
}
I create application in Spring, which stores albums, musicians and bands. Album can contain multiple bands and musicians. I created association between Album and Band/Musician. Jet I am unable to delete it. I don't want to delete objects, just the association. I tried to send REST PUT request and setting musicians and bands to null on Album site, yet nothing happens:
{
"id": 2,
"title": "Lulu",
"bands": null,
"musicians": null,
"duration": {
"hours": 1,
"minutes": 20,
"seconds": 4
},
"releaseDate": "31/10/2011",
"coverPath": "https://upload.wikimedia.org/wikipedia/en/4/40/Lou_Reed_and_Metallica_-_Lulu.jpg",
"spotifyPath": null
}
I have created following class and method to link Album and Musician, yet I am unable to "unlink" them:
#RestController
public class AlbumMusicianController {
#Autowired
AlbumRepository albumRepository;
#Autowired
MusicianRepository musicianRepository;
#Transactional
#PostMapping("/musician/{musicianId}/album/{albumId}")
public List<Album> associate(#PathVariable Long musicianId, #PathVariable Long albumId) {
Album album = this.albumRepository.findById(albumId).orElseThrow(() -> new MissingResourceException("Album",
"Album", albumId.toString()));
return this.musicianRepository.findById(musicianId).map((musician) -> { musician.getAlbums().add(album);
return this.musicianRepository.save(musician).getAlbums();
}).orElseThrow(() -> new MissingResourceException("Musician", "Musician", musicianId.toString()));
}
}
Would be thankful for any help.
Below are necessary sources.
Album class:
#Entity
#Table(name="album")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Album {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name="title")
private String title;
#ManyToMany(targetEntity = Band.class, mappedBy = "albums")
#JsonSerialize(using = BandsSerializer.class)
private List<Band> bands;
#ManyToMany(targetEntity = Musician.class, mappedBy = "albums")
#JsonSerialize(using = MusiciansSerializer.class)
private List<Musician> musicians;
#Embedded
#Column(name="duration")
private Duration duration;
#Column(name="releasedate")
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="dd/MM/yyyy", timezone="CET")
private Date releaseDate;
#Column(name="coverpath")
private String coverPath;
#Column(name="spotifypath")
private String spotifyPath;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Duration getDuration() {
return duration;
}
public void setDuration(Duration duration) {
this.duration = duration;
}
public Date getReleaseDate() {
return releaseDate;
}
public void setReleaseDate(Date releaseDate) {
this.releaseDate = releaseDate;
}
public String getCoverPath() {
return coverPath;
}
public void setCoverPath(String coverPath) {
this.coverPath = coverPath;
}
public String getSpotifyPath() {
return spotifyPath;
}
public void setSpotifyPath(String spotifyPath) {
this.spotifyPath = spotifyPath;
}
public List<Band> getBands() {
return bands;
}
public void setBands(List<Band> bands) {
this.bands = bands;
}
public List<Musician> getMusicians() {
return musicians;
}
public void setMusicians(List<Musician> musicians) {
this.musicians = musicians;
}
}
Musician class:
#Entity
#Table(name="musician")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Musician {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name="name")
private String name;
#Column(name="surname")
private String surname;
#Column(name="birthdate")
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="dd/MM/yyyy", timezone="CET")
private Date birthDate;
#Column(name="picturepath")
private String picturePath;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "album_musician",
joinColumns = #JoinColumn(name = "album_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "musician_id",
referencedColumnName = "id"))
private List<Album> albums;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public String getPicturePath() {
return picturePath;
}
public void setPicturePath(String picturePath) {
this.picturePath = picturePath;
}
public List<Album> getAlbums() {
return albums;
}
public void setAlbums(List<Album> albums) {
this.albums = albums;
}
}
Band class:
#Entity
#Table(name="band")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Band {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name="name")
private String name;
#Column(name="picturepath")
private String picturePath;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "album_band",
joinColumns = #JoinColumn(name = "album_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "band_id",
referencedColumnName = "id"))
#JsonSerialize(using = AlbumsSerializer.class)
private List<Album> albums;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPicturePath() {
return picturePath;
}
public void setPicturePath(String picturePath) {
this.picturePath = picturePath;
}
public List<Album> getAlbums() {
return albums;
}
public void setAlbums(List<Album> albums) {
this.albums = albums;
}
}
Based on your JSON body I'm going to assume you were sending a PUT request for the Album entity. There were two things that I found missing that got it to work for me after adjusting. I'm not sure if you were avoiding using them for one reason or another.
Cascade rules to cascade changes from Album to its relations.
Proper entity mapping for the join table from Album to its relations.
Not really sure why this was an issue - Hibernate did not seem to throw any exceptions related to this at execution time, but it did not seem to persist things correctly.
Here is an adjusted relation definition for Album's relation to Musician.
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name="album_musician", joinColumns = #JoinColumn(name = "musician_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "album_id",
referencedColumnName = "id"))
private List<Musician> musicians;
In this format, I was able to cascade changes from Album to Musician. You will have to do something similar for the Band entity to cascade operations from Album to Band.
I am trying to do mapping in hibernate i.e one to many, I have one "m_role" table and another is "m_privilege" table, so I wanna assign many privilege to role table, but I already have values inside my privilege table so I wanna map many privilege to one role, so how I can do using hibernate.
Below are my Model class:
#Table(name = "m_role")
#Entity
public class MasterRole {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#ElementCollection
#JoinTable(name="trans_role_privilege")
#OneToMany(cascade = CascadeType.ALL)
private Set<MasterPrivilege> mMasterPrivilege = new HashSet<MasterPrivilege>();
public Set<MasterPrivilege> getmMasterPrivilege() {
return mMasterPrivilege;
}
public void setmMasterPrivilege(Set<MasterPrivilege> mMasterPrivilege) {
this.mMasterPrivilege = mMasterPrivilege;
}
#Column(name = "role", nullable = false)
private String role;
#Column(name = "description", nullable = true)
private String description;
#Column(name = "status", nullable = false)
private Integer status;
#Column(name = "creationDate", nullable = true)
private Date creationDate;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
and another table for privilege:
#Table(name = "m_privilege")
#Entity
#Embeddable
public class MasterPrivilege {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
#Column(name = "Pid", nullable = false)
private Integer Pid;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "description", nullable = true)
private String description;
#Column(name = "status", nullable = false)
private Integer status;
#Column(name = "creationDate", nullable = true)
private Date creationDate;
public Integer getPid() {
return Pid;
}
public void setPid(Integer pid) {
Pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
So after running this will create an trans_role_privilege table to keep role_id and privilege_id. So in this I have already values in the privilege table. Now I want to map if we add new role with privilege id to trans_role_privilege.
I have this part of database schema:
and this User entity:
#Entity
#Table(name = "user", catalog = "ats")
public class User implements java.io.Serializable{
private static final long serialVersionUID = 1L;
private String username;
private boolean enabled;
private Role role;
private ClientVersion clientVersion;
private ClientLicense clientLicense;
#JsonIgnore
private Set<NotificationHasUser> notificationHasUsers = new HashSet<NotificationHasUser>(0);
public User() {
}
public User(String username, boolean enabled) {
this.username = username;
this.enabled = enabled;
}
public User(String username, boolean enabled, Role role, Set<NotificationHasUser> notificationHasUsers) {
this.username = username;
this.enabled = enabled;
this.role = role;
this.notificationHasUsers = notificationHasUsers;
}
#Id
#Column(name = "username", unique = true, nullable = false, length = 45)
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
#Column(name = "enabled", nullable = false)
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_role", nullable = false)
public Role getRole() {
return this.role;
}
public void setRole(Role role) {
this.role = role;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_clientVersion", nullable = false)
public ClientVersion getClientVersion() {
return this.clientVersion;
}
public void setClientVersion(ClientVersion clientVersion) {
this.clientVersion = clientVersion;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.user")
public Set<NotificationHasUser> getNotificationHasUser() {
return this.notificationHasUsers;
}
public void setNotificationHasUser(Set<NotificationHasUser> notificationHasUsers) {
this.notificationHasUsers = notificationHasUsers;
}
#OneToOne(fetch = FetchType.LAZY, mappedBy = "user")
public ClientLicense getClientLicense(){
return this.clientLicense;
}
public void setClientLicense(ClientLicense clientLicense){
this.clientLicense = clientLicense;
}
}
All works fine until I add a new clientlicense. If I add this I receive an infinite loop:
Could not write content: Infinite recursion (StackOverflowError) (through reference chain: com.domain.User["clientLicense"]->com.domain.ClientLicense["user"]->com.domain.User["clientLicense"]->com.domain.ClientLicense["user"]->com.domain.User["clientLicense"]->com.domain.ClientLicense["user"]->com.domain.User["clientLicense"]->com.domain.ClientLicense["user"]->com.domain.User["clientLicense"]-....
This is my ClientLicense entity
#Entity
#Table(name = "clientlicense", catalog = "ats")
public class ClientLicense implements java.io.Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private Integer idClientLicense;
private Date startDate;
private Date endDate;
private int counter;
private String macAddress;
private String cpuId;
private User user;
public ClientLicense() {
}
/**
* #param startDate
* #param endDate
* #param counter
* #param macAddress
* #param cpuId
* #param users
*/
public ClientLicense(Date startDate, Date endDate, int counter, String macAddress, String cpuId, User user) {
super();
this.startDate = startDate;
this.endDate = endDate;
this.counter = counter;
this.setMacAddress(macAddress);
this.setCpuId(cpuId);
this.user = user;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id_clientLicense", unique = true, nullable = false)
public Integer getIdClientLicense() {
return this.idClientLicense;
}
public void setIdClientLicense(Integer idClientLicense) {
this.idClientLicense = idClientLicense;
}
#Column(name = "startDate", nullable = false)
public Date getStartDate() {
return this.startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
#Column(name = "endDate", nullable = false)
public Date getEndDate() {
return this.endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
#Column(name = "counter", nullable = false)
public int getCounter() {
return this.counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
/**
* #return the macAddress
*/
#Column(name = "macAddress", nullable = false)
public String getMacAddress() {
return macAddress;
}
/**
* #param macAddress the macAddress to set
*/
public void setMacAddress(String macAddress) {
this.macAddress = macAddress;
}
/**
* #return the cpuId
*/
#Column(name = "cpuId", nullable = false)
public String getCpuId() {
return cpuId;
}
/**
* #param cpuId the cpuId to set
*/
public void setCpuId(String cpuId) {
this.cpuId = cpuId;
}
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "id_username")
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
}
This is my first OneToOne relationship, what is the correct annotation that I have to use? I read some example but I don't understand fine, they are different each other.
try something like this.
public class User {
private ClientLicense clientLicense;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "user")
public ClientLicense getClientLicense() {
return this.clientLicense;
}
}
public class ClientLicense {
private User user;
#OneToOne
#JoinColumn(name = "id_username")
public User getUser() {
return this.user;
}
}
The problem is that the two entities have no way of finding out that the two fields are actually specifying a single relationship. So hibernate assumes that they are not the same relationship and therefore tries to fetch them (because one-to-one relationships are fetched eagerly by default).
Add #OneToOne(mappedBy = "user") before the clientLicense field in the User class to tell hibernate that this field is "mapped by" the same column as the user field in the ClientLicense class