【问题标题】:How to fetch only some fields of an entity having associations using hibernate query?如何使用休眠查询仅获取具有关联的实体的某些字段?
【发布时间】:2018-03-14 17:48:40
【问题描述】:

我有一个具有多个字段的实体,其中一些是关联。 假设实体具有与以下类似的结构:

@Entity
@Table(name="foos")
public class Foo {

  public Foo() {}
  @Id
  private Long id;

  @Column
  private String name;

  @OneToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "bar_id")
  private Bar bar;

  @ManyToOne
  @JoinColumn(name = "bar1_id")
  private Bar1 bar1;

  @ManyToOne
  @JoinColumn(name = "bar2_id")
  private Bar2 bar2;

  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
  @JoinColumn(name = "foo_id")
  private List<Bar3> bar3;

  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
  @JoinColumn(name = "foo_id")
  private List<Bar4> bar4;

  //getters and setters
}

我的问题是如何使用 Hibernate 查询仅加载某些字段而不是整个实体?
我只想获取 id、bar、bar1 和 bar3,并且不想获取创建的 Foo 对象中的剩余字段(名称、bar2、bar4)。
我对 Hibernate 很陌生,所以任何建议都将不胜感激。

【问题讨论】:

标签: java hibernate


【解决方案1】:

休眠 5,春天 5

您有多种选择,但最简单的选择是使用您想要的列/字段创建一个简单的@Query,请参阅我的一个项目中的示例代码。

首先,您不需要使用子集字段创建 DTO,我将展示两者的选项和混合版本。

选项一:带有列/字段子集的 DTO 对象。

DTO 存储库代码:

@Query( "SELECT new com.your.package.name.customers.CompanyDTOIdName(c.id, c.name) " +
        "FROM Company c " +
        "WHERE c.enabled = 1 " +
        "ORDER BY c.name ASC"           )
List<CompanyDTOIdName> compDtoNameWhereEnabledTrue();

DTO 子集对象:

public class CompanyDTOIdName {

    private long id;
    private String name;

    public CompanyDTOIdName(long id, String name) {
        this.id = id;
        this.name = name;
    }

    // accessors/mutators methods
}

Hibernate SQL 执行代码:

SELECT
    new com.your.package.name.customers.CompanyDTOIdName(c.id, c.name) 
FROM
    Company c 
WHERE
    c.enabled = 1 
ORDER BY
    c.name ASC
            
select
    company0_.companyId as col_0_0_,
    company0_.name as col_1_0_ 
from
    ome_company company0_ 
where
    company0_.enabled=1 
order by
    company0_.name ASC

主要缺点是您需要一个单独的 DTO 对象,其中包含每个不同的列组合。


选项二:使用原始@Entity

您的第二个选项更简单,适用于任何列子集,如下所示:

存储库代码:

@Query( "SELECT new com.your.package.name.customers.Company(c.id, c.name) " +
        "FROM Company c " +
        "WHERE c.enabled = 1 " +
        "ORDER BY c.name ASC"
)
List<Company> compNameWhereEnabledTrue();

Company 是您的原始@Entity 对象。

在这种情况下,您的 Entity 对象必须包含空构造函数以及与您的 @Query 语句匹配的构造函数:

public void Company() {}
public void Company(Long id, String name) { /* code omitted */ }

Hibernate SQL 执行代码:

SELECT
    new com.your.package.name.customers.Company(c.id, c.name) 
FROM
    Company c 
WHERE
    c.enabled = 1 
ORDER BY
    c.name ASC

select
    company0_.companyId as col_0_0_,
    company0_.name as col_1_0_ 
from
    ome_company company0_ 
where
    company0_.enabled=1 
order by
    company0_.name ASC

注意:我相信您可以看到这种方法的问题。仅加载指示的列/字段。如果您尝试访问未启动的字段,则会出现异常。


使用接口的混合版本(两全其美)

使用必需的访问器字段创建接口。

public interface CompanyDTOIdNameInterface {
    
    public Long getId();
    public String getName();
}

在@Entity 对象上实现接口。

public class Company implements Serializable,
                                CompanyDTOIdNameInterface
{ /* omitted code */ }

存储库代码:

@Query( "SELECT new com.your.package.name.customers.Company(c.id, c.name) " +
            "FROM Company c " +
            "WHERE c.enabled = 1 " +
            "ORDER BY c.name ASC"
    )
    List<CompanyDTOIdNameInterface> compNameWhereEnabledTrue();

就这么简单。祝你好运:)

【讨论】:

  • 您的混合方法再次遇到了同样的问题,假设我们有 5 个这样的案例,其中包含多个字段的排列和组合。那么我们如何创建那么多接口,让我们说我们需要其中三个中的价格字段,然后呢?
  • @Shivang 如果您有多个排列并且加载字段是轻资源(即原语/盒装原语),则加载所有字段并扩展接口以覆盖特定集。如果要加载 objectscollection&lt;object&gt; 类型的字段,则必须为每组字段编写 @Query/InterfaceQuery + Interface 代码仍然比 DTO 少很多,返回的 obj 也可以转换为 Interface@Entity 如有必要。
【解决方案2】:

最简单的方法是创建一个存储库,该存储库将根据所选条件获取对象,然后如果您使用 Spring Data,则仅从对象列表中提取您需要的字段:https://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html

仅对于休眠它应该是这样的:

    EntityManager entityManager = entityManagerFactory.createEntityManager();
    entityManager.getTransaction().begin();

    Foo foo = entityManager.find(Foo.class,id);

    entityManager.getTransaction().commit();

或使用自定义 SQL 查询,仅针对选定字段:

    EntityManager entityManager = entityManagerFactory.createEntityManager();
    entityManager.getTransaction().begin();

    Query query = entityManager.createQuery("SELECT e FROM Foo e");
    List<Foo> workerList = query.getResultList();

【讨论】:

  • 感谢您的回答。但我认为上面的查询将从数据库中获取 foo 对象的所有字段。我想要的是仅将上述四个字段从 db 加载到 foo 对象中,即生成的 foo 对象不应从 db 获取 bar2、bar4 的任何值。
  • 修改 HQL 查询应该可以解决问题,即“SELECT e.name FROM Foo e”。如果您需要有关 HQL 的帮助,这里有一个教程 tutorialspoint.com/hibernate/hibernate_query_language.htm。如果您更喜欢 tutorialspoint.com/hibernate/hibernate_native_sql.htm,Hibernate 还允许使用本机 SQL 查询。
  • 我试过了,对于作为表格一部分的列,这会很好用。但是对于关联,必须编写很多连接,这可能会使查询变得复杂。我能够在this 链接的帮助下解决问题。我创建了一个新实体 FooShort,具有相同的表名“foos”,并且仅包含 Foo 所需的字段。
【解决方案3】:

我能够在以下link 的帮助下解决问题。我创建了一个新实体 FooShort,具有相同的表名“foos”,并且仅包含 Foo 所需的字段。

@Entity
@Table(name="foos")
public class FooShort {

  public FooShort() {}
  @Id
  private Long id;

  @OneToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "bar_id")
  private Bar bar;

  @ManyToOne
  @JoinColumn(name = "bar1_id")
  private Bar1 bar1;

  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
  @JoinColumn(name = "foo_id")
  private List<Bar3> bar3;

  //getters and setters
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-28
    • 1970-01-01
    • 2012-03-12
    • 2016-12-27
    • 1970-01-01
    相关资源
    最近更新 更多