Given a 'many-to-many' relationship between Event
and Users
, I'm having trouble retrieving attendees' details when I retrieve an event by its id.
Output: Event(id=1, attendees=[])
Classes are as follows.
Event.java
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Builder
public class Event {
@Id
@GeneratedValue
private Long id;
@ManyToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
@JoinTable(name="event_user",
joinColumns=@JoinColumn(name="event_id", referencedColumnName="id"),
inverseJoinColumns=@JoinColumn(name="user_id", referencedColumnName="user_id")
)
private Set<User> attendees;
}
User.java
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Builder
@Table(name = "users")
public class User{
@Id
@GeneratedValue
private Long user_id;
private String name;
@ManyToMany(mappedBy="attendees")
private Set<Event> events;
}
EventRepo.java
public interface EventRepo extends JpaRepository<Event, Long>{
Event getById(Integer id);
}
Init.Java
@Component
class Init implements CommandLineRunner {
final EventRepo eventRepo;
public Init(EventRepo eventRepo) {
this.eventRepo = eventRepo;
}
@Override
public void run(String... args) throws Exception {
User u = User.builder().name("test").build();
Set<User> uset = new HashSet<>();
uset.add(u);
Event e = Event.builder()
.attendees(uset)
.build();
eventRepo.save(e);
var event = eventRepo.getById(1);
System.out.println(event);
}
}
Database View
Here are some little problem with the code, Let us go through the problems and provide the solution.
Events.java
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "events")
public class Event {
@Id
@GeneratedValue
private Long id;
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name = "event_user",
joinColumns = @JoinColumn(name = "event_id"),
inverseJoinColumns = @JoinColumn(name = "user_id")
)
private Set<User> attendees = new HashSet<>();
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Event event = (Event) o;
return Objects.equals(id, event.id);
}
@Override
public int hashCode() {
return getClass().hashCode();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Set<User> getAttendees() {
return attendees;
}
public void setAttendees(Set<User> attendees) {
this.attendees = attendees;
}
@Override
public String toString() {
return "Event{" +
"id=" + id +
", attendees=" + attendees.size() +
'}';
}
}
Don't use @Data annotation, since it generates toString() method which will make your program stackOverflow due to infinite recursion of toString() from event to user and user to event and so on..., Therefore, generate your own getter and setter along with toString() method.
Another thing to notice is that equals and hashCode, while working with Set dataStructure in ManyToMany, always try to implement equals and hashCode.
User.java
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToMany(mappedBy = "attendees")
private Set<Event> events = new HashSet<>();
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
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 Set<Event> getEvents() {
return events;
}
public void setEvents(Set<Event> events) {
this.events = events;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", events=" + events.size() +
'}';
}
}
Please note that, It's equals and hashcode is different from Event.java
Now another issue is in the interface 'EventRepo'
EventRepo
As in JavaDoc it's mentioned that getById() method is depreciated.
public interface EventRepository extends JpaRepository<Event, Long> {
@Override
Optional<Event> findById(Long id);
}
we need to use findById() to fetch the Event based on Id.
and atlast the runner code will look something like
@Override
public void run(String... args) throws Exception {
User u = new User();
u.setName("test-1");
User u2 = new User();
u2.setName("test-2");
Set<User> uset = new HashSet<>();
uset.add(u);
uset.add(u2);
Event e = new Event();
e.setAttendees(uset);
u.getEvents().add(e);
u2.getEvents().add(e);
Event save = eventRepository.save(e);
System.out.println(save);
Optional<Event> event = eventRepository.findById(1L);
System.out.println(event);
System.out.println(event.get().getAttendees());
}
One of the major issue in your code is that you added users in event But event is not added in users which is the major issue.
u.getEvents().add(e);
Hope this would help. I executed this code and got the result.