JPA Join Query (собственный запрос) custom dto - PullRequest
0 голосов
/ 29 апреля 2019

Привет. Я пытаюсь присоединиться к 3 таблицам и получить общий результат, т.е. использовать следующий запрос

.
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 Workbench, когда я пытаюсь добавить этот запрос в @Query аннотацию, которую я получаю AbstractJpaQuery$TupleConverter$TupleBackedMap исключение, решение для этого было использовать ContructorExpression я добавил new выражение, но я не получилось, мои сущности БД такие, как показано ниже

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

Пользовательский объект DTO для вышеуказанного SQL-запроса:

@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 List результат.

Ответы [ 2 ]

1 голос
/ 29 апреля 2019

Вы не можете использовать выражение конструктора (оператор 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

0 голосов
/ 29 апреля 2019

Спасибо за быстрый ответ, я взял ссылку из вашего ответа, оба подхода были правильными. Размещение моего ответа здесь относительно вопроса:

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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...