Foros del Web » Programación para mayores de 30 ;) » Java »

Problemas con hibernate

Estas en el tema de Problemas con hibernate en el foro de Java en Foros del Web. Buenas: Hace tiempo que manejo hibernate, pero no consigo dar con la tecla para arreglar el problema que se me presenta. Tengo una base de ...
  #1 (permalink)  
Antiguo 24/09/2010, 03:22
 
Fecha de Ingreso: abril-2009
Mensajes: 82
Antigüedad: 15 años
Puntos: 2
Problemas con hibernate

Buenas:

Hace tiempo que manejo hibernate, pero no consigo dar con la tecla para arreglar el problema que se me presenta. Tengo una base de datos MySQL (impuesta por el cliente), que contienen 3 clases, una genérica de las otras dos. Como en MySQL no hay herencia, he hecho que hibernate lleve la cuenta de dicha unión. El problema se me presenta cuando intento borrar una de ellas. La inserción y la modificación van estupendas, no producen ningún problema. Os pongo el código a ver que os parece:

Tablas:

Código:
CREATE TABLE `abstractaction` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `id_scene` bigint(20) NOT NULL,
      `new_value` float NOT NULL,
      `id_actionType` bigint(20) NOT NULL,
      PRIMARY KEY (`id`),
      KEY `Action_FKIndex1` (`id_scene`),
      KEY `fk_AbstractAction_ActionType1` (`id_actionType`),
      CONSTRAINT `fk_AbstractAction_ActionType1` FOREIGN KEY (`id_actionType`) REFERENCES `actiontype` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
      CONSTRAINT `fk_B3B03319-9424-426C-B74C-BB07BAEE4B3D` FOREIGN KEY (`id_scene`) REFERENCES `scene` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8 COLLATE=utf8_bin PACK_KEYS=0;


CREATE TABLE `action` (
      `id` bigint(20) NOT NULL,
      `id_attributeVariable` bigint(20) NOT NULL,
      PRIMARY KEY (`id`),
      KEY `fk_Action_AbstractAction1` (`id`),
      KEY `fk_Action_AttributeVariable1` (`id_attributeVariable`),
      CONSTRAINT `fk_Action_AbstractAction1` FOREIGN KEY (`id`) REFERENCES `abstractaction` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
      CONSTRAINT `fk_Action_AttributeVariable1` FOREIGN KEY (`id_attributeVariable`) REFERENCES `attributevariable` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;


CREATE TABLE `delay` (
      `id` bigint(20) NOT NULL,
      PRIMARY KEY (`id`),
      KEY `fk_Delay_AbstractAction1` (`id`),
      CONSTRAINT `fk_Delay_AbstractAction1` FOREIGN KEY (`id`) REFERENCES `abstractaction` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
La tabla abstractaction es el padre del que heredarán action y delay en hibernate.

Por otra parte, la declaración de la configuración de hibernate es la siguiente:

Código:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="xxx.hibernate.model.AbstractAction" table="abstractaction" catalog="atbuilding">
       
        <id name="id" type="java.lang.Long">
            <column name="id" />
            <generator class="identity" />
        </id>
        
        <property name="newValue" type="java.lang.Float">
            <column name="new_value" not-null="true" />
        </property>
        
        <many-to-one name="scene" class="xxx.hibernate.model.Scene" fetch="select">
            <column name="id_scene" not-null="true" />
        </many-to-one>
        
        <many-to-one name="actionType" class="xxx.hibernate.model.ActionType" fetch="select">
            <column name="id_actionType" not-null="true" />
        </many-to-one>
        
        <!-- Especificación de la clase Action -->
        <joined-subclass name="xxx.hibernate.model.Action" table="action">
        	<key column="id" />
        		
	        <many-to-one name="attributeVariable" class="xxx.hibernate.model.AttributeVariable" fetch="select">
	            <column name="id_attributeVariable" not-null="true" />
	        </many-to-one>
        </joined-subclass>
        
        <!-- Especificación de la clase Delay -->
        <joined-subclass name="xxx.hibernate.model.Delay" table="delay">
        	<key column="id" />
        </joined-subclass>
    </class>
</hibernate-mapping>
Como veis, la clase Action y la clase Delay hace un joined-subclass para hacer lo antes mencionado.

La implementación de las clases no tiene ningún misterio y por tanto las voy a obviar.

Y en el caso de los DAO, tengo la siguiente estructura:

Código:
public class ActionDAO extends AbstractActionDAO<Action> {
	public static final String ATTRIBUTE_VARIABLE	= "attributeVariable";

	public ActionDAO() {
		super(Action.class);
	}
	
}



public class DelayDAO extends AbstractActionDAO<Delay> {
	public static final String NEW_VALUE	= "newValue";
	public static final String SCENE	= "scene";
	public static final String ACTION_TYPE	= "actionType";

	public DelayDAO() {
		super(Delay.class);
	}
}



public abstract class AbstractActionDAO<T extends AbstractAction> extends HibernateDAO<T> {
	public static final String NEW_VALUE	= "newValue";
	public static final String SCENE	= "scene";
	public static final String RULE		= "rule";
	public static final String ACTION_TYPE	= "actionType";

	public AbstractActionDAO(Class<T> persistentClass) {
		super(persistentClass);
	}
}



public abstract class HibernateDAO<T extends ProjectModel>{
	public final static String ID = "id";
	protected final Class<T> persistentClass;
	public final String modelName;

	public HibernateDAO(Class<T> persistentClass) {
		if(persistentClass == null){
			throw new RuntimeException("No persistent class specified.");
		}
		this.persistentClass	= persistentClass;
		this.modelName = this.persistentClass.getSimpleName();
		
		// Necesitamos abrir la sesión en caso de que no esté abierta.
		try {
			HibernateUtil.getSession();
		} catch (ATBHibernateException e) {
			e.printStackTrace();
		}
	}
	/** deletes a model */
	public void delete(T model){
		if (DEBUG)
			System.out.println("Borrar " + this.modelName + " #" + model.getId());
		this.getSession().delete(model);
	}
	
	public void deleteAll(List<T> models) {
		Session session = this.getSession();
		
		for (T model : models) {
			session.delete(model);
		}
	}
	
	public void insert(T model){
		if (DEBUG)
			System.out.println("Insertar nuevo " + modelName + " (" + model + ")");
		this.getSession().save(model);
	}
	
	public void update(T model){
		try {
			this.getSession().evict(model);
		} catch (NullPointerException npe) {
			// When model is new instance
		}
		
		if (DEBUG)
			System.out.println("Modificar " + modelName + " #" + model.getId() );
		this.getSession().update(model);
	}
	
	public void insertOrUpdate(T model){
		try {
			this.getSession().evict(model);
		} catch (NullPointerException npe) {
			// When model is new instance
		}
		
		if (DEBUG)
			if(model.getId() == null){
				System.out.println("Insertar (o modificar) nuevo " + modelName);
			}else{
				System.out.println("Modificar (o insertar) " + modelName + " #" + model.getId());
			}
		this.getSession().saveOrUpdate(model);
	}

	.......

}
En este caso, he generado un HibernateDAO que, de manera genérica, genera todas las acciones principales (no las he copiado todas porque es bastante extensa). AbstractActionDAO hereda de ésta, y es abstracta para que sea necesario que las hijas la hereden, indicando la clase a la que pertenecen.

Espero que no sea demasiado compliado de entender. Este esquema lo he probado en varios proyectos y no he tenido ningún problema con él, incluso con herencia (solo que la base de datos era Postgres que si soporta la herencia en un SGDB).

Bueno, solo me queda poner el error que se produce en el borrado:

Código:
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
	at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:61)
	at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:46)
	at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:68)
	at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:237)
	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:146)
	at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
	at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
	at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
	at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
	at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
	at xxx.hibernate.util.HibernateUtil.commitTransaction(HibernateUtil.java:173)
	at xxx.hibernate.Prueba.main(Prueba.java:28)
Como veis, el error se produce cuando le indico a hibernate que persista los cambiso que he hecho, es decir, el borrado de la acción. El método de ejemplo que aparece en este caso es:

Código:
	public static void main(String[] args) {
		Delay delay = new DelayDAO().findById(35l);
		
		HibernateUtil.beginTransaction();
		new DelayDAO().delete(delay);
		HibernateUtil.commitTransaction();
	}
Espero que me ayudeis si podeis.

Un saludo, Squar.

PD: No tened miedo a poner cualquier chorrada que se os ocurra... quien sabe por donde anda la solución ;)
  #2 (permalink)  
Antiguo 27/09/2010, 03:39
 
Fecha de Ingreso: abril-2009
Mensajes: 82
Antigüedad: 15 años
Puntos: 2
Respuesta: Problemas con hibernate

Buenas:

Ya he resuelto el problema. Voy a explicar como lo he resuelto por si a alguien le pasa lo mismo. El problema surge con un par de triggers que tiene la base de datos que al borrar un action o un delay borra automáticamente su padre (para mantener la integridad de la base de datos). Estos triggers los creó un compañero pensando en ello sin saber que hibernate, al generar las tablas con un join-subclass se encarga de borrar tambien la tupla padre. Así, cuando hibernate iba a borrar el elemento padre, el trigger ya había borrado dicha fila, haciendo que hibernate no encuentre nada, de ahí que te devuelva un 0 como númreo de filas modificadas en vez de 1.

Espero que si ha alguien le pasa lo mismo, primero pregunte a sus compañeros a ver si han tocado donde no deben! ;)

Un saludo.

Etiquetas: borrado, herencia, hibernate, mysql
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 05:52.