我正在使用以下JPA实体:
@Entity @Table(name = "FOO")
public class Foo {
@Id @Column(name = "ID")
private Integer id;
@Column(name = "NAME")
private String name;
// getters/setters ommited for brevity
}
我有一个服务类执行一些验证并持久化此类的实例(假设_entityManager是每个请求一个):
public static Foo save( Foo f ) {
if ( f.getName().length() > 4 )
throw new RuntimeException("Name is too big");
_entityManager.persist(f);
return f;
}
调用服务按预期工作:
// works
_entityManager.getTransaction().begin();
save( new Foo(1, "One") );
_entityManager.getTransaction().commit();
// fails
_entityManager.getTransaction().begin();
save( new Foo(2, "Large") );
_entityManager.getTransaction().commit();
但是,它是一个JPA功能,它将更改为对象传递到数据库(在事务结束时,可能更早)。因此,我可以写:
_entityManager.getTransaction().begin();
Foo f = FooService.save( new Foo( 1, "One" ) );
Foo g = _entityManager.find(Foo.class, 1);
g.setName("Large");
_entityManager.getTransaction().commit();
问题在于,通过修改服务实现之外的对象,开发人员可能会无意中绕过所有验证。
我知道如果您的数据库有约束,或者如果您在Foo上有注释验证输入,那么这不是问题。但是,如果存在不受数据库强制执行的约束,该怎么办?或者那需要外部信息,可能不会被写为注释?
在我的项目中,我们一直在使用经验法则:返回实体的每个公共服务必须首先分离()。这样,更改将不会持久化。然而,这使得服务组合更难。例如:
Foo f = FooService.findById(1); // returns detached instance
Bar b = new Bar();
f.getBars().add(b);
b.setFoo(f);
由于返回的Foo已分离,因此添加到其Bars列表不会对真实的实体管理的Foo执行任何操作。因此,(例如)删除(f)的尝试将失败,因为实体管理器甚至不知道首先要删除子实例。
有没有人遇到过这种情况?有没有关于这个主题的最佳实践?