Java JPA合并与持久化
到目前为止,我的首选是始终使用EntityManager的Java JPA合并与持久化,java,jpa,merge,entitymanager,persist,Java,Jpa,Merge,Entitymanager,Persist,到目前为止,我的首选是始终使用EntityManager的merge()处理插入和更新。但我也注意到merge在更新/插入之前执行额外的select查询,以确保数据库中不存在记录 现在,我正在处理一个需要大量(批量)插入数据库的项目。从性能的角度来看,在我绝对知道我总是在创建要持久化的对象的新实例的情况下,使用持久化而不是合并是否有意义?当持久化足够时,使用合并不是一个好主意-合并做了很多工作。本主题之前已经讨论过,并详细解释了它们之间的区别,使用了一些很好的流程图来说明问题。我肯定会使用per
merge()
处理插入和更新。但我也注意到merge在更新/插入之前执行额外的select查询,以确保数据库中不存在记录
现在,我正在处理一个需要大量(批量)插入数据库的项目。从性能的角度来看,在我绝对知道我总是在创建要持久化的对象的新实例的情况下,使用持久化而不是合并是否有意义?当
持久化足够时,使用合并不是一个好主意-合并做了很多工作。本主题之前已经讨论过,并详细解释了它们之间的区别,使用了一些很好的流程图来说明问题。我肯定会使用persistpersist()
如果,正如您所说:
(…)我绝对知道我总是在创建一个要持久化的对象的新实例(…)
这就是此方法的全部内容—在实体已存在的情况下,它将保护您(并将回滚您的事务)。如果您使用的是指定的生成器,则会影响性能
此外,这也是一个错误,因为受管实体由Hibernate自动管理,并且它们的状态由ON与数据库记录同步
要了解所有这些是如何工作的,您首先应该知道Hibernate将开发人员的思维方式从SQL语句转变为SQL语句
一旦一个实体由Hibernate主动管理,所有更改都将自动传播到数据库
Hibernate监视当前连接的实体。但对于要管理的实体,它必须处于正确的实体状态
首先,我们必须定义所有实体状态:
- 新的(暂时的)
如果新创建的对象从未与Hibernate
会话关联过(也称为“持久性上下文”),且未映射到任何数据库表行,则该对象将被视为处于新的(暂时)状态
要实现持久化,我们需要显式调用EntityManager#persist
方法或使用可传递持久化机制
- 持久(管理)
持久性实体已与数据库表行关联,并由当前运行的持久性上下文管理。对此类实体所做的任何更改都将被检测并传播到数据库(在会话刷新期间)。
使用Hibernate,我们不再需要执行INSERT/UPDATE/DELETE语句。Hibernate采用一种工作方式,在当前
会话期间的最后一个负责时刻同步更改
- 独立的
关闭当前运行的持久性上下文后,所有以前管理的实体都将分离。将不再跟踪连续的更改,也不会发生自动数据库同步
要将分离的实体与活动的Hibernate会话关联,可以选择以下选项之一:
- 重新连接
Hibernate(但不是JPA2.1)支持通过会话更新方法重新连接。
Hibernate会话只能为给定的数据库行关联一个实体对象。这是因为持久性上下文充当内存缓存(一级缓存),并且只有一个值(实体)与给定的键(实体类型和数据库标识符)关联。
只有当没有其他JVM对象(与同一数据库行匹配)已关联到当前Hibernate会话时,才能重新附加实体
- 合并
合并将把分离的实体状态(源)复制到托管实体实例(目标)。如果合并实体在当前会话中没有等效实体,则将从数据库中提取一个。
即使在合并操作之后,分离的对象实例仍将保持分离状态
- 除去
尽管JPA要求只允许删除托管实体,但Hibernate也可以删除分离的实体(但只能通过会话#delete方法调用)。
删除的实体仅计划删除,实际的数据库DELETE语句将在会话刷新期间执行
为了更好地理解JPA状态转换,您可以可视化下图:
或者,如果您使用特定于Hibernate的API:
这样做是否是一种好的编码实践:try{em.persist(xxx);}catch(RuntimeException re){try{xxx=em.merge(xxx);}catch(Exception e){throw e;}}尽管捕获RuntimeException
肯定不是一种好的实践(但我假设你的意思是EntityExistsException
)我不认为这是一种通用的良好编码实践。这取决于你的要求。如果需求要求必须持久化该对象,并且在该操作发生之前该对象不能存在,我肯定不会在捕获异常后尝试进行合并。此外,如果您使用JTA实体管理器,您的事务此时将被标记为回滚。您能否解释一下“此外,如果您使用JTA实体管理器,您的事务此时将被标记为回滚”是什么意思?我正在为EJB3使用CMT,并且我能够验证,只要我在方法边界内执行异常,就不会回滚任何事务。@phewataal我猜当您对已经存在的实体调用persist
时,您将获得EntityExistsException
,由于JPA实现者的原因,该事务将回滚(不是因为它是运行时异常)。换句话说,我认为(不确定)即使您尝试/捕获此异常,tx仍将标记为回滚。merge
将永远不会抛出EntityExistsException