提问者:小点点

JPQL在select语句中包含elementCollection映射


我有一个@Entity类Company,它有几个属性,在我的数据库中引用了一个公司表。其中一个表示一个Map companyProperties,其中公司表由一个company_properties表扩展,属性以键值格式保存。

@Entity
@Table(name = "companies")
public class Company extends AbstractEntity {

    private static final String TABLE_NAME = "companies";

    @Id
    @GeneratedValue(generator = TABLE_NAME + SEQUENCE_SUFFIX)
    @SequenceGenerator(name = TABLE_NAME + SEQUENCE_SUFFIX, sequenceName = TABLE_NAME + SEQUENCE_SUFFIX, allocationSize = SEQUENCE_ALLOCATION_SIZE)
    private Long id;

    //some attributes

    @ElementCollection
    @CollectionTable(name = "company_properties", joinColumns = @JoinColumn(name = "companyid"))
    @MapKeyColumn(name = "propname")
    @Column(name = "propvalue")
    private Map<String, String> companyProperties;

    //getters and setters
}

实体管理器能够正确执行查找子句

Company company = entityManager.find(Company.class, companyId);

但是,我无法在这个实体中执行JPQL查询并相应地检索Map。由于对象很大,我只需要选择实体类中的一些属性。我也不想按companyProperties过滤,而是检索所有带有正确分配的公司ID外键的属性。我尝试做的是:

TypedQuery<Company> query = entityManager.createQuery("SELECT c.id, c.name, c.companyProperties " +
            "FROM Company as c where c.id = :id", Company.class);
query.setParameter("id", companyId);
Company result = query.getSingleResult();

我得到的错误是:

java. lang.IllegalArgumentException:在EntityManager中创建查询时发生异常:异常描述:编译[SELECTc.id,c.name,c.companyProperties from Company as c wherec.id=:id]时出现问题。[21,40]状态字段路径'c.companyProperties'无法解析为集合类型。org.eclipse.持久化.内部.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1616)org.eclipse.持久化.内部.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1636)com.sun.企业.容器.公共.inpl.EntityManagerWrapper.createQuery(EntityManagerWrapper.java:476)

试图用连接来做这件事(我得到的最远的一点是

Query query = entityManager.createQuery("SELECT c.id, c.name, p " +
            "FROM Company c LEFT JOIN c.companyProperties p  where c.id = :id");

也没有给我正确的结果(它只返回属性的值,而不是带有key-value的属性列表)。

如何定义正确的查询来执行此操作?


共2个答案

匿名用户

我觉得你的JPA语法很奇怪。在你的第一个查询中,你选择了Company实体中的各个字段。但这不是JPA的工作方式;当你查询时,你会得到整个对象,你可以用它访问你想要的任何字段。我建议使用以下代码来代替:

TypedQuery<Company> query = entityManager.createQuery("from Company as c where c.id = :id", Company.class);
query.setParameter("id", companyId);
Company result = query.getSingleResult();

同样,对于第二个连接查询,我建议使用以下代码:

Query query = entityManager.createQuery("SELECT c" +
        "FROM Company c LEFT JOIN c.companyProperties p WHERE c.id = :id");
query.setParameter("id", companyId);
List<Company> companies = query.getResultList();

只选择Company而不选择属性实体的原因是属性将显示为Company类中的集合。假设公司和属性之间存在一对多,您可以从每个Company实体访问专业。

匿名用户

当仅对特定字段进行select时,您希望获得一个完整的Company对象,这是不可能的。如果您真的想节省一些内存(在大多数情况下不会那么成功)并仅选择一些字段,那么您应该期望一个List

List<Object[]> results = entityManager.createQuery("SELECT c.id, c.name, p " +
        "FROM Company c LEFT JOIN c.companyProperties p  where c.id = :id")
      .setParameter("id", companyId)
      .getResultList();

这里结果将包含选定字段的单个数组。您可以使用getSingleResult,但请注意,如果没有找到结果,它将引发异常。