I’m looking for solid guidance and example for putting together a many-to-many-to-many entity relationship. As a trivial example, I’m thinking of classes: Who, What, and Where, each with an int id field. I imagine the nexus of the three would come together in a single class, WhoWhatWhere, with a composite key of three int fields; I expect the class itself could be used as an @IdClass. When I tried this out, it seemed to create the table structure I expected: three int columns each being both a PK and FK. However, when I tried to save an instance (by persisting one of each and then adding them to the composite class), I got an exception about casting an int to the encapsulated class. Am I taking the wrong approach? Ultimately, I’d like to be able to manipulate the What & Where as they relate to the Who. What’s the right way to create and annotate these classes?
I would recommend you use @Embeddable
instead for the id as I have shown here: How to join more tables to the same id (FK)? - #2 by beikov
Ok, so there must be something more to your Table4 class. Perhaps a constructor that takes the nested classes and populates its EmbeddedId
class? Because I can’t just add tables 1-3 and then persist it as I get a null id exception.
Please share your model and the code you use to persist
Here’s my full set of code; the method at the bottom is my test that I’m using to try it out. It looks like it’s working - my next challenge is selecting a Who, with an eager fetch so that I can serialize it into json. When I try, though, I’m getting an infinite recursion through the composite table.
@Entity
@Data
@Table(name = "_Who")
public class Who {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "who")
Set<WhoWhatWhere> storage;
}
@Entity
@Data
@Table(name = "_What")
public class What {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String thing;
}
@Entity
@Data
@Table(name = "_Where")
public class Where {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String place;
}
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Entity
@NoArgsConstructor
@Table(name = "_WhoWhatWhere")
public class WhoWhatWhere {
public WhoWhatWhere(Who who, What what, Where where) {
this.who = who;
this.what = what;
this.where = where;
this.setId(new WhoWhatWhereId(who.getId(), what.getId(), where.getId()));
}
@EmbeddedId
WhoWhatWhereId id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "who_id", insertable = false, updatable = false)
private Who who;
@ManyToOne
@JoinColumn(name = "what_id", insertable = false, updatable = false)
private What what;
@ManyToOne
@JoinColumn(name = "where_id", insertable = false, updatable = false)
private Where where;
}
@Embeddable
@NoArgsConstructor
public class WhoWhatWhereId implements Serializable {
public WhoWhatWhereId(Long whoId, Long whatId, Long whereId) {
this.whoId = whoId;
this.whatId = whatId;
this.whereId = whereId;
}
@Column(name = "who_id")
Long whoId;
@Column(name = "what_id")
Long whatId;
@Column(name = "where_id")
Long whereId;
}
@Repository
public interface WhoRepository extends PagingAndSortingRepository<Who, Long> {
Iterable<Who> findWhoByName (String name);
}
@Repository
public interface WhatRepository extends PagingAndSortingRepository<What, Long> {
}
@Repository
public interface WhereRepository extends PagingAndSortingRepository<Where, Long> {
}
@Repository
public interface WhoWhatWhereRepository extends PagingAndSortingRepository<WhoWhatWhere, WhoWhatWhereId> {
}
@Test
public void tryMe() {
Who who =
// new Who();
// who.setName("Carl");
// whoRepository.save(who);
whoRepository.findById(1L).get();
What what =
new What();
what.setThing("picture");
whatRepository.save(what);
// whatRepository.findById(2L).get();
Where where =
new Where();
where.setPlace("wall");
whereRepository.save(where);
// whereRepository.findById(1L).get();
WhoWhatWhere whoWhatWhere = new WhoWhatWhere(who, what, where);
whoWhatWhereRepository.save(whoWhatWhere);
LOGGER.debug("finished");
Iterable<Who> examples = whoRepository.findWhoByName("Carl");
LOGGER.debug("found some:");
}
Discovered that, perhaps, the recursion was due to the hashCode()
method that was injected. I fixed this by using @EqualsAndHashCode(onlyExplicitlyIncluded = true)
Now my problem is that when I select a Who, it’s only filling the Set<WhoWhatWhere> storage;
with one record (the 1,1,1 record), despite making it eager.
Please create a new question stating your current model and problem. It’s hard to follow when you post code and then change something which you only describe in text.