5

これは私の最初の投稿です。ですから、質問が刺激的であることを願っています。Swing + JPA を使用して PostgreSQL データベースを操作する Java プログラムがあります。持続性プロバイダーとして EclipseLink JPA 2.0 を使用しています。私のエンティティクラスは Netbeans 7.2.1 によって自動的に生成されました

私が直面している問題は、更新中に、find() を使用して取得したオブジェクトの 4 つのフィールドを変更してから、merge() を使用してデータベース内のオブジェクトを更新することです。4 つの変更のうち 3 つが認識され、テーブルで更新されますが、そのうちの 1 つは更新されません。

この問題を回避するために、いくつかのことを試しました。永続ユニットの同期戦略に関連するオプションを変更しようとしました。コード内の行位置を変更しようとしました (他のフィールドが更新されるため)。エンティティ クラス フィールドのプレフィックスも試しました。 @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 + " ]";
    }

}

object を更新するメソッドの関連コードは次のとおりです。... 内のコードはオブジェクトに影響しません。

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 は更新されていますが、フィールド Panel (ブール型) は更新されていません。以下に、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 は、それが管理するエンティティ クラスに関連して変更されない場合、値を更新しないと思います。ただし、コードを実行する前に、データベース フィールドの paintel を手動で false に更新しました。それから:

  1. エンティティを取得するときにこの更新が認識されない理由がわかりませんでした。
  2. JPAにすべてのフィールドを強制的に更新させる方法(変更しない場合でも)?

可能な(あまり良くない)解決策:

このメッセージを投稿した瞬間に、可能な解決策に気付きました (あまり良くありません)。更新前にコードを変更してデータベースからエンティティを削除し (コミットを実行)、他のトランザクションを開始してオブジェクトを更新します。つまり、オブジェクトを削除して再度永続化します。できます。

同じトランザクション内で remove() と merge() を使用しようとする前に、機能しませんでした。

4

1 に答える 1

2

実装に応じて、あなたの場合のEclipseLinkでは、JPAは常にすべてのマップされた列を挿入し、変更された列のみを更新します。代替操作が必要な場合、これらの操作に使用されるクエリは、API、SQL、またはストアド プロシージャを使用してカスタマイズできます。

この動作はHibernateのdynamicInsertおよびdynamicUpdateで変更できますが、EclipseLinkでは変更できません。これらの移行ガイドはそれを教えてくれます。

于 2013-10-31T21:46:01.467 に答える