JPA merge () updates only some fields. Other changes not recognized

This is my first post. Therefore, I hope this question is fascinating. I have a Java program that uses Swing + JPA to work with a PostgreSQL database. I use EclipseLink JPA 2.0 as my Sustainability Provider. My Entity class was automatically created by Netbeans 7.2.1

The problem I am facing: during the update, I change the four fields of the object obtained with find (), then I use merge () to update the object in the database. Three four changes are recognized and updated in the table, but one of them is not updated.

I tried several things to get around this problem: I tried changing the parameters associated with the synchronization strategy of my permanent device, changing the position of the line in the code (as other fields were updated), I also tried to prefix the Entity class field with the @Basic annotation (optional = false). Any of my attemps worked.

Here is the code for my entity class (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 + " ]"; } } 

Here is the corresponding method code that updates my object . The code inside ... does not affect the 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(); 

When I look in the database, the Status Hchamada, Nchamadas fields are updated, but the Painel field (like boolean) is not updated. Follow the publication of excerpts from 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** 

Having looked at the code, I put an if before changing the value to see if the object controlled by the JPA already has the value I want to set. Here is the code that has been modified:

 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(); 

When I run the code, I received a message that indicates that my object is already set with the value that I want to write to the database. So, I think JPA / EclipseLink does not update the value if it does not change with respect to the entity class managed by it. However, before I run the code, I manually update the database painel file to false. THEN:

  • I did not understand why this update is not recognized when retrieving the object.
  • How to force JPA to update all fields (even if they do not change)?

POSSIBLE (NOT MUST) DECISION:

Just at the time of publication of this message, I realized a possible solution (not very good). Modify the code before updating to remove the object from the database (giving a commit), and then start another transaction by updating the object. This, deleting and saving the object again. It is working.

Before I tried to use remove () and merge () inside the same transaction, but that didn't work.

+4
source share
1 answer

Depending on the implementation of EclipseLink, in your case, the JPA will always insert all the mapped columns and will only update those columns that have changed . If alternative operations are required, the queries used for these operations can be configured using API, SQL or stored procedures.

You can change this behavior with Hibernate dynamicInsert and dynamicUpdate, but you cannot use EclipseLink. These migration guides inform you of this.

+2
source

Source: https://habr.com/ru/post/1468898/


All Articles