【问题标题】:Java JPA OneToMany relation and queryJava JPA OneToMany 关系和查询
【发布时间】:2020-06-19 19:18:29
【问题描述】:

可能是重复的问题,但我无法为我的案例找到解决方案,我认为这很简单。

我有两张这样的桌子:

这些是相关的 DTO 对象:

第一张桌子

@Entity
@Table(name = "DA10003_REF_SIGNALEMENT")
public class RefSignalement {

    @Id
    @Column(name = "CD_SIGNALEMENT")
    public String codeSignalement;

    @Column(name = "LIBELLE")
    public String libelle;

    @Column(name = "CATEGORIE")
    public String categorie;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "CD_SIGNALEMENT")
    public List<RefMessage> refMessages;
}

第二张桌子:

@Entity
@Table(name = "DA10004_REF_MESSAGE")
public class RefMessage {

    @Id
    @Column(name = "CD_SIGNALEMENT")
    public String codeSignalement;

    @Id
    @Column(name = "DESTINATAIRE")
    public String destinataires;

    @Column(name = "MESSAGE")
    public String message;
}

以下查询可获取所有 RefSignelement 以及相关消息:

List<RefSignalement> listRefSignalement = em.createQuery("SELECT p FROM RefSignalement p, RefMessage m", RefSignalement.class).getResultList();

不幸的是它返回了一个空列表,我尝试用join fetch 更改它,但没有任何改变。

感谢您的帮助

【问题讨论】:

  • 什么列表是空的,如果它是 RefSignalement 实例的列表,你如何检查数据库中有没有?将测试指向可能为空的错误测试数据库是一个常见错误。这个查询也是错误的,所以如果你有结果,你会为数据库中的每个 RefMessage 实例获得重复的 RefSignalement - 你将 RefMessage m 添加到 from 子句中,但没有将它与 RefSignalement p 关联起来。
  • @Chris 我对对象实体和代码进行了一些更改,现在它可以工作了,但是正如你提到的,我得到了重复的 RefSignalement,所以我尝试了这个查询SELECT s FROM RefSignalement s JOIN FETCH s.refMessages,结果是一样的为每个实例获取多个对象,如果 RefSignalement 在 RefMessage 中没有相关数据,则它根本不会出现
  • 我已经将 DISTINCT 添加到查询中,它以某种方式解决了问题,但是当我在我的数据库中添加数据时,结果并没有改变我的行为真的很奇怪
  • DISTINCT 对我来说没有意义,但如果它有效,那就太好了,但应该会导致更少的结果而不是更多。如果您要加入某些内容并希望包含空值,则应该使用 LEFT OUTER JOIN

标签: java jpa


【解决方案1】:

请记住,在 JPQL 中您必须考虑对象,而不是关系。您想获取所有“RefSignalement”并急切地获取它们的“refMessages”属性:

SELECT DISTINCT s FROM RefSignalement s JOIN FETCH s.refMessages

这里的“distinct”只有 JPA 在组装结果实体时才需要,但会给 SQL 查询增加不必要的开销。如果您的 Hibernate 版本 >= 5.2.2(我认为),那么您可以使用查询提示来避免这种情况:

List<RefSignalement> sigs = entityManager
.createQuery(
    "select distinct s " +
    "from RefSignalement s " +
    "left join fetch s.refMessages ")
.setHint("hibernate.query.passDistinctThrough", false)
.getResultList();

阅读更多相关信息here。

【讨论】:

  • 感谢您的回答,不幸的是我仍然得到一个空列表
  • @ImadElHitti 谢谢,我已将其添加到我的答案中。
【解决方案2】:

有几件事,RefMessage 类正在使用复合主键,所以我想您需要使用 @IdClass 或 @EmbeddedId 注释。在这里我提供使用 @IdClass

public class RefId implements Serializable {

private String codeSignalement;

private String destinataires;

// default constructor


public RefId() {
}

public RefId(String codeSignalement, String destinataires) {
    this.codeSignalement = codeSignalement;
    this.destinataires = destinataires;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    RefId refId = (RefId) o;
    return Objects.equals(codeSignalement, refId.codeSignalement) &&
            Objects.equals(destinataires, refId.destinataires);
}

@Override
public int hashCode() {
    return Objects.hash(codeSignalement, destinataires);
}
}

那么你需要像下面这样使用

@Entity
@Table(name = "DA10004_REF_MESSAGE")
@IdClass(RefId.class)
public class RefMessage   {

@Id
@Column(name = "CD_SIGNALEMENT")
public String codeSignalement;

@Id
@Column(name = "DESTINATAIRE")
public String destinataires;

@Column(name = "MESSAGE")
public String message;
} 

如下定义您的存储库:

public interface RefSignalementRepo  extends 
JpaRepository<RefSignalement, String> {
}

RefSignalement类定义如下:

@Entity
@Table(name = "DA10003_REF_SIGNALEMENT")
public class RefSignalement {

@Id
@Column(name = "CD_SIGNALEMENT")
public String codeSignalement;

@Column(name = "LIBELLE")
public String libelle;

@Column(name = "CATEGORIE")
public String categorie;

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
@JoinColumn(name = "CD_SIGNALEMENT")
public List<RefMessage> refMessages;
}

非常示例的应用程序

@SpringBootApplication
public class App {

public static void main(String[] args) {
    ConfigurableApplicationContext context = 
   SpringApplication.run(App.class, args);


    RefSignalementRepo repo = context.getBean(RefSignalementRepo.class);

    RefSignalement obj = new RefSignalement();
    obj.codeSignalement = "1";

    obj = repo.save(obj);

    obj.refMessages = new ArrayList<>();

    RefMessage message = new RefMessage();
    message.codeSignalement = "1";
    message.destinataires = "2";
    message.message = "custom message";

    obj.refMessages.add(message);

    obj = repo.save(obj);

    List<RefSignalement> objs = repo.findAll();

    System.out.println(objs.get(0).refMessages.size());


    EntityManager em = context.getBean(EntityManager.class);

    List<RefSignalement> listRefSignalement = em.createQuery("SELECT p FROM RefSignalement p, RefMessage m", RefSignalement.class).getResultList();


    System.out.println(listRefSignalement.get(0).refMessages.size());

}
}

【讨论】:

  • 感谢您的回答,我对复合键进行了更改,这确实是我应该采用的方式。不幸的是,它没有改变任何东西,我仍然得到一个空列表,我不明白为什么
  • @ImadElHitti 我已经更新了代码。您可以按照示例代码立即尝试吗?
  • 您好,我尝试了解决方案,它似乎有效但并不完全,因为我得到了两倍的结果,如果我有一个 RefSignalement 和两个 RefMessage 关联,我在我的数组列表中得到两个相同的对象 RefSignalement
猜你喜欢
  • 2014-07-30
  • 1970-01-01
  • 1970-01-01
  • 2010-10-27
  • 1970-01-01
  • 2017-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多