Java Spring JPA:在join with annotation/JPQL上选择特定列

Java Spring JPA:在join with annotation/JPQL上选择特定列,java,hibernate,jpa,join,Java,Hibernate,Jpa,Join,我正在努力熟悉spring和jpa。开始时,有一个包含动画详细信息的表anime\u details。由于db可以有许多类型,所以它有另一个名为类型的表。包含其多对多关系项的中间表也在那里。当我通过id查询任何动画时,它应该返回动画的详细信息以及类型 它确实会返回一个对象,其中包含动画的详细信息和类型对象列表(正如预期的那样)。但我想要的是限制将从流派对象获取的列。例如,仅id或仅id和名称(如果有更多列而不是这些列) 动物尾巴 现在,我得到结果并手动逐个获取列,然后将这些列设置为DTO。但这并

我正在努力熟悉spring和jpa。开始时,有一个包含动画详细信息的表anime\u details。由于db可以有许多类型,所以它有另一个名为类型的表。包含其多对多关系项的中间表也在那里。当我通过id查询任何动画时,它应该返回动画的详细信息以及类型

它确实会返回一个对象,其中包含动画的详细信息和类型对象列表(正如预期的那样)。但我想要的是限制将从流派对象获取的列。例如,仅id或仅id和名称(如果有更多列而不是这些列)

动物尾巴

现在,我得到结果并手动逐个获取列,然后将这些列设置为DTO。但这并不高效,因为数据库查询已经获取了超过需要的数据。
是否有任何特定的注释/属性/jpql来减少它?

确实在寻找关于同一问题的适当解决方案,因为正如您所指出的,它正在产生性能问题,因为应用程序和数据库之间存在大量无用的数据负载。想象一下,可能不仅有一个查询,而且还有更多查询,您需要一个全局优化解决方案

  • 从一开始,Spring数据不支持此操作,因此它将引导您进行手动配置,并在DTO参考上进行设置。如果您使用自定义对象并使用构造函数技巧在JPQL中返回该对象,或者编写本机查询,返回
    列表
    ,然后再次手动将数据映射回实际对象,这也是最有效但不优雅的解决方案 更多信息在此,但请尝试检查这两个答案的细节

  • 另一件事是,当您在下面使用hibernate时,它提供了自定义映射器,您可以编写自定义HQL(而不是jpql),设置适当的DAO,连接EntityManager或直接连接SessionFactory(这打破了抽象的JPA契约,但您可以利用hibernates提供的全部好处)然后返回相同的对象,但只返回所需的列

第二点的例子:

import javax.persistence.EntityManager;
import org.hibernate.query.Query;
import org.hibernate.transform.Transformers;

public CustomEntity getEntity(){
        Query<CustomEntity> q = (Query<CustomEntity>) entityManager.createQuery("select 
                         e.id,e.name from CustomEntity e where e.name = 'parameter'");
        q.setResultTransformer(Transformers.aliasToBean(CustomEntity.class));
        CustomEntity entity = (CustomEntity) q.getSingleResult();
        return name;
    }
import javax.persistence.EntityManager;
导入org.hibernate.query.query;
导入org.hibernate.transform.Transformers;
公共CustomEntity getEntity(){
Query q=(Query)entityManager.createQuery(“选择
e、 id,来自CustomEntity e的e.name,其中e.name='参数');
q、 setResultTransformer(Transformers.aliasToBean(CustomEntity.class));
CustomEntity=(CustomEntity)q.getSingleResult();
返回名称;
}
注意
CustomEntity
是数据库中的一个托管实体Bean/表,只是将此示例放在您可能需要实现的方面

试用

  • Spring boot 2.0.5.释放
  • Spring Data 2.0.5.发布
  • Hibernate core 5.2.17.最终版

我今天尝试了另一种解决方案。让我们先看一下代码

体裁

Hibernate提出的问题

Hibernate:从genre0类型中选择genre0\u0.id作为id1\u2\u0\u0,genre0\u0.name作为name2\u2\u0\u0,其中genre0\u0.id=?
休眠:选择animes0.genre\u id作为genre\u id2\u 1\u 0,animes0.details\u id作为details\u 1\u 0,animeident1\u.id作为id1\u 0\u 1,animeident1\u。名称作为name2\u 0\u 1\u来自animes\u genre animes0\u内部加入animes\u details\u animeident1\u关于animes0。details\u id=animeids,其中animes0.genre\u id=?

请随时向我展示此解决方案的优缺点。对我来说,如果我的需要仅限于此,这是一个很好的解决方案。但是在不同类型的查询中,生成越来越多的实体POJO将是一项令人厌烦的任务。

感谢您解释可能的方法。虽然我对第二种方法不太熟悉,但第一种方法对我来说似乎很好。在执行联接时,我试图查看查询日志。hibernate似乎执行了两个查询来获得这个结果。一是获取表1的详细信息。第二种方法是根据table1 id获取table2的条目,然后使POJO结合这两个结果。
@Data
@Entity
@Table(name = "genre")
public class Genre {
    @Id
    @SequenceGenerator(name = "genreSeq", sequenceName = "genre_id_seq",
        allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "genreSeq")
    private Integer id;
    private String name;

    @ManyToMany(mappedBy = "genres")
    List<AnimeDetails> animes;

    protected Genre() {

    }
}
 {
    "id": 2,
    "name": "Your Name",
    "episodes": 1,
    "started": "2016-08-25T18:00:00.000+0000",
    "ended": "2016-08-25T18:00:00.000+0000",
    "status": "Completed",
    "genres": [
        {
            "id": 5,
            "name": "Drama"
        },
        {
            "id": 10,
            "name": "Supernatural"
        }
    ]
}
import javax.persistence.EntityManager;
import org.hibernate.query.Query;
import org.hibernate.transform.Transformers;

public CustomEntity getEntity(){
        Query<CustomEntity> q = (Query<CustomEntity>) entityManager.createQuery("select 
                         e.id,e.name from CustomEntity e where e.name = 'parameter'");
        q.setResultTransformer(Transformers.aliasToBean(CustomEntity.class));
        CustomEntity entity = (CustomEntity) q.getSingleResult();
        return name;
    }
@Data
@Entity
@Table(name = "genre")
public class Genre {
    @Id
    @SequenceGenerator(name = "genreSeq", sequenceName = "genre_id_seq",
        allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "genreSeq")
    private Integer id;
    private String name;

    @ManyToMany
    @JoinTable(
        name = "anime_genre",
        joinColumns = @JoinColumn(name = "genre_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name = "details_id", referencedColumnName = "id"))
    List<AnimeIdentity> animes;

    protected Genre() {

    }
}
@Getter
@Setter
@Entity
@Table(name = "anime_details")
public class AnimeIdentity {
    @Id
    @SequenceGenerator(name = "animeDetailsSeq", sequenceName =
        "anime_details_id_seq",
        allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
        "animeDetailsSeq")
    private Integer id;

    private String name;

    protected AnimeIdentity() {};
}
Hibernate: select genre0_.id as id1_2_0_, genre0_.name as name2_2_0_ from genre genre0_ where genre0_.id=?<br>
Hibernate: select animes0_.genre_id as genre_id2_1_0_, animes0_.details_id as details_1_1_0_, animeident1_.id as id1_0_1_, animeident1_.name as name2_0_1_ from anime_genre animes0_ inner join anime_details animeident1_ on animes0_.details_id=animeident1_.id where animes0_.genre_id=?