JPA Join Query(本地查询)自定义dto

时间:2019-04-29 06:14:14

标签: mysql jpa spring-data-jpa

嗨,我正在尝试加入3个表并使用以下查询获取集体结果,即dto

SELECT f.id, u.email,
count(distinct l.id) as likes_count,
count(distinct c.id) as comments_count
FROM feeds f 
INNER JOIN users u ON f.user_id = u.id
INNER JOIN likes l on l.feed_id = f.id 
left join comments c on c.feed_id = f.id
WHERE u.id = 12055
group by f.id order by comments_count asc;

此查询在mysql工作台中工作正常,当我尝试将此查询添加到@Query注释中时,我正在解决AbstractJpaQuery$TupleConverter$TupleBackedMap异常,对此的解决方法是使用ContructorExpression我添加了{ {1}}表达式,但我无法正常工作,我的数据库实体如下所示

new

上述SQL查询的自定义DTO对象:

@Entity
@Table(name = "feeds")
@NoArgsConstructor
public class Feed {

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

    @Getter
    @Setter
    @Column(columnDefinition = "TEXT")
    String content;

    public Feed(String content, User user) {
        this.content = content;
        this.user = user;
    }

    @ManyToOne(optional = false, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @Getter
    @Setter
    User user;


    @OneToMany(mappedBy = "feed", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    @Getter
    @Setter
    List<Like> likes;


    @OneToMany(mappedBy = "feed", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    @Getter
    @Setter
    List<Comment> comments;

    @Override
    public String toString() {
        return "Feed{" +
                "id=" + id +
                ", user=" + user +
                '}';
    }
}

@NoArgsConstructor @AllArgsConstructor public class FeedDetails { @Getter @Setter private Long id; @Getter @Setter private Long likes; @Getter @Setter private Long comments; } 方法如下:

Repository

我不确定我在@Query("**sql_query**") List<FeedDetails> findAllFeedsByUserId(Long userId); 中写了什么叫喊,以便得到正确的sql_query FeedDetails结果。

2 个答案:

答案 0 :(得分:1)

您不能对本机查询使用构造函数表达式(NEW运算符)。

您可以使用JPA构造函数结果。看起来像:

Query q = em.createNativeQuery(
    "SELECT c.id, c.name, COUNT(o) as orderCount, AVG(o.price) AS avgOrder " +
    "FROM Customer c " +
    "JOIN Orders o ON o.cid = c.id " +
    "GROUP BY c.id, c.name",
    "CustomerDetailsResult");

@SqlResultSetMapping(name="CustomerDetailsResult",
    classes={
        @ConstructorResult(targetClass=com.acme.CustomerDetails.class,
            columns={
                @ColumnResult(name="id"),
                @ColumnResult(name="name"),
                @ColumnResult(name="orderCount"),
                @ColumnResult(name="avgOrder", type=Double.class)})
    })

或者您可以将FeedDetails转换为接口并尝试使用Spring Data JPA Interface投影:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

或者,如果您不喜欢这些选项,则有一个名为QLRM的小图书馆:https://github.com/simasch/qlrm/blob/master/ConstructorResult.md

答案 1 :(得分:0)

谢谢您的快速解答,我从您的回答中得到了参考,这两种方法都是正确的。关于问题,将我的答案发布在这里:

Feed.java

@Entity
@Table(name = "feeds")
@SqlResultSetMapping(name = "findAllFeedByUserIdMapping",
        classes = @ConstructorResult(
                targetClass = FeedDetailsClass.class,
                columns = {
                        @ColumnResult(name = "id", type = Long.class),
                        @ColumnResult(name = "likes", type = Long.class),
                        @ColumnResult(name = "comments", type = Long.class)
                }
        )
)
@NamedNativeQuery(name = "findAllFeedByUserIdNamedQuery",
        resultClass = FeedDetails.class, resultSetMapping ="findAllFeedByUserIdMapping",
        query = "SELECT f.id,count(distinct l.id) as likes, count(distinct c.id) as comments FROM feeds f INNER JOIN users u ON f.user_id = u.id INNER JOIN likes l on l.feed_id = f.id left join comments c on c.feed_id = f.id WHERE u.id = 12055 group by f.id order by comments asc")

@NoArgsConstructor
public class Feed {

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

    @Getter
    @Setter
    @Column(columnDefinition = "TEXT")
    String content;

    public Feed(String content, User user) {
        this.content = content;
        this.user = user;
    }

    @ManyToOne(optional = false, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @Getter
    @Setter
    User user;


    @OneToMany(mappedBy = "feed", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    @Getter
    @Setter
    List<Like> likes;


    @Fetch(value = FetchMode.JOIN)
    @OneToMany(mappedBy = "feed", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    @Getter
    @Setter
    List<Comment> comments;

    @Override
    public String toString() {
        return "Feed{" +
                "id=" + id +
                ", user=" + user +
                '}';
    }
}

存储库

@Query(value = "SELECT f.id, f.content, u.name, u.email, " +
            "count(distinct l.id) as likes, " +
            "count(distinct c.id) as comments " +
            "FROM feeds f " +
            "INNER JOIN users u ON f.user_id = u.id " +
            "INNER JOIN likes l on l.feed_id = f.id " +
            "left join comments c on c.feed_id = f.id " +
            "WHERE u.id = 12055 " +
            "group by f.id " +
            "order by comments asc",
            nativeQuery = true)
    List<FeedDetails> findAllFeedsByUserId(Long userId);


    @Query(nativeQuery = true, name = "findAllFeedByUserIdNamedQuery")
    List<FeedDetailsClass> findAllFeedByUserIdNamedQuery(Long userId);

接口方法适用于本机查询findAllFeedsByUserId

public interface FeedDetails {

    Long getId();

    String getContent();

    String getEmail();

    String getName();

    Long getLikes();

    Long getComments();
}

模型类方法与SqlResultSetMapping方法findAllFeedByUserIdNamedQuery配合得很好,我也不得不明确提及列的类型

@Data
@NoArgsConstructor
public class FeedDetailsClass {

    @Getter
    @Setter
    private Long id;

    @Getter
    @Setter
    private Long likes;

    @Getter
    @Setter
    private Long comments;

    public FeedDetailsClass(Long id, Long likes, Long comments) {
        this.id = id;
        this.likes = likes;
        this.comments = comments;
    }
}