Как использовать аннотацию @Filter в Hibernate в свойстве @EmbeddedId - PullRequest
0 голосов
/ 01 февраля 2019

tl; dr

Невозможно заставить фильтр Hibernate работать со встроенным свойством id.

Пример проекта для воспроизведения проблемы здесь

Актуальный вопрос

Я довольно долго борюсь с этим запросом.

Предположим, приведен следующий пример сопоставления сущностей:

@Entity
class Client {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "client_id")
    @Basic(optional = false)
    private Integer id;

    @Basic(optional = false)
    private String name;

    @OneToMany(mappedBy = "client")
    private List<CarRent> rentHistory;

    // ... getters and setters
}

@Entity
class Car {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "car_id")
    @Basic(optional = false)
    private Integer id;

    @Basic(optional = false)
    private String foo;

    // ... getters and setters
}

@Entity
class CarRent {
    @EmbeddedId
    private CarRentKey carRentKey;

    @MapsId("clientId")
    @ManyToOne()
    @JoinColumn(name = "client_id", nullable = false, insertable = false, updatable = false)
    private Client client;

    @MapsId("carId")
    @ManyToOne()
    @JoinColumn(name = "car_id",  nullable = false, insertable = false, updatable = false)
    private Car car;

    @Basic(optional = false)
    private String bar;

    // ... getters and setters
}

@Embeddable
class CarRentKey {
    private int clientId;
    private int carId;
    @Column(name = "date_due")
    private Date dateDue;

    // ... getters and setters
}

Мне нужно выбрать всех Клиентов с этимRentHistory заполняется CarRents с определенной даты.Следующий запрос отлично подойдет для меня:

from Client cl
    left outer join fetch c.rentHistory as rent with rent.car = c and rent.dateDue = :date

Но Hibernate продолжает подсказывать мне использовать фильтр при извлечении объединения в исключении.

Я пытался

@Entity
@FilterDef(name="dateDueFilter", parameters= {
    @ParamDef( name="dateDue", type="date" ),
})
@Filters( {
    @Filter(name="dateDueFilter", condition="dateDue = :dateDue"),
})
class CarRent {
    // ...
}

но потом, когда я запускаю свой запрос как:

EntityManager em;
// ...
Session hibernateSession = em.unwrap(Session.class);
hibernateSession.enableFilter("dateDueFilter").setParameter("dateDue", dateDue);
em.createQuery("from Client cl"
    + "left outer join fetch c.rentHistory");
List<Client> clientList = q.getResultList();
// clientList contains CarRent of all dates

Фильтр просто игнорируется.Тот же результат с condition="carRentKey.dateDue = :dateDue" и condition="date_due = :dateDue".

Я использую фильтры для других левых внешних объединений по тому же запросу, и они работают просто отлично.Но это одно отношение, которое развивает встроенный параметр, я не могу найти способ заставить его работать.

Возможно ли это?Есть ли альтернативы?

PS: фильтрация в секции where, например, from Client cl left outer join fetch c.rentHistory as rent where rent.dateDue is null or rent.dateDue = :date, не подходит, так как мой реальный запрос имеет другие объединения, результаты которых фильтруются и становятся очень медленными, когда я делаю это.

Ответы [ 2 ]

0 голосов
/ 06 февраля 2019

Оказывается, я неправильно использовал аннотацию @Filter в этом случае.

Влад Михалча помог мне решить проблему с моим сообщением на форуме Hibernate

Основано наЕго ориентации мне удалось решить проблему без фильтров, используя запрос соединения без выборки и преобразователь результата.

Решение для примера будет:

org.hibernate.query.Query<Client> q = em.createQuery("select cl, cr " + 
        "from Client cl " + 
        "join CarRent cr on cr.client = cl and cr.carRentKey.dateDue = :date")
        .setParameter("date", exampleDate)
        .unwrap(org.hibernate.query.Query.class).setResultTransformer(new BasicTransformerAdapter() {
            @Override
            public List transformList(List list) {
                Map<Serializable, Client> clientMap =
                        new LinkedHashMap<>(list.size());

                for (Object entityArray: list) {
                    if (Object[].class.isAssignableFrom(entityArray.getClass())) {
                        Client client = null;
                        CarRent carRent = null;

                        Object[] tuples = (Object[]) entityArray;

                        for (Object tuple : tuples) {
                            em.detach(tuple);

                            if (tuple instanceof Client) {
                                client = (Client) tuple;
                            }
                            else if (tuple instanceof CarRent) {
                                carRent = (CarRent) tuple;
                            }
                            else {
                                throw new UnsupportedOperationException(
                                    "Tuple " + tuple.getClass() + " is not supported!"
                                );
                            }
                        }

                        if (client != null) {
                            if (!clientMap.containsKey(client.getId())) {
                                clientMap.put(client.getId(), client);
                                client.setRentHistory(new ArrayList<>());;
                            }
                            if (carRent != null) {
                                client.getRentHistory().add(carRent);
                            }
                        }
                    }
                }
                return new ArrayList<>(clientMap.values());
            }
        });

List<Client> clientList = q.getResultList();
0 голосов
/ 03 февраля 2019

Не знаю, правильно ли я понял, но это:

em.createQuery("from Client cl"
+ "left outer join fetch c.rentHistory");

не использует @Embeddable, который должен быть отфильтрован.

Редактировать:

Вам необходимо использовать Session, который вы настроили.Откройте транзакцию и запросите результаты:

hibernateSession.openTransaction();
results = hibernateSession.createQuery(...).list();
hibernateSession.close();
...