Author: tchemit Date: 2009-01-16 18:53:25 +0000 (Fri, 16 Jan 2009) New Revision: 1310 Modified: topia/trunk/topia-persistence/changelog.txt topia/trunk/topia-persistence/src/main/java/org/codelutin/topia/TopiaContext.java topia/trunk/topia-persistence/src/main/java/org/codelutin/topia/framework/TopiaContextImpl.java Log: ajout des methodes replicate et replicateEntities sur TopiaContext car les methodes existantes importXML et exportXML ne peuvent pas prendre en compte tous les cas possibles (et on a peut-?\195?\170tre pas envie de passer par du xml...) Modified: topia/trunk/topia-persistence/changelog.txt =================================================================== --- topia/trunk/topia-persistence/changelog.txt 2009-01-16 18:52:46 UTC (rev 1309) +++ topia/trunk/topia-persistence/changelog.txt 2009-01-16 18:53:25 UTC (rev 1310) @@ -1,5 +1,10 @@ +2.1.3 ??? 200901?? +* 20090116 [chemit] - fix a NPE of TopiaDAOFlatFile when do a putAll on a map with some null values on entries. + - ajout des methodes replicate et replicateEntities sur TopiaContext car les methodes existantes + importXML et exportXML ne peuvent pas prendre en compte tous les cas possibles (et on a peut-être + pas envie de passer par du xml...) 2.1.2 chemit 20090115 -* 20090115 [chemit] - pour le moment pas d'embed-xml sur les association multiples +* 20090115 [chemit] - pour le moment pas d'embed-xml sur les association multiples * 20090114 [chemit] - improve exportXML (prepare queries then executes then when parameters are known to be fine) * 20090106 [chemit] - amélioration du générateur de mapping hibernate : - génération des clefs metier dans le mapping hibernate via la tag value naturalId Modified: topia/trunk/topia-persistence/src/main/java/org/codelutin/topia/TopiaContext.java =================================================================== --- topia/trunk/topia-persistence/src/main/java/org/codelutin/topia/TopiaContext.java 2009-01-16 18:52:46 UTC (rev 1309) +++ topia/trunk/topia-persistence/src/main/java/org/codelutin/topia/TopiaContext.java 2009-01-16 18:53:25 UTC (rev 1310) @@ -29,12 +29,6 @@ package org.codelutin.topia; -import java.beans.PropertyChangeListener; -import java.io.File; -import java.io.Reader; -import java.io.Writer; -import java.util.List; - import org.codelutin.topia.event.TopiaContextListener; import org.codelutin.topia.event.TopiaEntitiesVetoable; import org.codelutin.topia.event.TopiaEntityListener; @@ -44,11 +38,14 @@ import org.codelutin.topia.framework.TopiaService; import org.codelutin.topia.persistence.TopiaEntity; -/** - * @author poussin - * - */ +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.Reader; +import java.io.Writer; +import java.util.List; +/** @author poussin */ + public interface TopiaContext { /* Adders */ @@ -99,6 +96,7 @@ /** * Return true if specific service is available + * * @param <E> * @param interfaceService * @return @@ -108,6 +106,7 @@ /** * Return the service + * * @param <E> * @param interfaceService * @return @@ -118,12 +117,14 @@ /** * Permet de créer le schema de la base de données + * * @throws TopiaException if any exception */ public void createSchema() throws TopiaException; /** * Permet de mettre à jour le schema de la base de données + * * @throws TopiaException if any exception */ public void updateSchema() throws TopiaException; @@ -132,19 +133,21 @@ /** * applique les modifications apporté a ce context sur la base de données. + * * @throws TopiaException if any exception */ public void commitTransaction() throws TopiaException; /** * annule les modifications apporté a ce context + * * @throws TopiaException if any exception */ public void rollbackTransaction() throws TopiaException; /** * Permet de rechercher un entite directement par son TopiaId - * + * * @param topiaId l'id de l'entite recherche * @return l'entite trouvee (ou null si non trouve) * @throws TopiaException if any exception @@ -152,8 +155,9 @@ public TopiaEntity findByTopiaId(String topiaId) throws TopiaException; /** - * Permet de faire une requete HQL hibernate directement sur la base - * @param hql la requete a faire + * Permet de faire une requete HQL hibernate directement sur la base + * + * @param hql la requete a faire * @param args les arguments de la requete * @return La liste des resultats * @throws TopiaException si une erreur survient durant la requete @@ -164,10 +168,11 @@ * Permet de faire une requete HQL hibernate directement sur la base * en precisant la fenetre des elements a remonter avec les parametres <code>startIndex</code> * et <code>endIndex</code>. - * @param hql la requete a faire + * + * @param hql la requete a faire * @param startIndex la position du premier element a remonter - * @param endIndex la position du dernier element a remonter - * @param args les arguments de la requete + * @param endIndex la position du dernier element a remonter + * @param args les arguments de la requete * @return La liste des resultats * @throws TopiaException si une erreur survient durant la requete */ @@ -176,7 +181,8 @@ /** * Execute HQL operation on data (Update, Delete) - * @param hql la requete a faire + * + * @param hql la requete a faire * @param args les arguments de la requete * @return The number of entities updated or deleted. * @throws TopiaException if any exception @@ -186,6 +192,7 @@ /** * Permet d'ajouter dans le TopiaContext une TopiaEntity créé par un * autre context. + * * @param e l'entity a ajouter * @throws TopiaException if any exception */ @@ -193,6 +200,7 @@ /** * Permet d'importer des données en XML + * * @param xml le flux XML * @throws TopiaException si une erreur survient durant l'import */ @@ -200,57 +208,97 @@ /** * Permet d'exporter certaines données en XML - * @param xml le flux XML dans lequel il faut ecrire + * <p/> + * <b>Note:</b> Si le parametre <code>entityAndCondition</code> est vide, alors on duplique + * toutes les entités de la base. + * + * @param xml le flux XML dans lequel il faut ecrire * @param entityAndCondition paramètre qui vont par deux, qui represente - * la classe de l'entity a exporter et la condition where que doit - * respecter l'objet pour etre exporter - * (entityClass, condition) + * la classe de l'entity a exporter et la condition where que doit + * respecter l'objet pour etre exporter + * (entityClass, condition) * @throws TopiaException si une erreur survient durant l'export */ public void exportXML(Writer xml, Object... entityAndCondition) throws TopiaException; /** + * Permet de dupliquer de ce context vers un context d'une autre base des + * données sans modification des entites. + * <p/> + * <b>Note:</b> Si le parametre <code>entityAndCondition</code> est vide, alors on duplique + * toutes les entités de la base. + * <p/> + * <b>Note 2:</b> Il se peut que la replication simple ne soit pas suffisante (par example + * si l'on veut repliquer q'une partie d'une entité), on utilisera donc la seconde méthode + * {@link #replicateEntities(TopiaContext, List<TopiaEntity>)}. + * + * @param dstCtxt le context de la base destination + * @param entityAndCondition paramètre qui vont par deux, qui represente + * la classe de l'entity a exporter et la condition where que doit + * respecter l'objet pour etre exporter + * (entityClass, condition) + * @throws TopiaException si une erreur pendant la duplication + * @throws IllegalArgumentException si l'un des context n'est pas ouvert, ou si on essaye de + * dupliquer dans la même base. + */ + public void replicate(TopiaContext dstCtxt, Object... entityAndCondition) + throws TopiaException, IllegalArgumentException; + + /** + * Permet de dupliquer les entités du type donné vers un autre context. + * + * @param dstCtxt le context de la base destination + * @param entities les entités à répliquer + * @param <T> le type des entités à répliquer + * @throws TopiaException si une erreur pendant la duplication + * @throws IllegalArgumentException si l'un des context n'est pas ouvert, ou si on essaye de + * dupliquer dans la même base. + */ + public <T extends TopiaEntity> void replicateEntities(TopiaContext dstCtxt, List<T> entities) + throws TopiaException, IllegalArgumentException; + + /** * Sauve la base de données dans un format natif a la base, la * representation n'est pas portable d'une base a l'autre. Cette methode * ne doit être utilisé que pour un stockage temporaire utile à une * application - * - * @param file le nom du fichier ou stocker les informations + * + * @param file le nom du fichier ou stocker les informations * @param compress si vrai compress le fichier avec gzip - * * @throws TopiaException if any exception */ public void backup(File file, boolean compress) throws TopiaException; /** * Supprime toutes les tables et autres elements de la database. - * + * * @param dropDatabase si vrai alors supprime aussi la base de données - * si la base utilise des fichiers les fichiers seront supprimé (ex: h2) - * ou sera fait sur la base (pastgresql) + * si la base utilise des fichiers les fichiers seront supprimé (ex: h2) + * ou sera fait sur la base (pastgresql) * @throws TopiaException if any exception */ public void clear(boolean dropDatabase) throws TopiaException; /** * l'inverse de la methode {@link #backup(File,boolean)} - * + * * @param file le fichier ou prendre les informations, il peut-etre - * compressé avec gzip ou non. - * + * compressé avec gzip ou non. * @throws TopiaException if any exception */ public void restore(File file) throws TopiaException; /** * Ferme le contexte + * * @throws TopiaException if any exception */ public void closeContext() throws TopiaException; /** * Indique si le contexte a ete ferme + * * @return <code>true</code> si le context est ferme, <code>false</code> autrement */ public boolean isClosed(); Modified: topia/trunk/topia-persistence/src/main/java/org/codelutin/topia/framework/TopiaContextImpl.java =================================================================== --- topia/trunk/topia-persistence/src/main/java/org/codelutin/topia/framework/TopiaContextImpl.java 2009-01-16 18:52:46 UTC (rev 1309) +++ topia/trunk/topia-persistence/src/main/java/org/codelutin/topia/framework/TopiaContextImpl.java 2009-01-16 18:53:25 UTC (rev 1310) @@ -816,7 +816,7 @@ return closed; } - private void checkClosed(String message) throws TopiaException { + protected void checkClosed(String message) throws TopiaException { if (closed) { throw new TopiaException(message); } @@ -919,9 +919,7 @@ dao.update(e); } - /** (non-Javadoc) - * @see org.codelutin.topia.TopiaContext#importXML(java.io.Reader) - */ + @Override public void importXML(Reader xml) throws TopiaException { checkClosed("Ce contexte a ete ferme, impossible d'effectuer l'import"); Document doc; @@ -948,66 +946,20 @@ log.warn("Echec de replication sur " + entity, he); } } + // must commit data, otherwise : no effects... + sessionDom4j.flush(); } else { throw new TopiaException("Document vide"); } } - /** (non-Javadoc) - * @see org.codelutin.topia.TopiaContext#exportXML(java.io.Writer, java.lang.Object...) - */ + @Override public void exportXML(Writer xml, Object... entityAndcondition) throws TopiaException { checkClosed("Ce contexte a ete ferme, impossible d'effectuer l'export"); - Class entityClass; - String condition; + String[] queries = buildQueries(entityAndcondition); - // si entityAndcondition est vide alors il faut le remplir - // avec toutes les entités du mapping (class, null) - if (entityAndcondition.length == 0) { - entityAndcondition = new Object[getHibernateFactory() - .getAllClassMetadata().size() * 2]; - int i = 0; - for (Object className : getHibernateFactory() - .getAllClassMetadata().keySet()) { - try { - entityAndcondition[i++] = Class.forName((String) className); - entityAndcondition[i++] = null; - } catch (ClassNotFoundException e) { - // cette exception ne devrait pas survenir - throw new TopiaException("Can't export XML", e); - } - } - } - - // prepare queries to perform beofre opening any transaction - if (entityAndcondition.length % 2 != 0) { - throw new IllegalArgumentException("entityAndcondition muts be a couple of (Class, String)"); - } - String queries[] = new String[entityAndcondition.length / 2]; - for (int i = 0; i < entityAndcondition.length;) { - try { - entityClass = (Class) entityAndcondition[i++]; - condition = (String) entityAndcondition[i++]; - String query = "from " + entityClass.getName(); - if (condition != null && !condition.isEmpty()) { - query += " where " + condition; - } - queries[(i-1) / 2] = query; - } catch (ClassCastException e) { - if (i % 2 == 0) { - throw new IllegalArgumentException( - "Others arguement must be String not " - + entityAndcondition[i - 1], e); - } else { - throw new IllegalArgumentException( - "Others arguement must be Class not " - + entityAndcondition[i - 1], e); - } - } - } - // performs queries try { Session sessionDom4j = getHibernate().getSession(EntityMode.DOM4J); @@ -1020,7 +972,7 @@ for (String query : queries) { List list = sessionDom4j.createQuery(query).list(); - for (Object o : list) { + for (Object o : list) { rootElement.add((Element) o); } } @@ -1067,10 +1019,45 @@ } } - /* - * (non-Javadoc) - * @see org.codelutin.topia.framework.TopiaContextImplementor#getFiresSupport() - */ + @Override + public void replicate(TopiaContext dstCtxt, Object... entityAndCondition) throws TopiaException, IllegalArgumentException { + checkClosed("Ce contexte a ete ferme, impossible d'effectuer l'export"); + TopiaContextImpl dstContextImpl = (TopiaContextImpl) dstCtxt; + dstContextImpl.checkClosed("Ce contexte a ete ferme, impossible d'effectuer l'export"); + if (getRootContext().equals(dstContextImpl.getRootContext())) { + throw new IllegalArgumentException("Impossible de dupliquer dans la même base"); + } + + String[] queries = buildQueries(entityAndCondition); + try { + for (String query : queries) { + if (log.isDebugEnabled()) { + log.debug("acquire entities " + query); + } + // acquire data to replicate + List entities = find(query); + replicate0(dstContextImpl,entities.toArray()); + if (log.isDebugEnabled()) { + log.debug("replication of entities " + query + " was sucessfully done."); + } + } + } catch (HibernateException e) { + throw new TopiaException("Could not duplicate data for reason " + e.getMessage(), e); + } + } + + @Override + public <T extends TopiaEntity> void replicateEntities(TopiaContext dstCtxt, List<T> entities) throws TopiaException, IllegalArgumentException { + checkClosed("Ce contexte a ete ferme, impossible d'effectuer l'export"); + TopiaContextImpl dstContextImpl = (TopiaContextImpl) dstCtxt; + dstContextImpl.checkClosed("Ce contexte a ete ferme, impossible d'effectuer l'export"); + if (getRootContext().equals(dstContextImpl.getRootContext())) { + throw new IllegalArgumentException("Impossible de dupliquer dans la même base"); + } + replicate0(dstContextImpl, entities.toArray()); + } + + @Override public TopiaFiresSupport getFiresSupport() { return firesSupport; } @@ -1333,4 +1320,80 @@ getFiresSupport().removeTopiaEntitiesVetoable(vetoable); } + /** + * Build the list of queries from the given parameter <code>entityAndCondition>/code>. + * + * If no parameter is given, then build the queries for all entities is db, with no condition. + * + * @param entityAndCondition the list of tuples (Class,String) + * @return the list of queries. + * @throws TopiaException if any pb of db while getting entities classes. + * @throws IllegalArgumentException if any pb with the given parameter (mainly ClassCastException). + */ + protected String[] buildQueries(Object... entityAndCondition) throws TopiaException, IllegalArgumentException { + Class entityClass; + String condition; + + // si entityAndcondition est vide alors il faut le remplir + // avec toutes les entités du mapping (class, null) + if (entityAndCondition.length == 0) { + entityAndCondition = new Object[getHibernateFactory() + .getAllClassMetadata().size() * 2]; + int i = 0; + for (Object className : getHibernateFactory() + .getAllClassMetadata().keySet()) { + try { + entityAndCondition[i++] = Class.forName((String) className); + } catch (ClassNotFoundException e) { + // should never happen! + throw new TopiaException("class cast exception for entity " + className); + } + entityAndCondition[i++] = null; + + } + } + + // prepare queries to perform beofre opening any transaction + if (entityAndCondition.length % 2 != 0) { + throw new IllegalArgumentException("entityAndCondition must be a couple of (Class, String)"); + } + String queries[] = new String[entityAndCondition.length / 2]; + for (int i = 0; i < entityAndCondition.length;) { + try { + entityClass = (Class) entityAndCondition[i++]; + condition = (String) entityAndCondition[i++]; + String query = "from " + entityClass.getName(); + if (condition != null && !condition.isEmpty()) { + query += " where " + condition; + } + queries[(i - 1) / 2] = query; + } catch (ClassCastException e) { + if (i % 2 == 0) { + throw new IllegalArgumentException( + "Others arguement must be String not " + + entityAndCondition[i - 1], e); + } else { + throw new IllegalArgumentException( + "Others arguement must be Class not " + + entityAndCondition[i - 1], e); + } + } + } + return queries; + } + + protected void replicate0(TopiaContextImpl dstContextImpl, Object... entities) throws TopiaException { + try { + for (Object entity : entities) { + // dettach entity to source session, to make possible copy of collection + // without a hibernate exception (list opened in two session...) + getHibernate().evict(entity); + dstContextImpl.getHibernate().replicate(entity, ReplicationMode.EXCEPTION); + } + + } catch (HibernateException e) { + throw new TopiaException("Could not duplicate data for reason " + e.getMessage(), e); + } + } + } //TopiaContextImpl