JPA merge()仅更新某些字段。其他变更未得到承认

时间:2013-03-13 06:29:10

标签: java hibernate jpa merge persistence

这是我的第一篇文章。所以,我希望这个问题令人兴奋。 我有一个使用Swing + JPA来处理的java程序 PostgreSQL数据库。我使用EclipseLink JPA 2.0作为我的 持久性提供者。我的实体类是自动的 由Netbeans 7.2.1生成

我面临的问题是:在更新过程中,我改变了四个 使用find()检索对象的字段,然后我使用 merge()更新数据库中的对象。三个 表中识别并更新了四个更改, 但其中一个没有更新。

我尝试了几个方法来解决这个问题:我尝试更改与持久性单元的同步策略相关的选项,更改代码中的行位置(因为其他字段已更新),我也尝试使用前缀注释为@Basic的实体类字段(可选= false)。我的任何一个尝试都有效。

以下是我的实体类(Senha.java)的代码:

package model;

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.xml.bind.annotation.XmlRootElement;

/**
 *
 * @author Diego Dias
 */
@Entity
@Table(name = "senha")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Senha.findAll", query = "SELECT s FROM Senha s"),
    @NamedQuery(name = "Senha.findById", query = "SELECT s FROM Senha s WHERE s.id = :id"),
    @NamedQuery(name = "Senha.findByGuiche", query = "SELECT s FROM Senha s WHERE s.guiche = :guiche"),
    @NamedQuery(name = "Senha.findByStatus", query = "SELECT s FROM Senha s WHERE s.status = :status"),
    @NamedQuery(name = "Senha.findByHchamada", query = "SELECT s FROM Senha s WHERE s.hchamada = :hchamada"),
    @NamedQuery(name = "Senha.findByAtendente", query = "SELECT s FROM Senha s WHERE s.atendente = :atendente"),
    @NamedQuery(name = "Senha.findByHcriacao", query = "SELECT s FROM Senha s WHERE s.hcriacao = :hcriacao"),
    @NamedQuery(name = "Senha.findByDtcriacao", query = "SELECT s FROM Senha s WHERE s.dtcriacao = :dtcriacao"),
    @NamedQuery(name = "Senha.findByNumeracao", query = "SELECT s FROM Senha s WHERE s.numeracao = :numeracao"),
    @NamedQuery(name = "Senha.findByNchamadas", query = "SELECT s FROM Senha s WHERE s.nchamadas = :nchamadas"),
    @NamedQuery(name = "Senha.findByPainel", query = "SELECT s FROM Senha s WHERE s.painel = :painel"),
    @NamedQuery(name = "Senha.findMaxNumeracaoByDtcriacao", query = "SELECT MAX(s.numeracao) FROM Senha s WHERE s.dtcriacao = :dtcriacao"),
    @NamedQuery(name = "Senha.findByStatusAndHchamadaAndHcriacao", query = "SELECT s FROM Senha s WHERE s.status = :status AND s.hchamada <= :hchamada AND s.hcriacao >= :hcriacao")})
public class Senha implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;
    @Column(name = "guiche")
    private String guiche;
    @Column(name = "status")
    private Character status;
    @Column(name = "hchamada")
    @Temporal(TemporalType.TIME)
    private Date hchamada;
    @Column(name = "atendente")
    private Integer atendente;
    @Column(name = "hcriacao")
    @Temporal(TemporalType.TIME)
    private Date hcriacao;
    @Column(name = "dtcriacao")
    @Temporal(TemporalType.DATE)
    private Date dtcriacao;
    @Column(name = "numeracao")
    private Integer numeracao;
    @Column(name = "nchamadas")
    private Integer nchamadas;
    @Column(name = "painel")
    private Boolean painel;

    public Senha() {
    }

    public Senha(Integer id) {
        this.id = id;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getGuiche() {
        return guiche;
    }

    public void setGuiche(String guiche) {
        this.guiche = guiche;
    }

    public Character getStatus() {
        return status;
    }

    public void setStatus(Character status) {
        this.status = status;
    }

    public Date getHchamada() {
        return hchamada;
    }

    public void setHchamada(Date hchamada) {
        this.hchamada = hchamada;
    }

    public Integer getAtendente() {
        return atendente;
    }

    public void setAtendente(Integer atendente) {
        this.atendente = atendente;
    }

    public Date getHcriacao() {
        return hcriacao;
    }

    public void setHcriacao(Date hcriacao) {
        this.hcriacao = hcriacao;
    }

    public Date getDtcriacao() {
        return dtcriacao;
    }

    public void setDtcriacao(Date dtcriacao) {
        this.dtcriacao = dtcriacao;
    }

    public Integer getNumeracao() {
        return numeracao;
    }

    public void setNumeracao(Integer numeracao) {
        this.numeracao = numeracao;
    }

    public Integer getNchamadas() {
        return nchamadas;
    }

    public void setNchamadas(Integer nchamadas) {
        this.nchamadas = nchamadas;
    }

    public Boolean getPainel() {
        return painel;
    }

    public void setPainel(Boolean painel) {
        this.painel = painel;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Senha)) {
            return false;
        }
        Senha other = (Senha) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "model.Senha[ id=" + id + " ]";
    }

}

以下是更新我的对象的方法的相关代码。里面的代码......不会影响对象。

EntityManagerFactory emf = ctrlCreateEntityManager();
EntityManager em = emf.createEntityManager();

...

current = em.find(Senha.class, getCurrentId());
if (current.getStatus().equals('W')) {
    em.getTransaction().begin();
    current.setStatus(current.getNchamadas() >= 2 ? 'L' : current.getStatus());
    current.setHchamada(Calendar.getInstance().getTime());
    current.setNchamadas(current.getNchamadas() + 1);
    current.setPainel(true);
    em.merge(current);
    em.getTransaction().commit();
    frame.getLbNumeroSenha().setText(formatSenha(current.getNumeracao()));
} 
... 
em.close();
emf.close();

当我查看数据库时,状态Hchamada,Nchamadas字段已更新,但字段Painel(类型为boolean)未更新。接下来我将介绍JPA / EclipseLink的日志记录摘录:

**begin transaction**

**[EL Fine]**: 
Thread(Thread[AWT-EventQueue-1,4,file:...-threadGroup])--UPDATE senha SET
 hchamada = ?, nchamadas = ? WHERE (id = ?)
    bind => [02:15:49, 2, 4]

**[EL Finer]**:
Thread(Thread[AWT-EventQueue-1,4,file:...-threadGroup])

**commit transaction**

查看代码,我在更改值之前添加了一个if,以查看JPA管理的实体是否已经设置了我想要设置的值。这是修改后的代码:

EntityManagerFactory emf = ctrlCreateEntityManager();
EntityManager em = emf.createEntityManager();

...

current = em.find(Senha.class, getCurrentId());
if (current.getStatus().equals('W')) {
    em.getTransaction().begin();
    current.setStatus(current.getNchamadas() >= 2 ? 'L' : current.getStatus());
    current.setHchamada(Calendar.getInstance().getTime());
    current.setNchamadas(current.getNchamadas() + 1);
    if (current.getPainel()) {
        System.out.println("Painel is already true");
    } 
    current.setPainel(true);
    em.merge(current);
    em.getTransaction().commit();
    frame.getLbNumeroSenha().setText(formatSenha(current.getNumeracao()));
} 
... 
em.close();
emf.close();

当我运行代码时,我收到的消息表明我的实体已经设置了我想在数据库中写入的值。所以,我认为如果JPA / EclipseLink相对于它管理的实体类没有改变,它就不会更新该值。但是,在运行代码之前,我手动将数据库字段painel更新为false。然后:

  1. 我不明白为什么在检索实体时无法识别此更新。
  2. 如何强制JPA更新所有字段(即使不更改)?
  3. 可能(不是那么好)解决方案:

    就在发布此消息的那一刻,我意识到了一种可能的解决方案(不太好)。在更新之前修改代码以从数据库中删除实体(给出提交),然后启动其他事务,更新对象。它是,再次删除和持久化对象。有用。

    在我尝试在同一个事务中使用remove()和merge()之前,它没有用。

1 个答案:

答案 0 :(得分:2)

根据实现情况,在您的情况下,JPA 将始终插入所有已映射的列,并且仅更新已更改的列。如果需要备用操作,则可以使用API​​,SQL或存储过程自定义用于这些操作的查询。

您可以使用Hibernate dynamicInsert和dynamicUpdate修改此行为,但不能使用EclipseLink。这些迁移指南告诉您。