我正在开发一个带有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,大多数业务逻辑都在服务层。
我在测试应用程序时发现了另一个缺点 - 我无法真正单独测试GenericDao类,如果我想使用其对象字段填充了Player对象的Club对象。
我需要先保留Player对象,以便将其添加到Club对象中,以便将其传递给正在测试的GenericDao。
我一直在寻找Fowler的PoEAA书籍来寻求解决方案,但我对Hibernate这样的O / R框架符合他描述的模式感到困惑。
答案 0 :(得分:2)
使用内存中的数据源
看起来您最好使用内存中的DataSource,而不是重新发明JPA级联的解决方法。我建议您考虑配置测试环境,以允许您选择以内存中HSQLDB实例为目标的不同DataSource。然后,您可以注释您的域对象以级联并测试您的内容。这是一个演示简单配置的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样式方法。
答案 1 :(得分:1)
抱歉,这是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; } }
}
答案 2 :(得分:1)
我面临的问题是如何 适当地持有一个有的对象 也引用其他对象 需要坚持下去。我知道那个级联 在JPA中可以解决这个问题 要求我为我的课程添加新课程 持久层。 然而,那会 让我依赖于具体的 实施(也会破坏 我的内存DAO实现。)
您确实意识到JPA是持久性的Java标准,它不是实际的实现。要使用JPA,您仍然需要PersistenceProvider(实现),但您可以在多个实现(Toplink,Hibernate等)之间进行选择。
我认为你应该在这里使用JPA Cascade。
答案 3 :(得分:0)
您有一个泛型类型(DOA),但您需要实现的行为不是通用的。这就像试图在一个圆孔中放一个方形钉:它永远不会起作用。
一些可能的方法:
要具体:如果是我,我会使用定义特定内容的类型:Club
会(只考虑可能的示例)address
,DJ
会有Genre / Style
。
是的,我最终会选择很多课程,但是每节课都会有一份工作,并且会做得很好。因为他们是分开的,所以他们会与变革分离 - 这在很大程度上是你现在遇到的问题。
使用继承:如果您的所有类都有这些基本的共同点,那么您可以将它作为基础使用,并分别处理其余的吗?
使用多个界面:有点像固有但不同。您保留现有的处理基础的界面;然后,您可以为其他行为定义其他接口。当一个对象进入时,需要对其执行某些操作,评估它实现的接口并依次处理它们。您还可以选择更改现有界面以更好地支持此方法 - 如果这有意义的话。
您可以混合使用这些选项或其中的一些选项。