JPA Join Query(native query) custom dto

前端 未结 2 521
我寻月下人不归
我寻月下人不归 2021-01-27 07:10

Hi I am trying to join 3 table and fetch the collective result i.e dto using following query

SELECT f.id, u.email,
count(distinct l.id) as likes_count,
count(dis         


        
相关标签:
2条回答
  • 2021-01-27 07:54

    Thank you for quick answer, i have taken reference from your answer both approaches were correct. Posting my answer here with respect to question:

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

    Repository

    @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);
    

    Interface approach works with native query findAllFeedsByUserId

    public interface FeedDetails {
    
        Long getId();
    
        String getContent();
    
        String getEmail();
    
        String getName();
    
        Long getLikes();
    
        Long getComments();
    }
    

    Model Class approach works well with SqlResultSetMapping method findAllFeedByUserIdNamedQuery, also i had to mention type of the column explicitly

    @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;
        }
    }
    
    0 讨论(0)
  • 2021-01-27 08:02

    You cannot use constructor expression (NEW operator) with Native Queries.

    You could use JPA constructor result. This looks like:

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

    Or you could convert FeedDetails to an interface and try Spring Data JPA Interface projection: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

    Or if you don't like these option there is a little library called QLRM: https://github.com/simasch/qlrm/blob/master/ConstructorResult.md

    0 讨论(0)
提交回复
热议问题