我知道这是一个常见的问题,但是我还没有找到解决我的疑问的另一个问题。
通常,如果项目很小,我会在表示域对象的同一对象中添加持久性注释。这允许从数据库加载实体并保持所有设置者为私有,从而确保任何实例始终处于有效状态。就像是:
@Entity
class SomeEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String attribute1;
private String attribute2;
private String attribute3;
// ... other attributes
protected SomeEntity() {}
/* Public getters */
public Long getId() { ... }
public String getAttribute1() { ... }
public String getAttribute2() { ... }
/* Expose some behaviour */
public void updateAttributes(String attribute1, String attribute2) {
/* do some validations before updating */
}
}
如果我要使用其他持久模型,则会出现我的问题。然后我会有类似的东西:
/* SomeEntity without persistent info */
class SomeEntity {
private Long id;
private String attribute1;
private String attribute2;
private String attribute3;
// ... other attributes
protected SomeEntity() {}
/* Public getters */
public Long getId() { ... }
public String getAttribute1() { ... }
public String getAttribute2() { ... }
/* Expose some behaviour */
public void updateAttributes(String attribute1, String attribute2) {
/* do some validations before updating */
}
}
和DAO:
@Entity
class SomeEntityDAO {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String attribute1;
private String attribute2;
private String attribute3;
public SomeEntityDAO() {}
/* All getters and setters */
}
我的问题是,如何在不暴露SomeEntity属性的情况下将SomeEntityDAO映射到SomeEntity?
如果创建类似
public SomeEntity(String attribute1, String attribute2, ...) {}
的构造函数,则任何人都可以创建SomeEntity的无效实例。如果我在SomeEntity中公开所有二传手,也会发生同样的情况。我也不认为使用
updateAttributes()
构建对象是一个有效的解决方案,因为这将执行一些我现在不想执行的验证(我们相信数据库中持久化的数据)。我正在考虑保护所有的设置者,以便DAO可以扩展Entity并可以访问设置者...但是我不确定这是否是一个好选择。
解决此问题的最佳或通用方法是哪种?
最佳答案
我遇到过同样的问题。环顾四周,我找不到解决方案。相信我,如果存在的话很好地藏在某个地方。没有一个建议可以解决当您必须处理一个老项目时该怎么做的问题,ORM实体无处不在,并且在Domain和ORM模型之间迈出了一大步。
鉴于此,我推断出,如果您真的想保持Domain实体纯净(因此无法获取和设置-我永远也不会接受!),您必须进行一些交易。因为如果不给实体提供一些额外的知识,就无法共享内部信息。当心,这并不意味着您必须使Domain实体知道ORM层,也不意味着您必须使用getter。就像我得出的结论一样,域实体应该具有将它们公开为不同模型的方法。
因此,总而言之,在您遇到的情况下,我要做的是建立访客模式。域实体EntityA将实现EntityAVisitable接口以接受EntityAVisitor或类似的东西。
interface EntityAVisitable {
accepts(EntityAVisitor visitor);
}
构建器实现来访者EntityAVisitor所需的接口。
interface EntityAVisitor<T>{
setCombinedValue1_2(String attribute1_attribute2_combinedInEntity);
<T> build();
}
接口EntityAVisitor的build()函数使用通用类型T。通过这种方式,域实体与EntityAVisitor的具体实现的返回类型无关。
完美吗?没有。
完美的解决方案是摆脱ORM(实际上我会说我讨厌它们,因为在大多数情况下使用的方式是错误的-但这是我个人的想法)。
是不是不错?没有。
由于语言限制,不允许使用任何好的解决方案(我想您使用Java)。
封装域实体的真实内容是否很好?是。
不仅如此,您还可以准确地决定可能公开的内容以及如何公开。因此,我认为,在保持实体纯净与必须与位于座位下的ORM一起工作之间是一个很好的选择。