JPA一对一映射与继承:2查询应该是一个

时间:2018-01-09 13:50:33

标签: spring hibernate spring-data-jpa jpql

我在Media授权和MediaAnalysis实体之间存在一对一关系,其中Media实体是抽象基类:

新闻报道实体

@Entity
@DiscriminatorValue("N")
public class NewsReport extends Media {

    @Column(name = "BODY", nullable = false)
    private String body;

    NewsReport(){}

    public NewsReport(String title, String link, String author, String body) {
        super(title, link, author);
        this.body= body;
    }

    public String getBody() {
        return body;
    }
}

媒体实体

@Entity
@Inheritance(
        strategy = InheritanceType.SINGLE_TABLE
)
@DiscriminatorColumn(name = "TYPE", length = 1, discriminatorType = DiscriminatorType.STRING)
public abstract class Media {

    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Long id;

    @Column(name = "TITLE", nullable = false)
    private String title;

    @Column(name = "LINK", length = 500, nullable = false)
    private String link;

    @Column(name = "AUTHOR", length = 45, nullable = false)
    private String author;

    @OneToOne(mappedBy = "media")
    private MediaAnalysis analysis;

    Media(){}

    public Media(String title, String link, String author) {
        this.title = title;
        this.link = link;
        this.author = author;
    }

    // getters

    public Optional<MediaAnalysis> getAnalysis() {
        return Optional.ofNullable(analysis);
    }
}

媒体分析

@Entity
public class MediaAnalysis {

    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Long id;

    @Column(name = "SUCCESS", nullable = false)
    private Boolean success;

    @OneToOne
    @JoinColumn(
            name = "MED_ID",
            nullable = false,
            foreignKey = @ForeignKey(name="MEA_MED_FK")
    )
    private Media media;

    @Column(name = "CONTENT", nullable = false)
    private String content;

    MediaAnalysis() { }

    public MediaAnalysis(Media media, Boolean success, String content) {
        this.media = media;
        this.success = success;
        this.content = content;
    }

    // getters

    public Media getMedia() {
        return media;
    }

    public String getContent() {
        return content;
    }
}

现在我想使用AnalysisRepository.getByMedia(..a NewsReport...)

public interface AnalysisRepository extends JpaRepository<MediaAnalysis,Long> {

    @Query("SELECT a FROM MediaAnalysis a LEFT JOIN FETCH a.media WHERE a.media = ?1")
    Optional<MediaAnalysis> getByMedia(Media media);

}

要按MediaAnalysis查找NewsReport,我希望hibernate可以运行单个SELECT查询,例如:

  

从媒体分析m中选择m。*,其中m.med_id =?

但是当我启用查询记录时,我看到2:

  

DEBUG ohSQL:92 - 选择mediaanaly0_.id为id1_0_0_,media1_.id为id2_1_1_,mediaanaly0_.med_id为med_id3_0_0_,mediaanaly0_.success为success2_0_0_,media1_.author为author3_1_1_,media1_.link为link4_1_1_,media1_.title为title5_1_1_,media1_.body as body6_1_1_,media1_.type as type1_1_1_ from mea_media_analysis mediaanaly0_ left outer join med_media media1_ on mediaanaly0_.med_id = media1_.id where mediaanaly0_.med_id =?

     

TRACE o.h.t.d.s.BasicBinder:65 - 绑定参数[1]为[BIGINT] - [1]

     

DEBUG ohSQL:92 - 选择mediaanaly0_.id为id1_0_1_,mediaanaly0_.med_id为med_id3_0_1_,mediaanaly0_.success为success2_0_1_,media1_.id为id2_1_0_,media1_.author为author3_1_0_,media1_.link为link4_1_0_,media1_.title为title5_1_0_,media1_.body as body6_1_0_,media1_.type as type1_1_0_ from mea_media_analysis mediaanaly0_ inner join med_media media1_ on mediaanaly0_.med_id = media1_.id where mediaanaly0_.med_id =?

     

TRACE o.h.t.d.s.BasicBinder:65 - 绑定参数[1]为[BIGINT] - [1]

似乎首先按预期选择MediaAnalysis,但是还有另外一个看似不必要的查询。我可以在这两个查询之间区分的唯一区别是连接类型。我认为问题与Media继承有关。

为什么会这样? +我该怎么做才能确保这是一个查询?

另外请注意,如果我从我的存储库中删除@Query,实际上有三个查询!。

  

DEBUG ohSQL:92 - 选择mediaanaly0_.id为id1_0_,mediaanaly0_.med_id为med_id3_0_,mediaanaly0_.success为success2_0_来自mea_media_analysis mediaanaly0_ left outer join med_media media1_ on mediaanaly0_.med_id = media1_.id其中media1_.id =?< / p>      

TRACE o.h.t.d.s.BasicBinder:65 - 绑定参数[1]为[BIGINT] - [1]

     

DEBUG ohSQL:92 - 选择media0_.id为id2_1_0_,media0_.author为author3_1_0_,media0_.link为link4_1_0_,media0_.title为title5_1_0_,media0_.body为body6_1_0_,media0_.type为type1_1_0_,mediaanaly1_.id as id1_0_1_,mediaanaly1_.med_id为med_id3_0_1_,mediaanaly1_.success为success2_0_1_来自med_media media0_ left outer join mea_media_analysis mediaanaly1_ on media0_.id = mediaanaly1_.med_id其中media0_.id =?

     

TRACE o.h.t.d.s.BasicBinder:65 - 绑定参数[1]为[BIGINT] - [1]

     

DEBUG ohSQL:92 - 选择mediaanaly0_.id为id1_0_1_,mediaanaly0_.med_id为med_id3_0_1_,mediaanaly0_.success为success2_0_1_,media1_.id为id2_1_0_,media1_.author为author3_1_0_,media1_.link为link4_1_0_,media1_.title为title5_1_0_,media1_.body as body6_1_0_,media1_.type as type1_1_0_ from mea_media_analysis mediaanaly0_ inner join med_media media1_ on mediaanaly0_.med_id = media1_.id where mediaanaly0_.med_id =?

     

TRACE o.h.t.d.s.BasicBinder:65 - 绑定参数[1]为[BIGINT] - [1]

3 个答案:

答案 0 :(得分:1)

我没有使用JpaRepository的经验,只是(很多)使用Hibernate(主要是&amp; CriteriaBuilder),但有些想法:

  • 尝试仅在两个实体中的一个中映射@OneToOne(根据您的示例应该是MediaAnalysis)
  • 尝试将MediaAnalysis映射为1:n关系(好像每个媒体可能有多个MediaAnalysis,这可能在我的理解中,可能不在您的域中)。

查看是否有任何问题有助于生成查询。

答案 1 :(得分:1)

我尝试在此dedicated branch in github中重现您的问题。除了实体类中的getter setter,我在上面的代码示例中没有任何改动,特别是对于Repository类。从simple unit test code显示的内容来看,您的查询没有任何问题,而且它只生成了1个查询,如下所示:

---------- WATCH GENERATED QUERY HERE ----------
2018-01-19 09:20:26.880 DEBUG 22769 --- [main] org.hibernate.SQL : select mediaanaly0_.id as id1_1_0_, media1_.id as id2_0_1_, mediaanaly0_.content as content2_1_0_, mediaanaly0_.med_id as med_id4_1_0_, mediaanaly0_.success as success3_1_0_, media1_.author as author3_0_1_, media1_.link as link4_0_1_, media1_.title as title5_0_1_, media1_.body as body6_0_1_, media1_.type as type1_0_1_ from media_analysis mediaanaly0_ left outer join media media1_ on mediaanaly0_.med_id=media1_.id where mediaanaly0_.med_id=?
---------- END OF GENERATED QUERY HERE ----------

然而,你的问题可能是因为:

  1. 您目前使用的JPA / Hibernate / Spring Data版本中的错误(此处未提及具体版本)。
  2. 在另一层(服务,控制器层)的代码中进行不必要的调用。
  3. 另一件可能有用的事情是您可以在关系中添加@Fetch(FetchMode.JOIN)注释,例如:

    @Fetch(FetchMode.JOIN)
    @OneToOne
    @JoinColumn(
        name = "MED_ID",
        nullable = false,
        foreignKey = @ForeignKey(name="MEA_MED_FK")
    )
    private Media media;
    

    但我强烈建议您检查或升级库版本,和/或编写数据库测试,以确保问题的确切位置。

    HTH。

答案 2 :(得分:0)

只有在所有者方面指定@OneToOne关系才能实现仅执行一个数据库查询的一种方法,我理解的是MediaAnalysis实体。

public class MediaAnalysis {

    // ...
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "MED_ID")
    private Media media;
    // ...
}

analysis实体中删除完整的Media字段。

试过这个,它按预期工作,测试中执行的唯一查询是:

select mediaanaly0_.id as id1_3_0_, 
       media1_.id as id2_2_1_, 
       mediaanaly0_.content as content2_3_0_, 
       mediaanaly0_.med_id as med_id4_3_0_, 
       mediaanaly0_.success as success3_3_0_, 
       media1_.author as author3_2_1_, 
       media1_.link as link4_2_1_, 
       media1_.title as title5_2_1_, 
       media1_.body as body6_2_1_, 
       media1_.type as type1_2_1_ 
from mediaanalysis mediaanaly0_ 
left outer join media media1_ on mediaanaly0_.med_id=media1_.id 
where mediaanaly0_.med_id=?

您可以在github repository中找到示例代码。