1

我有一个带有嵌入式 id 的实体,其中包含一个 id 和 version 字段。

@Entity
@Table(name = "MyEntity")
public class MyEntity {
    @EmbeddedId
    private MyEntityEmbeddedId compositeId;

    @Column
    private DateTime begin;

    @Column
    private DateTime end;

}

@Embeddable
public class MyEntityEmbeddedId {
    @Column(name = "ID", nullable = false, updatable = false)
    private Long id;

    @Column(name = "VERSION", nullable = false, updatable = false)
    private Long version;

    public MyEntityEmbeddedId () {}

}

我需要使用一些标准查询数据库作为实体的过滤器。例如,我将请求一个日期和一个 ID 列表。

List<MyEntity> listEntities = getMyEntities(List<Long> ids, DateTime date);

我成功创建了一个查询,该查询检索其 id 在 ids 列表中的所有实体,并且提供的日期在 MyEntity 的“开始”和“结束”日期属性之间。

但是由于这个实体有一个复合 id,所以有许多 MyEntity 具有相同的“id”,可能对应于请求参数。如果有许多具有相同的“id”,我想添加另一个过滤器来检索具有最高“版本”号的 MyEntity。

以下是数据库中可能存在的示例:

+------+-----------+--------------------+
| id   | version   | began    |   end   |
+------+-----------+--------------------+
| 1    | 1         | ...      |   ...   |
| 1    | 2         | ...      |   ...   |
| 1    | 3         | ...      |   ...   |
| 2    | 1         | ...      |   ...   |
| 2    | 2         | ...      |   ...   |
+------+-----------+--------------------+

如果之前所有的数据库记录都对应过滤参数,我只需要得到这一条,因为它们是对应id的版本号最高的:

+------+-----------+--------------------+
| id   | version   | began    |   end   |
+------+-----------+--------------------+ 
| 1    | 3         | ...      |   ...   |
| 2    | 2         | ...      |   ...   |
+------+-----------+--------------------+

目前,我执行请求,但我在服务内的方法中过滤版本,但如果版本已在数据库请求中过滤,我更喜欢它。我不需要检索过滤版本后将被丢弃的记录。

我正在寻找一种使用 JPA Criteria-API 进行自我加入的方法。

在以下链接中,似乎接受的答案将解决我的问题,因为我想知道如何在 JPA Criteria-API 中翻译建议的 SQL。

SQL 仅选择列上具有最大值的行

select yt1.*
from yourtable yt1
left outer join yourtable yt2
on (yt1.id = yt2.id and yt1.rev < yt2.rev)
where yt2.id is null;

我的问题是当我尝试创建连接时。您需要提供来自第一个实体的属性,该属性链接到连接中的第二个实体。由于我想进行自联接,因此我的实体内部没有指向自身的属性。

Join<MyEntity, MyEntity> selfJoin = root.join(MyEntity_.???);

如您所见,我使用的是 MyEntity 的静态元模型。有没有办法使用 JPA Criteria-API 进行自我加入?

或者,也许有另一种方法来构建我的请求,而不是像另一个 stackoverflow 问题中所建议的那样使用自左连接。

我正在使用 Spring Data JPA Specification 来构建我的 Predicate 查询。然后使用 findAll(Specification)。我已经有另外两种规范方法来为 withIds() 和 withDate() 生成谓词。这让我可以使用用户提供的“未确定数量”的参数创建查询。

public static Specification<MyEntity> withIdAndDate(final List<Long> ids, 
        final DateTime date) {
    return new Specification<MyEntity>() {

        @Override
        public Predicate toPredicate(Root<MyEntity> root, CriteriaQuery<?> query, 
                CriteriaBuilder cb){
            Join<MyEntity, MyEntity> selfJoin = root.join();
            Predicate pIds = withIds(ids).toPredicate(root, query, cb);
            Predicate pDate = withDate(date).toPredicate(root, query, cb);

            return cb.and(pIds, pDate);
        }

    };
}

非常感谢您的帮助和建议!

4

1 回答 1

0

最后,我成功地找到了一种使用 JPA Criteria API 将 MyEntity 过滤为最新版本的方法,因此我将与您分享我的代码,希望它对这里的人有所帮助。

public static Specification<MyEntity> withIdAndDate(final List<Long> ids, 
        final DateTime date) {
    return new Specification<MyEntity>() {

        @Override
        public Predicate toPredicate(Root<MyEntity> root, CriteriaQuery<?> query, 
                CriteriaBuilder cb){

            Subquery<Long> subqueryVersion = query.subquery(Long.class);
            Root<MyEntity> entity1 = subqueryVersion.from(MyEntity.class);

            Expression<Long> expMaxVersion = cb.greatest(entity1
                .get(MyEntity_.compositeId).get(MyEntityEmbeddedId_.version));
            Expression<Long> expIdRoot = root.get(MyEntity_.compositeId)
                .get(MyEntityEmbeddedId_.id);
            Expression<Long> expIdEntity1 = entity1 .get(MyEntity_.compositeId)
                .get(MyEntityEmbeddedId_.id);
            Expression<Long> expVersionRoot = root.get(MyEntity_.compositeId)
                .get(MyEntityEmbeddedId_.version);

            Predicate pId = cb.equal(expIdRoot, expIdEntity1);
            Predicate pIds = withIds(ids).toPredicate(entity1, query, cb);
            Predicate pDate = withDate(date).toPredicate(entity1, query, cb);

            subqueryVersion.select(expMaxVersion);
            subqueryVersion.where(pId, pIds, pDate);

            Predicate pQuery = cb.equal(expVersionRoot, subqueryVersion);

            return cb.and(pQuery);
        }

    };
}

在以下链接中,接受的答案帮助我在 JPQL 中构建查询,之后,我成功地将其转换为 JPA Criteria API。我删除了查询的最后一部分,即“AND NOT EXISTS”部分。 JPA 为每个项目选择最新实例

于 2014-10-03T19:49:50.870 回答