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