JPQL:如何通过@ElementCollection与@MapKeyJoinColumn

时间:2018-01-23 15:06:52

标签: jpa jpa-2.0 jpql

我在创建正确的JPQL查询时遇到问题,无法通过以下表格加入:

enter image description here

在GROUPS和USERS之间有一个传统的@ManyToMany映射表, DOCUMENTS_GROUPS 是导致问题的原因。正如您在以下实体中所看到的,我希望将 DOCUMENTS GROUPS 之间的关系映射为包含 access_mode 的Map(其工作原理)除了查询之外就好了:

@Entity
@Table(name = "DOCUMENTS")
@NamedQueries({
    @NamedQuery(
        name = "Documents.findAccessibleByUser",
        query = "SELECT d FROM Document d INNER JOIN d.groups g INNER JOIN KEY(g).members m WHERE m.id = :userId"
    )
})
public class Document {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @ElementCollection
    @CollectionTable(name = "DOCUMENTS_GROUPS", joinColumns = {@JoinColumn(name = "document_id")})
    @MapKeyJoinColumn(name = "group_id")
    @Column(name = "access_mode")
    @Enumerated(EnumType.STRING)
    private Map<Group, AccessMode> groups = new HashMap<>();

    /* ... */

}

随着群体变得正常:

@Entity
@Table(name = "GROUPS")
public class Group {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @Column(length = 255)
    private String name;

    @ManyToMany
    @JoinTable(name = "USERS_GROUPS", //
            joinColumns = {@JoinColumn(name = "group_id")}, //
            inverseJoinColumns = {@JoinColumn(name = "user_id")} //
    )
    private Set<User> members = new HashSet<>();

    /* ... */

}

我的问题是:我如何修改JPQL查询中的第二个JOIN?

SELECT d FROM Document d
  INNER JOIN d.groups g
  INNER JOIN KEY(g).members m
WHERE m.id = :userId

语法错误(KEY之后的意外INNER JOIN

当然,我已经尝试过简单INNER JOIN g.members m,但由于我们正在处理Map<Group, AccessMode>,因此cannot dereference scalar collection element: members失败。

1 个答案:

答案 0 :(得分:0)

我也遇到了一个简单的键值Map<String, String>遇到的相同问题:

@Entity Item.java

@ElementCollection
@MapKeyColumn(name = "name")
@Column(name = "value")
@CollectionTable(indexes = @Index(columnList = "value"))
private Map<String, String> attributes = new HashMap<>();

可以加入属性:

Query query = em.createQuery("SELECT i FROM Item i INNER JOIN i.attributes attr");

查询字段:

Query query = em.createQuery("SELECT i FROM Item i INNER JOIN i.attributes attr WHERE attr.value = 'something'");

我调试了Hibernate内部结构,发现别名attr已经解析为值(e.attributes.value),因此您在这里只能做的事情是

Query query = em.createQuery("SELECT i FROM Item i INNER JOIN i.attributes attr WHERE attr = 'something'");

但是我没有找到任何指出这一点的文档或JPQL示例。在我的情况下,这种行为是没有用的,因为我想同时拥有键和值的条件。这就是为什么我使用键映射和组合主键迁移到外部实体集合的原因。它的方法比较复杂,但可以正常工作。

防止单个主键的组合键实体

@Embeddable
public class ItemAttributeName implements Serializable {

    private String name;

    @ManyToOne
    @JoinColumn(nullable = false)
    private Item item;

    // Empty default constructor is important
    public ItemAttributeName() {

    }

    public ItemAttributeName(Item item, String name) {
        this.item = article;
        this.name = name;
    }
}

真实属性实体

@Entity
public class ItemAttribute {

    @EmbeddedId
    private ItemAttributeName id;

    private String value;

    // Empty default constructor is important
    public ItemAttribute() {

    }

   
    public ItemAttribute(Item item, String name) {
        this.id = new ItemAttributeName (item, name);
    }

    public String getValue() {
        return value;
    }
}

@Entity Item.java

@OneToMany(mappedBy = "id.item",cascade = CascadeType.PERSIST)
@MapKeyColumn(name = "name")
public Map<String, ItemAttribute> attributes = new HashMap<>();

创建实体

Item item = new Item ();
ItemAttribute fooAttribute = new ItemAttribute(item, "foo");
fooAttribute.setValue("356");
item.attributes.put("foo", fooAttribute);

查询实体

Query query = em.createQuery("SELECT i FROM Item i JOIN i.attributes attr WHERE attr.id.name = 'foo' AND attr.value='bar'");
List<Item> resultList = query.getResultList();
System.out.println(resultList.get(0).attributes.get("foo").getValue());

打印出: bar