ManyToManyToMany - 使用Hibernate注释连接三个表

时间:2017-02-01 07:49:05

标签: java hibernate jpa annotations

起初我以为this solution可能会解决我的问题:

@Entity
public class User {

    @JoinTable(name = "user_permission",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "permission_id"))
    @MapKeyJoinColumn(name = "project_id")
    @ElementCollection
    private Map<Project, Permission> permissions = new HashMap<>();

}

@Entity
public class Project {
    ...
}

@Entity
public class Permission {
    ...
}

但在此实施中,每Permission只能设置一个Project。我希望能够为项目设置多个权限,以便以下内容成立:

| user_id | project_id | permission_id |
|---------|------------|---------------|
| 1       | 1          | 1             |
|---------|------------|---------------|
| 1       | 1          | 2             |
|---------|------------|---------------|
| 1       | 2          | 1             |
|---------|------------|---------------|
| 1       | 2          | 2             |
|---------|------------|---------------|
| 2       | 1          | 1             |
|---------|------------|---------------|
| 2       | 1          | 2             |
|---------|------------|---------------|
| 2       | 2          | 1             |
|---------|------------|---------------|
| 2       | 2          | 2             |

2 个答案:

答案 0 :(得分:4)

您可以使用专用于关系表的实体。这就是我们用自己的属性声明关系的方式 这将导致以下实现:

@Entity
@IdClass(PermissionAssignation.class)
public class PermissionAssignation {

     @Id
     @ManyToOne
     @JoinColumn(name="user_id")
     private User user;

     @Id
     @ManyToOne
     @JoinColumn(name="project_id")
     private Project project;

     @Id
     @ManyToOne
     @JoinColumn(name="permission_id")
     private Permission permission;
     ...
}

我使用了这篇文章中的解决方案:Hibernate and no PK

它解释了如何用字段创建PK(我没有测试它)。 如果它不起作用,您最好使用EmbeddedId类。

如果您希望您的关系是双向的,那么您可以根据自己的喜好/需要使用Set<PermissionAssignation>(或List):

 @Entity
 public class User {

      @OneToMany(mappedBy="user")
      private Set<PermissionAssignation> permissions;

 }

答案 1 :(得分:0)

因为我最近遇到了这个问题并且还在挣扎,所以我想分享一个 complete code example。此示例使用单独的 @EmbeddedId 类,该类仍将创建具有 3 个 PK/FK 列的表。我的示例使用 Lombok 来填充一堆样板代码,例如 getter/setter、构造函数等。还需要覆盖 equalshashcode 方法。这是使用 Spring 框架编写的,它连接了 repos 和测试。希望有人认为这是一个有用的指南。

/* ENTITY CLASSES */
@Entity
@Data
@Table(name = "_Who")
public class Who {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "who", fetch = FetchType.EAGER)
    @JsonManagedReference
    List<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.EAGER)
    @JsonBackReference
    @JoinColumn(name = "who_id", insertable = false, updatable = false)
    private Who who;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "what_id", insertable = false, updatable = false)
    private What what;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "where_id", insertable = false, updatable = false)
    private Where where;
}

@Embeddable
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class WhoWhatWhereId implements Serializable {
    @Column(name = "who_id")
    Long whoId;
    @Column(name = "what_id")
    Long whatId;
    @Column(name = "where_id")
    Long whereId;
}

/* REPOSITORIES */
@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 CLASS */
@SpringBootTest
@Slf4j
public class ThreeWayAssocTest {

    private final WhoRepository whoRepository;
    private final WhatRepository whatRepository;
    private final WhereRepository whereRepository;
    private final WhoWhatWhereRepository whoWhatWhereRepository;

    @Autowired
    public ThreeWayAssocTest(WhoRepository whoRepository, WhatRepository whatRepository, WhereRepository whereRepository, WhoWhatWhereRepository whoWhatWhereRepository) {
        this.whoRepository = whoRepository;
        this.whatRepository = whatRepository;
        this.whereRepository = whereRepository;
        this.whoWhatWhereRepository = whoWhatWhereRepository;
    }

    @Test
    public void attemptPersistence() {
        /*
        * the commented pieces can be used to do the initial inserts.  Later, fetch existing values so as not to fill
        * up the database
        */
        Who who =
        /*        new Who();
        who.setName("Carl");
        whoRepository.save(who);*/
                whoRepository.findById(1L).get();
        What what =
        /*        new What();
        what.setThing("strawberry");
        whatRepository.save(what);
        what.setThing("salad");
        whatRepository.save(what);*/
                whatRepository.findById(2L).get();
        Where where =
        /*        new Where();
        where.setPlace("plate");
        whereRepository.save(where);*/
                whereRepository.findById(1L).get();
        WhoWhatWhere whoWhatWhere = new WhoWhatWhere(who, what, where);
        whoWhatWhereRepository.save(whoWhatWhere);
        LOGGER.debug("finished");
    }

    @Test
    public void testSerializing() throws JsonProcessingException {
        Iterable<Who> examples = whoRepository.findWhoByName("Carl");
        Who carl = examples.iterator().next();
        LOGGER.debug("Carl: {}", carl);
        LOGGER.debug("found some: \n {}", new ObjectMapper().writeValueAsString(examples));
    }
}