【问题标题】:How to use Hibernate's @Filter annotation in an @EmbeddedId property如何在 @EmbeddedId 属性中使用 Hibernate 的 @Filter 注释
【发布时间】:2019-06-26 10:08:52
【问题描述】:

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
}

我需要从某个日期获取所有带有 CarRents 的rentHistory 的客户。以下查询非常适合我:

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

【问题讨论】:

    标签: java postgresql hibernate jpa jpql


    【解决方案1】:

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

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

    不使用应该被过滤的@Embeddable

    编辑:

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

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

    【讨论】:

    • 感谢您的回复。由于我的 @Embeddable 在 CarRent 上,我该如何使用它以便过滤器正常工作?
    • CarRent 上它会起作用,因为有正在过滤的字段。就像你做的那样 - 将过滤器传递给Session
    • 对不起。这在示例中是错误的。刚刚在编辑中修复了这个问题:“rentHistory”实际上是与 CarRent 的关系
    • 啊,是的,现在我知道了。您正在设置 Session 但没有使用它。请参阅此示例:concretepage.com/hibernate/…
    • 我加了一个提示,怎么做:)
    【解决方案2】:

    原来我在这种情况下误用了@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();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-07-28
      • 1970-01-01
      • 1970-01-01
      • 2013-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多