【问题标题】:ManyToManyToMany - Joining three tables with Hibernate annotationsManyToManyToMany - 使用 Hibernate 注释连接三个表
【发布时间】:2017-06-17 21:16:30
【问题描述】:

一开始我以为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 {
    ...
}

但在此实现中,每个Project 只能设置一个Permission。我想完成为一个项目设置多个权限的能力,以便以下情况为真:

| 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             |

【问题讨论】:

  • 如果 Map 的键/值是实体,那么这不是 ElementCollection;根据 JPA 规范,它需要 ManyToMany

标签: java hibernate jpa annotations


【解决方案1】:

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

@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&lt;PermissionAssignation&gt;(或List,根据您的喜好/需要):

 @Entity
 public class User {

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

 }

【讨论】:

  • 这非常有效。我唯一添加的是用户的项目列表,以帮助我浏览权限。将项目列入列表将允许用户“查看”项目,并且权限将允许他们对项目采取各种操作。
  • 这是一个很好的答案,不是每个人都会使用,但我喜欢这个解决方案。你能分享嵌入式id的例子吗? spring jpa 中是否有此改进的版本?
  • 如果你使用 CrudRepository 来访问这个类,你需要在类和 PK 中都使用它,对吧? CrudRepository&lt;PermissionAssignation, PermissionAssignation&gt;
  • 如何添加 PermissionAssignation?我收到错误 ConversionNotSupportedException
【解决方案2】:

由于我最近遇到了这个问题并且仍然很挣扎,所以我想分享一个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));
    }
}

【讨论】:

    猜你喜欢
    • 2013-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    • 2012-06-06
    相关资源
    最近更新 更多