建模数据库关系

时间:2012-03-31 04:21:53

标签: java design-patterns data-modeling

我认为描述问题的最佳方式是使用一个例子。我将通过删除不需要的实现细节来尽可能简单地保持它。

说有一家书店。商店使用后端数据库来存储所有书籍客户订单以存储所有数据,并将Java前端跟踪到把它们呈现给商店的经理。

数据库包含以下关系:

  
      
  1. 预订 id 标题作者
  2.   
  3. 客户 id 名称 tel 地址
  4.   
  5. 订单 id 日期 custId bookId
  6.   

另一方面,Java接口使用JDBC驱动程序连接到数据库并检索数据。该应用程序包含以下类:

  

簿
  BooksDataLoader
  BooksTableModel
  BooksView

     

客户
  CustomersDataLoader
  CustomersTableModel
  CustomersView

     

订单
  OrdersDataLoader
  OrdersTalbeModel
  OrdersView

这些类使用相应的设计指南,您可以使用以下源代码作为参考:

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 pattern来实现Book,Customer和Order类。这些类的实例是使用SwingWorker线程内的数据库检索的数据构建的,并使用AbstractTableModel发布到视图。因此,实际上应用程序包含以下视图(JPanels):BooksView,CustomersView和OrdersView,每个视图都包含一个带有列的JTable,如下所示:

  

BooksView.booksTable 图书ID | 书名 | 图书作者

     

CustomersView.customersTable 客户ID | 客户名称

     

OrdersView.ordersTable 订单ID | 日期 | 客户名称 | 书名 | 图书作者

当我们尝试将表示数据库中的外键的实例变量解析为它链接的数据时,会出现问题。例如,OrdersTableModel具有在数据库中找到的所有Order对象的List结构,但是OrdersView表的第3,4和5列不能直接从Order对象访问,因为它只包含book的id和客户,而不是实际数据。我尝试的一个解决方案是在Book,Customer和Order类中创建一个静态HashMap,以便跟踪所有检索到的对象,但它导致数据重复,因为我们已经在表中有一个List结构的检索对象每个视图的模型。

我正在寻找一种高效且可扩展(面向对象)的设计解决方案/推荐。

提前谢谢。

2 个答案:

答案 0 :(得分:3)

你绝对应该使用像Hibernate或EclipseLink这样的ORM或任何适合你的技术。目前,JPA2是每个这样的工具实现的通用标准。您可以使用注释或xml文件定义对象和数据库模型之间的映射。

这些工具还提供了根据对象模型生成数据库模式的方法(如果您有遗留方案,则可以采用其他方式)。

我建议你不要使用jpa标准api,因为它的设计存在很大的缺陷。有许多框架可以帮助您构建查询。 QueryDSL对我来说非常好看。我使用规范模式(我实际上使用引擎盖下的标准api实现)来抽象查询构造。有关初次参考,请参阅http://martinfowler.com/apsupp/spec.pdfhttp://adrianhummel.wordpress.com/2010/07/02/composed-specifications-using-jpa-2-0/

对DAO模式和存储库(来自域驱动设计的术语)进行一些搜索。

答案 1 :(得分:0)

这是在关系数据库表上映射OO设计时的典型问题。

我们以OrdersTableModel为例:

订单ID |日期|客户名称|书名|图书作者

最后三列是数据库外键ID,而不是您要显示的值。

要正确管理,您有两种可能的解决方案:

FIRST: 像这样设置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 ...

现在假设您查询数据库以检索订单列表。 从返回的行中,您必须构建一个对象列表。当然,db会为客户,书籍和作者返回外键ID:

  1. 查询db
  2. 的表顺序
  3. 对于每个行构建和Orders对象,使用行的值
  4. 填充id和日期
  5. 获取客户的外键ID。在客户数据库表上构建一个新查询,并根据ID获取正确的客户。构建一个新的客户ID,用第二个查询的结果填充其值。将Customer对象分配给对象的字段客户Orders
  6. 书和作者相同
  7. 将对象Order添加到列表
  8. 现在您有一个假设10个订单的列表

    迭代它并填写您显示的订单表。 要显示例如客户字段,您将拥有

    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字段。保持从db

    中检索的int引用键

    再次查询表格订单。

    为每一行构建一个Order对象。只需使用db的id。填充客户等值。

    现在您要显示订单列表。

    迭代列表。 当displayng客户使用时

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

    注意,customer字段是geCustomer应该

    的int引用键
    1. 在db customer table上执行e query以根据id
    2. 检索正确的客户
    3. 构建填写其字段的Customer对象
    4. 重新加载对象
    5. 因此两个方面存在差异:

      第一个构建一个完整的Order对象,它也包含Customer对象等。当需要显示某些东西时,你就拥有了所需的一切。

      第二种方法构建一个轻型Order对象。当需要显示例如需要查询db的客户数据时(这称为延迟加载)

      我建议您考虑使用ORM,它真的可以帮助您在数据库上映射OO设计,并帮助您构建直接返回对象而不是“ids”的查询