为什么JPA实体在会话之外被这样对待?

时间:2015-06-20 13:03:24

标签: jpa entity

HY,

我在jpa中有一个“Solve未能懒惰地初始化角色集合....例外”。我理解在会话之外,当你想要检索一个惰性集合时,如果没有会话绑定,你会得到这个错误,很好。但我不明白的是,如果我有这个弹簧控制器(代码不是100%正确,只是为了解释我的情况):

@Controller

@Autowired
EnterpriseService enterpriseService ; 

public List<Enterprise> getAll(){


    List<Enterprise> enterprises = enterpriseService.getAll();      

    for(Enterprise enterprise:enterprises){

        enterprise.getEmployees();

    }
    return enterprises;

}

当我打电话给“enterprise.getEmployees()”时,我知道会话不再是为什么当我尝试做“enterprise.getEmployees()”时,为什么企业被视为一个jpa实体而不仅仅像普通的豆子?我的意思是;据我所知,jpa实体在会话中被视为这样,但在外部就像在这种情况下它应该被视为普通的java bean,因此对enterprise.getEmployees()的调用应该被视为调用get方法java bean,不应该抛出任何延迟异常....

也许是因为spring控制器像jpa实体那样处理企业对象而不只是像java bean一样?这种行为特定于弹簧控制器吗?

由于

2 个答案:

答案 0 :(得分:1)

EntityManager返回的实体不一定是您的实体类的实例,而是扩展您的类的代理类。在许多情况下,对于此类实体的持久属性也是如此(特别是对于使用(一个/多个)To(一个/多个)注释的那些实体)。

例如,如果您使用基于字段的访问

@Entity
public class Enterprise {
    @OneToMany
    private List<Employee> employees = new ArrayList<>();

    public List<Employee> getEmployees() {
        return employees;
    }
}

这里JPA提供程序将创建一个扩展Enterprise的代理类,并以某种方式从DB中记住以前的状态。此外,它会将employees列表更改为自己的List实现(不需要扩展ArrayList)。

因为代理类可以“覆盖”您的方法,所以它可以知道您何时调用getEmployees()并检查会话。但我不认为这会发生在这里,因为该方法没有使用任何JPA特定注释进行注释。

此外,某些框架(如Hibernate)支持字节代码增强字节代码检测。这会改变您的类的实现(以字节代码形式),并用一些提供者特定的代码替换对employees的每次访问。我不知道 Spring JPA 是否提供此功能,但这可能导致检查会话。

否则,对enterprise.getEmployees()的任何调用都应该只返回employees的当前值 - 不对会话进行任何检查,也不使用 LazyInitializationException

但是调用enterprise.getEmployees().size()会尝试初始化列表并检查会话 - 这可能会导致上述异常。

如果您使用基于属性的访问,情况会有所不同:

@Entity
public class Enterprise {
    private List<Employee> employees = new ArrayList<>();

    @OneToMany
    public List<Employee> getEmployees() {
        return employees;
    }
}

此处代理类将委托给您的实现,但会覆盖getEmployees()方法并返回自己的List实现,而不会更改employees。因此,可以为enterprise.getEmployees()获取 LazyInitalizationException

备注:这描述了大多数JPA实现的工作方式 - 但由于这是特定于实现的,因此一些不寻常的框架可以采取不同的方式。

答案 1 :(得分:0)

它无法做任何其他事情。唯一的选择是返回一个空的员工集合,这会更糟糕:你会错误地认为企业有0名员工,这是一个有效但完全错误的结果。

为了实现这样做会有多糟糕,让我们想象一下HospitalAnalysis实体,它拥有一系列DetectedDisease实体。并且让我们假设您尝试显示分析结果但忘记初始化集合。该页面会告诉您,您完全健康,并且您可以安全地回家,而实际上,您患有癌症,并且该程序有一个错误。我非常希望程序能够以异常崩溃并修复而不是不开始我的治疗。

尝试在没有初始化集合的情况下访问员工,因此不知道实际的员工集合,只是一个错误。通过抛出运行时异常来发出此错误信号。