2016-10-26 11 views
4

После referenciation ситуации лиц:JPA CriteriaBuilder генерировать запрос, где карта пуста, используя @MapKeyColumn

Столкновение -> CollisionStatus < -> CollisionWorkgroup (присоединиться к организации) -> Workgroup

Collision.java:

@Entity 
@Table(name = "...") 
public class Collision 
{ 
    ... 

    @OneToOne(fetch = FetchType.EAGER, optional = false) 
    @JoinColumn(name = "CSSTATE_ID", referencedColumnName = "CSSTATE_ID") 
    private CollisionStatus collisionStatus; 

    ... 
} 

CollisionStatus.java: (класс интереса, смотри ниже)

@Entity 
@Table(name = "...") 
public class CollisionStatus 
{ 
    ... 

    // THIS IS THE MAPPING OF INTEREST: 
    @OneToMany(mappedBy = "collisionStatus", fetch = FetchType.LAZY) 
    @MapKeyColumn(name = "CLIENT_ID", insertable = false, updatable = false) 
    private Map<Long, CollisionWorkgroup> collisionWorkgroups; 

    ... 
} 

CollisionWorkgroup.java: (присоединиться сущность/таблица между CollisionStatus и Workgroup, с PK = [CollisionStatusId, ClientID], оба типа Long)

@Entity 
@Table(name = "...") 
public class CollisionWorkgroup 
{ 
    @EmbeddedId 
    protected CollisionWorkgroupEmbeddedPK pk; 

    @MapsId("collisionStatusId") 
    @JoinColumn(name = "CSSTATE_ID", referencedColumnName = "CSSTATE_ID") 
    private CollisionStatus  collisionStatus; 

    @MapsId("clientId") 
    @ManyToOne 
    @JoinColumn(name = "CLIENT_ID", referencedColumnName = "CLIENT_ID") 
    private Client    client; 

    @ManyToOne(fetch = FetchType.LAZY, optional = false) 
    @JoinColumn(name = "WORKGROUP_ID", referencedColumnName = "WORKGROUP_ID") 
    private Workgroup   workgroup; 

    ... 
} 

CollisionWorkgroupEmbeddedPK.java: (присоединиться предприятие Класс PK)

@Embeddable 
public class CollisionWorkgroupEmbeddedPK implements Serializable 
{ 
    private static final long serialVersionUID = 1L; 

    @Column(name = "CSSTATE_ID") 
    private Long    collisionStatusId; 

    @Column(name = "CLIENT_ID") 
    private Long    clientId; 

    ... 
} 

Workgroup.java: (на самом деле не очень интересно, просто используется для сравнения ClientId в запросе критериев)

@Entity 
@Table(name = "...") 
public class Workgroup 
{ 
    @Column(name = "CLIENT_ID", insertable = false, updatable = false) 
    private Long    clientId; 

    @ManyToOne(fetch = FetchType.EAGER, optional = false) 
    @JoinColumn(name = "CLIENT_ID", referencedColumnName = "CLIENT_ID") 
    private Client   client; 

    ... 
} 

Интересным отображение в этом есть от CollisionStatus к присоединения объекта CollisionWorkgroup для подключения рабочих групп с помощью @MapKeyColumn ,

Это означает, что каждый клиент может связывать ровно одну рабочую группу с сущностью статуса столкновения. Каждый пользователь, подключенный к системе, видит только один из своих клиентов (каждый пользователь принадлежит одному клиенту), но другие не должны быть видимыми в пользовательском интерфейсе.

При выполнении запроса as-is любые состояния столкновений, с которыми несколько клиентов установили рабочую группу, дают дополнительные результаты в наших результатах таблицы данных/запроса.

Это имеет смысл, поэтому мне нужно добавить некоторые пользовательские предикаты к запросу в , чтобы генерировать записи, содержащие идентификатор клиента текущего пользователя, связанный с. Отображение записей других клиентов неверно.

Я нашел это на SO:

Using JPA CriteriaBuilder to generate query where attribute is either in a list or is empty

Я попробовал код:

@Override 
protected List<Predicate> createCustomPredicates(CriteriaBuilder builder, From<?, ?> root) 
{ 
    List<Predicate> predicates = new ArrayList<>(); 

    Long clientId = this.sessionHelper.getCurrentClientId(); 
    Join<Collision, CollisionStatus> collisionStatus = root.join("collisionStatus"); 
    MapJoin<CollisionStatus, Long, CollisionWorkgroup> collisionWorkgroups = collisionStatus.<CollisionStatus, Long, CollisionWorkgroup>joinMap("collisionWorkgroups", JoinType.LEFT); 
    predicates.add(builder.and(builder.or(builder.isEmpty(collisionWorkgroups), 
              builder.equal(collisionWorkgroups.<String>get("workgroup").<String>get("clientId"), clientId)))); 

    ... 
} 

Это дает мне ошибку компиляции на builder.isEmpty( говоря:

Bound mismatch: The generic method isEmpty(Expression<C>) of type CriteriaBuilder 
is not applicable for the arguments (MapJoin<CollisionStatus,Long,CollisionWorkgroup>). 
The inferred type CollisionWorkgroup is not a valid substitute for the bounded 
parameter <C extends Collection<?>> 

Очевидно, проблема в том, что Map является n от подкласса Collection.

Q:

Как вы тест на пустой Map в JPA, используя критерии API?

ответ

2

Правильно, потому что Карта не является коллекцией. JPA определяет только предикат IS_EMPTY в терминах Collection в отношении API критериев. Из-за этого и связанной с ним сигнатуры метода CriteriaBuilder вы не сможете это сделать с помощью CriteriaBuilder от JPA.

Действительно JPA следует добавить перегрузку для этого метода:

// existing method 
<C extends Collection<?>> Predicate isEmpty(Expression<C> collection); 
// overload 
<C extends Map<?>> Predicate isEmpty(Expression<C> map); 

JPQL однако должен работать.

+0

FWIW - https://hibernate.atlassian.net/browse/HHH-11225 –

+0

Спасибо Стив. Я хотел бы сообщить RFE экспертной группе JPA. Не могли бы вы доставить меня туда, где я могу это сделать, поскольку вы являетесь частью команды экспертов? – Kawu

+0

Существует https://java.net/jira/browse/JPA_SPEC, но не было решено, что произойдет с планируемым списком java.net. –

 Смежные вопросы

  • Нет связанных вопросов^_^