Отображение проблем в классы DTO с помощью отношений «многие ко многим» с использованием JDBi3 в Dropwizard - PullRequest
1 голос
/ 27 февраля 2020

У меня проблема с отображением полученных данных через JDBi3 с использованием запроса PostgreSQL в моем интерфейсе DAO. В моем приложении Dropwizard у меня есть класс Book DTO, который имеет отношение «многие ко многим» с классами Author и Category DTO и имеет проблему с отображением запрашиваемых строк в классе BookDTO. Вот фрагменты кода классов DTO:

class BookDTO {
  private Long bookId;
  // other fields are left for code brevity 
  private List<Long> authors; 
  private List<Long> categories;

  // empty constructor + constructor with all fields excluding Lists + getters + setters
}

class AuthorDTO {
  private Long authorId;
  // other fields are left for code brevity 
  private List<Long> books; 

  // empty constructor + constructor with all fields excluding List + getters + setters
}

class CategoryDTO {
   private Long categoryId;
   // other fields are left for code brevity 
   private List<Long> books;

   // empty constructor + constructor with all fields excluding List + getters + setters
}

... и поскольку я использую интерфейсы DAO JDBi3 для выполнения операций CRUD, мой метод запроса всех книг в базе данных выглядит следующим образом:

    @Transaction
    @UseRowMapper(BookDTOACMapper.class)
    @SqlQuery("SELECT book.book_id AS b_id, book.title, book.price, book.amount, book.is_deleted, author.author_id AS aut_id, category.category_id AS cat_id FROM book " +
            "LEFT JOIN author_book ON book.book_id = author_book.book_id " +
            "LEFT JOIN author ON author_book.author_id = author.author_id " +
            "LEFT JOIN category_book ON book.book_id = category_book.book_id " +
            "LEFT JOIN category ON category_book.category_id = category.category_id ORDER BY b_id ASC, aut_id ASC, cat_id ASC")
    List<BookDTO> getAllBooks();

... и это map метод класса BookDTOACMapper выглядит следующим образом:

public class BookDTOACMapper implements RowMapper<BookDTO> {

        @Override
        public BookDTO map(ResultSet rs, StatementContext ctx) throws SQLException {      
            final long bookId = rs.getLong("b_id");
            // normally retrieving values by using appropriate rs.getXXX() methods

            Set<Long> authorIds = new HashSet<>();
            Set<Long> categoryIds = new HashSet<>();
            long authorId = rs.getLong("aut_id");
            if (authorId > 0) {
                authorIds.add(authorId);
            }
            long categoryId = rs.getLong("cat_id");
            if (categoryId > 0) {
                categoryIds.add(categoryId);
            }    

            while (rs.next()) {
                if (rs.getLong("b_id") != bookId) {
                    break;
                } else {
                    authorId = rs.getLong("aut_id");
                    if (authorId > 0) { authorIds.add(authorId); }

                    categoryId = rs.getLong("cat_id");
                    if (categoryId > 0) { categoryIds.add(categoryId); }
                }
            }
            final List<Long> authorIdsList = new ArrayList<>(authorIds);
            final List<Long> categoryIdsList = new ArrayList<>(categoryIds);

            return new BookDTO(bookId, title, price, amount, is_deleted, authorIdsList, categoryIdsList);
        }
}

Проблема, с которой я сталкиваюсь, заключается в том, что при вызове моего метода GET (определенного в классе Resource, который вызывает метод getAllBooks() из BookDAO класс) отображает противоречивые результаты , а сам запрос возвращает правильные результаты .

Многие вопросы, которые мне удалось найти в Stackoverflow, официальном API Документов JDBi3 и Группах Google рассматриваем отношение один-ко-многим и используем аннотацию @UseRowReducer, которая содержит класс, который реализует LinkedHashMapRowReducer<TypeOfEntityIdentifier, EntityName>, но для этого случая я не смог найти способ его реализовать. Любой пример / предложение приветствуется. :) Заранее спасибо.

Версии подержанных инструментов : Фреймворк Dropwizard 1.3.8 PostgreSQL 11,7 Java8

Ответы [ 2 ]

2 голосов
/ 27 февраля 2020

Это будет слишком долго для комментария:

  1. Это в основном вопрос отладки. Почему?

while (rs.next ()) {if (rs.getLong ("b_id")! = BookId) {break; } else {

Первый if после while питается строкой после текущей (той, которая была текущей при вызове преобразователя строк). Там вы пропускаете обработку (помещая данные в Java объекты) для bookId, authorId и т. Д. c. Вот почему вы получаете

противоречивые результаты, а сам запрос возвращает правильные результаты.

Так что вам нужно пересмотреть, как вы обрабатываете данные. Я вижу два пути:

  • Пересмотреть логи c обработки l oop, чтобы сохранить данные при остановке обработки для данного bookId. Этого можно добиться с помощью прокручиваемого ResultSet с - то есть запросить прокручиваемый ResultSet и до brake; вызова rs.previous(). При следующем вызове преобразователя строк обработка начнется с правильной строки в наборе результатов.
  • Используйте мощность SQL / PostgreSQL и сделайте это правильно: https://dba.stackexchange.com/questions/173831/convert-right-side-of-join-of-many-to-many-into-array Агрегировать и формировать данные в базе данных. База данных - лучший инструмент для этой работы.

Также не торопитесь и проверьте другие ответы https://dba.stackexchange.com/users/3684/erwin-brandstetter. Они дают бесценную информацию о SQL и PostgreSQL.

1 голос
/ 28 февраля 2020

Поскольку zloster упоминается в его ответе, я выбрал 2-й вариант ( этот ответ для отношений "многие ко многим"), который должен был использовать для редактирования моего PostgreSQL запроса @SqlQuery аннотация выше List<BookDTO> getAllBooks(); метод. Запрос теперь использует array_agg статистическую функцию в операторе SELECT для группировки моих результатов в массиве и теперь выглядит следующим образом:

    @UseRowMapper(BookDTOACMapper.class)
    @SqlQuery("SELECT b.book_id AS b_id, b.title, b.price, b.amount, b.is_deleted, ARRAY_AGG(aut.author_id) as aut_ids, ARRAY_AGG(cat.category_id) as cat_ids " +
            "FROM book b " +
            "LEFT JOIN author_book ON author_book.book_id = b.book_id " +
            "LEFT JOIN author aut ON aut.author_id = author_book.author_id " +
            "LEFT JOIN category_book ON category_book.book_id = b.book_id " +
            "LEFT JOIN category cat ON cat.category_id = category_book.category_id " +
            "GROUP BY b_id " +
            "ORDER BY b_id ASC")
    List<BookDTO> getAllBooks();

Поэтому map(..) метод класса BookDTOACMapper пришлось редактировать и теперь выглядит например:

  @Override
  public BookDTO map(ResultSet rs, StatementContext ctx) throws SQLException {
    final long bookId = rs.getLong("b_id");
    String title = rs.getString("title");
    double price = rs.getDouble("price");
    int amount = rs.getInt("amount");
    boolean is_deleted = rs.getBoolean("is_deleted");

    Set<Long> authorIds = new HashSet<>();
    Set<Long> categoryIds = new HashSet<>();

    /* rs.getArray() retrives java.sql.Array and after it getArray gets
       invoked which returns array of Object(s) which are being casted 
       into array of Long elements */
    Long[] autIds = (Long[]) (rs.getArray("aut_ids").getArray());
    Long[] catIds = (Long[]) (rs.getArray("cat_ids").getArray());

    Collections.addAll(authorIds, autIds);
    Collections.addAll(categoryIds, catIds);

    final List<Long> authorIdsList = new ArrayList<>(authorIds);
    final List<Long> categoryIdsList = new ArrayList<>(categoryIds);

    return new BookDTO(bookId, title, price, amount, is_deleted, authorIdsList, categoryIdsList);
  }

Теперь все результаты согласованы, и вот снимок экрана запроса в pgAdmin4.

...