【问题标题】:Hibernate/JPA query with criteria for elements in list field (referenced as OneToMany)具有列表字段中元素条件的 Hibernate/JPA 查询(引用为 OneToMany)
【发布时间】:2017-08-17 08:09:55
【问题描述】:

案例:我有 2 个具有以下 OneToMany 关系的表:TagAbstract 1..* TagConf,其中:

  • tag_abstract_ID 是一个标识许多 TagConf 记录的字段,这些记录是单个标签对象的历史配置,
  • “活动配置”被识别为具有valid_until = NULL 的配置,
  • 活动标签是最新配置处于活动状态的标签。

数据库架构是:

CREATE TABLE TagAbstract (
  tag_abstract_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,

  PRIMARY KEY (tag_abstract_ID)
);

CREATE TABLE TagConf (
  tag_conf_ID          INT UNSIGNED NOT NULL AUTO_INCREMENT,
  tag_abstract_ID      INT UNSIGNED NOT NULL,
  valid_until          DATETIME,

  PRIMARY KEY (tag_conf_ID),
  FOREIGN KEY (tag_abstract_ID) REFERENCES TagAbstract (tag_abstract_ID)
    ON DELETE CASCADE
    ON UPDATE CASCADE
);

问题:是否可以使用这些 TagAbstract 记录的条件定义 Hibernate/JPA 查询(可能是 HQL),其中最后一个引用的 TagConf“处于活动状态”(如上所述)?

我卡在这里了:

public List<TagAbstract> fetchTagAbstractActive() {
    //noinspection unchecked

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();

    CriteriaQuery<TagAbstract> criteria = builder.createQuery(TagAbstract.class);
    Root<TagAbstract> tagAbstractRoot = criteria.from(TagAbstract.class);
    criteria.select(tagAbstractRoot);

    // TODO: how to correctly define following criterion in Hibernate/JPA query?
    // ---------------------------------------------------------------------
    Predicate predicate = ?; // for tagConfigs[last].validUntil == NULL
    // ---------------------------------------------------------------------

    criteria.where(predicate);
    return entityManager.createQuery(criteria).getResultList();
}

对于这个示例数据,它应该只选择记录1 和2:

record 0   
    tagConfigs  
        ref 0   
            tagConfId      1
            tagAbstractId  1
            validUntil     "11-08-2017 08:11:45"

record 1   
    tagConfigs  
        ref 0   
            tagConfId      2
            tagAbstractId  2
            validUntil     "11-08-2017 08:19:19"
        ref 1   
            tagConfId      4
            tagAbstractId  2
            validUntil     NULL

record 2   
    tagConfigs  
        ref 0   
            tagConfId      3
            tagAbstractId  3
            validUntil     NULL

数据映射定义如下:

@Entity
@Table(name = "TagAbstract")
public class TagAbstract {
    private static final long serialVersionUID = 1L;

    @Id()
    @GeneratedValue(generator = "sequence", strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(name = "sequence", allocationSize = 10)
    @Column(name = "tag_abstract_ID")
    private long tagAbstractId;

    @OneToMany(mappedBy = "tagAbstractId", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<TagConf> tagConfigs = new ArrayList<>();

    public List<TagConf> getTagConfigs() {
        return tagConfigs;
    }
}

@Entity
@Table(name = "TagConf")
public class TagConf {
    private static final long serialVersionUID = 1L;

    @Id()
    @GeneratedValue(generator = "sequence", strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(name = "sequence", allocationSize = 10)
    @Column(name = "tag_conf_ID")
    private long tagConfId;

    @Column(name = "tag_abstract_ID")
    private long tagAbstractId;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = Const.DATE_TIME_PATTERN)
    @Column(name = "valid_until")
    private Date validUntil;

    public long getTagConfId() {
        return tagConfId;
    }

    public long getTagAbstractId() {
        return tagAbstractId;
    }

    public Date getValidUntil() {
        return validUntil;
    }
}

【问题讨论】:

  • 那么,您想检索所有具有活动 TagConf 的 TagAbstract 吗?那正确吗?首先修复你的映射:如果你的 OneToMany 是 mappedBy,那么 TagConf 中应该有一个 ManyToOne。 Hibernate 文档包含所有关联类型的映射示例。然后只需使用select distinct t from TagAbstract t join t.tagConfigs tc where tc.validUntil is null。
  • 也许,也许不是。我认为 OP 过度指定了查询。似乎每个标签只能激活一个标签配置,因此无需寻找最后一个。
  • @JBNizet 你是对的。我想多了。

标签: java mysql hibernate jpa


【解决方案1】:

所以我根据上面评论中的 JB Nizet 建议更正了映射 - 将 @ManyToOne 映射添加到 TagConf:

@Entity
@Table(name = "TagAbstract")
public class TagAbstract {
    private static final long serialVersionUID = 1L;

    @Id()
    @GeneratedValue(generator = "sequence", strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(name = "sequence", allocationSize = 10)
    @Column(name = "tag_abstract_ID")
    private long tagAbstractId;

    @OneToMany(mappedBy = "tagAbstract", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<TagConf> tagConfigs = new ArrayList<>();

    public List<TagConf> getTagConfigs() {
        return tagConfigs;
    }
}

@Entity
@Table(name = "TagConf")
public class TagConf {
    private static final long serialVersionUID = 1L;

    @Id()
    @GeneratedValue(generator = "sequence", strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(name = "sequence", allocationSize = 10)
    @Column(name = "tag_conf_ID")
    private long tagConfId;

    @ManyToOne
    @JoinColumn(name = "tag_abstract_ID", foreignKey = @ForeignKey(name = "tag_abstract_ID"))
    private TagAbstract tagAbstract;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = Const.DATE_TIME_PATTERN)
    @Column(name = "valid_until")
    private Date validUntil;

    public long getTagConfId() {
        return tagConfId;
    }

    public long getTagAbstractId() {
        return tagAbstract.getTagAbstractId();
    }

    public Date getValidUntil() {
        return validUntil;
    }
}

它可以工作......

    String hql = "select t from TagAbstract as t join t.tagConfigs tc where tc.validUntil is null";
    return (List<TagAbstract>) entityManager.createQuery(hql).getResultList();

谢谢JB 尼泽!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-29
    • 1970-01-01
    • 2011-02-16
    • 1970-01-01
    • 2021-12-11
    • 1970-01-01
    • 2020-03-04
    • 1970-01-01
    相关资源
    最近更新 更多