Hibernate 中的 JPA root 无法使用 IdClass 属性

发布于 12-07 17:04 字数 2933 浏览 1 评论 0原文

我正在尝试对具有 IdClass 的实体进行多重选择。我无法获取映射为 ID 一部分的列。很明显为什么我不能,因为标记为 @Ids 的列都不是 hibernate 正在创建的 EntityType 中属性的一部分,它们是 IdAttributes 映射的一部分。

这段代码在 openJPA 中运行良好,但由于各种原因我决定转向休眠。

失败的代码:

CriteriaBuilder queryBuilder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Tuple> query = queryBuilder.createTupleQuery();
Root<ProductSearchFilter> productSearchFilterRoot = query.from(ProductSearchFilter.class);
query.multiselect(productSearchFilterRoot.get("productId").alias("productId"),
            productSearchFilterRoot.get("category").alias("category"),
            productSearchFilterRoot.get("name").alias("name"),
            productSearchFilterRoot.get("fdaStatus").alias("fdaStatus"));
    query.distinct(true);

错误:

java.lang.IllegalArgumentException: Unable to resolve attribute [productId] against path

我的映射设置:

@Table(name = "PRODUCT_SEARCH_FILTER")
@Entity()
@IdClass(ProductSearchFilterPK.class)
public class ProductSearchFilter {

private String source;
private String productId;
private String name;
private String category;
private String searchColumn;
private String fdaStatus;

@Column(name = "SOURCE", length = 11)
public String getSource() {
    return source;
}

public void setSource(String source) {
    this.source = source;
}

@Column(name = "PRODUCT_ID", length = 46, insertable = false, updatable = false)
@Id
public String getProductId() {
    return productId;
}

public void setProductId(String productId) {
    this.productId = productId;
}

@Column(name = "NAME", length = 510)
public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

@Column(name = "CATEGORY", length = 10)
public String getCategory() {
    return category;
}

public void setCategory(String category) {
    this.category = category;
}


@Column(name = "SEARCH_COLUMN", length = 1088, insertable = false, updatable = false)
@Id
public String getSearchColumn() {
    return searchColumn;
}

public void setSearchColumn(String searchColumn) {
    this.searchColumn = searchColumn;
}

@Column(name = "FDA_STATUS", insertable = false, updatable = false)
@Id
public String getFdaStatus() {
    return fdaStatus;
}

public void setFdaStatus(String fdaStatus) {
    this.fdaStatus = fdaStatus;
}
}



public class ProductSearchFilterPK implements Serializable {
private String productId;
private String searchColumn;
private String fdaStatus;

public String getFdaStatus() {
    return fdaStatus;
}

public void setFdaStatus(String fdaStatus) {
    this.fdaStatus = fdaStatus;
}

public String getProductId() {
    return productId;
}

public void setProductId(String productId) {
    this.productId = productId;
}

public String getSearchColumn() {
    return searchColumn;
}

public void setSearchColumn(String searchColumn) {
    this.searchColumn = searchColumn;
}
}

I'm trying to do a multiselect on an entity with an IdClass. I can't get a column that is mapped as part of the ID. It's clear why I can't, as none of the columns that are marked as @Ids are a part of the attributes in the EntityType that hibernate is creating, they are a part of the IdAttributes map.

This bit of code worked fine in openJPA, but I've decided to make the move to hibernate for various reasons.

The code that fails:

CriteriaBuilder queryBuilder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Tuple> query = queryBuilder.createTupleQuery();
Root<ProductSearchFilter> productSearchFilterRoot = query.from(ProductSearchFilter.class);
query.multiselect(productSearchFilterRoot.get("productId").alias("productId"),
            productSearchFilterRoot.get("category").alias("category"),
            productSearchFilterRoot.get("name").alias("name"),
            productSearchFilterRoot.get("fdaStatus").alias("fdaStatus"));
    query.distinct(true);

The error:

java.lang.IllegalArgumentException: Unable to resolve attribute [productId] against path

My Mapping setup:

@Table(name = "PRODUCT_SEARCH_FILTER")
@Entity()
@IdClass(ProductSearchFilterPK.class)
public class ProductSearchFilter {

private String source;
private String productId;
private String name;
private String category;
private String searchColumn;
private String fdaStatus;

@Column(name = "SOURCE", length = 11)
public String getSource() {
    return source;
}

public void setSource(String source) {
    this.source = source;
}

@Column(name = "PRODUCT_ID", length = 46, insertable = false, updatable = false)
@Id
public String getProductId() {
    return productId;
}

public void setProductId(String productId) {
    this.productId = productId;
}

@Column(name = "NAME", length = 510)
public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

@Column(name = "CATEGORY", length = 10)
public String getCategory() {
    return category;
}

public void setCategory(String category) {
    this.category = category;
}


@Column(name = "SEARCH_COLUMN", length = 1088, insertable = false, updatable = false)
@Id
public String getSearchColumn() {
    return searchColumn;
}

public void setSearchColumn(String searchColumn) {
    this.searchColumn = searchColumn;
}

@Column(name = "FDA_STATUS", insertable = false, updatable = false)
@Id
public String getFdaStatus() {
    return fdaStatus;
}

public void setFdaStatus(String fdaStatus) {
    this.fdaStatus = fdaStatus;
}
}



public class ProductSearchFilterPK implements Serializable {
private String productId;
private String searchColumn;
private String fdaStatus;

public String getFdaStatus() {
    return fdaStatus;
}

public void setFdaStatus(String fdaStatus) {
    this.fdaStatus = fdaStatus;
}

public String getProductId() {
    return productId;
}

public void setProductId(String productId) {
    this.productId = productId;
}

public String getSearchColumn() {
    return searchColumn;
}

public void setSearchColumn(String searchColumn) {
    this.searchColumn = searchColumn;
}
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

戏舞2024-12-14 17:04:12

有一个解决方法,那就是使用规范元模型。

不幸的是,我们从productSearchFilterRoot获得的EntityModel对我们没有多大帮助,因为它
只给我们一个集合的视图。因此,我们无法立即通过属性名称查询 IdClass 的属性。
但无论如何,它们就在那里:

//following will contain three attributes that are part of id.
Set<SingularAttribute<? super ProductSearchFilter, ?>> s = model.getIdClassAttributes();

相反,我们将采用规范元模型。为此,我们需要一个可以实现的新类
我们自己,或者让 Hibenate 来做:

@StaticMetamodel(ProductSearchFilter.class)
public abstract class ProductSearchFilter_ {
    public static volatile SingularAttribute<ProductSearchFilter, String> category;
    public static volatile SingularAttribute<ProductSearchFilter, String> fdaStatus;
    public static volatile SingularAttribute<ProductSearchFilter, String> source;
    public static volatile SingularAttribute<ProductSearchFilter, String> name;
    public static volatile SingularAttribute<ProductSearchFilter, String> searchColumn;
    public static volatile SingularAttribute<ProductSearchFilter, String> productId;
}

然后我们将使用 ProductSearchFilter_ 的字段作为参数来获取 ProductSearchFilterRoot:

CriteriaBuilder queryBuilder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Tuple> query = queryBuilder.createTupleQuery();
Root<ProductSearchFilter> productSearchFilterRoot = query.from(ProductSearchFilter.class);
query.multiselect(
    productSearchFilterRoot.get(ProductSearchFilter_.productId).alias("productId"),
    productSearchFilterRoot.get(ProductSearchFilter_.category).alias("category"),
    productSearchFilterRoot.get(ProductSearchFilter_.name).alias("name"),
    productSearchFilterRoot.get(ProductSearchFilter_.fdaStatus).alias("fdaStatus"));
query.distinct(true);

因为我们现在有了元模型,我也用它来创建名称选择,但因为那个不是 id 的一部分,我们可以保持原样:

productSearchFilterRoot.get("name").alias("name"),

There is workaround, which is using canonical metamodel.

Unfortunately EntityModel we get from productSearchFilterRoot does not help us much, because it
only gives us view as a set. So we cannot query Attributes of IdClass right away by name of attribute from there.
But there they are anyway:

//following will contain three attributes that are part of id.
Set<SingularAttribute<? super ProductSearchFilter, ?>> s = model.getIdClassAttributes();

Instead we we will go for canonical metamodel. For that we need one new class which we can implement
by ourselves, or let Hibenate do it:

@StaticMetamodel(ProductSearchFilter.class)
public abstract class ProductSearchFilter_ {
    public static volatile SingularAttribute<ProductSearchFilter, String> category;
    public static volatile SingularAttribute<ProductSearchFilter, String> fdaStatus;
    public static volatile SingularAttribute<ProductSearchFilter, String> source;
    public static volatile SingularAttribute<ProductSearchFilter, String> name;
    public static volatile SingularAttribute<ProductSearchFilter, String> searchColumn;
    public static volatile SingularAttribute<ProductSearchFilter, String> productId;
}

And then we will use fields of ProductSearchFilter_ as argument to get in productSearchFilterRoot:

CriteriaBuilder queryBuilder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Tuple> query = queryBuilder.createTupleQuery();
Root<ProductSearchFilter> productSearchFilterRoot = query.from(ProductSearchFilter.class);
query.multiselect(
    productSearchFilterRoot.get(ProductSearchFilter_.productId).alias("productId"),
    productSearchFilterRoot.get(ProductSearchFilter_.category).alias("category"),
    productSearchFilterRoot.get(ProductSearchFilter_.name).alias("name"),
    productSearchFilterRoot.get(ProductSearchFilter_.fdaStatus).alias("fdaStatus"));
query.distinct(true);

Because we now have metamodel, I used it also to create selection for name, but because that one is not part of id, we could have left it as it was:

productSearchFilterRoot.get("name").alias("name"),
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文