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