5

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

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

我尝试了几件事来解决这个问题:我尝试更改与我的持久单元的同步策略相关的选项,更改代码中的行位置(随着其他字段的更新),我还尝试为实体类字段添加前缀带有注释 @Basic(optional = 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();

当我查看数据库时,Status Hchamada、Nchamadas 字段已更新,但 Painel 字段(布尔类型)未更新。跟随我展示了来自 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 更新所有字段(即使不会更改)?

可能(不是很好)的解决方案:

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

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

4

1 回答 1

2

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

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

于 2013-10-31T21:46:01.467 回答