Как предотвратить дубликаты в отношении «один ко многим» с помощью Java и JDBC?

#java #oracle #jdbc

Вопрос:

Я пытаюсь создать контактную программу, в которой каждый человек указан со своими электронными письмами. Как вы знаете, у каждого человека может быть несколько электронных писем. Так, например, у Джона Смита есть 3 электронных письма.

 public class OneToManyDB {
   static final String DB_URL = "jdbc:oracle:thin:@1.1.1.1:1521:orcl";
   static final String USER = "user";
   static final String PASS = "pass";
   static final String QUERY = "select prs.person_id, prs.first_name, prs.last_name, pe.email"  
           " from persons prs"  
           " left join person_emails pe on prs.person_id = pe.person_fk"  
           " where prs.person_id = 1";

   public static void main(String[] args) throws SQLException {
      
        Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
        if (conn != null) {
            try {
                Statement stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery(QUERY);
                      
                while(rs.next()){
                   System.out.print("ID: "   rs.getInt("person_id"));
                   System.out.print(", First: "   rs.getString("first_name"));
                   System.out.print(", Last: "   rs.getString("last_name"));
                   System.out.println(", Email: "   rs.getString("email"));
                }
            } catch (SQLException e) {
                e.printStackTrace();       
            }
        }
    }
      
}
 

Как вы можете видеть, там указаны имя и фамилия человека и его/ее контакты по электронной почте.
Вы также можете увидеть проблему с их перечислением. На выходе получается:
вывод программы

Как можно избежать этого дублирования? Я нигде не могу найти чистое решение Java, потому что я не могу использовать Hibernate. Пожалуйста, помогите!

Результат, который я хочу получить, — это 1 запись со списком всех писем с разделителем, или, если в данном случае это не лучшая практика, вы можете сказать мне по-другому.

P.S. Вот данные в таблице person_emails: таблица электронных писем человека

Комментарии:

1. Вы должны решить эту проблему, добавив соответствующие ограничения в таблицу в своей базе данных, чтобы предотвратить существование повторяющихся записей. Если это не то, что вы имеете в виду, то, пожалуйста, будьте более откровенны в том, какую проблему вы пытаетесь решить и какого результата вы ожидаете.

2. Они не являются дубликатами. У пользователя 1 есть три адреса электронной почты. Какой из них ты хочешь?

3. Правильное название должно быть: «Как предотвратить получение N записей в отношениях «Один ко многим»?»

Ответ №1:

Ну, они не совсем дубликаты — их адреса электронной почты разные. Итак, вопрос в следующем: что бы вы хотели вернуть в таком случае?

Например, вы можете выбрать возврат любого адреса электронной почты; использование агрегатной функции помогает изменить запрос, например, на

   SELECT prs.person_id,
         prs.first_name,
         prs.last_name,
         MAX (pe.email) email                           --> this
    FROM persons prs LEFT JOIN person_emails pe ON prs.person_id = pe.person_fk
   WHERE prs.person_id = 1
GROUP BY prs.person_id, prs.first_name, prs.last_name   --> this
 

Или, возможно, вы захотите вернуть все их адреса электронной почты; используйте LISTAGG для этого:

   SELECT prs.person_id,
         prs.first_name,
         prs.last_name,
         LISTAGG (pe.email, ';') WITHIN GROUP (ORDER BY NULL) email  --> this
    FROM persons prs LEFT JOIN person_emails pe ON prs.person_id = pe.person_fk
   WHERE prs.person_id = 1
GROUP BY prs.person_id, prs.first_name, prs.last_name                --> this
 

Я не думаю, что применение UNIQUE ограничений к PERSON_EMAILS таблице-хороший вариант. Черт возьми, большинство из нас использует несколько адресов электронной почты (на работе, личные, еще один частный, …), и Джон Смит тоже.


Есть и другие варианты, но вы должны сказать, чего вы действительно хотите.

Комментарии:

1. так что да, пример с LISTAGG великолепен — это именно то, что я хочу отобразить в своем Java-приложении. Является ли это лучшей практикой — например: если у меня есть еще одна таблица в базе данных, называемая — номера телефонов, и я хочу перечислить электронные письма и номера телефонов этого человека, должен ли я продолжать использовать LISTAGG?

2. Я верю в это. Это просто зависит от того, какой формат вы хотите; я думаю, что два столбца (один для списка адресов электронной почты кого-то, а другой для их телефонных номеров) — самый простой вариант. В противном случае вы могли бы объединить два результата LISTAGG в один столбец. Или вы даже можете «смешать» адреса электронной почты с номерами телефонов …

3. @NewJavaEnthusiast Если вы согласны с тем, что LISTAGG не существует во многих базах данных, тогда действуйте. В противном случае простым методом было бы выполнить два запроса.

4. @Страшно, тег Oracle предполагает, что это Oracle, так что я думаю, что в конце концов все в порядке.

5. @ScaryWombat так что в моем случае база данных наверняка будет Oracle. Два запроса, безусловно, более чистый способ (для меня), но представьте, что я хочу расширить проект, добавив — person_address, person_university и т. Д. Таким образом, я думаю, что будет много запросов к базе данных, которых я надеюсь избежать.