如何仅查询 Hibernate 中的有限列,并将它们映射到给定的 POJO?

Posted

技术标签:

【中文标题】如何仅查询 Hibernate 中的有限列,并将它们映射到给定的 POJO?【英文标题】:How to query only limited columns in Hibernate, and map them to a given POJO? 【发布时间】:2021-08-29 06:36:10 【问题描述】:

我的项目涉及在 Spring Boot 应用程序中使用 GraphQL。出于演示目的,这是我的 GraphQL 架构:

type Company
  name: String,
  parentOrganization: String,
  flag:Int

我还在学习 Spring Boot 和 JPA,所以我对所有的 JPA、Hibernate 等都使用 spring-boot-starter-data-jpa

我的问题是,当有人只查询 nameorganization 时,Hibernate 会查询 所有 列,而 GraphQL 会选择请求的列。

@Repository
@Transactional
public interface CompanyRepository extends JpaRepository<Company,Long> 



上面的代码在限制查询的列方面并没有真正给我任何灵活性。我也尝试过使用 Hibernate 的 Criteria API,但无论采用哪种方式,我都会收到此错误:

Unable to locate appropriate constructor on class [packagee.entity.company]. Expected arguments are: java.lang.String, java.lang.String [select new package.entity.Company(generatedAlias0.company, generatedAlias0.organization) from package.entity.Company as generatedAlias0]

下面是我的 Criteria 实现的代码:

    public static List<Company> get(EntityManager em, List<String> fieldsAsked)
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Company> cq = cb.createQuery(Company.class);

        Root<Company> root = cq.from(Company.class);
        List<Selection<?>> selectionList = new LinkedList<Selection<?>>();

        for(String name: fieldsAsked)
            selectionList.add(root.get(name));
        
        cq.multiselect(selectionList);

        return em.createQuery(cq).getResultList();
    

如何从 Hibernate 获取有限的列?我在网上看到很多答案要求在实体类中创建适当的构造函数,但这对我来说实际上是不可能的,因为我的实体参数主要是字符串,我不能为所有可能的排列创建构造函数(因为我使用的是 GraphQL,查询内容的控制权归我项目的最终用户所有)。

我该怎么办?提前致谢!

【问题讨论】:

你在使用 GraphQLQueryResolver 吗?这可以解决您的问题吗baeldung.com/spring-graphql 我只使用 GraphQLQueryResolver。 【参考方案1】:

直接使用 Hibernate 并不能真正实现您想要做的事情,但您可以查看 Blaze-Persistence Entity-Views,它还具有 GraphQL 集成,可以完全支持您正在寻找的内容。见https://persistence.blazebit.com/documentation/1.6/entity-view/manual/en_US/#graphql-integration

这是一个示例项目,展示了如何使用它:https://github.com/Blazebit/blaze-persistence/tree/master/examples/spring-data-graphql

【讨论】:

我已经决定我的项目可以获取所有记录,反正性能差异不会那么大。但既然你想出了一个我可以选择的替代方案,我会接受这个。谢谢! 有时它并没有太大的区别,这是真的,但总的来说,这只是数据库必须做的不必要的工作和不必要的网络流量。如果列很宽或者您有很多列,这将是一个问题,而且如果您加入 *-to-many 关联,因为这会将基数更改为 N * M。将其与多个 *-to-many 关联和您获取 1k+ 行,其中列的数量开始很重要。【参考方案2】:

解决方案 1:

您可以创建一个新的 DTO 类,该类将由您的查询返回。

DTO 类:

public class CompanyDTO()
   //fields,constructor

在存储库中:

 @Query(value = "SELECT new com.example.dto.companyDTO" +
        "(c.name,c.parentOrganization)" +
        " FROM Company c")
    List<CompanyDTO>findCompanySelectedColumns(PageRequest pageable);

解决方案2(清洁解决方案):

您可以使用界面。 不要实现接口

interface customCustomer
   String getName();
   String getParentOrganization();

在存储库中:

List<CustomCustomer>findAllByNameAndParentOrganization();

【讨论】:

问题在于,因为要查询的内容由使用 GraphQL 的用户控制,所以我不能真正为传递的每个字段组合创建 DTO 类或构造函数。除了 CompanyDTO,我还有 5 个其他 DTO,每个都有 7 个字段。有没有办法在不创建构造函数或 DTO 的情况下做到这一点? 对不起,如果我之前的评论不清楚,但是没有其他方法可以在不创建所有构造函数和接口的情况下使用 Hibernate 查询数据吗?因为我看不出实现接口和制作构造函数的区别:两者对我来说都是不可行的。 您始终可以使用本机查询。@Query(value = "SELECT name, parentOrganization FROM Company";, nativeQuery = true) public List findCompanyNameAndParentOrganization();

以上是关于如何仅查询 Hibernate 中的有限列,并将它们映射到给定的 POJO?的主要内容,如果未能解决你的问题,请参考以下文章

Laravel中的MySQL子查询,如何加入查询并将第二个查询的结果作为新列添加到第一个查询?

CodeIgniter 查询:如何将列值移动到同一行中的另一列并将当前时间保存在原始列中?

Hibernate 查询目标列顺序

是遍历jTable并将列值传递给java中的sql查询

如何查询仅出现特定列中具有最高值的行的行?

在Hibernate中如何配置二级缓存。