春季数据 JPA 去重返回重复值

huangapple go评论74阅读模式
英文:

Spring Data JPA Distinct Returning Duplicate Values

问题

在我的 Spring Boot 应用程序中,我有一个查询,应该返回一个不同的“Focus”对象列表(在 MySQL 中运行得很好)

@Query(value = "SELECT DISTINCT * FROM Focus F " +
                "JOIN FrameworkFoci FF on FF.focusID = F.focusID " +
                "JOIN FocusGroups FG on FF.frameworkID = FG.frameworkID " +
                "JOIN GroupMembers GM on FG.groupID = GM.groupID " +
                "JOIN Users U on GM.userID = U.userID " +
                "WHERE U.userID = :userID", nativeQuery = true)
List<Focus> findByUserID(@Param("userID") Long userID);

然而,这并没有返回不同的值,结果列表中包含了重复项。另一个问题是,我不能使用 @Query 注解返回整个实体 - 将查询更改为 SELECT DISTINCT(F) FROM Focus F 会出现错误 java.sql.SQLSyntaxErrorException: Unknown column 'F' in 'field list'

此外,我尝试将查询调整为以下内容:

@Query(value = "SELECT DISTINCT * FROM FrameworkFoci FF " +
            "JOIN FocusGroups FG on FF.frameworkID = FG.frameworkID " +
            "JOIN GroupMembers GM on FG.groupID = GM.groupID " +
            "JOIN Users U on GM.userID = U.userID " +
            "WHERE U.userID = :userID", nativeQuery = true)

然而,这会产生错误 java.sql.SQLException: Column 'focusCategory' not found.

为什么这个查询没有返回不同的值?为什么我不能返回整个实体,或者使用第二个查询?

Focus 实体:

@Entity
public class Focus {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long focusID;

    @Column(name = "focusCategory")
    private String focusCategory;

    private String focusName;

    private String focusExplanation;

    @OneToMany(mappedBy = "focus")
    private Set<Rating> ratings;

    @ManyToMany
    @JoinTable(name = "FrameworkFoci",
            joinColumns = @JoinColumn(
                    name = "focusID"),
            inverseJoinColumns = @JoinColumn(
                    name = "frameworkID"))
    private Set<Framework> frameworks;

    //image

    protected Focus(){}

    public Focus(String focusName, String focusCategory, String focusExplanation) {
        this.focusCategory = focusCategory;
        this.focusName = focusName;
        this.focusExplanation = focusExplanation;
    }

    public Focus(String focusCategory, String focusName, String focusExplanation, Set<Rating> ratings){
        this.focusCategory = focusCategory;
        this.focusName = focusName;
        this.focusExplanation = focusExplanation;
        this.ratings = ratings;
    }

    public Long getFocusId() {
        return focusID;
    }

    public void setFocusId(Long focusID) {
        this.focusID = focusID;
    }

    public String getFocusCategory() {
        return focusCategory;
    }

    public void setFocusCategory(String focusCategory) {
        this.focusCategory = focusCategory;
    }
}

编辑:

我从 SQL 切换到了 JPQL,使用以下查询:

@Query(value = "SELECT DISTINCT focus FROM Focus focus " +
                    "WHERE focus.frameworks.groups.groupMembers.user.userID =:userID ")

我现在得到错误 org.hibernate.QueryException: illegal attempt to dereference collection [focus0_.focusID.frameworks] with element property reference [groups]

Framework 实体:

@Entity
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
            property = "frameworkID")
public class Framework {
    
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long frameworkID;
    
    private String frameworkName;
    
    @ManyToMany
    @JoinTable(name = "FrameworkFoci",
                joinColumns = @JoinColumn(
                        name = "frameworkID"),
                inverseJoinColumns = @JoinColumn(
                        name = "focusID"))
    private Set<Focus> frameworkFoci = new HashSet<>();
    
    @OneToMany(mappedBy = "framework", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE)
    private Set<Group> groups;
    
    public Framework(){}
}
英文:

In my Spring boot application I have a query which should return a distinct List of Focus' (works perfectly in MySQL)

@Query(value = &quot;SELECT DISTINCT * FROM Focus F &quot; +
&quot;JOIN FrameworkFoci FF on FF.focusID = F.focusID &quot; +
&quot;JOIN FocusGroups FG on FF.frameworkID = FG.frameworkID &quot; +
&quot;JOIN GroupMembers GM on FG.groupID = GM.groupID &quot; +
&quot;JOIN Users U on GM.userID = U.userID &quot; +
&quot;WHERE U.userID = :userID&quot;, nativeQuery = true)
List&lt;Focus&gt; findByUserID(@Param(&quot;userID&quot;) Long userID);

However this does not return distinct values, duplicates are contained in the resulting list. Another issue is that I can't return a whole entity using @Query annotation - changing my query to SELECT DISTINCT(F) FROM Focus F gives the error java.sql.SQLSyntaxErrorException: Unknown column &#39;F&#39; in &#39;field list&#39;.

Furthermore, I tried changing adjusting the query to the following

@Query(value = &quot;SELECT DISTINCT * FROM FrameworkFoci FF &quot; +
&quot;JOIN FocusGroups FG on FF.frameworkID = FG.frameworkID &quot; +
&quot;JOIN GroupMembers GM on FG.groupID = GM.groupID &quot; +
&quot;JOIN Users U on GM.userID = U.userID &quot; +
&quot;WHERE U.userID = :userID&quot;, nativeQuery = true)

however this produced the error java.sql.SQLException: Column &#39;focusCategory&#39; not found.

Why is the query not returning distinct values? And why can't I return a whole entity nor use the second query?

Focus Entity:

@Entity
public class Focus {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long focusID;
@Column(name = &quot;focusCategory&quot;)
private String focusCategory;
private String focusName;
private String focusExplanation;
@OneToMany(mappedBy = &quot;focus&quot;)
private Set&lt;Rating&gt; ratings;
@ManyToMany
@JoinTable(name = &quot;FrameworkFoci&quot;,
joinColumns = @JoinColumn(
name = &quot;focusID&quot;),
inverseJoinColumns = @JoinColumn(
name = &quot;frameworkID&quot;))
private Set&lt;Framework&gt; frameworks;
//image
protected Focus(){}
public Focus(String focusName, String focusCategory, String focusExplanation) {
this.focusCategory = focusCategory;
this.focusName = focusName;
this.focusExplanation = focusExplanation;
}
public Focus(String focusCategory, String focusName, String focusExplanation, Set&lt;Rating&gt; ratings){
this.focusCategory = focusCategory;
this.focusName = focusName;
this.focusExplanation = focusExplanation;
this.ratings = ratings;
}
public Long getFocusId() {
return focusID;
}
public void setFocusId(Long focusID) {
this.focusID = focusID;
}
public String getFocusCategory() {
return focusCategory;
}
public void setFocusCategory(String focusCategory) {
this.focusCategory = focusCategory;
}

EDIT:

I've switched from SQL to JPQL with the following query:

@Query(value = &quot;SELECT DISTINCT focus FROM Focus focus &quot; +
&quot;WHERE focus.frameworks.groups.groupMembers.user.userID =:userID &quot;)

I now get an error org.hibernate.QueryException: illegal attempt to dereference collection [focus0_.focusID.frameworks] with element property reference [groups]

Framework entity:

@Entity
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = &quot;frameworkID&quot;)
public class Framework {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long frameworkID;
private String frameworkName;
@ManyToMany
@JoinTable(name = &quot;FrameworkFoci&quot;,
joinColumns = @JoinColumn(
name = &quot;frameworkID&quot;),
inverseJoinColumns = @JoinColumn(
name = &quot;focusID&quot;))
private Set&lt;Focus&gt; frameworkFoci = new HashSet&lt;&gt;();
@OneToMany(mappedBy = &quot;framework&quot;, fetch = FetchType.EAGER, cascade = CascadeType.REMOVE)
private Set&lt;Group&gt; groups;
public Framework(){}

答案1

得分: 1

以下查询解决了这个问题:

@Query(value = "SELECT DISTINCT focus FROM Focus focus " +
               "JOIN focus.frameworks frameworks " +
               "JOIN frameworks.groups groups " +
               "JOIN groups.groupMembers groupMembers " +
               "WHERE groupMembers.userID = :userID")
List<Focus> findByUserID(@Param("userID") Long userID);

Frameworks 和 GroupMembers 是集合,因此需要连接它们,否则会产生 illegal attempt to dereference collection [focus0_.focusID.frameworks] with element property reference [groups] 错误。

英文:

The following query solves the issue

@Query(value = &quot;SELECT DISTINCT focus FROM Focus focus &quot; +
&quot;JOIN focus.frameworks frameworks &quot; +
&quot;JOIN frameworks.groups groups &quot; +
&quot;JOIN groups.groupMembers groupMembers &quot;+
&quot;WHERE groupMembers.userID =:userID &quot;)     
List&lt;Focus&gt; findByUserID(@Param(&quot;userID&quot;) Long userID);

Frameworks and GroupMembers are collections and hence needed to be joined, otherwise illegal attempt to dereference collection [focus0_.focusID.frameworks] with element property reference [groups] was produced

答案2

得分: 0

你应该像这样编写你的查询:
'
SELECT DISTINCT f FROM Focus F '

英文:

you should write your query like this:
'
SELECT DISTINCT f FROM Focus F '

答案3

得分: 0

问题源于您在使用SQL时指定了nativeQuery = true。SQL不了解实体,只了解表。

因为您可能对于每个Focus行都有许多FrameworkFoci行(以及所有其他表中的行),所以每个匹配的FrameworkFoci行都会为每个Focus行重复一次。这在某种程度上会使Focus行重复,但是生成的行仍然是不同的,因为它们在其他表中的列方面有所不同。

然后,每一行都会转换为一个Focus实体,可能在framework集合中只有一个单独的元素。

因此,查询返回的结果与其说是重复结果,不如说是拆分成多个实体的结果。

幸运的是,解决方案应该相当简单:使用JPQL应该是完全可能的,因为您只使用了简单的连接。

以下内容应该能帮助您入门:

@Query(value = "SELECT DISTINCT * FROM Focus F " +
    "WHERE F.framework.groupMembers.user.id=:userID")
List<Focus> findByUserID(@Param("userID") Long userID);
英文:

The problem stems from you using SQL by specifying nativeQuery = true. SQL doesn't know about entities, just tables.

Since you presumably have many FrameworkFoci rows (and rows in all the other tables) for each Focus row, each Focus row gets repeated for each matching row in FrameworkFoci. This kind of duplicates the Focus row but the resulting rows are still distinct, because they differ in the columns from the other tables.

And then each row gets turned into a Focus entity, probably with a single element in the framework set.

So therefore query doesn't so much return duplicate results as results split into multiple entities.

Fortunately the solution should be fairly simple: Use JPQL which should be perfectly possible, since you're using only simple joins.

The following should give you a start:

@Query(value = &quot;SELECT DISTINCT * FROM Focus F &quot; +
&quot;WHERE F.framework.groupMembers.user.id=:userID&quot;)
List&lt;Focus&gt; findByUserID(@Param(&quot;userID&quot;) Long userID);

huangapple
  • 本文由 发表于 2020年10月20日 13:08:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/64438797.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定