我知道这是一个常见的问题,但我找不到另一个能解决我疑虑的问题。
通常,如果项目很小,我会在表示域对象的同一对象中进行持久性注释。这允许从数据库加载实体并使所有setter保持私有,从而确保任何实例始终处于有效状态。类似的东西:
@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中公开所有setter,也会出现同样的情况。
我也不认为是使用updateAttributes()
构建对象的有效解决方案,因为这会执行一些我此刻不想执行的验证(我们相信&#39 ; s persistet in database)。
我正在考虑让所有的setter受到保护,因此DAO可以扩展实体并且可以访问setter ......但是我不确定这是不是一个好的选择。
解决此问题的最佳或常用方法是什么?
答案 0 :(得分:1)
我遇到了同样的问题。环顾四周,我发现没有解决方案。相信我,如果它存在于某处隐藏得很好。没有任何建议当你必须处理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.这样,Domain实体就不知道具体实现的返回类型。的 EntityAVisitor 强>
完美吗?没有。
完美的解决方案是摆脱ORM(实际上我会说我讨厌它们,因为使用的方式大部分都是错误的 - 但这是我个人的想法)。
好吗?没有。
由于语言限制(我想你使用Java),不允许使用一个很好的解决方案。
在封装域实体的真实内容方面做得好吗?是。
不仅如此,您可以通过这种方式确定可以暴露的内容以及方式。因此,在我看来,在保持实体纯洁和必须与座位下的ORM合作之间是一个很好的协议。
答案 1 :(得分:0)
域实体应该是自我验证的,这意味着它应该只根据它的内部值进行验证。如果更新需要依赖于外部依赖性的验证,那么我将创建一个负责更新的更新程序类。在updater类中,您可以使用规范模式(作为可注入依赖项)来实现验证。
修改时使用域实体,DTO用于只读投影。当您以只读方式使用直接DTO时,可以获得性能和简化收益。这用于CQRS模式。
class SomeEntity {
private Long id;
private String attribute1;
private String attribute2;
private String attribute3;
// ... other attributes
public SomeEntity() {}
/* Public getters/setter */
public Long getId() { ... }
public String getAttribute1() { ... }
public String getAttribute2() { ... }
public Long setId() { ... }
public String setAttribute1() { ... }
public String setAttribute2() { ... }
}
//classes/interfaces named for clarity
class EntityUpdater implements IEntityUpdater {
public EntityUpdater (ISpecification spec){
}
public updateEntity(SomeEntity entity){
//assert/execute validation
}
}
答案 2 :(得分:0)
某些ORM允许通过字段访问设置实体值(与setter方法相反)。
JPA使用@Access注释。见What is the purpose of AccessType.FIELD, AccessType.PROPERTY and @Access
我创建了一个可以使用字段访问的ORM sormula。请参阅@Row fieldAccess和测试用例org.sormula.tests.fieldaccess。