Index: topia/src/java/org/codelutin/topia/persistence/PersistenceStorage.java diff -u topia/src/java/org/codelutin/topia/persistence/PersistenceStorage.java:1.6 topia/src/java/org/codelutin/topia/persistence/PersistenceStorage.java:1.7 --- topia/src/java/org/codelutin/topia/persistence/PersistenceStorage.java:1.6 Thu Aug 4 15:56:24 2005 +++ topia/src/java/org/codelutin/topia/persistence/PersistenceStorage.java Wed Aug 24 15:03:45 2005 @@ -23,10 +23,10 @@ * Created: 16 juillet 2005 23:40:21 CEST * * @author Benjamin POUSSIN - * @version $Revision: 1.6 $ + * @version $Revision: 1.7 $ * - * Last update: $Date: 2005/08/04 15:56:24 $ - * by : $Author: dessard $ + * Last update: $Date: 2005/08/24 15:03:45 $ + * by : $Author: bpoussin $ */ package org.codelutin.topia.persistence; @@ -46,6 +46,11 @@ * java.util.Properties */ public interface PersistenceStorage { // PersistenceStorage + + /** + * Demande la sauvegarde de l'entité dans le context de la transaction + */ + public void store(TopiaContext context, Collection tpos) throws TopiaPersistenceException; /** * Demande la sauvegarde de l'entité dans le context de la transaction Index: topia/src/java/org/codelutin/topia/persistence/PersistenceStorageJDBC.java diff -u topia/src/java/org/codelutin/topia/persistence/PersistenceStorageJDBC.java:1.19 topia/src/java/org/codelutin/topia/persistence/PersistenceStorageJDBC.java:1.20 --- topia/src/java/org/codelutin/topia/persistence/PersistenceStorageJDBC.java:1.19 Fri Aug 19 14:52:47 2005 +++ topia/src/java/org/codelutin/topia/persistence/PersistenceStorageJDBC.java Wed Aug 24 15:03:45 2005 @@ -23,9 +23,9 @@ * Created: 20 juillet 2005 15:25:06 CEST * * @author Benjamin POUSSIN - * @version $Revision: 1.19 $ + * @version $Revision: 1.20 $ * - * Last update: $Date: 2005/08/19 14:52:47 $ + * Last update: $Date: 2005/08/24 15:03:45 $ * by : $Author: bpoussin $ */ @@ -698,20 +698,22 @@ throws SQLException, TopiaPersistenceException { // TODO supprimer le time apres analyse des perfs long time = System.currentTimeMillis(); - synchronized(tpo.getModifiedFields()){ + synchronized(tpo){ if(forceInsert){ PreparedStatement insert = conn.prepareStatement(SQL.getProperty(SQL_INSERT_DATA)); try{ insert.setString(1, tpo.getManagement().getId()); insert.setLong(2, tpo.getManagement().getDate()); for(String field: tpo.getData().keySet()){ - insert.setString(3, field); Object value = tpo.getData().get(field); - String type = value.getClass().getName(); - byte[] bytesValue = objectToDatabase(type, value); - insert.setBytes(4, bytesValue); - insert.setString(5, type); - insert.execute(); + if(value != null){ + insert.setString(3, field); + String type = value.getClass().getName(); + byte[] bytesValue = objectToDatabase(type, value); + insert.setBytes(4, bytesValue); + insert.setString(5, type); + insert.execute(); + } } } finally { insert.close(); @@ -722,13 +724,15 @@ update.setString(3, tpo.getManagement().getId()); update.setLong(4, tpo.getManagement().getDate()); for(String field: tpo.getData().keySet()){ - update.setString(5, field); Object value = tpo.getData().get(field); - String type = value.getClass().getName(); - byte[] bytesValue = objectToDatabase(type, value); - update.setBytes(1, bytesValue); - update.setString(2, type); - update.execute(); + if(value != null){ + update.setString(5, field); + String type = value.getClass().getName(); + byte[] bytesValue = objectToDatabase(type, value); + update.setBytes(1, bytesValue); + update.setString(2, type); + update.execute(); + } } } finally { update.close(); Index: topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceHelper.java diff -u topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceHelper.java:1.8 topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceHelper.java:1.9 --- topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceHelper.java:1.8 Thu Aug 11 16:42:27 2005 +++ topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceHelper.java Wed Aug 24 15:03:45 2005 @@ -23,10 +23,10 @@ * Created: Jul 16, 2005 * * @author Benjamin POUSSIN - * @version $Revision: 1.8 $ + * @version $Revision: 1.9 $ * - * Last update : $Date: 2005/08/11 16:42:27 $ - * by : $Author: thimel $ + * Last update : $Date: 2005/08/24 15:03:45 $ + * by : $Author: bpoussin $ */ package org.codelutin.topia.persistence; @@ -132,19 +132,20 @@ getCache().clean(); } - /** - * Recupere la version de schema sur les MetaInfos - */ - protected long getSchemaVersion(Class entityClass){ - return ClassInfoHelper.getSchemaVersion(entityClass); - } + // /** + // * Recupere la version de schema sur les MetaInfos + // */ + // protected long getSchemaVersion(Class entityClass){ + // return ClassInfoHelper.getSchemaVersion(entityClass); + // } public A create(Class entityClass) throws TopiaException { String id = TopiaId.create(entityClass); - TopiaPersistenceObject tpo = new TopiaPersistenceObject(id); + TopiaPersistenceObject tpo = new TopiaPersistenceObject(id).initDefaultValue(); tpo.getManagement().setDate(getContext().getTransaction().getId()); - tpo.getManagement().setSchemaVersion(getSchemaVersion(entityClass)); + // BB maintenant calculé directement dans management + // tpo.getManagement().setSchemaVersion(getSchemaVersion(entityClass)); tpo.getManagement().setNew(true); A result = TopiaPersistenceProxy.newProxy(this, entityClass, tpo); @@ -302,7 +303,7 @@ if(result == null){ Class entityClass = TopiaId.getClassName(id); TopiaPersistenceObject tpo = new TopiaPersistenceObject(id); - tpo.getManagement().setSchemaVersion(getSchemaVersion(entityClass)); + // tpo.getManagement().setSchemaVersion(getSchemaVersion(entityClass)); result = TopiaPersistenceProxy.newProxy(this, entityClass, tpo); getCache().put(id, result); } Index: topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceObject.java diff -u topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceObject.java:1.3 topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceObject.java:1.4 --- topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceObject.java:1.3 Thu Jul 21 16:51:39 2005 +++ topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceObject.java Wed Aug 24 15:03:45 2005 @@ -23,9 +23,9 @@ * Created: 16 juillet 2005 23:44:18 CEST * * @author Benjamin POUSSIN - * @version $Revision: 1.3 $ + * @version $Revision: 1.4 $ * - * Last update: $Date: 2005/07/21 16:51:39 $ + * Last update: $Date: 2005/08/24 15:03:45 $ * by : $Author: bpoussin $ */ @@ -34,14 +34,16 @@ import java.io.Serializable; import java.lang.Cloneable; import java.util.HashSet; -import java.util.logging.Level; -import java.util.logging.Logger; import java.util.Set; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.codelutin.topia.annotation.ClassInfoHelper; +import org.codelutin.topia.Util; public class TopiaPersistenceObject implements Serializable, Cloneable { // TopiaPersistenceObject /** to use log facility, just put in your code: log.info(\"...\"); */ - static private Logger log = Logger.getLogger("org.codelutin.topia.persistence.TopiaPersistenceObject"); + static private Log log = LogFactory.getLog(TopiaPersistenceObject.class); protected TopiaPersistenceObjectData data = null; protected TopiaPersistenceObjectManagementData management = null; @@ -53,11 +55,43 @@ data = new TopiaPersistenceObjectData(); } + /** + * Initialise tous les champs de l'objet avec les valeurs par defaut + * @return l'objet lui meme pour pouvoir ecrire + * TopiaPersistenceObject tpo = new TopiaPersistenceObject().initDefaultValue(); + */ + public TopiaPersistenceObject initDefaultValue(){ + Class clazz = getManagement().getEntityClass(); + TopiaPersistenceObjectData data = getData(); + + String [] fields = ClassInfoHelper.fields(clazz); + Class [] fieldTypes = ClassInfoHelper.fieldTypes(clazz); + String [] fieldValues = ClassInfoHelper.fieldValues(clazz); + + for(int i=0; i getModifiedFields(){ return modifiedFields; } - public Set getAskedFields(){ + public Set getAskedFields(){ return askedFields; } @@ -101,13 +135,15 @@ public Object getField(String fieldName){ Object result = getData().getField(fieldName); if(result == data.UNLOADED){ - askedFields.add(fieldName); + synchronized(this){ + askedFields.add(fieldName); + } } return result; } public void setField(String fieldName, Object value){ - synchronized(getModifiedFields()){ + synchronized(this){ getData().setField(fieldName, value); // on vient de modifier le champs donc s'il etait demandé //ce n'est plus la peine de le recuperer Index: topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceObjectManagementData.java diff -u topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceObjectManagementData.java:1.4 topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceObjectManagementData.java:1.5 --- topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceObjectManagementData.java:1.4 Thu Aug 11 16:42:27 2005 +++ topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceObjectManagementData.java Wed Aug 24 15:03:45 2005 @@ -23,10 +23,10 @@ * Created: 16 juillet 2005 23:46:51 CEST * * @author Benjamin POUSSIN - * @version $Revision: 1.4 $ + * @version $Revision: 1.5 $ * - * Last update: $Date: 2005/08/11 16:42:27 $ - * by : $Author: thimel $ + * Last update: $Date: 2005/08/24 15:03:45 $ + * by : $Author: bpoussin $ */ package org.codelutin.topia.persistence; @@ -39,6 +39,7 @@ import org.codelutin.topia.TopiaId; import org.codelutin.topia.TopiaNotFoundException; +import org.codelutin.topia.annotation.ClassInfoHelper; public class TopiaPersistenceObjectManagementData implements Serializable { // TopiaPersistenceObjectManagementData @@ -57,9 +58,9 @@ } protected String id = null; + transient protected Class entityClass = null; // pour servir de cache protected EnumSet state = EnumSet.noneOf(State.class); protected long date = 0; //Correspond à la dernière date de modification (transaction en cours ou dernière trasaction commitée) - protected long schemaVersion = 0; public TopiaPersistenceObjectManagementData(){} @@ -119,20 +120,19 @@ } public long getSchemaVersion(){ - return schemaVersion; - } - public void setSchemaVersion(long schemaVersion){ - this.schemaVersion = schemaVersion; + return ClassInfoHelper.getSchemaVersion(getEntityClass()); } public Class getEntityClass() { - try { - return TopiaId.getClassName(id); - } catch (TopiaNotFoundException e) { - // Ca n'est pas censé arriver - log.log(Level.SEVERE, e.getMessage()); - return Void.class; + if(entityClass == null){ + try { + entityClass = TopiaId.getClassName(id); + } catch (TopiaNotFoundException eee) { + // Ca n'est pas censé arriver + throw new RuntimeException("Impossible de retrouver la classe de l'id: " + id, eee); + } } + return entityClass; } public void setRollbacked(boolean v) { Index: topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceProxy.java diff -u topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceProxy.java:1.7 topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceProxy.java:1.8 --- topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceProxy.java:1.7 Tue Aug 16 08:11:48 2005 +++ topia/src/java/org/codelutin/topia/persistence/TopiaPersistenceProxy.java Wed Aug 24 15:03:45 2005 @@ -23,10 +23,10 @@ * Created: 16 juillet 2005 23:52:35 CEST * * @author Benjamin POUSSIN -* @version $Revision: 1.7 $ +* @version $Revision: 1.8 $ * -* Last update: $Date: 2005/08/16 08:11:48 $ -* by : $Author: thimel $ +* Last update: $Date: 2005/08/24 15:03:45 $ +* by : $Author: bpoussin $ */ package org.codelutin.topia.persistence; @@ -41,11 +41,10 @@ import java.util.Collections; import java.util.Date; import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; import org.apache.commons.beanutils.MethodUtils; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; +import org.codelutin.topia.Util; import org.codelutin.topia.annotation.ClassInfoHelper; import org.codelutin.topia.annotation.ClassType; import org.codelutin.topia.annotation.MethodInfoHelper; @@ -59,11 +58,13 @@ import org.codelutin.topia.TopiaPersistenceService; import org.codelutin.topia.TopiaQuery; import org.codelutin.util.ArrayUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; public class TopiaPersistenceProxy implements InvocationHandler { // TopiaPersistenceProxy /** to use log facility, just put in your code: log.info(\"...\"); */ - static private Logger log = Logger.getLogger("org.codelutin.topia.persistence.TopiaPersistenceProxy"); + static private Log log = LogFactory.getLog(TopiaPersistenceProxy.class); static public A newProxy(PersistenceHelper ph, Class clazz, TopiaPersistenceObject o) throws TopiaException { A result = (A) Proxy.newProxyInstance(TopiaPersistenceProxy.class.getClassLoader(), @@ -138,19 +139,14 @@ // toujours pas de valeur, soit parce que l'objet est nouveau // soit parce qu'on a jamais assigné de valeur a l'objet - Object[] values = ClassInfoHelper.fieldValues(entity.getEntityClass()); - String[] fields = ClassInfoHelper.fields(entity.getEntityClass()); - if (values == null || values.length == 0 || fields == null || fields.length != values.length) { - // si pas de valeur par defaut dans le meta, - // on retourne la valeur par defaut Java - result = getDefaultValue(fieldType); - } else { - for (int i = 0; i < values.length; i++) { - if (fields[i].equals(fieldName)) { - result = values[i]; - break; - } - } + String fieldValue = ClassInfoHelper + .fieldValue(entity.getEntityClass(), fieldName); + try{ + result = Util.getDefaultValue(fieldType, fieldValue); + } catch(Exception eee){ + log.warn("Impossible de récupérer la valeur par defaut (" + + fieldValue + ") pour le champs " + fieldName, eee); + result = Util.getDefaultValue(fieldType); } } } @@ -332,8 +328,8 @@ return getObject().getManagement().getEntityClass(); } else if("get_topiaId_".equals(methodName)){ return getObject().getManagement().getId(); - } else if ("get_topiaVersion_".equals(methodName)) { - return getObject().getManagement().getSchemaVersion(); + // } else if ("get_topiaVersion_".equals(methodName)) { + // return getObject().getManagement().getVersion(); // } else if ("get_topiaCreationDate_".equals(methodName)) { // return getObject().getManagement().getCreationDate(); } else if ("get_topiaLastUpdateDate_".equals(methodName)) { @@ -343,9 +339,9 @@ } else if ("set_topiaId_".equals(methodName)) { getObject().getManagement().setId((String)args[0]); return null; - } else if ("set_topiaVersion_".equals(methodName)) { - getObject().getManagement().setSchemaVersion((Long)args[0]); - return null; + // } else if ("set_topiaVersion_".equals(methodName)) { + // getObject().getManagement().setVersion((Long)args[0]); + // return null; // } else if ("set_topiaCreationDate_".equals(methodName)) { // getObject().getManagement().setCreationDate((Long)args[0]); // return null; @@ -355,8 +351,8 @@ // } else if ("set_topiaLastUpdateUser_".equals(methodName)) { // getObject().getManagement().setLastUpdateUser((TopiaUser)args[0]); // return null; - } - + } + // TODO voir si les autres methodes declarees sur TopiaEntity ont une utilité et si oui comment les gérer // on commence par regarder si l'utilisateur n'a pas implanté la @@ -418,7 +414,7 @@ // methode pour acceder au champs Object result = getField(entity, fieldName, fieldType); if (!ph.exists((String)result)) { - log.log(Level.WARNING, "L'objet d'id \"" + + log.warn("L'objet d'id \"" + result + "\" n'existe pas. Le champ \"" + fieldName + "\" est mis à null."); result = null; @@ -483,31 +479,10 @@ } } } - log.severe("Can't find called method: " + method + " methodName: " + method.getName() + " methodArgType: " + Arrays.toString(method.getParameterTypes())); + log.fatal("Can't find called method: " + method + " methodName: " + method.getName() + " methodArgType: " + Arrays.toString(method.getParameterTypes())); // FIXME gérer les appels de méthode spécifique d'attribut (add, remove, ...) throw new UnsupportedOperationException("This proxy don't support this operation: " + method); } - protected Object getDefaultValue(Class type){ - Object result = null; - if(type.equals(Boolean.TYPE)){ - result = Boolean.FALSE; - } else if(type.equals(Character.TYPE)){ - result = Character.valueOf(Character.MIN_VALUE); - } else if(type.equals(Byte.TYPE)){ - result = Byte.valueOf((byte)0); - } else if(type.equals(Short.TYPE)){ - result = Short.valueOf((short)0); - } else if(type.equals(Integer.TYPE)){ - result = Integer.valueOf(0); - } else if(type.equals(Long.TYPE)){ - result = Long.valueOf(0); - } else if(type.equals(Float.TYPE)){ - result = Float.valueOf(0); - } else if(type.equals(Double.TYPE)){ - result = Double.valueOf(0); - } - return result; - } } // TopiaPersistenceProxy Index: topia/src/java/org/codelutin/topia/persistence/PersistenceStorageJDBCMultiTable.java diff -u /dev/null topia/src/java/org/codelutin/topia/persistence/PersistenceStorageJDBCMultiTable.java:1.1 --- /dev/null Wed Aug 24 15:03:51 2005 +++ topia/src/java/org/codelutin/topia/persistence/PersistenceStorageJDBCMultiTable.java Wed Aug 24 15:03:45 2005 @@ -0,0 +1,1256 @@ +/* *##% + * Copyright (C) 2005 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * PersistenceStorageJDBCMultiTable.java + * + * Created: 20 juillet 2005 15:25:06 CEST + * + * @author Benjamin POUSSIN + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2005/08/24 15:03:45 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.persistence; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Properties; +import org.apache.commons.beanutils.MethodUtils; +import org.apache.commons.dbcp.ConnectionFactory; +import org.apache.commons.dbcp.DriverManagerConnectionFactory; +import org.apache.commons.dbcp.PoolableConnectionFactory; +import org.apache.commons.dbcp.PoolingDriver; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.pool.impl.GenericObjectPool; +import org.apache.commons.pool.impl.StackKeyedObjectPoolFactory; +import org.codelutin.queryparser.QueryHelperException; +import org.codelutin.topia.annotation.ClassInfoHelper; +import org.codelutin.topia.persistence.jdbctransformer.JDBCTransformer; +import org.codelutin.topia.persistence.jdbctransformer.JDBCTransformerFactory; +import org.codelutin.topia.TopiaConst; +import org.codelutin.topia.TopiaContext; +import org.codelutin.topia.TopiaEntity; +import org.codelutin.topia.TopiaException; +import org.codelutin.topia.TopiaId; +import org.codelutin.topia.TopiaNotFoundException; +import org.codelutin.topia.TopiaQuery; +import org.codelutin.topia.Util; + +/** +* Storage implante sur JDBC. +*

+* propriétés: +*

  • persistence.storage.jdbc.driver +*
  • persistence.storage.jdbc.url +*
  • persistence.storage.jdbc.login +*
  • persistence.storage.jdbc.password +* @todo peut-etre a l'instanciation faire un peu de menage dans la base, +* en supprimant tous les objets de transaction (date negative), car si le +* storage demarre, cela doit vouloir dire que l'appli c planté, ou que des +* transations, non pas ete commiter/rollbacker avant de quitter. +*/ +public class PersistenceStorageJDBCMultiTable implements PersistenceStorage { // PersistenceStorageJDBCMultiTable + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(PersistenceStorageJDBCMultiTable.class); + + protected Properties SQL = null; + static private Properties SQL_DEFAULT = new Properties(); + static private Properties SQL_POSTGRESQL = new Properties(SQL_DEFAULT); + static private Properties SQL_MYSQL = new Properties(SQL_DEFAULT); + static private Properties SQL_MCKOI = new Properties(SQL_DEFAULT); + static private Properties SQL_ORACLE = new Properties(SQL_DEFAULT); + static private Properties SQL_DERBY = new Properties(SQL_DEFAULT); + + // byte[] postgresql bytea, mysql LONGBLOB, mckoi LONGVARBINARY (voirJAVA_OBJECT) + // string postgresql text, mysql text, mckoi text + // long postgresql bigint, mysql bigint, mckoi bigint + // boolean postgresql boolean, mysql boolean, mckoi boolean + static private final String SQL_CREATE_TABLE_MAPPING = "SQL_CREATE_TABLE_MAPPING"; + static private final String SQL_CREATE_TABLE_HERITAGE = "SQL_CREATE_TABLE_HERITAGE"; + static private final String SQL_CREATE_TABLE_DATA = "SQL_CREATE_TABLE_DATA"; + static private final String SQL_LOAD_ALL = "SQL_LOAD_ALL"; + static private final String SQL_PURGE_TABLE = "SQL_PURGE_TABLE"; + + static private final String SQL_INSERT_MAPPING = "SQL_INSERT_MAPPING"; + static private final String SQL_INSERT_HERITAGE = "SQL_INSERT_HERITAGE"; + static private final String SQL_REMOVE_HERITAGE = "SQL_REMOVE_HERITAGE"; + static private final String SQL_DROP_TABLE = "SQL_DROP_TABLE"; + static private final String SQL_GET_TABLE_NAME = "SQL_GET_TABLE_NAME"; + static private final String SQL_GET_CHILD = "SQL_GET_CHILD"; + + static private final String SQL_TABLE_EXISTS = "SQL_TABLE_EXISTS"; + + /** charge tous les management existant pour un id, tri de facon a avoir + * la version la plus recente en premier dans le ResultSet. Le premier + * est celui de la transaction s'il exist sinon celui qui a la date la + * plus elevé mais ne depassant pas la transaction */ + static private final String SQL_LOAD_HISTORY = "SQL_LOAD_HISTORY"; + /** supprime les données de la table data */ + static private final String SQL_REMOVE_DATA = "SQL_REMOVE_DATA"; + /** retourne tous les id des objets existant non delete */ + static private final String SQL_GET_ALL_ID = "SQL_GET_ALL_ID"; + static private final String SQL_INSERT_DATA = "SQL_INSERT_DATA"; + static private final String SQL_LOAD_DATA = "SQL_LOAD_DATA"; + static private final String SQL_UPDATE_DATA = "SQL_UPDATE_DATA"; + static private final String SQL_ROLLBACK_DATA = "SQL_ROLLBACK_DATA"; + static private final String SQL_COMMIT_DATA = "SQL_COMMIT_DATA"; + + static private final String SQL_TYPE_BOOLEAN = "BOOLEAN"; + static private final String SQL_TYPE_BYTE = "BYTE"; + static private final String SQL_TYPE_BYTES = "BYTES"; + static private final String SQL_TYPE_DATE = "DATE"; + static private final String SQL_TYPE_DOUBLE = "DOUBLE"; + static private final String SQL_TYPE_FLOAT = "FLOAT"; + static private final String SQL_TYPE_INTEGER = "INTEGER"; + static private final String SQL_TYPE_LONG = "LONG"; + static private final String SQL_TYPE_OBJECT = "OBJECT"; + static private final String SQL_TYPE_SHORT = "SHORT"; + static private final String SQL_TYPE_STRING = "STRING"; + + static { + // MAPPING des types SQL suivant les bases + SQL_DEFAULT.put(SQL_TYPE_BOOLEAN, "boolean"); + SQL_DEFAULT.put(SQL_TYPE_BYTE, "smallint"); + SQL_DEFAULT.put(SQL_TYPE_BYTES, "bytea"); + SQL_DEFAULT.put(SQL_TYPE_DATE, "timestamp"); + SQL_DEFAULT.put(SQL_TYPE_DOUBLE, "double precision"); + SQL_DEFAULT.put(SQL_TYPE_FLOAT, "real"); + SQL_DEFAULT.put(SQL_TYPE_INTEGER, "int"); + SQL_DEFAULT.put(SQL_TYPE_LONG, "bigint"); + SQL_DEFAULT.put(SQL_TYPE_OBJECT, "bytea"); + SQL_DEFAULT.put(SQL_TYPE_SHORT, "smallint"); + SQL_DEFAULT.put(SQL_TYPE_STRING, "text"); + + SQL_MYSQL.put(SQL_TYPE_BYTES, "longblob"); + SQL_MYSQL.put(SQL_TYPE_OBJECT, "longblob"); + SQL_MYSQL.put(SQL_TYPE_FLOAT, "float"); + + SQL_MCKOI.put(SQL_TYPE_BYTES, "longvarbinary"); + SQL_MCKOI.put(SQL_TYPE_OBJECT, "longvarbinary"); + SQL_MCKOI.put(SQL_TYPE_DOUBLE, "double"); + + SQL_ORACLE.put(SQL_TYPE_BYTES, "blob"); + SQL_ORACLE.put(SQL_TYPE_OBJECT, "blob"); + SQL_ORACLE.put(SQL_TYPE_LONG, "number(21)"); + SQL_ORACLE.put(SQL_TYPE_STRING, "varchar(255)"); + + SQL_DERBY.put(SQL_TYPE_BYTES, "blob"); + SQL_DERBY.put(SQL_TYPE_OBJECT, "blob"); + SQL_DERBY.put(SQL_TYPE_STRING, "varchar(255)"); + SQL_DERBY.put(SQL_TYPE_BOOLEAN, "integer"); + + + // MAPPING des requetes SQL suivant les bases + + // {0} est a remplacer par le nom de la table + SQL_DEFAULT.put(SQL_TABLE_EXISTS, + "SELECT count(*) FROM {0}"); + + SQL_DEFAULT.put(SQL_CREATE_TABLE_MAPPING, + "CREATE TABLE topia_mapping_table (topia_class varchar(255) NOT NULL, topia_table varchar(255) UNIQUE NOT NULL, topia_schemaVersion bigint, PRIMARY KEY (topia_class, topia_schemaVersion))"); + SQL_DEFAULT.put(SQL_CREATE_TABLE_HERITAGE, + "CREATE TABLE topia_heritage_table (topia_class varchar(255) NOT NULL, topia_child varchar(255) NOT NULL, PRIMARY KEY (topia_class, topia_child))"); + // {0} est a remplacer par le nom de la table + // {1} est a remplacer par (, field_ field_)+ + SQL_DEFAULT.put(SQL_CREATE_TABLE_DATA, + "CREATE TABLE {0} (topia_id varchar(255) NOT NULL, topia_longdate bigint, topia_class varchar(255), topia_isNew boolean, topia_isDeleted boolean{1}, PRIMARY KEY (topia_id, topia_longdate))"); + + // {0} est a remplacer par le nom de la table + SQL_DEFAULT.put(SQL_LOAD_ALL, "SELECT * FROM {0}"); + + // {0} est a remplacer par le nom de la table + SQL_DEFAULT.put(SQL_PURGE_TABLE, "DELETE FROM {0} WHERE topia_longdate<0"); + + // {0} est a remplacer par le nom de la table + SQL_DEFAULT.put(SQL_DROP_TABLE, "DROP TABLE {0}"); + + SQL_DEFAULT.put(SQL_GET_TABLE_NAME, "SELECT * FROM topia_mapping_table where topia_class=? AND topia_schemaVersion=?"); + + SQL_DEFAULT.put(SQL_GET_CHILD, "SELECT * FROM topia_heritage_table where topia_class=?"); + + // on tri sur la valeur abasolue puis sur la valeur reel, pour que les transactions arrive en premier dans les resultats + // {0} doit-etre remplace par le nom de la table + SQL_DEFAULT.put(SQL_LOAD_HISTORY, + "SELECT * FROM {0} WHERE topia_id=? AND ((topia_longdate > 0 AND topia_longdate<=?) OR topia_longdate in (" + Util.SQL_IDS_KEY + ")) order by abs(topia_longdate) desc, topia_longdate"); + // {0} doit-etre remplace par le nom de la table + SQL_DEFAULT.put(SQL_REMOVE_DATA, + "DELETE FROM {0} WHERE topia_id=? and topia_longdate=?"); + SQL_DEFAULT.put(SQL_GET_ALL_ID, + "SELECT DISTINCT(topia_id) FROM {0} WHERE (topia_longdate in (" + Util.SQL_IDS_KEY + ") OR (topia_longdate>0 AND topia_longdate<=?)) AND topia_id NOT IN (SELECT topia_id FROM {0} WHERE topia_isDeleted=true AND (topia_longdate in (" + Util.SQL_IDS_KEY + ") OR (topia_longdate>0 AND topia_longdate<=?)))"); + + // {0} le nom de la table + // {1} le nom des champs a inserer (commence par une ',') + // {2} la valeur des champs a inserer (commence par une ',') + SQL_DEFAULT.put(SQL_INSERT_DATA, + "INSERT INTO {0} (topia_id, topia_longdate, topia_class, topia_isNew, topia_isDeleted {1}) VALUES (?, ?, ?, ?, ? {2})"); + SQL_DEFAULT.put(SQL_INSERT_MAPPING, + "INSERT INTO topia_mapping_table VALUES (?, ?, ?)"); + SQL_DEFAULT.put(SQL_INSERT_HERITAGE, + "INSERT INTO topia_heritage_table (topia_class, topia_child) VALUES (?, ?)"); + SQL_DEFAULT.put(SQL_REMOVE_HERITAGE, + "DELETE FROM topia_heritage_table WHERE topia_child=?"); + + // {0} est le nom de la table + SQL_DEFAULT.put(SQL_LOAD_DATA, + "SELECT * FROM {0} WHERE topia_id=? AND topia_longdate=?"); + + // {0} est le nom de la table + // {1} sont les valeurs des champs, separé par des ',' + SQL_DEFAULT.put(SQL_UPDATE_DATA, + "UPDATE {0} SET topia_id=?, topia_longdate=?, topia_class=?, topia_isNew=?, topia_isDeleted=?{1} WHERE topia_id=? AND topia_longdate=?"); + + // {0} est le nom de la table + SQL_DEFAULT.put(SQL_ROLLBACK_DATA, + "DELETE FROM {0} WHERE topia_longdate=?"); + // {0} est le nom de la table + SQL_DEFAULT.put(SQL_COMMIT_DATA, + "UPDATE {0} SET topia_longdate=? WHERE topia_longdate=?"); + + + + // SURCHARGE DES REQUETES PAR DEFAUT pour les specificités de chaque base + + SQL_MYSQL.put(SQL_CREATE_TABLE_DATA, + "CREATE TABLE {0} (topia_id varchar(255) NOT NULL, topia_longdate bigint, topia_class varchar(255), topia_isNew bool, topia_isDeleted bool{1} , PRIMARY KEY (topia_id, topia_longdate))"); + + + SQL_ORACLE.put(SQL_CREATE_TABLE_MAPPING, + "CREATE TABLE topia_mapping_table (topia_class varchar(255) NOT NULL, topia_table varchar(255) UNIQUE NOT NULL, topia_schemaVersion number(21), PRIMARY KEY (topia_class, topia_schemaVersion))"); + SQL_ORACLE.put(SQL_CREATE_TABLE_DATA, + "CREATE TABLE {0} (topia_id varchar(255) NOT NULL, topia_longdate number(21), topia_class varchar(255), topia_isNew number(1), topia_isDeleted number(1){1} , PRIMARY KEY (topia_id, topia_longdate))"); + SQL_ORACLE.put(SQL_GET_ALL_ID, + "SELECT DISTINCT(topia_id) FROM {0} WHERE (topia_longdate in (" + Util.SQL_IDS_KEY + ") OR (topia_longdate>0 AND topia_longdate<=?)) AND topia_id NOT IN (SELECT topia_id FROM {0} WHERE topia_isDeleted=1 AND (topia_longdate in (" + Util.SQL_IDS_KEY + ") OR (topia_longdate>0 AND topia_longdate<=?)))"); + + + SQL_DERBY.put(SQL_CREATE_TABLE_DATA, + "CREATE TABLE {0} (topia_id varchar(255) NOT NULL, topia_longdate bigint, topia_class varchar(255), topia_isNew integer, topia_isDeleted integer{1}, PRIMARY KEY (topia_id, topia_longdate))"); + SQL_DERBY.put(SQL_GET_ALL_ID, + "SELECT DISTINCT(topia_id) FROM {0} WHERE (topia_longdate in (" + Util.SQL_IDS_KEY + ") OR (topia_longdate>0 AND topia_longdate<=?)) AND topia_id NOT IN (SELECT topia_id FROM {0} WHERE topia_isDeleted=1 AND (topia_longdate in (" + Util.SQL_IDS_KEY + ") OR (topia_longdate>0 AND topia_longdate<=?)))"); + } + + + + protected Properties properties = null; + protected DriverManagerConnectionFactory connectionPool = null; + protected HashMap childCache = new HashMap(); + + public PersistenceStorageJDBCMultiTable(Properties properties) throws TopiaException { + this.properties = properties; + String driver = properties.getProperty(TopiaConst.PERSISTENCE_STORAGE_JDBC_DRIVER); + String url = properties.getProperty(TopiaConst.PERSISTENCE_STORAGE_JDBC_URL); + String login = properties.getProperty(TopiaConst.PERSISTENCE_STORAGE_JDBC_LOGIN); + String password = properties.getProperty(TopiaConst.PERSISTENCE_STORAGE_JDBC_PASSWORD); + + if(driver.indexOf("mysql") != -1){ + SQL = SQL_MYSQL; + } else if(driver.indexOf("mckoi") != -1){ + SQL = SQL_MCKOI; + } else if(driver.indexOf("oracle") != -1){ + SQL = SQL_ORACLE; + } else if(driver.indexOf("derby") != -1){ + SQL = SQL_DERBY; + } else { + SQL = SQL_POSTGRESQL; + } + + Util.getClazz(driver); + + + GenericObjectPool connectionPool = new GenericObjectPool(null); + ConnectionFactory connectionFactory = + new DriverManagerConnectionFactory(url, login, password); + StackKeyedObjectPoolFactory statementPool = + new StackKeyedObjectPoolFactory(); + PoolableConnectionFactory poolableConnectionFactory = + new PoolableConnectionFactory(connectionFactory, connectionPool, + statementPool, null, false, false); + PoolingDriver poolDriver = new PoolingDriver(); + poolDriver.registerPool("topia",connectionPool); + + try{ + checkManagementTable(); + }catch(SQLException eee){ + throw new TopiaPersistenceException("Erreur durant la creation des tables", eee); + } + try{ + checkOldTransaction(); + }catch(SQLException eee){ + throw new TopiaPersistenceException("Erreur durant la suppression des anciennes transactions", eee); + } + try{ + checkSchemaVersion(); + }catch(SQLException eee){ + throw new TopiaPersistenceException("Erreur durant la converion de schema des données", eee); + } + } + + protected String objectToDatabaseFieldName(String f){ + return "field_" + f; + } + + protected String databaseToObjectFieldName(String f){ + return f.substring("field_".length()); + } + + protected Connection getConnection() throws SQLException { + return DriverManager.getConnection("jdbc:apache:commons:dbcp:topia"); + } + + + /** + * Verifie l'existance des tables de management, si elle n'existe pas les + * cree. + */ + protected void checkManagementTable() throws SQLException { + Connection conn = getConnection(); + try{ + try{ + PreparedStatement sta = null; + String sql = MessageFormat.format(SQL.getProperty(SQL_TABLE_EXISTS), "topia_mapping_table"); + sta = conn.prepareStatement(sql); + sta.execute(); + }catch(SQLException eee){ + log.info("Creating mapping table ..."); + conn.rollback(); + PreparedStatement sta = null; + String sql = SQL.getProperty(SQL_CREATE_TABLE_MAPPING); + sta = conn.prepareStatement(sql); + sta.execute(); + conn.commit(); + log.info("... mapping table created"); + } + + try{ + PreparedStatement sta = null; + String sql = MessageFormat.format(SQL.getProperty(SQL_TABLE_EXISTS), "topia_heritage_table"); + sta = conn.prepareStatement(sql); + sta.execute(); + }catch(SQLException eee){ + log.info("Creating heritage table ..."); + conn.rollback(); + PreparedStatement sta = null; + String sql = SQL.getProperty(SQL_CREATE_TABLE_HERITAGE); + sta = conn.prepareStatement(sql); + sta.execute(); + conn.commit(); + log.info("... heritage table created"); + } + + } finally { + conn.close(); + } + } + + /** + * Vide les vielles transactions non commitées + */ + protected void checkOldTransaction() throws SQLException { + Connection conn = getConnection(); + try{ + PreparedStatement sta = null; + String sql = MessageFormat.format(SQL.getProperty(SQL_LOAD_ALL), "topia_mapping_table"); + + sta = conn.prepareStatement(sql); + ResultSet rs = sta.executeQuery(); + while (rs.next()){ + String classname = rs.getString("topia_class"); + String tablename = rs.getString("topia_table"); + long schemaVersion = rs.getLong("topia_schemaVersion"); + + log.info("purge table " + tablename + " ..."); + PreparedStatement purge = null; + sql = MessageFormat.format(SQL.getProperty(SQL_PURGE_TABLE), tablename); + purge = conn.prepareStatement(sql); + purge.execute(); + conn.commit(); + log.info("... table " + tablename + " purged"); + } + } finally { + conn.close(); + } + + } + + /** + * Methode executé au démarrage du storage. On parcours tous les objets + * contenu dans le mapping, si les schemaVersion sont different alors + * la migration des objets vers la nouvelle version est faite automatiquement + */ + protected void checkSchemaVersion() throws SQLException, TopiaPersistenceException { + Connection conn = getConnection(); + try{ + PreparedStatement sta = null; + String sql = MessageFormat.format(SQL.getProperty(SQL_LOAD_ALL), "topia_mapping_table"); + sta = conn.prepareStatement(sql); + ResultSet rs = sta.executeQuery(); + while (rs.next()){ + String classname = rs.getString("topia_class"); + String tablename = rs.getString("topia_table"); + long schemaVersion = rs.getLong("topia_schemaVersion"); + + long currentSchemaVersion = -1; + try{ + Class clazz = Class.forName(classname); + currentSchemaVersion = ClassInfoHelper.getSchemaVersion(clazz); + if (schemaVersion != currentSchemaVersion) { + log.info("Conversion du schema des objets " + classname + + " de la version " + schemaVersion + " vers " + currentSchemaVersion); + // pour tous les lignes de l'ancienne table + String allRowsSql = MessageFormat.format(SQL.getProperty(SQL_LOAD_ALL), tablename); + PreparedStatement allRows = conn.prepareStatement(allRowsSql); + ResultSet row = allRows.executeQuery(); + while(row.next()){ + // chargement d'un tpo + String id = row.getString("topia_id"); + TopiaPersistenceObject tpo = new TopiaPersistenceObject(id); + loadTpo(row, null, tpo, true); + // convert du tpo + tpo = convert(tpo, schemaVersion); + // storage du tpo dans la nouvelle table + store(conn, null, tpo); + } + // Suppression de l'ancienne table dans le mapping + rs.deleteRow(); + + // Suppression de l'ancienne table + String deleteSql = MessageFormat.format(SQL.getProperty(SQL_DROP_TABLE), tablename); + PreparedStatement delete = conn.prepareStatement(deleteSql); + delete.execute(); + delete.close(); + + conn.commit(); + } + } catch(SQLException eee) { + throw new TopiaPersistenceException( + "Impossible de convertir les objets: " + + classname + "(" + schemaVersion + "->" + + currentSchemaVersion +")", eee); + } catch(ClassNotFoundException eee){ + throw new TopiaPersistenceException( + "Impossible de retrouver la classe demandée: " + + classname, eee); + } + } + } finally { + conn.close(); + } + } + + protected String getSQLFieldDeclaration(Class clazz) { + StringBuffer result = new StringBuffer(); + String [] fieldnames = ClassInfoHelper.fields(clazz); + Class [] fieldTypes = ClassInfoHelper.fieldTypes(clazz); + for(int i=0; i tmp = getChidTableName(conn, clazz.getName()); + result = tmp.toArray(new String[tmp.size()]); + childCache.put(clazz, result); + } finally { + conn.close(); + } + } + return result; + } + protected ArrayList getChidTableName(Connection conn, String classname) throws SQLException { + String sql = SQL.getProperty(SQL_GET_CHILD); + PreparedStatement sta = conn.prepareStatement(sql); + sta.setString(1, classname); + ResultSet rs = sta.executeQuery(); + ArrayList result = new ArrayList(); + ArrayList tmp = new ArrayList(); + while(rs.next()){ + tmp.add(rs.getString("topia_child")); + } + sta.close(); + result.addAll(tmp); + for(String child: tmp){ + result.addAll(getChidTableName(conn, child)); + } + return result; + } + + public long time_store = 0; + public long time_managementExists = 0; + public long time_storeOne = 0; + public long time_storeList = 0; + public long time_saveManagement = 0; + public long time_saveData = 0; + public long time_objectToDatabase = 0; + public long time_databaseToObject = 0; + + public void store(TopiaContext context, Collection tpos) throws TopiaPersistenceException { + long time = System.currentTimeMillis(); + Connection conn = null; + try{ + conn = getConnection(); + try{ + for(TopiaPersistenceObject tpo: tpos){ + store(conn, context, tpo); + } + } finally { + conn.commit(); + conn.close(); + } + } catch(SQLException eee) { + if(conn != null){ + try{ + conn.rollback(); + conn.close(); + } + catch(SQLException zzz){ + log.info("Erreur durant le rollback du a une exception", zzz); + } + } + throw new TopiaPersistenceException("Erreur durant la sauvegarde de la liste d'entites", eee); + } + time_storeList += System.currentTimeMillis() - time; + } + + public void store(TopiaContext context, TopiaPersistenceObject tpo) throws TopiaPersistenceException { + long time = System.currentTimeMillis(); + Connection conn = null; + try{ + conn = getConnection(); + try{ + store(conn, context, tpo); + } finally { + conn.commit(); + conn.close(); + } + } catch(SQLException eee) { + if(conn != null){ + try{ + conn.rollback(); + conn.close(); + } + catch(SQLException zzz){ + log.info("erreur précédente", eee); + log.info("Erreur durant le rollback du a une exception", zzz); + } + } + throw new TopiaPersistenceException("Erreur durant la sauvegarde de l'entite: " + tpo.getManagement().getId(), eee); + } + time_storeOne += System.currentTimeMillis() - time; + } + + /** + * Demande la sauvegarde de l'entité dans le context de la transaction + * @param conn la connexion a utiliser + * @param context le pour lequel on fait l'action, null si on est en conversion + * @param tpo l'objet a sauver + */ + protected void store(Connection conn, TopiaContext context, TopiaPersistenceObject tpo) + throws SQLException, TopiaPersistenceException { + long time = System.currentTimeMillis(); + // si l'objet n'etait pas encore dans la transaction, on le met + if(context != null){ + tpo.getManagement().setDate(context.getTransaction().getId()); + } + + boolean exists = exists(conn, context, tpo); + if(exists){ + // l'objet existait on le met a jour + saveData(conn, context, tpo, false); + } else { + // l'objet n'existait pas, on le creer + saveData(conn, context, tpo, true); + } + time_store += System.currentTimeMillis() - time; + } + + protected boolean exists(Connection conn, TopiaContext context, + TopiaPersistenceObject tpo) throws SQLException, TopiaPersistenceException { + long time = System.currentTimeMillis(); + boolean result = false; + + Class clazz = tpo.getManagement().getEntityClass(); + String tablename = getTableName(clazz); + String sql = MessageFormat.format(SQL.getProperty(SQL_LOAD_DATA), tablename); + PreparedStatement sta = conn.prepareStatement(sql); + try{ + sta.setString(1, tpo.getManagement().getId()); + sta.setLong(2, tpo.getManagement().getDate()); + ResultSet rr = sta.executeQuery(); + result = rr.next(); + } finally { + sta.close(); + } + time_managementExists += System.currentTimeMillis() - time; + return result; + } + + /** + * Demande la restauration de l'entité dans le context de la transaction + */ + public void restore(TopiaContext context, TopiaPersistenceObject tpo) throws TopiaPersistenceException { + if(!(tpo.getManagement().isUnloaded() || tpo.getAskedFields().size() != 0 || tpo.getManagement().isRollbacked())){ + // deja chargé et pas de champs a charger on sort tout de suite + return; + } + try{ + Class clazz = tpo.getManagement().getEntityClass(); + String tablename = getTableName(clazz); + Connection conn = getConnection(); + try{ + String sql = MessageFormat.format(SQL.getProperty(SQL_LOAD_DATA), tablename); + PreparedStatement sta = conn.prepareStatement(sql); + try{ + sta.setString(1, tpo.getManagement().getId()); + sta.setLong(2, context.getTransaction().getId()); + ResultSet management = sta.executeQuery(); + if(!management.next()){ + // on a pas trouve un management specifique pour la + // transaction on recherche le plus recent des objet + // commit pour cette transaction + sta.close(); + String query = MessageFormat.format(SQL.getProperty(SQL_LOAD_HISTORY), tablename); + query = Util.setAllMultipleIds(query, context.getTransaction()); + sta = conn.prepareStatement(query); + sta.setMaxRows(1); // on veut uniquement le premier + sta.setString(1, tpo.getManagement().getId()); + sta.setLong(2, -context.getTransaction().getId()); + management = sta.executeQuery(); + if(!management.next()){ + // on a pas trouvé d'objet sattisfaisant la demande + throw new TopiaPersistenceException("This object(" + + tpo.getManagement().getId() + ") don't exist in this transaction(" + + context.getTransaction().getId() + ")"); + } + } + + loadTpo(management, context, tpo, false); + } finally { + sta.close(); + } + conn.commit(); + } finally { + conn.close(); + } + } catch(SQLException eee) { + throw new TopiaPersistenceException("Erreur durant la restauration de l'objet: " + tpo.getManagement().getId(), eee); + } + } + + /** + * Fait la conversion des datas, l'objet retourné peut etre un nouvel objet + * on l'objet data passé en paramètre modifié pour eviter une creation + * d'objet. + * La version du schema souhaité est la version dans management + * management.getSchemaVersion(). + * La version des datas est donné par le paramètre actualSchemaVersion + * @param management pour avoir l'id, la version souhaité + * @param actualSchemaVersion la version actuelle des datas + * @param data les données a convertir + * @return l'objet data passé en paramètre une fois modifier ou un nouveau + */ + protected TopiaPersistenceObject convert(TopiaPersistenceObject tpo, long actualSchemaVersion){ + // FIXME appeler l'objet qui fait la conversion + return tpo; + } + + protected JDBCTransformer getTransformer(Class type) { + JDBCTransformer result = JDBCTransformerFactory.getTransformer(type); + if(result == null){ + log.debug("Can't get transformer for " + type.getName() + " get Object transformer"); + result = JDBCTransformerFactory.getTransformer(Object.class); + } + return result; + } + + /** + * Met a jour le context de l'objet passé en parametre si celui-ci a une + * methode setContext + * @param context le context a utiliser + * @param value l'objet sur lequel il faut appeler setContext + */ + protected void setContext(TopiaContext context, Object value) throws TopiaPersistenceException { + if(context != null && value != null){ + // s'il a une methode setContext(TopiaContext) + // alors on l'appelle avec le context qui est en parametre + Method m = MethodUtils.getMatchingAccessibleMethod( + value.getClass(), "setContext", new Class[]{context.getClass()}); + if(m != null){ + try{ + m.invoke(value, context); + }catch(Exception eee){ + log.warn("Can't set context on object", eee); + } + } + } + } + + /** + * Charge les champs demandé dans management.getAskedFields(). Si le champs + * existe déja dans data, alors il n'est pas chargé meme s'il est demandé + * et vide la liste des champs demandée. + * @param rs le ResultSet a utiliser pour charger l'objet + * @param context le context dans lequel est fait la demande. null si + * la methode est appelée pour la conversion de données + * @param tpo le tpo pour lequel il faut charger les données + * @param forceLoadAllField force le chargement de tous les champs meme ceux + * non demandé + */ + public void loadTpo(ResultSet rs, TopiaContext context, + TopiaPersistenceObject tpo, boolean forceLoadAllField) + throws SQLException, TopiaPersistenceException { + Class clazz = tpo.getManagement().getEntityClass(); + + if(tpo.getManagement().isUnloaded() || tpo.getManagement().isRollbacked()){ + tpo.getManagement().setDate(rs.getLong("topia_longdate")); + tpo.getManagement().setStored(true); + tpo.getManagement().setDeleted(rs.getBoolean("topia_isDeleted")); + // l'objet quoi qu'il arrive ne sera plus nouveau + } + + Collection fieldnames = null; + // FIXME pour l'instant on force le chargement de tous les champs + // tout le temps, car sinon on a un probleme lors de la sauvegarde + // de l'objet dans une transaction, car il faut reecrire tous les + // champs, mais on ne les a pas s'il ne sont pas chargé. + // Il faudrait lors de la sauvegarde faire une copie en SQL de la + // derniere ligne valide pour l'objet en modifiant simplement l'id + // de transaction. Puis faire un update des données. + // La copie en SQL est plus rapide, car si on charge le TPO, il + // y a potentiellement des conversions de type + if(forceLoadAllField || true) { + fieldnames = Arrays.asList(ClassInfoHelper.fields(clazz)); + } else { + fieldnames = tpo.getAskedFields(); + } + for(String fieldname: fieldnames){ + // si pas UNLOADED, ca veut dire que l'utilisateur a modifier + // la valeur, et qu'il ne faut pas perdre cette valeur + if(tpo.getData().getField(fieldname) == tpo.getData().UNLOADED){ + JDBCTransformer t = getTransformer(ClassInfoHelper + .fieldType(clazz, fieldname)); + Object value = t.getValue(rs, + objectToDatabaseFieldName(fieldname)); + setContext(context, value); + tpo.getData().setField(fieldname, value); + } + } + tpo.getAskedFields().clear(); + } + + // /** + // * Charge les champs demandé dans management.getAskedFields(). Si le champs + // * existe déja dans data, alors il n'est pas chargé meme s'il est demandé + // * et vide la liste des champs demandée. + // * @param conn la connection a utiliser pour acceder a la base + // * @param tt la transaction pour lequel la demande est faite + // * @param tpo le tpo pour lequel il faut charger les données + // * @param data l'objet data a charger, on utilise pas l'objet data de tpo + // * car quelque fois il faut charger des données sans faire + // * attention a ce qu'il y a deja dans le tpo + // * @param forceLoadAllField force le chargement de tous les champs meme ceux + // * non demandé + // */ + // protected void loadData(Connection conn, TopiaContext context, + // TopiaPersistenceObject tpo, + // TopiaPersistenceObjectData data, + // boolean forceLoadAllField) throws SQLException, TopiaPersistenceException { + // synchronized(tpo){ + // Class clazz = tpo.getManagement().getEntityClass(); + // String tablename = getTableName(clazz); + // String sql = MessageFormat.format(SQL.getProperty(SQL_LOAD_DATA), tablename); + // PreparedStatement sta = conn.prepareStatement(sql); + // try{ + // sta.setString(1, tpo.getManagement().getId()); + // sta.setLong(2, tpo.getManagement().getDate()); + // ResultSet rr = sta.executeQuery(); + // if(rr.next()){ + // Collection fieldnames = null; + // if(forceLoadAllField) { + // fieldnames = Arrays.asList(ClassInfoHelper.fields(clazz)); + // else { + // fieldnames = tpo.getAskedFields(); + // } + // for(String fieldname: fieldnames){ + // if(data.getField(fieldname) == data.UNLOADED){ + // JDBCTransformer t = getTransformer(ClassInfoHelper + // .fieldType(clazz, fieldname)); + // Object value = t.getValue(rr, + // objectToDatabaseFieldName(fieldname)); + // setContext(context, value); + // data.setField(fieldname, value); + // } + // } + // tpo.getAskedFields().clear(); + // } + // }finally{ + // sta.close(); + // } + // } + // } + + /** + * Sauve tous les champs modifier et vide la liste des champs modifiée + * @param forceInsert si vrai fait un insert, sinon fait un update + */ + protected void saveData(Connection conn, TopiaContext context, + TopiaPersistenceObject tpo, boolean forceInsert) + throws SQLException, TopiaPersistenceException { + // TODO supprimer le time apres analyse des perfs + long time = System.currentTimeMillis(); + synchronized(tpo){ + Class clazz = tpo.getManagement().getEntityClass(); + String tablename = getTableName(clazz); + + Collection fieldnames = null; + if(forceInsert) { + fieldnames = Arrays.asList(ClassInfoHelper.fields(clazz)); + } else { + fieldnames = tpo.getModifiedFields(); + } + + String sql = null; + if(forceInsert){ + // rechercher les champs a inserer depuis le tpo pour construire + // la bonne requete + String fields = ""; + String qmarks = ""; + for(String fieldname: fieldnames){ + fields += ", " + objectToDatabaseFieldName(fieldname); + qmarks += ", ?"; + } + sql = MessageFormat.format(SQL.getProperty(SQL_INSERT_DATA), tablename, fields, qmarks); + } else { + // rechercher les champs a updater depuis le tpo pour construire + // la bonne requete + String fields = ""; + for(String fieldname: fieldnames){ + fields += ", " + objectToDatabaseFieldName(fieldname) + "=?"; + } + sql = MessageFormat.format(SQL.getProperty(SQL_UPDATE_DATA), tablename, fields); + } + PreparedStatement insert = conn.prepareStatement(sql); + try{ + int i = 1; + insert.setString(i++, tpo.getManagement().getId()); + insert.setLong(i++, tpo.getManagement().getDate()); + insert.setString(i++, clazz.getName()); + insert.setBoolean(i++, tpo.getManagement().isNew()); + insert.setBoolean(i++, tpo.getManagement().isDeleted()); + for(String fieldname: fieldnames){ + Object value = tpo.getData().get(fieldname); + JDBCTransformer t = getTransformer(ClassInfoHelper + .fieldType(clazz, fieldname)); + t.setValue(insert, i++, value); + } + + if(!forceInsert){ + insert.setString(i++, tpo.getManagement().getId()); + insert.setLong(i++, tpo.getManagement().getDate()); + } + + insert.execute(); + } finally { + insert.close(); + } + tpo.getModifiedFields().clear(); + } + time_saveData += System.currentTimeMillis() - time; + } + + /** + * Retourne tous les id de tous les objets encore existant dans le + * context de la transaction + */ + public Collection getAllId(TopiaContext context) throws TopiaPersistenceException { + List result = new ArrayList(); + try{ + Connection conn = getConnection(); + try{ + String sql = MessageFormat.format(SQL.getProperty(SQL_LOAD_ALL), "topia_mapping_table"); + PreparedStatement allTable = conn.prepareStatement(sql); + ResultSet rs = allTable.executeQuery(); + while (rs.next()){ + String classname = rs.getString("topia_class"); + String tablename = rs.getString("topia_table"); + + String query = MessageFormat.format(SQL.getProperty(SQL_GET_ALL_ID), tablename); + query = Util.setAllMultipleIds(query, context.getTransaction()); + PreparedStatement sta = conn.prepareStatement(query); + try{ + sta.setLong(1, -context.getTransaction().getId()); + sta.setLong(2, -context.getTransaction().getId()); + ResultSet rr = sta.executeQuery(); + while(rr.next()){ + result.add(rr.getString("topia_id")); + } + } finally { + sta.close(); + } + } + conn.commit(); + } finally { + conn.close(); + } + } catch(SQLException eee) { + throw new TopiaPersistenceException("Erreur durant la recherche des objets existant", eee); + } + return result; + } + + /** + * Permet d'indiquer au storage le debut d'une transaction + */ + public void beginTransaction(TopiaContext context) throws TopiaPersistenceException { + // On pourrait conservé l'id de la transaction, et lors d'appel + // d'autre methode verifier que cet Id est bien l'id d'une transation + // qui a un beginTransaction. + // Mais normalement seul le framework utilise le storage, donc si + // tout est bien fait, il n'y a pas besoin de verifier, ce qui peut + // faire gagne un peu de temps. + } + + /** + * Indique au storage de commiter la transaction passé en paramètre. La + * TopiaTransaction du Context est mise à jour automatiquement. + * @todo pour l'instant on commit tous les objets meme les nouveaux && supprimé + * pour optimiser un peu on pourrait ne pas les commiter. Ca demande un peu + * plus de travaille pour les data, car il faut voir si dans le management + * il sont supprimé on non. Ensuite, il faut supprimer avec les meme requete + * que rollback ces objets. + */ + public void commitTransaction(TopiaContext context) throws TopiaPersistenceException { + try{ + TopiaTransaction actualtt = context.getTransaction(); + TopiaTransaction newtt = context.getTransaction().regenerateTransaction(); + // peut-etre faire quelque chose comme dans beginTransaction ? + // Comme on a fait un regenerateTransaction, le parent a deja pris en compte le changement de transaction, alors il faut impérativement le valider (au cas où une erreur survienne) + context.setTransaction(newtt); + Connection conn = getConnection(); + try{ + String allTableSql = MessageFormat.format(SQL.getProperty(SQL_LOAD_ALL), "topia_mapping_table"); + PreparedStatement allTable = conn.prepareStatement(allTableSql); + ResultSet rs = allTable.executeQuery(); + while (rs.next()){ + String classname = rs.getString("topia_class"); + String tablename = rs.getString("topia_table"); + + String sql = MessageFormat.format(SQL.getProperty(SQL_COMMIT_DATA), tablename); + PreparedStatement sta = conn.prepareStatement(sql); + try{ + sta.setLong(1, newtt.getCommitId()); + sta.setLong(2, actualtt.getId()); + sta.execute(); + } finally { + sta.close(); + } + } + conn.commit(); + } finally { + if(!conn.isClosed()){ + conn.close(); + } + } + } catch(SQLException eee){ + throw new TopiaPersistenceException("Erreur durant le commit transaction: " + context.getTransaction().getId(), eee); + + } + } + + /** + * Indique au storage de faire un rollback de la transaction passé en + * paramètre. La Transaction du Context est mise à jour automatiquement. + */ + public void rollbackTransaction(TopiaContext context) throws TopiaPersistenceException { + try{ + Connection conn = getConnection(); + try{ + String allTableSql = MessageFormat.format(SQL.getProperty(SQL_LOAD_ALL), "topia_mapping_table"); + PreparedStatement allTable = conn.prepareStatement(allTableSql); + ResultSet rs = allTable.executeQuery(); + while (rs.next()){ + String classname = rs.getString("topia_class"); + String tablename = rs.getString("topia_table"); + + String sql = MessageFormat.format(SQL.getProperty(SQL_ROLLBACK_DATA), tablename); + PreparedStatement sta = conn.prepareStatement(sql); + try{ + sta.setLong(1, context.getTransaction().getId()); + sta.execute(); + } finally { + sta.close(); + } + } + conn.commit(); + } finally { + conn.close(); + } + // creation d'un nouvelle transation + context.setTransaction(context.getTransaction().regenerateTransaction()); + // peut-etre faire quelque chose comme dans beginTransaction ? + } catch(SQLException eee){ + throw new TopiaPersistenceException("Erreur durant le rollback transaction: " + context.getTransaction().getId(), eee); + } + } + + /** + * Indique si le storage implante une methode de recherche rapide + * @return vrai si la methode find est implanté + */ + public boolean haveFindImplemented(){ + return false; // FIXME losque find sera refait le mettre a true + } + + /** + * Permet de faire une recherche dans le storage dans le context de la + * transaction passée en argument. Si cette méthode n'est pas implantée + * il faut que haveFindImplemented return false, et que cette méthode + * leve l'exception UnsupportedOperationException + */ + public List find(TopiaContext context, TopiaQuery query) throws TopiaPersistenceException { + // FIXME a refaire entierement avec un TopiaJDBCMultiTableQueryHelper + // TopiaJDBCMultiTableQueryHelper queryHelper = new TopiaJDBCMultiTableQueryHelper(); + // try { + // queryHelper.setQuery(query.getQueryString()); + // queryHelper.setArgs(query.getArgs()); + // } catch (IOException eee) { + // throw new TopiaPersistenceException("Creation de la requete impossible, requete source mal formée", eee); + // } + // List results = new ArrayList(); + // try { + // String preparedQuery = queryHelper.getPreparedQuery(context.getTransaction()); + // try { + // Connection conn = getConnection(); + // try{ + // PreparedStatement sta = conn.prepareStatement(preparedQuery); + // try{ + // int i = 1; + // for (Object o : queryHelper.getArgs()) { + // if(o instanceof TopiaEntity){ + // try { + // sta.setObject(i, ((TopiaEntity)o).get_topiaId_()); + // } catch (TopiaException eee) { + // log.error(o + " is a TopiaEntity but has no method called get_topiaId_()"); + // sta.setObject(i, o); + // } + // }else{ + // sta.setObject(i, o); + // } + // i++; + // } + // ResultSet rr = sta.executeQuery(); + // while(rr.next()){ + // results.add(rr.getString("id")); + // } + // } finally { + // sta.close(); + // } + // conn.rollback(); //On est dans un find, pas de modif !!! + // } finally { + // if (!conn.isClosed()) //Si exception il y a eu, la connection est potentiellement fermée + // conn.close(); + // } + // } catch (SQLException eee) { + // throw new TopiaPersistenceException("Erreur durant le find transaction : " + context.getTransaction().getId() + " query: "+ preparedQuery, eee); + // } + // } catch (QueryHelperException eee) { + // throw new TopiaPersistenceException("Execution de la requete impossible, requete source mal formée", eee); + // } + // return results; + return null; + } + + /** + * Retourne l'historique de l'objet ayant l'id passé en paramètre + */ + public List getHistory(TopiaContext context, String id) throws TopiaPersistenceException { + // TODO + return new ArrayList(); + } + + /** + * Nettoie l'historique pour ne conserver qu'une profondeur d'historique + * egal à depth. Doit prendre en compte le fait que toutes les transactions + * en cours doit avoir toujours au moins la vision convenable de toutes + * les entités. + */ + public void cleanHistory(int depth) throws TopiaPersistenceException { + // TODO + // Pour tous les objets plus vieux que la derniere transaction + // - supprimer tous les objets effacés + // delete * from management where isDeleted=true and longdate>0 + // and longdate < + // (select min(longdate) from management where longdate < 0); + // - ne garder que les n(depth) dernier enregistrement de chaque id. + // for(String id: getAllId()){ + // ResultSet rs = SQL_LOAD_HISTORY(id); + // for(int i=0; i 0 and date < rs.longdate; + } + + /** + * Permet de savoir si un id existe encore dans le context de la transaction + */ + public boolean exists(TopiaContext context, String id) throws TopiaPersistenceException { + boolean result = false; + try{ + Connection conn = getConnection(); + try{ + Class clazz = TopiaId.getClassName(id); + String tablename = getTableName(clazz); + + String query = MessageFormat.format(SQL.getProperty(SQL_LOAD_HISTORY), tablename); + query = Util.setAllMultipleIds(query, context.getTransaction()); + PreparedStatement sta = conn.prepareStatement(query); + try{ + sta.setString(1, id); + sta.setLong(2, -context.getTransaction().getId()); + ResultSet rr = sta.executeQuery(); + if(rr.next()){ + result = !rr.getBoolean("topia_isDeleted"); + } + } finally { + sta.close(); + } + conn.commit(); + } finally { + conn.close(); + } + } catch(TopiaNotFoundException eee){ + throw new TopiaPersistenceException("Impossible de retrouver la classe de l'identifiant: " + id, eee); + } catch(SQLException eee){ + throw new TopiaPersistenceException("Erreur durant le test d'existance de l'entite: " + id, eee); + } + return result; + } + +} // PersistenceStorageJDBCMultiTable +