在没有 ORM 的情况下实现实体类之间的关系

Posted

技术标签:

【中文标题】在没有 ORM 的情况下实现实体类之间的关系【英文标题】:implementing relationships between entity classes w/out ORM 【发布时间】:2012-08-17 00:27:22 【问题描述】:

我有一些实体类与其他实体具有一对一和/或一对多的关系。

我尝试制作一个非常通用的示例来反映我的情况...假设我将以下表存储在数据库中(不允许空值):

   operations (ID, workerID, customerID, etc_etc)
      workers (ID, email, password, etc_etc)
    customers (ID, etc_etc)
 mobilePhones (workerID, phoneNumber)

应该很清楚:

1 个操作只有 1 个工人和 1 个客户。 1 位客户拥有至少 1 个手机号码。

因此我有以下实体类:

public class Customer 
    private int id;
    //other fields, then constructors, getters and setters


public class Worker 
    private int id;
    //other fields
    private String[] mobilePhones;

正如我在标题中已经说过的,我不能使用诸如 Hibernate 之类的 ORM,所以我将使用 DAO。 编辑:我应该知道你很想知道为什么我不能使用 ORM...好吧,正如我在下面的评论中所写的那样:这是一项作业(软件工程考试的项目)。

现在,我想我不会对WorkerDAO 有任何问题,它可以通过从mobilePhones 中选择workerID 等于实际工人ID 的所有电话号码轻松管理一对多关系.

对我来说真正的问题是如何管理Operation 与其关联WorkerCustomer 之间的关系。 如果我想避免浪费内存,我是否应该像这样设计我的Operation 实体:

public class Operation  // here I have some doubts
    private int id;
    private int workerId;
    private int customerId;
    //other fields

或者可能是这样的:

public class Operation  // here I have some doubts
    private int id;
    private Worker worker;
    private Customer customer;
    //other fields

?

后者似乎更面向对象,但有一个微不足道的含义:worker 和 customer 的实例必须在内存中,即使 Operation 的客户端可能不需要它们。 p>

更糟糕的是:如果 OperationDAO 将 worker 和 customer 设置为新的 Worker 和 Customer 实例,这将导致 多个实例引用同一个 worker/customer (例如,由同一个工人)。 除了浪费内存之外,这肯定会导致数据不一致(例如,修改一个实例而没有更新其他实例)。

为了避免这种情况,应该引入一些知道当前加载了哪些实例的类(例如使用List<Worker>List<Customer> 等)......老实说,这在我看来是有点矫枉过正。

我还认为我可以尝试实现某种延迟获取,例如仅在第一个请求时设置工作实例,但我仍然需要一些类来跟踪内存中的内容以及必须查询的内容;我什至不知道这个类是否应该与 Data Access LogicBusiness Logic 相关(我猜是前者,但仍不确定)。

无论如何,没有理由实现所有这些,因为我不需要缓存系统(而且我觉得它看起来很像那样)。

有什么建议吗?

【问题讨论】:

只是想知道为什么不能使用 ORM? 这是一个作业(软件工程考试的项目)。 【参考方案1】:

您不能使用框架,但可以使用框架本身使用的模式。所以我推荐你这些基于P of EAA. by Martin Fowler的解决方案

    您可以使用您的第一个选项(带有 workerId 和 customerId 字段的操作),并为每个实体(操作、工人、客户)使用 Row Data Gateway。 现在,如果内存是一个问题并且您的模型需要它,请使用 Identity Map 控制所有 Worker 和 Customer 加载的实例,并避免重新加载。

    您可以使用您的第二个选项(在操作中有一个工作人员和客户实例),并使用Table Data Gateway 和Lazy Load 仅在需要时加载实例。

担心是否加载多个实例取决于您正在构建的应用程序的类型和实例的范围;例如,如果您是创建基于 CRUD 的应用程序,您的实例通常会有一个请求范围,因此请使用第二种方法,不必担心内存浪费或数据不一致。

建议:使用接口来定义您的类型。

【讨论】:

感谢您的回答,这是目前最接近我正在寻找的建议。我想实现类似 Identity Map 的东西,但主要是为了避免数据不一致而不是为了节省内存。我主要担心内存中有worker1operation1operation2,它们都指的是worker1;如果用户修改了worker1 的数据,我也应该更新operation1operation2...如果只有operation1operation2 引用worker1 而不是复制副本,这很容易避免它每个都在一个新的实例中。【参考方案2】:

我会选择 Spring JDBC 或其他模板方法模式的实现。每个实体 1 个 DAO 类。关于事务问题,您可以通过在 JDBC 连接上调用 setAutoCommit(false) 并处理所有查询的结果来简单地自己管理它们。

祝你好运重新发明***:]

【讨论】:

对不起,我不能使用 Spring 也不能使用其他任何东西。我讨厌这样做,但实际上我必须重新发明***。 :\【参考方案3】:

通常我更喜欢

public class Operation  
    private int id;
    private Worker worker;
    private Customer customer;
    //other fields

因为它更加面向对象并且使获取更容易。但是由于您与ORM特别遥远(我不知道为什么???)所以看起来

public class Operation  
    private int id;
    private int workerId;
    private int customerId;
    //other fields

作为更好的选择,您可以在需要时加载对象。

ORM 本来是首选... :)

【讨论】:

【参考方案4】:

我在回答自己,哈哈。

参考我在davidmontoyago答案的评论中解释的问题,我认为实现我的目标的最简单方法可能如下:

有一个类来跟踪已从数据库加载并在内存中的实例。这可能是一种实用程序类,我的意思是私有构造函数和所有静态方法。 确保所有返回实体类实例的 DAO 检查请求的实例是否在缓存中;如果是,则返回该实例,否则从数据库加载它并将其添加到缓存中。

我尝试绘制一些代码:

/* The class that will cache istances. */
static final class Cache  // package-private, so that only DAOs can see it.
    private static final int CAPACITY = 100; // max 100 istance per entity

    /* I will track each instance by its id, that is an Integer */
    private static Map<Integer,Worker> workers;
    private static Map<Integer,Customer> customers;
    private static Map<Integer,Operation> operations;

    private Cache()  // I don't really need to istantiate this class

    // SOME OPERATIONS

    public static boolean isCached(Worker w);
    public static boolean isCached(Customer c);
    public static boolean isCached(Operation o);

    public static Worker getWorker(int id);
    public static Worker getCustomer(int id);
    public static Worker getOperation(int id);

    public static void addWorker(Worker w);
    public static void addCustomer(Customer c);
    public static void addOperation(Operation o);



class DAOWorkerImpl implements DAOWorker 
    public find(int id) 
        Worker w = null;
        if (Cache.isCached(id))
            w = Cache.getWorker(id);
        else 
            //retrieve worker
            Cache.addWorker(w);
        
        return w;
    

你怎么看?它看起来很简单,应该可以完成它的工作。

顺便说一句,在编写此代码时,我认为最好避免编写 Cache 类并让管理所有到它们各自的 DAO(例如 DAOWorker 管理 Worker 内存中的实例等)。

唯一不能说服我的是,这可能会破坏Single Responsability Principle...你怎么看?

【讨论】:

以上是关于在没有 ORM 的情况下实现实体类之间的关系的主要内容,如果未能解决你的问题,请参考以下文章

JPA

JPA入门及深入

JPA入门及深入

JPA与hibernate-------JPA

JPA与hibernate-------JPA

手把手带你撸一套Android简易ORM框架