0

I have a problem with Hibernate when updating certain data in a table.

If I generate a new record, it is recorded successfully in the table. But if what I want to do is perform an update of the primary key, I throw the following error if the scope of the managed bean is "RequestScoped": "Batch update returned unexpected row count from update [0]; current row count: 0; expected: 1"

If the scope of the managed bean is "ViewScoped", the error is as follows: "Could not extract ResultSet".

If what I modify is another data, the record is recorded satisfactorily.

I understand that it can be a permissions problem to modify the primary key, since this is in turn a foreign key from another table, but this last table does not contain records yet. If from the pgAdmin I change a primary key, I can do it without any problem, therefore it is not a referential integrity problem.

This puzzles me a lot.

I add POJO code:

import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * Combustibles generated by hbm2java
 */
public class Combustibles  implements java.io.Serializable {


 private int idcombustible;
 private String descripcion;
 private boolean baja;
 private Date fechabaja;
 private Set valeses = new HashSet(0);

public Combustibles() {
}


public Combustibles(int idcombustible, String descripcion, boolean baja) {
    this.idcombustible = idcombustible;
    this.descripcion = descripcion;
    this.baja = baja;
}
public Combustibles(int idcombustible, String descripcion, boolean baja, Date fechabaja, Set valeses) {
   this.idcombustible = idcombustible;
   this.descripcion = descripcion;
   this.baja = baja;
   this.fechabaja = fechabaja;
   this.valeses = valeses;
}

public int getIdcombustible() {
    return this.idcombustible;
}

public void setIdcombustible(int idcombustible) {
    this.idcombustible = idcombustible;
}
public String getDescripcion() {
    return this.descripcion;
}

public void setDescripcion(String descripcion) {
    this.descripcion = descripcion;
}
public boolean isBaja() {
    return this.baja;
}

public void setBaja(boolean baja) {
    this.baja = baja;
}
public Date getFechabaja() {
    return this.fechabaja;
}

public void setFechabaja(Date fechabaja) {
    this.fechabaja = fechabaja;
}
public Set getValeses() {
    return this.valeses;
}

public void setValeses(Set valeses) {
    this.valeses = valeses;
}
}

Also from the mapping file:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- Generated 24/03/2017 21:42:13 by Hibernate Tools 4.3.1 -->
<hibernate-mapping>
    <class name="Pojos.Combustibles" table="combustibles" schema="public" optimistic-lock="version">
    <id name="idcombustible" type="int">
        <column name="idcombustible" />
        <generator class="assigned" />
    </id>
    <property name="descripcion" type="string">
        <column name="descripcion" not-null="true" />
    </property>
    <property name="baja" type="boolean">
        <column name="baja" not-null="true" />
    </property>
    <property name="fechabaja" type="time">
        <column name="fechabaja" length="15" />
    </property>
    <set name="valeses" table="vales" inverse="true" lazy="true" fetch="select">
        <key>
            <column name="idcombustible" not-null="true" />
        </key>
        <one-to-many class="Pojos.Vales" />
    </set>
</class>
</hibernate-mapping>

I add the code of the Managed Bean, inside which is the method called modifyCombustible (), which is in charge of the update.

package ManagedBeansRequest;

import Daos.DaoCombustibles;
import HibernateUtil.HibernateUtil;
import Pojos.Combustibles;
import java.util.List;
import javax.inject.Named;
import javax.enterprise.context.RequestScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import org.hibernate.Session;
import org.hibernate.Transaction;

/**
 *
 * @author Gustavo
 */
@Named(value = "mbCombustibles")
@RequestScoped
public class MbCombustibles {

private Combustibles combustible;
private List<Combustibles> listaCombustibles;
private Session sesion;
private Transaction transaccion;

/**
 * Creates a new instance of MbCombustibles
 */
public MbCombustibles() {
    this.combustible = new Combustibles();
}

public void registrar() throws Exception{
    //Antes era public String, pero se cambió a "void" ya que en la vista se cambió el "action" que requiere una cadena, por "actionListener"
    this.sesion = null;
    this.transaccion = null;

    try{

        this.sesion = HibernateUtil.getSessionFactory().openSession();
        this.transaccion = this.sesion.beginTransaction();

        DaoCombustibles daoC = new DaoCombustibles();
        daoC.registrar(this.sesion, this.combustible);

        this.transaccion.commit();

        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Registro","Se registró satisfactoriamente el combustible"));

        //RequestContext.getCurrentInstance().execute("limpiarFormulario('frmRegistrarCombustible')");
        this.combustible = new Combustibles();  //Esto reemplaza a la función javascript de borrado de campos del formulario, debido a que al instanciar un nuevo objeto, viene con sus atributos limpios

        //return "/combustibles/combustiblealta"; //Se reemplaza el return, ya que en la vista se cambió el "action" que requiere una cadena, por "actionListener"


    }catch(Exception e){
        if(this.transaccion != null){
            this.transaccion.rollback();
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Ocurrió un error","Descripcion: " + e.getMessage()));
        }
        return;  //Se reemplaza el return "null", ya que en la vista se cambió el "action" que requiere una cadena, por "actionListener"
    }
    finally{
        if(sesion != null){
            sesion.close();
        } 
    }

}

public List<Combustibles> getTodos(){

    this.sesion = null;
    this.transaccion = null;

    try{

        DaoCombustibles daoC = new DaoCombustibles();

        this.sesion = HibernateUtil.getSessionFactory().openSession();
        this.transaccion = this.sesion.beginTransaction();

        this.listaCombustibles = daoC.verTodos(this.sesion);

        this.transaccion.commit();

        return listaCombustibles;

    }catch(Exception e){
        if(this.transaccion != null){
            this.transaccion.rollback();
        }
        return null;
    }finally{
        if(this.sesion != null){
            this.sesion.close();
        }
    }
}

public void modificarCombustible() throws Exception{
    //Antes era public String, pero se cambió a "void" ya que en la vista se cambió el "action" que requiere una cadena, por "actionListener"
    this.sesion = null;
    this.transaccion = null;

    try{

        this.sesion = HibernateUtil.getSessionFactory().openSession();
        this.transaccion = this.sesion.beginTransaction();

        DaoCombustibles daoC = new DaoCombustibles();
        daoC.modificar(this.sesion, this.combustible);

        this.transaccion.commit();

        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Registro","Se guardaron satisfactoriamente los cambios"));

        //RequestContext.getCurrentInstance().execute("limpiarFormulario('frmRegistrarCombustible')");
        //this.combustible = new Combustibles();  //Esto reemplaza a la función javascript de borrado de campos del formulario, debido a que al instanciar un nuevo objeto, viene con sus atributos limpios

        //return "/combustibles/combustiblealta"; //Se reemplaza el return, ya que en la vista se cambió el "action" que requiere una cadena, por "actionListener"


    }catch(Exception e){
        if(this.transaccion != null){
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Ocurrió un error","Descripcion: " + e.getMessage()));
            this.transaccion.rollback();
        }
    }
    finally{
        if(sesion != null){
            sesion.close();
        } 
    }

}

public Combustibles getCombustible() {
    return combustible;
}

public void setCombustible(Combustibles combustible) {
    this.combustible = combustible;
}

public List<Combustibles> getListaCombustibles() {
    return listaCombustibles;
}

public void setListaCombustibles(List<Combustibles> listaCombustibles) {
    this.listaCombustibles = listaCombustibles;
}
}

For clarity I also add the code portion of the view from where I called the dialog of PrimeFaces, and the complete code of the dialog:

<p:column>
    <p:commandButton value="Editar" oncomplete="PF('dialogoEditarCombustible').show()" update=":frmEditarCombustible">
        <f:setPropertyActionListener target="#{mbCombustibles.combustible}" value="#{fila}"/>
     </p:commandButton>
</p:column>


<h:form id="frmEditarCombustible">
    <p:dialog header="Editar Combustible" widgetVar="dialogoEditarCombustible" modal="true" resizable="false" width="900" showEffect="explode" hideEffect="explode" >
        <p:panelGrid id="editarCombustible" columns="3">
                <p:outputLabel value="Identificador de Combustible:" for="txtIdentificador"/>
                <p:inputText id="txtIdentificador" label="Identificador" value="#{mbCombustibles.combustible.idcombustible}">
                    <f:validator validatorId="validadorVacio"/>
                </p:inputText>
                <p:message for="txtIdentificador"/>
                <p:outputLabel value="Nombre de combustible:" for="txtDescripcion"/>
                 <p:inputText id="txtDescripcion" label="Nombre" value="#{mbCombustibles.combustible.descripcion}">
                     <f:validator validatorId="validadorVacio"/>
                 </p:inputText>
                 <p:message for="txtDescripcion"/>
                 <p:commandButton value="Confirmar Edición" actionListener="#{mbCombustibles.modificarCombustible()}" update=":frmListaCombustibles,editarCombustible"/>
         </p:panelGrid>
     </p:dialog>
</h:form>
  • 3
    Can you please add the code you are using to execute the update? If you are trying to use plain JPA to update the primary key you likely can't. However you can use a Query to do it as seen [here](http://stackoverflow.com/a/15540014/1495050). However, you should re-evaluate whether this is something you truly want to do. Typically, the whole purpose of a primary key is that it shouldn't change. – Daniel Bickler Apr 07 '17 at 14:48
  • @DanielBickler: Dude, I already uploaded the Managed Bean code and part of the view. I understand what you tell me about a primary key is designed not to be changed, but trying, seeing that I was allowed to change the primary key in the base of Postgres and not in the view, I raised the doubt. And by searching Google I found no explanation that would satisfy me. – Gustavo Echenique Apr 07 '17 at 15:07

1 Answers1

2

While you might find certain platforms that permit the change of a table row's primary key value, that is generally not acceptable practice for any relational compliant database.

The accepted way to modify a row's PK value is to first disable or drop the PK on the table that owns the row. This may have the consequence that any other table that has a FK constraint may have to also be altered to disable or drop the FK prior to. Once that is done, you can make any changes necessary to the PK or its values without any database problem. Once your changes are complete, you'll need to re-add or enable the PK followed by the associated FKs.

That's just the nature of a relational database which has nothing to do specifically with Hibernate.

I believe (I'd have to double check the code to be absolute) that Hibernate by default specifies that PK columns are insertable=true, updatable=false. In other words, it assumes relational compliance, PKs don't change unless some manual work occurs as described above.

So even if you attempt to modify the value in your java code, Hibernate silently ignores your column update and thus the error you see is the result of such.

Naros
  • 19,928
  • 3
  • 41
  • 71