JPA实体由新查询隐式更新

时间:2014-06-23 06:14:52

标签: java jpa

标题无法反映我的问题,但我不知道如何表达。我有一个JPA实体(VatOperatorBalance,其中有一个字段saleBalance),假设我是第一次检索实体,并获得一个实体(VatOperatorBalance@3d6396f5),其{{1}是saleBalance。现在还有其他操作已将100.0修改为saleBalance,现在我从数据库查询并获取新实体(200),确保此实体的VatOperatorBalance@10f8edsaleBalance。然而,让我感到困惑的是旧实体的200.0saleBalance)也是VatOperatorBalance@3d6396f5

所有这些查询和操作都在一个事务中,查询不是200.0,它将从缓存中返回实体。

以下是我的代码

EntityManager.find(java.lang.Class<T> entityClass, java.lang.Object primaryKey)

这个测试用例会失败,我不明白为什么会这样。 JPA实体经理会更新同一实体类型的所有实体吗? @Rollback(true) @Test public void testSale_SingleBet_OK() throws Exception { // prepare request ... // query the VatOperatorBalance first VatOperatorBalance oldBalance = this.getVatOperatorBalanceDao().findByOperator("OPERATOR-111"); //this.entityManager.detach(oldBalance); logger.debug("------ oldBalance(" + oldBalance + ")."); // the operation which will modify the oldBalance Context saleReqCtx = this.getDefaultContext(TransactionType.SELL_TICKET.getRequestType(), clientTicket); saleReqCtx.setGameTypeId(GameType.VAT.getType() + ""); Context saleRespCtx = doPost(this.mockRequest(saleReqCtx)); RaffleTicket respTicket = (RaffleTicket) saleRespCtx.getModel(); this.entityManager.flush(); this.entityManager.clear(); // assert vat sale balance VatOperatorBalance newBalance = this.getVatOperatorBalanceDao().findByOperator("OPERATOR-111"); logger.debug("------ newBalance(" + newBalance + ")."); assertEquals(oldBalance.getSaleBalance().add(respTicket.getTotalAmount()).doubleValue(), newBalance .getSaleBalance().doubleValue(), 0); } 实体和oldBalance实体具有相同的newBlance,但是它们是不同的Java实例,JPA实体管理器中发生了什么?如果我从entityId分离oldBalance实体,则testcase将通过。

注意:我的测试使用的是Spring4.0.5和JPA2.1

  

@ piet.t因为entityManager会通过其主键识别它是同一个实体(随意尝试)。因此,通过同一个entityManager对该实体所做的所有更改都将影响同一个java实例

因此在实体管理器中,具有给定主键的给定实体类型,应该只有一个java实例或管理实体(如果来自实体管理器的查询,无论查询条件是什么,不管是不是id,都是相同的java实例(管理实体)将被退回)。

然而,在我的测试案例中,实体“oldBalance&#39;将更新&#34; 将修改oldBalance &#34;的操作,然后调用entityManager.clear()将分离由此实体管理器管理的所有实体,即& #39; oldBalance&#39;也是分离的。

&#39; newBalance&#39;然后是托管实体,这就是为什么他们有不同的java实例标识符。如果&old;旧平衡&#39;例如,通过调用entityManager.merge()进行管理,它将是&#39; newBalance&#39;的相同实例。

1 个答案:

答案 0 :(得分:0)

我认为您的大部分混淆都来自flush() - 您的代码中的调用。

  1. 调用flush将始终将更改后的值存储到数据库中 - 这就是调用flush的对象。使用事务时,由于数据库事务机制,更改的值可能仍然不会通过其他连接可见,但您的entityManager只会看到更改的值。
  2. 没有clear - 调用您的查询 - 即使它没有使用find - 仍会返回先前创建的相同实例(VatOperatorBalance @ 3d6396f5),因为entityManager会识别它是通过其主键实现相同的实体(随意尝试)。因此,通过同一个entityManager对此实体所做的所有更改都将影响同一个java实例,而通过另一个实体管理器进行的修改很可能会导致异常,因为该实体是从另一个事务更新的。
  3. 某些查询可能会导致隐式flush,因为缓存的更改可能会影响查询结果,因此在执行查询以获取正确的结果集之前,必须将所有更改写入数据库。
  4. 我希望这确实有所帮助。