如何映射具有双向关系的三元日期映射?

How to Map a Ternary Map of Dates With a Bidirectional Relationship?

我读过:JPA: How to map a Map with Date as key 但我有相反的问题,我将 Map 的值组件作为日期。我应该如何映射这个?地图:Map<Club, java.util.Date> 你能展示一个例子,包括俱乐部 class 的代码吗?需要注意的是,这是一个三元映射。我不确定这是否重要,但 ClubClubber 也有双向 ManyToMany 关系。我最初的猜测是:

public class Clubber{
   @Id
   @Column(name = "Clubber_Id")
   private final int id;
   @Temporal(TemporalType.TIMESTAMP)
   @ElementCollection
   @MapKeyJoinColumn(name = "id")
   private final Map<Club, Date> joinDate;
   @ManyToMany(cascade = CascadeType.ALL)
   private final Collection<Club> clubs;
}

public class Club {
   @Id
   @Column(name = "Club_ID")
   private final int id;
   @ManyToMany(cascade = CascadeType.ALL, mappedBy = "clubs")
   private final Collection<Clubber> clubbers;
}

主要:

Map<Club, Date> dates = randomGeneration(Date.class);
Collection<Club> clubs = randomGeneration(Club.class);
Clubber clubber = new Clubber(clubs, dates);
Club club = new Club(Arrays.asList(clubber));
session.saveOrUpdate(club);

当我尝试保存一个 Club 其中有几个 Clubbers 时出现的异常:

 org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Club

当我将 @Transient 添加到 ClubberjoinDate 时,异常消失了。

实际上异常告诉我们在保存 Club 对象列表时 Clubber(Collection clubbers)没有被保存。您保存俱乐部的代码,在保存 Club 之前,您必须在其中保存每个 Clubber 然后为 Club.

调用 save

应用@Transient 注解用于表示一个字段不被持久化到数据库中。这就是为什么你在应用 @Transient.

后没有得到异常

您的实体应如下所示:

@Entity(name = "Clubber")
public class Clubber{

    @Id
    @GeneratedValue
    @Column(name = "Clubber_Id")
    private Integer id;

    @Temporal(TemporalType.TIMESTAMP)
    @ElementCollection
    @CollectionTable(name="CLUB_ASSIGNMENTS", joinColumns=@JoinColumn(name="Clubber_Id", referencedColumnName="Clubber_Id"))
    @Column(name="CLUB_DATE")
    @MapKeyJoinColumn(name = "Club_ID", referencedColumnName="Club_ID")
    private Map<Club, Date> joinDate = new HashMap<>();

    public Integer getId() {
        return id;
    }

    public Map<Club, Date> getJoinDate() {
        return joinDate;
    }

    public Collection<Club> getClubs() {
        return joinDate.keySet();
    }

    public void addClub(Club club) {
        joinDate.put(club, new Date());
        //clubs.add(club);
        club.getClubbers().add(this);
    }
}

@Entity(name = "Club")
public class Club {

    @Id
    @GeneratedValue
    @Column(name = "Club_ID")
    private Integer id;

    @ManyToMany(mappedBy = "joinDate", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    private List<Clubber> clubbers = new ArrayList<>();

    public Integer getId() {
        return id;
    }

    public List<Clubber> getClubbers() {
        return clubbers;
    }
}

这些是此映射生成的表:

create table CLUB_ASSIGNMENTS (Clubber_Id integer not null, CLUB_DATE timestamp, Club_ID integer not null, primary key (Clubber_Id, Club_ID))
create table Club (Club_ID integer generated by default as identity (start with 1), primary key (Club_ID))
create table Clubber (Clubber_Id integer generated by default as identity (start with 1), primary key (Clubber_Id))
alter table CLUB_ASSIGNMENTS add constraint FK_i1d8m16i8ytv7jybg8aneo9hj foreign key (Club_ID) references Club
alter table CLUB_ASSIGNMENTS add constraint FK_6oitm1mry43ga5iovtfamp3q3 foreign key (Clubber_Id) references Clubber
alter table CLUB_ASSIGNMENTS add constraint FK_3xj613grja6o0xkjeb7upms4 foreign key (CLUB_DATE) references Club

这就是将 Clubber 关联到 Club 的方式:

final Clubber clubberReference = doInTransaction(session -> {
    Clubber clubber = new Clubber();
    Club club = new Club();
    clubber.addClub(club);
    session.persist(club);
    return clubber;
});

doInTransaction(session -> {
    Clubber clubber = (Clubber) session.get(Clubber.class, clubberReference.getId());
    assertEquals(1, clubber.getClubs().size());
    assertEquals(1, clubber.getJoinDate().size());
});

我在 GitHub 上创建了一个测试来检查这个并且它有效:

insert into Club (Club_ID) values (default)
insert into Clubber (Clubber_Id) values (default)]
insert into CLUB_ASSIGNMENTS (Clubber_Id, Club_ID, CLUB_DATE) values (1, 1, '2015-06-10 16:37:36.487')