使用JPA和detach()时的最佳实践

时间:2013-04-25 20:46:34

标签: jpa entitymanager detach

我正在使用以下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)的尝试将失败,因为实体管理器甚至不知道首先要删除子实例。

有没有人遇到过这种情况?有没有关于这个主题的最佳实践?

0 个答案:

没有答案