【问题标题】:JPA merge does not work on first time but works on second timeJPA合并第一次不起作用,但第二次起作用
【发布时间】:2020-04-16 17:23:03
【问题描述】:

我知道这很常见,并且有很多关于合并不更新数据库中的数据的问题。但这有点不同。

从前端单击保存按钮时,我可以看到 CaseSpecimen 中的数据已针对所有三个属性进行了更新,但是当在此对象上调用合并时,它不会更新数据库。返回响应后,如果我再次单击保存按钮,因为该字段具有我想要的数据,在再次对 CaseSpecimen 对象调用合并后,这一次它将数据保存在数据库中。

这种情况只发生在一种标本类型上。 假设我们有针对案例 c 的 2 个样本类型 A 和 B。 如果我更新类型 A 中的值并单击保存,它将更新数据库中的数据。但是如果我对 Type B 做同样的事情,它不会第一次更新数据,但是如果我第二次点击保存按钮,那么数据库中的值将被更新。

SpecModel.java

@Entity
@Table(name = "CASE_SPECIMEN")
@NamedQuery(name = "CaseSpecimen.findAll", query = "SELECT c FROM CaseSpecimen c")
@XmlAccessorType(XmlAccessType.FIELD)
public class CaseSpecimen extendsBaseEntity implements Serializable, 
Comparable<CaseSpecimen> {
    private static final long serialVersionUID = 1L;

    @Column(name = "SPECIMEN_BODY_SITE")
    private String bodySite;

    @Column(name = "SPECIMEN_PROCEDURE")
    private String procedureName;

    @Column(name = "SPECIMEN_SOURCE")
    private String specimenSource;

   ... 
   getters and setters
}

主要实体

@Entity
@Table(name = "`CASE`")
@XmlAccessorType(XmlAccessType.FIELD)
public class Case extends BaseEntity implements Serializable {
public static final String CASE_CODE_SEPERATOR = "-";

private static final long serialVersionUID = 1L;

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "CASE_ID")
   @GsonIgnore
   @XmlTransient
   private int id;

   @OneToMany(mappedBy = "patientCase", cascade = { CascadeType.MERGE, CascadeType.PERSIST })
   @NotFound(action = NotFoundAction.IGNORE)
   private Set<CaseSpecimen> caseSpecimens;

   ...
   getters and setters
}

案例道:

@Singleton
public class CaseDao extends GenericEntityDAO<PendingList> {
    @PersistenceContext(name = "APP_DB")
    protected EntityManager entityManager; 

    public CaseSpecimen updateCaseSpecimen(CaseSpecimen specimen) {
        return entityManager.merge(specimen);
    }
}

即使我在合并后调用了刷新方法,但数据库中仍然没有更新

  public CaseSpecimen updateCaseSpecimen(CaseSpecimen specimen) {
        CaseSpecimen spec = entityManager.merge(specimen);
        entityMannger.flush();
        return spec;
  }

控制器:

 for (CaseSpecimen specimen : case.getCaseSpecimens()) {
    ... if the updated object from front end the below line will execute
    caseSpecimensUpdatedList.add(caseService.updateCaseSpecimen(specimen));
}

CaseService:目前它是一个额外的层。这只是调用道方法。

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" 
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
            http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
        <persistence-unit name="APP_DB"  >
             <jta-data-source>java:jboss/datasources/AppDataSource</jta-data-source>
        <properties>
        <property name="jboss.entity.manager.jndi.name" value="java:/AppEntityManager"/>
        <property name="wildfly.jpa.default-unit" value="true"/>
        <property name="hibernate.default_batch_fetch_size" value="100"/>
        <property name="hibernate.event.merge.entity_copy_observer" value="allow"/>

    </properties>
</persistence-unit>

【问题讨论】:

  • 你有异常吗?您是否检查了生成的 SQL 语句?此外,您在 Java EE 容器中。您的 DAO 应该是无状态的,而不是单例,因为 EntityManager 不是线程安全的
  • @SimonMartinelli 不,我没有遇到任何异常。我试过了,但在达成这个声明之前还有很多事情要做。因此,即使我启用了日志记录,对于这个特定的合并运行哪个 select 和 update 语句也是模棱两可的。关于 Singleton,我知道它应该是无状态的,我目前无法在获得批准之前进行更改。
  • 我遇到了同样的问题,不知道发生了什么以及如何解决它。
  • @Dmitry 经过深入调查和逐行调试,我们发现调用了另一个方法,其中再次调用了 save 但它是在旧数据上,并且它们都在同一个事务块中这就是为什么它会导致上述行为。

标签: java hibernate jpa merge ejb-3.0


【解决方案1】:

如果您阅读 merge 的文档,您会发现:

将给定实体的状态合并到当前持久化上下文中。

这并不意味着数据库会立即更新!持久性上下文充当事务性后写缓存,对任何实体状态更改进行排队。与任何后写缓存一样,更改首先在内存中应用,并在刷新期间与数据库同步。刷新操作获取每个实体状态更改并将其转换为 INSERT、UPDATE 或 DELETE 语句。

确实 flush 文档说:

将持久化上下文同步到底层数据库。

这在当前事务中显然是正确的。在事务提交之前,看不到任何更改。

如果未显式调用刷新,则持久性提供程序遵循可能具有不同设置的策略:AUTO 或 COMMIT。

FlushModType 的文档又说:

在事务中执行查询时,如果 FlushModeType.AUTO 设置在 Query 或 TypedQuery 对象,或者如果持久性上下文的刷新模式设置为 AUTO(默认)并且尚未为 Query 或 TypedQuery 对象指定刷新模式设置,则持久性提供程序负责确保对状态的所有更新持久性上下文中可能影响查询结果的所有实体对查询的处理都是可见的。持久性提供程序实现可以通过将这些实体刷新到数据库或通过其他方式来实现。

如果设置了 FlushModeType.COMMIT,则未指定对持久性上下文中的实体进行更新对查询的影响。

【讨论】:

  • 我已经更新了这个问题。我已经显式调用了flush方法来检查它是否会更新DB中的数据但是DB中仍然没有变化。
  • 这可能取决于该类是否是单例 ... 关于事务管理。让我们尝试在方法 updateCaseSpecimen 上使用 JTA 指令作为 @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  • 我使用了您的建议,我尝试将类设为无状态,但它不会在第一次单击保存按钮时更新数据。一旦我第二次点击它就会更新数据
  • Transaction 指令怎么样?你确定每次点击按钮都会调用该方法吗?
  • 也使用了指令,但结果相同,数据库中的数据没有改变。 CaseSpecimen spec = entityManager.merge(specimen);规范对象是包含旧数据,我知道这个规范对象现在是一个托管对象,但它有旧数据而不是我要更新的新数据
猜你喜欢
  • 1970-01-01
  • 2017-12-31
  • 1970-01-01
  • 2021-01-13
  • 1970-01-01
  • 2014-01-31
  • 2015-06-21
  • 2011-10-04
  • 1970-01-01
相关资源
最近更新 更多