保留对其他对象的引用的对象

发布于 2024-10-04 14:15:58 字数 1636 浏览 5 评论 0原文


我正在开发一个带有 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 框架在哪里适合他描述的模式。

I'm working on a rather simple CRUD application with web interface. I'm using the generic DAO pattern in the persistence layer. The interface of my generic DAO looks like this:

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;
}

This interface is implemented using JPA, but I also have an in-memory implementation, mainly for testing purposes. I don't have any other classes in the persistence layer, I just create one instance of this DAO for every domain class.

The problem I'm facing is how to properly persist an object that has references to other objects that also need persisting. I know that cascading in JPA would solve this without requiring me to add new classes to my persistence layer. However, that would make me dependent on a specific implementation (and would also break my in-memory DAO implementation).

Right know, I'm using a service layer to manage the DAO classes and I solve the problem by working with multiple DAOs. I'll show an example:

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

}

The first disadvantage of this approach is that it makes my domain model anemic - the majority of methods are just getters and setters and most of the business logic is in the service layer.

I've found the other disadvantage when testing the application - I can't really test the GenericDao class in isolation if I want to use Club objects that have their collection fields populated with Player objects.
I would need to persist the Player object first so I can add it to the Club object so I can pass that to the GenericDao under test.

I've been looking through Fowler's PoEAA book for a solution, but I'm a little confused where O/R framework like Hibernate fits within the patterns he describes.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

被翻牌 2024-10-11 14:15:59

使用内存中数据源

看来您最好使用内存中数据源,而不是重新发明 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>

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

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

贫血域对象不是反模式

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

Use an in-memory DataSource

It seems that you'd be better off using an in-memory DataSource rather than reinventing a workaround for JPA cascade. I'd suggest you look at configuring your test environment to allow you to select a different DataSource that targets an in-memory HSQLDB instance. You can then annotate your domain objects to cascade and test away to your hearts content. Here's a Spring context that demonstrates a simple configuration:

  <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>

Your test code can have a Factory that creates certain scenarios that you wish to test (using collections of transient domain objects until they reach the DAO which will populate their primary key fields). Since you may find yourself adding many potential duplicates into your test datasets you should consider adding a UUID field into your domain objects to allow them to be differentiated when the primary key is null. Typically this is just a lazily-loaded getUUID() method that is persisted alongside the other fields. You could even consider using it as your primary key since the chances of a collision are infinitesimal by definition.

In addition, I would approach the testing by having both a JDBC connection and the DAO JPA connection. You look at the state of the database for asserting through JDBC connection and you do your updates through your DAO connection.

Anaemic domain objects is not an anti-pattern

Depending on your strategy for domain modelling, there's nothing specifically wrong with anaemic domain objects. For example, you could have a bunch of fine-grained business objects that perform state change operations on the domain objects according to business rules. Essentially you'd have a Unit of Work/Command/DTO style approach with your service layer providing an Application Transaction.

尘世孤行 2024-10-11 14:15:59

在 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; } }
}

in C# I would probably consider to create an attribute which tells the dao if a property should be treated as part of the parent ("child"). I know there is something similar in Java, but I can't remember how it is called.

Sorry, it's C#:

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

It needs some reflection to find all the children and its children. If you don't like this, you could just consider to write an interface which returns the children:

class Club : IComplexEntity
{
  List<Player> Players { get; private set; }

  public IEnumerable<object> EntityChildren { get { return Players; } }
}
叹沉浮 2024-10-11 14:15:59

我面临的问题是如何
正确地保留一个对象
对其他对象的引用也
需要坚持。我知道级联
在 JPA 中将解决这个问题而无需
要求我向我的课程添加新课程
持久层。 但是,这会
让我依赖于特定的
实施
(并且也会破坏
我的内存中 DAO 实现)。

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

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

The problem I'm facing is how to
properly persist an object that has
references to other objects that also
need persisting. I know that cascading
in JPA would solve this without
requiring me to add new classes to my
persistence layer. However, that would
make me dependent on a specific
implementation
(and would also break
my in-memory DAO implementation).

You do realise that JPA is the Java standard for persistence and it is not an actual implementation. To use JPA you still need a PersistenceProvider (the implementation) but you can choose between several implementations (Toplink, Hibernate, etc).

I think you should use JPA Cascade here.

淤浪 2024-10-11 14:15:59

您有一个通用类型(DOA),但您需要实现的行为不是通用的。这就像试图将方钉插入圆孔:永远行不通。

一些可能的方法:

具体:如果是我,我会使用定义特定事物的类型:Club 将有(仅考虑可能的示例)< code>address,DJ 会有一个流派/风格

是的,我最终会上很多软课,但每个课都有一份工作,而且会做得很好。因为它们是独立的,所以它们会与变化隔离——这基本上就是你现在遇到的问题。

使用继承:如果你的所有类都有这些基本的共同点,那么你可以使用它作为基础,并单独处理其余的吗?

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

您可以混合搭配其中的一些选项或其中的一部分。

You have a generic type (the DOA) but the behaviour you need to implement is not generic. It's like trying to put a square peg in a round hole: it's never going to work.

Some possible aproaches:

Be specific: if it was me, i'd be using types which defines specific things: a Club would have (just thinking of possible examples) an address, a DJ would have a Genre / Style.

Yes I'd end up with lot sof classes, but each class would have one job, and would do that well. Because they were seperate they'd be isolated from change - which is bascially the proble you now have.

Use inheritance: if all your classes have these basic things in common then can you use that as a base, and handle the rest separately?

Use multiple interfaces: a bit like the inheretance but different. You retain the existing interface which handles basics; you then define additional interfaces for additional behaviours. When an object comes in that needs some action performed on it evaluate what interfaces it implements and process them in turn. You also retain the option to change your existing interface to better support this approach - if that makes sense.

You can probably mix and match some of these options or bits of them.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文