Spring JPA:选择带有注释/ JPQL的联接上的特定列

时间:2018-09-18 19:10:01

标签: java hibernate jpa join

我正在尝试熟悉spring和jpa。首先,有一个表格 anime_details 包含动漫的详细信息。由于它具有多种类型,因此db有另一个名为 gen 的表。中间表还包含多对多关系条目。当我通过ID查询任何动漫时,它应该返回动漫的详细信息以及流派。

它确实返回一个对象,其中包含动漫的详细信息和Genre对象的列表(符合预期)。但是我想要的是限制将从Genre对象中获取的列。例如,仅id或仅id和name(以防其他列)。

  

AnimeDetails

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

    private String name;
    private Integer episodes;
    private Date started;
    private Date ended;
    private String status;

    @ManyToMany
    @JoinTable(
        name = "anime_genre",
        joinColumns = @JoinColumn(name = "details_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name = "genre_id", referencedColumnName = "id"))
    @JsonManagedReference
    private List<Genre> genres;

    protected AnimeDetails() {
    }
}
  

类型

@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"
        }
    ]
}

现在,我得到结果并手动逐一获取列并将其设置在DTO中。但这并不高效,因为数据库查询已经获取了比所需更多的数据。 是否有任何特定的注释/属性/ jpql来减少它?

2 个答案:

答案 0 :(得分:1)

Indeed一直在寻找针对同一问题的正确解决方案,因为您指出它正在造成性能问题,因为APP和DB之间存在大量无用的数据负载。想象一下,不仅可能有一个查询,而且还有更多查询,并且您需要一个全局优化解决方案...

  • Spring DATA首先不支持此操作,因此它会引导您进行手动配置并在DTO参考上进行设置。如果您使用的是自定义对象,并使用构造函数把它返回到JPQL内,或者使用本机查询,返回List<Object>并再次手动将数据映射回您的实际对象(即最有效但不优雅的解决方案 link中提供了更多信息,但请尝试检查两个答案的详细信息。

  • 另一件事是,当您使用下面的hibernate(提供自定义映射器)时,您始终可以编写自定义HQL(not jpql),设置适当的DAO,连接EntityManager或直接连接EntityManager。 SessionFactory(这违反了抽象的JPA契约,但您可以利用休眠提供的全部功能),然后返回相同的对象,但仅包含所需的列。

第二点示例:

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;
    }

注意CustomEntity是数据库中的一个受管实体Bean /表,只是将本示例放在您可能需要实现的目标上。

尝试过

  • Spring boot 2.0.5.RELEASE
  • Spring Data 2.0.5.RELEASE
  • Hibernate-core 5.2.17.Final

答案 1 :(得分:0)

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

  

类型

@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() {

    }
}
  

AnimeIdentity

@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进行的查询

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=?

随时向我展示此解决方案的优缺点。对我来说,如果我的需要仅限于此,这是一个很好的解决方案。但是,在使用不同类型的查询的情况下,越来越多的实体pojos将是一项繁琐的任务。