Моделирование отношений с базой данных - PullRequest
2 голосов
/ 31 марта 2012

Я думаю, что лучший способ описать проблему - использовать пример. Я сделаю это максимально простым, удалив ненужные детали реализации.

Скажем, есть книжный магазин. Магазин отслеживает все книги , клиентов и заказов , используя внутреннюю базу данных для хранения всех данных и внешний интерфейс Java, чтобы представить их менеджер магазина.

База данных содержит следующие отношения:

  1. Книга ( id , название , автор )
  2. Заказчик ( id , имя , тел , адрес )
  3. Заказ ( id , дата , custId , bookId )

С другой стороны, интерфейс Java использует драйвер JDBC для подключения к базе данных и получения данных. Приложение состоит из следующих классов:

Книга
BooksDataLoader
BooksTableModel
BooksView

Клиент
CustomersDataLoader
CustomersTableModel
CustomersView

Заказ
OrdersDataLoader
OrdersTalbeModel
Виды заказов

Эти классы используют соответствующие руководящие принципы проектирования, и вы можете использовать следующий исходный код в качестве ссылки:

public class Book {
    private String id;
    private String title;
    private String author;

    /*
     * Builder pattern is used so constructor should be hidden. Book objects
     * are built in the BooksDataLoader SwingWorker thread.
     */
    private Book() {}
}

public class BooksDataLoader extends SwingWorker<List<Book>, Book> {
    private final BooksTableModel booksModel;
    private final List<Book> books = new ArrayList<Book>();
}

public class BooksTableModel extend AbstractTableModel {
    private final String columnNames = { "Book ID", "Book Title", "Book Author" };
    private final List<Book> books = new ArrayList<Book>();
}

public class BooksView extends JPanel {
    private final JTable booksTable;
    private final BooksTableModel booksModel;
}

Я использую шаблон Builder для реализации классов Book, Customer и Order. Экземпляры этих классов создаются с использованием данных, извлеченных базой данных в потоке SwingWorker, и публикуются в представлении с использованием AbstractTableModel. Итак, на самом деле приложение состоит из следующих представлений (JPanels): BooksView, CustomersView и OrdersView, каждое из которых содержит одну таблицу JTable со столбцами, как показано ниже:

BooksView.booksTable : ID книги | Название книги | Автор книги

CustomersView.customersTable : ID клиента | Имя клиента

OrdersView.ordersTable : Код заказа | Дата | Имя клиента | Название книги | Автор книги

Проблема возникает, когда мы пытаемся разрешить переменную экземпляра, которая представляет внешний ключ в базе данных, для данных, которые она связывает. Например, OrdersTableModel имеет структуру List всех объектов Order, найденных в базе данных, однако к столбцам 3, 4 и 5 таблицы OrdersView нельзя напрямую получить доступ из объекта Order, поскольку он содержит только идентификаторы для книги и клиент, а не фактические данные. Одним из решений, которое я попробовал, было создание статического HashMap внутри каждого из классов Book, Customer и Order, чтобы отслеживать все извлеченные объекты, но это приводит к дублированию данных, поскольку у нас уже есть структура List извлеченных объектов в таблице. модель каждого вида.

Я ищу эффективное и расширяемое (объектно-ориентированное) проектное решение / рекомендацию.

Заранее спасибо.

Ответы [ 2 ]

3 голосов
/ 31 марта 2012

Вам определенно следует использовать ORM, например Hibernate или EclipseLink, или любую другую технологию, которая вам подходит.В настоящее время JPA2 является общим стандартом, внедренным каждым таким инструментом.Вы определяете отображение между вашим объектом и моделью БД, используя аннотации или файлы XML.

Эти инструменты также предлагают способы создания схемы базы данных в соответствии с вашей объектной моделью (даже другой способ возможен, если у вас есть устаревшие схемы).

Я рекомендую вам не использовать критерии jpa api, так как его дизайн довольно несовершенен.Существует ряд платформ, которые помогут вам построить ваши запросы.QueryDSL мне кажется очень приятным.Я использовал шаблон спецификации (который я фактически реализовал, используя критерии api под капотом) для абстрагирования построения запроса.См. http://martinfowler.com/apsupp/spec.pdf и http://adrianhummel.wordpress.com/2010/07/02/composed-specifications-using-jpa-2-0/ для первых ссылок.

И выполните некоторый поиск по шаблону DAO и репозиториям (термин, происходящий из дизайна, управляемого доменом).

0 голосов
/ 31 марта 2012

Что ж, это типичная проблема при отображении структуры ОО на таблицы реляционных баз данных.

Давайте возьмем в качестве примера вашу OrdersTableModel:

ID заказа | Дата | Имя клиента | Название книги | Автор книги

последние три столбца - это идентификаторы внешнего ключа базы данных вместо значений, которые вы хотите показать.

Для правильного управления у вас есть 2 возможных решения:

ПЕРВОЕ: описать класс Order следующим образом

 public class Order{
private String id;
private Date date;
private Customer customer;
private Book book;
private Author author;

get and set methods
}

Обратите внимание, что клиент относится к типу CUSTOMER, книга типа BOOK ...

Теперь предположим, что вы запрашиваете базу данных, чтобы получить список заказов. Из возвращенных строк необходимо составить список объектов Order. Конечно, БД возвращает идентификаторы внешнего ключа для клиента, книги и автора так:

  1. Запрос таблицы заказов db
  2. Для каждого строения строки и объекта Orders заполните идентификатор и дату значениями строк
  3. Возьмите идентификатор внешнего ключа для клиента. Создайте новый запрос в таблице базы данных клиентов и выберите нужного клиента на основе идентификатора. Создайте новый идентификатор клиента, заполнив его значения результатами второго запроса. Присвоить объект Customer полевому клиенту объекта Orders
  4. То же самое для книги и автора
  5. Добавить объект Order в список

Теперь у вас есть список из 10 заказов

Итерируйте его и заполните отображаемую таблицу заказов. Например, для отображения поля Customer у вас будет

listOrders[i].getCustomer().getName(). // listOrders[i] is an Order object. getCusotmer returns a customer object. getName is a Customer's method that return the String name.

То же самое для книги и автора

ВТОРОЙ ПОДХОД

дизайн Заказать класс, как это:

  public class Order{
private String id;
private Date date;
private int customer;
private int book;
private int author;

get and set methods
}

Обратите внимание, что клиент и т. Д. Являются полями INT. Удержание ссылочной клавиши int извлечено из db

Снова вы запрашиваете заказы таблицы.

Для каждой строки построить объект Order. Заполните значения клиента и т. Д. Просто идентификатором базы данных.

Теперь вы хотите отобразить список заказов.

Итерация по списку. когда клиент использует

listOrders[i].getCustomer().getName().

ПРИМЕЧАНИЕ, поскольку поле customer является ссылочным ключом int, geCustomer должен

  1. Выполнить запрос к таблице клиентов БД, чтобы получить правильного клиента на основе идентификатора
  2. Создание объекта Customer, заполняющего его поля
  3. Возврат объекта

Итак, различия между двумя оценками:

Сначала создайте полный объект Order, который также содержит объект Customer и т. Д. Когда вам нужно что-то отобразить, у вас есть все, что вам нужно.

Второй подход - построить светлый объект Порядка. Когда нужно, например, отобразить данные клиента, когда нужно запросить базу данных (это называется отложенной загрузкой)

Я предлагаю вам рассмотреть возможность использования ORM, который действительно помогает отображать дизайн ОО в БД и помогает создавать запросы, которые возвращают непосредственно объекты вместо "идентификаторов"

...