Index: topia2/src/java/org/codelutin/topia/framework/TopiaContextImpl.java diff -u topia2/src/java/org/codelutin/topia/framework/TopiaContextImpl.java:1.1 topia2/src/java/org/codelutin/topia/framework/TopiaContextImpl.java:1.2 --- topia2/src/java/org/codelutin/topia/framework/TopiaContextImpl.java:1.1 Wed Jan 4 13:24:35 2006 +++ topia2/src/java/org/codelutin/topia/framework/TopiaContextImpl.java Thu Jan 5 04:50:47 2006 @@ -23,9 +23,9 @@ * * @author poussin * - * @version $Revision: 1.1 $ + * @version $Revision: 1.2 $ * - * Last update: $Date: 2006/01/04 13:24:35 $ by : $Author: bpoussin $ + * Last update: $Date: 2006/01/05 04:50:47 $ by : $Author: bpoussin $ */ package org.codelutin.topia.framework; @@ -43,12 +43,17 @@ import org.codelutin.topia.TopiaContext; import org.codelutin.topia.TopiaException; import org.codelutin.topia.TopiaNotFoundException; +import org.codelutin.topia.TopiaVetoException; import org.codelutin.topia.event.TopiaEntityEvent; import org.codelutin.topia.event.TopiaEntityListener; +import org.codelutin.topia.event.TopiaVetoableEntityEvent; +import org.codelutin.topia.event.TopiaVetoableEntityListener; import org.codelutin.topia.persistence.TopiaDAO; import org.codelutin.topia.persistence.TopiaDAODelegator; import org.codelutin.topia.persistence.TopiaEntity; +import org.codelutin.topia.persistence.TopiaEntityAbstract; import org.codelutin.util.CategorisedListenerSet; +import org.codelutin.util.ListenerSet; import org.hibernate.EmptyInterceptor; import org.hibernate.FlushMode; import org.hibernate.HibernateException; @@ -56,6 +61,22 @@ import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; +import org.hibernate.event.PostDeleteEvent; +import org.hibernate.event.PostDeleteEventListener; +import org.hibernate.event.PostInsertEvent; +import org.hibernate.event.PostInsertEventListener; +import org.hibernate.event.PostLoadEvent; +import org.hibernate.event.PostLoadEventListener; +import org.hibernate.event.PostUpdateEvent; +import org.hibernate.event.PostUpdateEventListener; +import org.hibernate.event.PreDeleteEvent; +import org.hibernate.event.PreDeleteEventListener; +import org.hibernate.event.PreInsertEvent; +import org.hibernate.event.PreInsertEventListener; +import org.hibernate.event.PreLoadEvent; +import org.hibernate.event.PreLoadEventListener; +import org.hibernate.event.PreUpdateEvent; +import org.hibernate.event.PreUpdateEventListener; import org.hibernate.type.Type; /** @@ -75,7 +96,7 @@ *
la liste des classes que doit géré hibernate. On peut tres bien utiliser * topia.persistence.directories pour un ensemble d'entié du meme repertoire et * topia.persistence.classes pour d'autres classes - * + * * * @author poussin * @@ -126,6 +147,8 @@ protected CategorisedListenerSet listeners = new CategorisedListenerSet( TopiaEntityListener.class); + protected ListenerSet vetoableListeners = new ListenerSet( + TopiaVetoableEntityListener.class); /** * constructeur utilisé par la factory pour creer les contexts initiaux @@ -149,14 +172,22 @@ this.parentContext = parentContext; } - /* (non-Javadoc) + public Set getChildContext() { + return this.childContext; + } + + /* + * (non-Javadoc) + * * @see org.codelutin.topia.framework.TopiaContextImplementor#getParentContext() */ public TopiaContextImplementor getParentContext() { return parentContext; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.codelutin.topia.framework.TopiaContextImplementor#getRootContext() */ public TopiaContextImplementor getRootContext() { @@ -167,7 +198,9 @@ return result; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.codelutin.topia.framework.TopiaContextImplementor#getConfig() */ public Properties getConfig() { @@ -177,7 +210,9 @@ return config; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.codelutin.topia.framework.TopiaContextImplementor#getHibernate() */ public Session getHibernate() throws TopiaException { @@ -188,11 +223,12 @@ return hibernate; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.codelutin.topia.framework.TopiaContextImplementor#getHibernateFactory() */ - public SessionFactory getHibernateFactory() - throws TopiaNotFoundException { + public SessionFactory getHibernateFactory() throws TopiaNotFoundException { if (hibernateFactory == null) { if (getParentContext() != null) { hibernateFactory = getParentContext().getHibernateFactory(); @@ -240,35 +276,44 @@ return hibernateFactory; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.codelutin.topia.framework.TopiaContextImplementor#getDAO(java.lang.Class) */ @SuppressWarnings("unchecked") - public TopiaDAO getDAO(Class entityClass) throws TopiaException { + public TopiaDAO getDAO(Class entityClass) + throws TopiaException { if (getRootContext() == this) { - throw new TopiaException("Vous êtes sur le root context vous devez ouvrir une transaction pour pouvoir accèder aux données"); + throw new TopiaException( + "Vous êtes sur le root context vous devez ouvrir une transaction pour pouvoir accèder aux données"); } TopiaDAO result = (TopiaDAO) daoCache.get(entityClass); if (result == null) { // recherche du type de DAO a instancier pour cette entity - String defaultDAOClassname = getConfig().getProperty("topia.dao.default.class", "hibernate"); - String daoClassname = getConfig().getProperty("topia.dao." + entityClass.getName(), defaultDAOClassname); + String defaultDAOClassname = getConfig().getProperty( + "topia.dao.default.class", "hibernate"); + String daoClassname = getConfig().getProperty( + "topia.dao." + entityClass.getName(), defaultDAOClassname); if ("hibernate".equals(daoClassname)) { daoClassname = "org.codelutin.topia.persistence.hibernate.TopiaDAOHibernate"; } else if ("flatfile".equals(daoClassname)) { daoClassname = "org.codelutin.topia.persistence.flatfile.TopiaDAOFlatFile"; } - + try { - Class> resultClass = (Class>)Class.forName(daoClassname); + Class> resultClass = (Class>) Class + .forName(daoClassname); result = resultClass.newInstance(); result.init(this, entityClass); } catch (ClassNotFoundException eee) { throw new TopiaException("Can't find DAO class " + daoClassname); } catch (InstantiationException eee) { - throw new TopiaException("Can't instanciate DAO class " + daoClassname); + throw new TopiaException("Can't instanciate DAO class " + + daoClassname); } catch (IllegalAccessException eee) { - throw new TopiaException("Can't access DAO class " + daoClassname); + throw new TopiaException("Can't access DAO class " + + daoClassname); } // looking for specialized DAO @@ -288,40 +333,84 @@ daoCache.put(entityClass, result); // si quelqu'un se met listener sur le TopiaContext, il faut qu'il // soit prevenu des evenements de tout type d'entite + // on pourrait ne pas faire ca, et lorsque quelque demande + // a etre listener sur le context, on lui ajouter une entre + // dans les listeners pour la Class TopiaEntity dont herite + // toutes les classes topia, mais du coup le framework ne + // fonctionne plus pour les simples pojo non Topia pour lequel + // il fonction avec ca. getListeners().addCategory(TopiaContext.class, entityClass); } return result; } - - /* (non-Javadoc) + + /* + * (non-Javadoc) + * * @see org.codelutin.topia.framework.TopiaContextImplementor#getListeners() */ - public CategorisedListenerSet getListeners() { - return listeners; - } + public CategorisedListenerSet getListeners() { + return listeners; + } - /* (non-Javadoc) + public ListenerSet getVetoableListeners() { + return this.vetoableListeners; + } + + /* + * (non-Javadoc) + * * @see org.codelutin.topia.TopiaContext#addTopiaEntityListener(org.codelutin.topia.event.TopiaEntityListener) */ - public void addTopiaEntityListener(TopiaEntityListener l) { - getListeners().add(this.getClass(), l); - } + public void addTopiaEntityListener(TopiaEntityListener l) { + // Note: si jamais on prend la decision de dire que toutes les entites + // doivent heriter de TopiaEntity alors il suffit de remplacer + // this.getClass() par TopiaEntity.class, de faire la meme chose + // pour le remove et de supprimer l'appel à addCategory dans le + // getDAO + getListeners().add(this.getClass(), l); + } + + /* + * (non-Javadoc) + * + * @see org.codelutin.topia.TopiaContext#addTopiaEntityListener(java.lang.Class, + * org.codelutin.topia.event.TopiaEntityListener) + */ + public void addTopiaEntityListener(Class entityClass, TopiaEntityListener l) { + getListeners().add(entityClass, l); + } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.codelutin.topia.TopiaContext#removeTopiaEntityListener(org.codelutin.topia.event.TopiaEntityListener) */ - public void removeTopiaEntityListener(TopiaEntityListener l) { - getListeners().remove(this.getClass(), l); - } + public void removeTopiaEntityListener(TopiaEntityListener l) { + getListeners().remove(this.getClass(), l); + } - - /* (non-Javadoc) + /* + * (non-Javadoc) + * + * @see org.codelutin.topia.TopiaContext#removeTopiaEntityListener(java.lang.Class, + * org.codelutin.topia.event.TopiaEntityListener) + */ + public void removeTopiaEntityListener(Class entityClass, + TopiaEntityListener l) { + getListeners().remove(entityClass, l); + } + + /* + * (non-Javadoc) + * * @see org.codelutin.topia.TopiaContext#beginTransaction() */ public TopiaContext beginTransaction() throws TopiaNotFoundException { TopiaContextImpl result = new TopiaContextImpl(this); childContext.add(result); - result.hibernate = getHibernateFactory().openSession(new TopiaInterceptor(result)); + result.hibernate = getHibernateFactory().openSession( + new TopiaInterceptor(result)); // on ne synchronise jamais les données avec la base tant que // l'utilisateur n'a pas fait de commit du context result.hibernate.setFlushMode(FlushMode.NEVER); @@ -330,12 +419,18 @@ return result; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.codelutin.topia.TopiaContext#commitTransaction() */ public void commitTransaction() throws TopiaException { + if (getRootContext() == this) { + throw new TopiaException( + "Vous êtes sur le root context le commit est impossible"); + } try { - for(TopiaDAO dao : daoCache.values()) { + for (TopiaDAO dao : daoCache.values()) { dao.commitTransaction(); } Transaction tx = hibernate.beginTransaction(); @@ -347,12 +442,18 @@ } } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.codelutin.topia.TopiaContext#rollbackTransaction() */ public void rollbackTransaction() throws TopiaException { + if (getRootContext() == this) { + throw new TopiaException( + "Vous êtes sur le root context le rollback est impossible"); + } try { - for(TopiaDAO dao : daoCache.values()) { + for (TopiaDAO dao : daoCache.values()) { dao.rollbackTransaction(); } Transaction tx = hibernate.beginTransaction(); @@ -364,27 +465,87 @@ } } - /* (non-Javadoc) + public void fireVetoableLoad(Object id) { + try { + getVetoableListeners().fire("loadEntity", + new TopiaVetoableEntityEvent(this, id)); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + + public void fireVetoableCreate(Object id) { + try { + getVetoableListeners().fire("createEntity", + new TopiaVetoableEntityEvent(this, id)); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + + public void fireVetoableUpdate(Object id) { + try { + getVetoableListeners().fire("updateEntity", + new TopiaVetoableEntityEvent(this, id)); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + + public void fireVetoableDelete(Object id) { + try { + getVetoableListeners().fire("deleteEntity", + new TopiaVetoableEntityEvent(this, id)); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + + public void fireOnLoaded(Object entity) { + // FIXME a faire + // il faut auss que l'interceptor Hibernate et les autres persistences + // appelle cette methode + + if (entity instanceof TopiaEntityAbstract) { + ((TopiaEntityAbstract) entity).setTopiaContext(this); + } + try { + getListeners().fire(entity.getClass(), "entityLoaded", + new TopiaEntityEvent(this, entity)); + } catch (Exception eee) { + if (log.isWarnEnabled()) { + log.warn("Can't fire event loaded for entity: " + entity, eee); + } + } + } + + /* + * (non-Javadoc) + * * @see org.codelutin.topia.framework.TopiaContextImplementor#fireOnCreated(java.lang.Object) */ public void fireOnCreated(Object entity) { - log.info("onCreated " + entity); + log.info("onCreated " + entity); try { - getListeners().fire(entity.getClass(), "entityCreated", new TopiaEntityEvent(this, entity)); + getListeners().fire(entity.getClass(), "entityCreated", + new TopiaEntityEvent(this, entity)); } catch (Exception eee) { if (log.isWarnEnabled()) { log.warn("Can't fire event created for entity: " + entity, eee); } } } - - /* (non-Javadoc) + + /* + * (non-Javadoc) + * * @see org.codelutin.topia.framework.TopiaContextImplementor#fireOnUpdated(java.lang.Object) */ public void fireOnUpdated(Object entity) { - log.info("onUpdated " + entity); + log.info("onUpdated " + entity); try { - getListeners().fire(entity.getClass(), "entityUpdated", new TopiaEntityEvent(this, entity)); + getListeners().fire(entity.getClass(), "entityUpdated", + new TopiaEntityEvent(this, entity)); } catch (Exception eee) { if (log.isWarnEnabled()) { log.warn("Can't fire event updated for entity: " + entity, eee); @@ -392,54 +553,230 @@ } } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.codelutin.topia.framework.TopiaContextImplementor#fireOnDeleted(java.lang.Object) */ public void fireOnDeleted(Object entity) { - log.info("onDeleted " + entity); + log.info("onDeleted " + entity); try { - getListeners().fire(entity.getClass(), "entityDeleted", new TopiaEntityEvent(this, entity)); + getListeners().fire(entity.getClass(), "entityDeleted", + new TopiaEntityEvent(this, entity)); } catch (Exception eee) { if (log.isWarnEnabled()) { log.warn("Can't fire event deleted for entity: " + entity, eee); } } } - - /* (non-Javadoc) + + /* + * (non-Javadoc) + * * @see org.codelutin.topia.framework.TopiaContextImplementor#fireOnCommited() */ public void fireOnCommited() { - log.info("onCommited"); + log.info("onCommited"); // TODO } - - /* (non-Javadoc) + + /* + * (non-Javadoc) + * * @see org.codelutin.topia.framework.TopiaContextImplementor#fireOnRollbacked() */ public void fireOnRollbacked() { - log.info("onRollbacked"); + log.info("onRollbacked"); // TODO } - + static protected class TopiaInterceptor extends EmptyInterceptor { - + protected TopiaContextImplementor context = null; - + private static final long serialVersionUID = -6787010517746197446L; - + public TopiaInterceptor(TopiaContextImplementor context) { this.context = context; } - - public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types){ + + @Override + public boolean onLoad(Object entity, Serializable id, Object[] state, + String[] propertyNames, Type[] types) { + boolean result = super.onLoad(entity, id, state, propertyNames, + types); + context.fireVetoableLoad(id); + return result; + } + + @Override + public void onDelete(Object entity, Serializable id, Object[] state, + String[] propertyNames, Type[] types) { super.onDelete(entity, id, state, propertyNames, types); + context.fireVetoableDelete(id); context.fireOnDeleted(entity); } - public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { - boolean result = super.onSave(entity, id, state, propertyNames, types); + + @Override + public boolean onSave(Object entity, Serializable id, Object[] state, + String[] propertyNames, Type[] types) { + boolean result = super.onSave(entity, id, state, propertyNames, + types); + context.fireVetoableUpdate(id); context.fireOnUpdated(entity); return result; } } + + /** + * Object permettant de faire le lien entre les events hibernate et topia + * + * @author poussin + */ + static protected class TopiaHibernateEvent implements + PostDeleteEventListener, PostInsertEventListener, + PostLoadEventListener, PostUpdateEventListener, + PreDeleteEventListener, PreInsertEventListener, + PreLoadEventListener, PreUpdateEventListener { + + /** */ + private static final long serialVersionUID = 7303593133642169218L; + + protected TopiaContextImplementor rootContext; + + /** + * + */ + public TopiaHibernateEvent(TopiaContextImplementor rootContext) { + this.rootContext = rootContext; + } + + /** + * Recherche le context utilisant la session hibernate passe en + * parametre + * + * @param hibernate la session hibernate que doit utiliser le + * TopiaContext pour etre retourne + * @return le TopiaContext utilisant cette session hibernate ou null si + * aucun TopiaContext n'utilise cette session. + */ + protected TopiaContextImplementor getContext( + TopiaContextImplementor parent, Session hibernate) { + for (TopiaContextImplementor context : parent.getChildContext()) { + try { + if (context.getHibernate() == hibernate) { + return context; + } else { + return getContext(context, hibernate); + } + } catch (TopiaException eee) { + if (log.isWarnEnabled()) { + log.warn("Error durant la recherche d'un context pour" + + " lancer un event", + eee); + } + } + } + return null; + } + + /* + * (non-Javadoc) + * + * @see org.hibernate.event.PreLoadEventListener#onPreLoad(org.hibernate.event.PreLoadEvent) + */ + public void onPreLoad(PreLoadEvent event) { + TopiaContextImplementor context = getContext(rootContext, event.getSession()); + if (context != null) { + context.fireVetoableLoad(event.getId()); + } + } + org.hibernate.impl.SessionImpl b; + /* + * (non-Javadoc) + * + * @see org.hibernate.event.PreInsertEventListener#onPreInsert(org.hibernate.event.PreInsertEvent) + */ + public boolean onPreInsert(PreInsertEvent event) { + TopiaContextImplementor context = getContext(rootContext, event.getSession()); + if (context != null) { + context.fireVetoableCreate(event.getId()); + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.hibernate.event.PreUpdateEventListener#onPreUpdate(org.hibernate.event.PreUpdateEvent) + */ + public boolean onPreUpdate(PreUpdateEvent event) { + TopiaContextImplementor context = getContext(rootContext, event.getSession()); + if (context != null) { + context.fireVetoableUpdate(event.getId()); + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.hibernate.event.PreDeleteEventListener#onPreDelete(org.hibernate.event.PreDeleteEvent) + */ + public boolean onPreDelete(PreDeleteEvent event) { + TopiaContextImplementor context = getContext(rootContext, event.getSession()); + if (context != null) { + context.fireVetoableDelete(event.getId()); + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.hibernate.event.PostLoadEventListener#onPostLoad(org.hibernate.event.PostLoadEvent) + */ + public void onPostLoad(PostLoadEvent event) { + TopiaContextImplementor context = getContext(rootContext, event.getSession()); + if (context != null) { + context.fireOnLoaded(event.getEntity()); + } + } + + /* + * (non-Javadoc) + * + * @see org.hibernate.event.PostInsertEventListener#onPostInsert(org.hibernate.event.PostInsertEvent) + */ + public void onPostInsert(PostInsertEvent event) { + TopiaContextImplementor context = getContext(rootContext, event.getSession()); + if (context != null) { + context.fireOnCreated(event.getEntity()); + } + } + + /* + * (non-Javadoc) + * + * @see org.hibernate.event.PostUpdateEventListener#onPostUpdate(org.hibernate.event.PostUpdateEvent) + */ + public void onPostUpdate(PostUpdateEvent event) { + TopiaContextImplementor context = getContext(rootContext, event.getSession()); + if (context != null) { + context.fireOnUpdated(event.getEntity()); + } + } + + /* + * (non-Javadoc) + * + * @see org.hibernate.event.PostDeleteEventListener#onPostDelete(org.hibernate.event.PostDeleteEvent) + */ + public void onPostDelete(PostDeleteEvent event) { + TopiaContextImplementor context = getContext(rootContext, event.getSession()); + if (context != null) { + context.fireOnDeleted(event.getEntity()); + } + } + } } Index: topia2/src/java/org/codelutin/topia/framework/TopiaContextImplementor.java diff -u topia2/src/java/org/codelutin/topia/framework/TopiaContextImplementor.java:1.1 topia2/src/java/org/codelutin/topia/framework/TopiaContextImplementor.java:1.2 --- topia2/src/java/org/codelutin/topia/framework/TopiaContextImplementor.java:1.1 Wed Jan 4 13:24:35 2006 +++ topia2/src/java/org/codelutin/topia/framework/TopiaContextImplementor.java Thu Jan 5 04:50:47 2006 @@ -23,15 +23,16 @@ * Created: 3 janv. 2006 21:27:24 * * @author poussin - * @version $Revision: 1.1 $ + * @version $Revision: 1.2 $ * - * Last update: $Date: 2006/01/04 13:24:35 $ + * Last update: $Date: 2006/01/05 04:50:47 $ * by : $Author: bpoussin $ */ package org.codelutin.topia.framework; import java.util.Properties; +import java.util.Set; import org.codelutin.topia.TopiaContext; import org.codelutin.topia.TopiaException; @@ -39,6 +40,7 @@ import org.codelutin.topia.persistence.TopiaDAO; import org.codelutin.topia.persistence.TopiaEntity; import org.codelutin.util.CategorisedListenerSet; +import org.codelutin.util.ListenerSet; import org.hibernate.Session; import org.hibernate.SessionFactory; @@ -50,6 +52,11 @@ public interface TopiaContextImplementor extends TopiaContext { /** + * @return Returns the childContext. + */ + public Set getChildContext(); + + /** * @return Returns the parentContext. */ public TopiaContextImplementor getParentContext(); @@ -82,7 +89,6 @@ * @return * @throws TopiaException */ - @SuppressWarnings("unchecked") public TopiaDAO getDAO(Class entityClass) throws TopiaException; @@ -91,14 +97,75 @@ */ public CategorisedListenerSet getListeners(); + /** + * @return Returns the vetoableListeners. + */ + public ListenerSet getVetoableListeners(); + + /** + * Appele avant de charger un objet, le listener peut lever une exception + * s'il ne souhaite pas que l'objet soit chargé + * @param id identifiant de l'objet a charger + * @throws Exception If one listener don't accepte this action + */ + public void fireVetoableLoad(Object id); + + /** + * Appele avant de creer un objet, le listener peut lever une exception + * s'il ne souhaite pas que l'objet soit cree + * @param id identifiant de l'objet a creer + * @throws Exception If one listener don't accepte this action + */ + public void fireVetoableCreate(Object id); + + /** + * Appele avant d'updater un objet, le listener peut lever une exception + * s'il ne souhaite pas que l'objet soit updater + * @param id identifiant de l'objet a updater + * @throws Exception If one listener don't accepte this action + */ + public void fireVetoableUpdate(Object id); + + /** + * Appele avant de supprimer un objet, le listener peut lever une exception + * s'il ne souhaite pas que l'objet soit supprimé + * @param id identifiant de l'objet a supprimé + * @throws Exception If one listener don't accepte this action + */ + public void fireVetoableDelete(Object id); + + /** + * Appeler apres le chargement d'une entity + * @param entity l'entite qui vient d'etre chargée + */ + public void fireOnLoaded(Object entity); + + /** + * Appeler apres la creation d'une entity + * @param entity l'entite qui vient d'etre creee + */ public void fireOnCreated(Object entity); + /** + * Appeler apres la mise a jour d'une entity + * @param entity l'entite qui vient d'etre modifee + */ public void fireOnUpdated(Object entity); + /** + * Appeler apres l'effacement d'une entity + * @param entity l'entite qui vient d'etre effacée + */ public void fireOnDeleted(Object entity); + /** + * Appeler apres le commit d'une transaction + */ public void fireOnCommited(); + /** + * Appeler apres le rollback d'une transaction + */ public void fireOnRollbacked(); }