如何在 @EmbeddedId 属性 中使用 Hibernate 的 @Filter 注释

How to use Hibernate's @Filter annotation in an @EmbeddedId property

tl;博士

无法使 Hibernate 过滤器使用嵌入的 ID 属性。

重现问题的示例项目here

真题

我已经为这个查询苦苦挣扎了一段时间。

假设以下实体映射示例:

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

我需要从特定日期获取所有带有租车历史记录的客户。以下查询非常适合我:

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 不是一个选项,因为我的真实查询有其他连接,结果会被过滤并且在我这样做时变得非常慢。

我不知道,如果我理解正确,但是这个:

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

没有使用应该过滤的 @Embeddable

编辑:

您需要使用您设置的Session。用它打开交易并查询结果:

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

原来我在这种情况下误用了@Filter 注释。

Vlad Mihalcea 帮助我解决了 my post in the Hibernate forum

上的问题

根据他的指导,我设法在没有过滤器的情况下使用没有提取和结果转换器的连接查询解决了这个问题。

示例的解决方案是:

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();