【问题标题】:Persisting objects with references to other objects持久化具有对其他对象的引用的对象
【发布时间】:2011-05-15 19:04:07
【问题描述】:


我正在开发一个带有 Web 界面的相当简单的 CRUD 应用程序。我在持久层中使用通用 DAO 模式。我的通用 DAO 的界面如下所示:

public interface GenericDAO<T> {

    void create( T entity ) throws CannotPersistException;
    T findById( Long id ) throws NotFoundException;
    Collection<T> findAll();
    void update( T entity ) throws CannotPersistException, NotFoundException;
    void delete( Long id ) throws CannotPersistException, NotFoundException;
}

这个接口是使用 JPA 实现的,但我也有一个内存实现,主要用于测试目的。 我在持久层中没有任何其他类,我只是为每个域类创建此 DAO 的一个实例。

我面临的问题是如何正确地持久化一个对象,该对象具有对其他也需要持久化的对象的引用。 我知道 JPA 中的级联可以解决这个问题,而无需我向我的持久层添加新类。 但是,这会使我依赖于特定的实现(并且还会破坏我的内存中 DAO 实现) )。

没错,我正在使用服务层来管理 DAO 类,我通过使用多个 DAO 来解决问题。我将展示一个示例:

public class PlayerServiceImpl implements PlayerService {

    private GenericDAO<Player> playerDAO;
    private GenericDAO<Club> clubDAO;

    //constructor ommited

    public Player addNewPlayer( Player player, Long clubId ) throws NotFoundException,
            CannotPersistException {
        Club club = clubDAO.findById( clubId );
        player.setClub( club );
        playerDAO.create( player );
        club.addPlayer( player );
        clubDAO.update( club );
        return player;
    }

    //... other methods

}

这种方法的第一个缺点是它使我的领域模型乏力 - 大多数方法只是 getter 和 setter,大部分业务逻辑都在服务层中。

我在测试应用程序时发现了另一个缺点 - 如果我想使用其集合字段填充有 Player 对象的 Club 对象,我无法真正单独测试 GenericDao 类
我需要先持久化 Player 对象,以便将其添加到 Club 对象,以便将其传递给正在测试的 GenericDao。

我一直在翻阅 Fowler 的 PoEAA 书籍以寻找解决方案,但我有点困惑像 Hibernate 这样的 O/R 框架是否适合他描述的模式。

【问题讨论】:

    标签: design-patterns orm architecture dao


    【解决方案1】:

    使用内存数据源

    您似乎最好使用内存中的数据源,而不是为 JPA 级联重新发明一种解决方法。我建议您考虑配置您的测试环境,以允许您选择针对内存中 HSQLDB 实例的不同数据源。然后,您可以注释您的域对象以级联和测试您的内心内容。这是一个演示简单配置的 Spring 上下文:

      <bean id="testDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="org.hsqldb.jdbcDriver"/>
        <property name="jdbcUrl" value="jdbc:hsqldb:mem:test"/>
        <property name="user" value="sa"/>
        <property name="password" value=""/>
      </bean>
    

    您的测试代码可以有一个工厂来创建您希望测试的某些场景(使用瞬态域对象的集合,直到它们到达将填充其主键字段的 DAO)。由于您可能会发现自己在测试数据集中添加了许多潜在的重复项,因此您应该考虑将 UUID 字段添加到域对象中,以便在主键为空时区分它们。通常,这只是一个延迟加载的 getUUID() 方法,与其他字段一起持久保存。您甚至可以考虑将其用作主键,因为根据定义,发生冲突的可能性很小。

    此外,我将通过同时拥有 JDBC 连接和 DAO JPA 连接来进行测试。您查看数据库的状态以通过 JDBC 连接进行断言,然后通过 DAO 连接进行更新。

    贫血域对象不是反模式

    根据您的域建模策略,贫血的域对象没有什么特别的问题。例如,您可能有一堆细粒度的业务对象,它们根据业务规则对域对象执行状态更改操作。本质上,您将拥有一种工作单元/命令/DTO 风格的方法,您的服务层提供应用程序事务。

    【讨论】:

      【解决方案2】:

      在 C# 中,我可能会考虑创建一个属性,告诉 dao 是否应将属性视为父级(“子级”)的一部分。我知道Java中有类似的东西,但我不记得它是如何调用的。

      抱歉,这是 C#:

      class Club
      {
        [ChildEntity]
        List<Player> Players { get; private set; }
      }
      

      它需要一些反思才能找到所有的孩子和它的孩子。如果你不喜欢这样,你可以考虑编写一个返回子接口的接口:

      class Club : IComplexEntity
      {
        List<Player> Players { get; private set; }
      
        public IEnumerable<object> EntityChildren { get { return Players; } }
      }
      

      【讨论】:

        【解决方案3】:

        我面临的问题是如何 正确持久化具有 对其他对象的引用 需要坚持。我知道级联 在 JPA 中可以解决这个问题 要求我添加新课程到我的 持久层。 但是,那会 让我依赖于一个特定的 实施(也会破坏 我的内存中 DAO 实现)。

        您确实意识到 JPA 是 Java 持久性标准,它不是一个实际的实现。要使用 JPA,您仍然需要 PersistenceProvider(实现),但您可以在多个实现(Toplink、Hibernate 等)之间进行选择。

        我认为你应该在这里使用 JPA Cascade。

        【讨论】:

        • 我知道 JPA 有不同的实现。但是假设我想使用 iBatis 实现持久层。
        【解决方案4】:

        您有一个泛型类型(DOA),但您需要实现的行为不是泛型的。这就像试图将一个方形钉子放在一个圆孔中:它永远不会起作用。

        一些可能的方法:

        要具体:如果是我,我会使用定义具体事物的类型:Club 将(仅考虑可能的示例)address,@987654324 @ 会有一个Genre / Style

        是的,我最终会参加很多软课程,但每个班级都会有一份工作,并且会做得很好。因为他们是分开的,所以他们会与变化隔绝——这基本上是你现在遇到的问题。

        使用继承:如果您的所有类都具有这些共同点,那么您可以将其用作基础,并分别处理其余部分吗?

        使用多个接口:有点像继承,但又不同。您保留处理基础的现有界面;然后,您为其他行为定义其他接口。当一个对象进入需要对其执行一些操作时,评估它实现的接口并依次处理它们。您还可以保留更改现有界面以更好地支持这种方法的选项 - 如果这有意义的话。

        您可能可以混合和匹配其中一些选项或其中的一些选项。

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-07-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-05
        相关资源
        最近更新 更多