X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=source%2Fmir%2Fstorage%2FDatabase.java;h=0f886dbb41a72934d309157a6d19ce7995782702;hb=e44404fac09c8da04b5ef7874160cb91f8fc98a9;hp=c0caf6d70938548a3e3e7b2f8d0457906854d0c5;hpb=462a3b8839a8a841351adae553e9fa7b1f2d219c;p=mir.git diff --git a/source/mir/storage/Database.java b/source/mir/storage/Database.java index c0caf6d7..0f886dbb 100755 --- a/source/mir/storage/Database.java +++ b/source/mir/storage/Database.java @@ -29,7 +29,9 @@ */ package mir.storage; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.sql.Connection; import java.sql.PreparedStatement; @@ -48,10 +50,9 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TimeZone; -import java.util.Vector; import mir.config.MirPropertiesConfiguration; -import mir.config.MirPropertiesConfiguration.PropertiesConfigExc; +import mir.entity.AbstractEntity; import mir.entity.Entity; import mir.entity.EntityList; import mir.entity.StorableObjectEntity; @@ -63,68 +64,42 @@ import mir.storage.store.StoreContainerType; import mir.storage.store.StoreIdentifier; import mir.storage.store.StoreUtil; import mir.util.JDBCStringRoutines; +import mircoders.global.MirGlobal; -import com.codestudio.util.SQLManager; - +import org.apache.commons.dbcp.DelegatingConnection; +import org.postgresql.PGConnection; +import org.postgresql.largeobject.LargeObject; +import org.postgresql.largeobject.LargeObjectManager; /** - * Diese Klasse implementiert die Zugriffsschicht auf die Datenbank. - * Alle Projektspezifischen Datenbankklassen erben von dieser Klasse. - * In den Unterklassen wird im Minimalfall nur die Tabelle angegeben. - * Im Konfigurationsfile findet sich eine Verweis auf den verwendeten - * Treiber, Host, User und Passwort, ueber den der Zugriff auf die - * Datenbank erfolgt. + * Implements database access. * - * @version $Id: Database.java,v 1.44.2.11 2003/11/24 23:37:18 rk Exp $ + * @version $Id: Database.java,v 1.44.2.27 2005/02/10 16:22:33 rhindes Exp $ * @author rk * */ -public class Database implements StorageObject { +public class Database { private static Class GENERIC_ENTITY_CLASS = mir.entity.StorableObjectEntity.class; - private static Class STORABLE_OBJECT_ENTITY_CLASS = mir.entity.StorableObjectEntity.class; - - - private static Map POPUP_EMPTYLINE = new HashMap(); protected static final ObjectStore o_store = ObjectStore.getInstance(); private static final int _millisPerHour = 60 * 60 * 1000; - private static final int _millisPerMinute = 60 * 1000; - - static { - // always same object saves a little space - POPUP_EMPTYLINE.put("key", ""); - POPUP_EMPTYLINE.put("value", "--"); - } protected LoggerWrapper logger; + protected MirPropertiesConfiguration configuration; - protected String theTable; - protected String theCoreTable = null; - protected String thePKeyName = "id"; - protected int thePKeyType; - protected int thePKeyIndex; - protected boolean evaluatedMetaData = false; - protected ArrayList metadataFields; - protected ArrayList metadataLabels; - protected ArrayList metadataNotNullFields; - protected int[] metadataTypes; - protected Class theEntityClass; - protected List popupCache = null; - protected boolean hasPopupCache = false; - protected Map hashCache = null; - protected boolean hasTimestamp = true; - private String database_driver; - private String database_url; + protected String mainTable; + protected String primaryKeySequence = null; + protected String primaryKeyField = "id"; + + protected List fieldNames; + protected int[] fieldTypes; + protected Map fieldNameToType; + + protected Class entityClass; private int defaultLimit; TimeZone timezone; SimpleDateFormat internalDateFormat; SimpleDateFormat userInputDateFormat; -/* - private SimpleDateFormat _dateFormatterOut; - private SimpleDateFormat _dateFormatterIn; - _dateFormatterOut = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - _dateFormatterIn = new SimpleDateFormat("yyyy-MM-dd HH:mm"); -*/ /** * Kontruktor bekommt den Filenamen des Konfigurationsfiles ?bergeben. @@ -133,16 +108,9 @@ public class Database implements StorageObject { * Database.Host und Database.Adaptor * ausgelesen und ein Broker f?r die Verbindugen zur Datenbank * erzeugt. - * - * @param String confFilename Dateiname der Konfigurationsdatei */ public Database() throws StorageObjectFailure { - try { - configuration = MirPropertiesConfiguration.instance(); - } - catch (PropertiesConfigExc e) { - throw new StorageObjectFailure(e); - } + configuration = MirPropertiesConfiguration.instance(); logger = new LoggerWrapper("Database"); timezone = TimeZone.getTimeZone(configuration.getString("Mir.DefaultTimezone")); internalDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @@ -151,12 +119,11 @@ public class Database implements StorageObject { userInputDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); userInputDateFormat.setTimeZone(timezone); - String theAdaptorName = configuration.getString("Database.Adaptor"); defaultLimit = Integer.parseInt(configuration.getString("Database.Limit")); try { - theEntityClass = GENERIC_ENTITY_CLASS; + entityClass = GENERIC_ENTITY_CLASS; } catch (Throwable e) { logger.error("Error in Database() constructor with " + theAdaptorName + " -- " + e.getMessage()); @@ -164,15 +131,20 @@ public class Database implements StorageObject { } } - /** - * Liefert die Entity-Klasse zur?ck, in der eine Datenbankzeile gewrappt - * wird. Wird die Entity-Klasse durch die erbende Klasse nicht ?berschrieben, - * wird eine mir.entity.GenericEntity erzeugt. - * - * @return Class-Objekt der Entity - */ public java.lang.Class getEntityClass() { - return theEntityClass; + return entityClass; + } + + public Entity createNewEntity() throws StorageObjectFailure { + try { + AbstractEntity result = (AbstractEntity) entityClass.newInstance(); + result.setStorage(this); + + return result; + } + catch (Throwable t) { + throw new StorageObjectFailure(t); + } } /** @@ -185,13 +157,8 @@ public class Database implements StorageObject { return defaultLimit; } - /** - * Liefert den Namen des Primary-Keys zur?ck. Wird die Variable nicht von - * der erbenden Klasse ?berschrieben, so ist der Wert PKEY - * @return Name des Primary-Keys - */ - public String getIdName() { - return thePKeyName; + public String getIdFieldName() { + return primaryKeyField; } /** @@ -200,69 +167,39 @@ public class Database implements StorageObject { * @return Name der Tabelle */ public String getTableName() { - return theTable; - } - - /* - * Dient dazu vererbte Tabellen bei objectrelationalen DBMS - * zu speichern, wenn die id einer Tabelle in der parenttabelle verwaltet - * wird. - * @return liefert theCoreTabel als String zurueck, wenn gesetzt, sonst - * the Table - */ - public String getCoreTable() { - if (theCoreTable != null) { - return theCoreTable; - } - else { - return theTable; - } + return mainTable; } /** - * Liefert Feldtypen der Felder der Tabelle zurueck (s.a. java.sql.Types) - * @return int-Array mit den Typen der Felder - * @exception StorageObjectException + * Returns the id that was most recently added to the database */ - public int[] getTypes() throws StorageObjectFailure { - if (metadataTypes == null) { - get_meta_data(); - } + private String getLatestInsertedId(Connection aConnection) throws SQLException { + if (primaryKeySequence==null) + primaryKeySequence = mainTable+"_id_seq"; - return metadataTypes; - } + PreparedStatement statement = aConnection.prepareStatement("select currval('" + primaryKeySequence + "')"); - /** - * Liefert eine Liste der Labels der Tabellenfelder - * @return ArrayListe mit Labeln - * @exception StorageObjectException - */ - public List getLabels() throws StorageObjectFailure { - if (metadataLabels == null) { - get_meta_data(); - } - - return metadataLabels; + ResultSet rs = statement.executeQuery(); + rs.next(); + return rs.getString(1); } /** - * Liefert eine Liste der Felder der Tabelle - * @return ArrayList mit Feldern - * @exception StorageObjectException + * Returns a list of field names for this Database */ - public List getFields() throws StorageObjectFailure { - if (metadataFields == null) { - get_meta_data(); + public List getFieldNames() throws StorageObjectFailure { + if (fieldNames == null) { + acquireMetaData(); } - return metadataFields; + return fieldNames; } /** * Gets value out of ResultSet according to type and converts to String * @param rs ResultSet. * @param aType a type from java.sql.Types.* - * @param index index in ResultSet + * @param valueIndex index in ResultSet * @return returns the value as String. If no conversion is possible * /unsupported value/ is returned */ @@ -293,7 +230,7 @@ public class Database implements StorageObject { case java.sql.Types.NUMERIC: - /** @todo Numeric can be float or double depending upon + /** todo Numeric can be float or double depending upon * metadata.getScale() / especially with oracle */ long outl = rs.getLong(valueIndex); @@ -401,14 +338,14 @@ public class Database implements StorageObject { } // ask object store for object - if (StoreUtil.implementsStorableObject(theEntityClass)) { + if (StoreUtil.extendsStorableEntity(entityClass)) { String uniqueId = id; - if (theEntityClass.equals(StorableObjectEntity.class)) { - uniqueId += ("@" + theTable); + if (entityClass.equals(StorableObjectEntity.class)) { + uniqueId += ("@" + mainTable); } - StoreIdentifier search_sid = new StoreIdentifier(theEntityClass, uniqueId); + StoreIdentifier search_sid = new StoreIdentifier(entityClass, uniqueId); logger.debug("CACHE: (dbg) looking for sid " + search_sid.toString()); Entity hit = (Entity) o_store.use(search_sid); @@ -419,34 +356,30 @@ public class Database implements StorageObject { } Statement stmt = null; - Connection con = getPooledCon(); + Connection con = obtainConnection(); Entity returnEntity = null; try { ResultSet rs; - /** @todo better prepared statement */ + /** todo better prepared statement */ String selectSql = - "select * from " + theTable + " where " + thePKeyName + "=" + id; + "select * from " + mainTable + " where " + primaryKeyField + "=" + id; stmt = con.createStatement(); rs = executeSql(stmt, selectSql); if (rs != null) { - if (evaluatedMetaData == false) { - evalMetaData(rs.getMetaData()); - } - if (rs.next()) { returnEntity = makeEntityFromResultSet(rs); } else { - logger.debug("No data for id: " + id + " in table " + theTable); + logger.warn("No data for id: " + id + " in table " + mainTable); } rs.close(); } else { - logger.debug("No Data for Id " + id + " in Table " + theTable); + logger.warn("No Data for Id " + id + " in Table " + mainTable); } } catch (SQLException sqe) { @@ -465,7 +398,7 @@ public class Database implements StorageObject { /** * This method makes it possible to make selects across multiple tables - * + * * @param mainTablePrefix prefix for the mainTable * @param extraTables a vector of tables for relational select * @param aWhereClause whereClause @@ -473,40 +406,27 @@ public class Database implements StorageObject { * @throws StorageObjectFailure */ - public EntityList selectByWhereClauseWithExtraTables(String mainTablePrefix, + public EntityList selectByWhereClauseWithExtraTables(String mainTablePrefix, List extraTables, String aWhereClause ) throws StorageObjectFailure { - return selectByWhereClause( mainTablePrefix, extraTables, aWhereClause, "", 0, -1); + return selectByWhereClause( mainTablePrefix, extraTables, aWhereClause, "", 0, defaultLimit); } - /** - * select-Operator um Datensaetze zu bekommen, die key = value erfuellen. - * @param key Datenbankfeld der Bedingung. - * @param value Wert die der key anehmen muss. - * @return EntityList mit den gematchten Entities - */ public EntityList selectByFieldValue(String aField, String aValue) throws StorageObjectFailure { return selectByFieldValue(aField, aValue, 0); } - /** - * select-Operator um Datensaetze zu bekommen, die key = value erfuellen. - * @param key Datenbankfeld der Bedingung. - * @param value Wert die der key anehmen muss. - * @param offset Gibt an ab welchem Datensatz angezeigt werden soll. - * @return EntityList mit den gematchten Entities - */ public EntityList selectByFieldValue(String aField, String aValue, int offset) throws StorageObjectFailure { - return selectByWhereClause(aField + "=" + aValue, offset); + return selectByWhereClause(aField + "='" + JDBCStringRoutines.escapeStringLiteral(aValue)+"'", offset); } /** * select-Operator liefert eine EntityListe mit den gematchten Datens?tzen zur?ck. * Also offset wird der erste Datensatz genommen. * - * @param wc where-Clause + * @param where where-Clause * @return EntityList mit den gematchten Entities - * @exception StorageObjectException + * @exception StorageObjectFailure */ public EntityList selectByWhereClause(String where) throws StorageObjectFailure { return selectByWhereClause(where, 0); @@ -516,10 +436,10 @@ public class Database implements StorageObject { * select-Operator liefert eine EntityListe mit den gematchten Datens?tzen zur?ck. * Als maximale Anzahl wird das Limit auf der Konfiguration genommen. * - * @param wc where-Clause + * @param whereClause where-Clause * @param offset ab welchem Datensatz. * @return EntityList mit den gematchten Entities - * @exception StorageObjectException + * @exception StorageObjectFailure */ public EntityList selectByWhereClause(String whereClause, int offset) throws StorageObjectFailure { return selectByWhereClause(whereClause, null, offset); @@ -530,24 +450,28 @@ public class Database implements StorageObject { * Also offset wird der erste Datensatz genommen. * Als maximale Anzahl wird das Limit auf der Konfiguration genommen. * - * @param wc where-Clause - * @param ob orderBy-Clause + * @param where where-Clause + * @param order orderBy-Clause * @return EntityList mit den gematchten Entities - * @exception StorageObjectException + * @exception StorageObjectFailure */ public EntityList selectByWhereClause(String where, String order) throws StorageObjectFailure { return selectByWhereClause(where, order, 0); } + public EntityList selectByWhereClause(String mainTablePrefix, List extraTables, String where, String order) throws StorageObjectFailure { + return selectByWhereClause(mainTablePrefix, extraTables, where, order, 0, defaultLimit); + } + /** * select-Operator liefert eine EntityListe mit den gematchten Datens?tzen zur?ck. * Als maximale Anzahl wird das Limit auf der Konfiguration genommen. * - * @param wc where-Clause - * @param ob orderBy-Clause + * @param whereClause where-Clause + * @param orderBy orderBy-Clause * @param offset ab welchem Datensatz * @return EntityList mit den gematchten Entities - * @exception StorageObjectException + * @exception StorageObjectFailure */ public EntityList selectByWhereClause(String whereClause, String orderBy, int offset) throws StorageObjectFailure { return selectByWhereClause(whereClause, orderBy, offset, defaultLimit); @@ -560,59 +484,65 @@ public class Database implements StorageObject { * @param offset ab welchem Datensatz * @param limit wieviele Datens?tze * @return EntityList mit den gematchten Entities - * @exception StorageObjectException + * @exception StorageObjectFailure */ public EntityList selectByWhereClause(String aWhereClause, String anOrderByClause, int offset, int limit) throws StorageObjectFailure { - return selectByWhereClause("", null, aWhereClause, anOrderByClause, offset, limit); + return selectByWhereClause("", null, aWhereClause, anOrderByClause, offset, limit); } + /** * select-Operator returns EntityList with matching rows in Database. * @param aWhereClause where-Clause * @param anOrderByClause orderBy-Clause - * @param offset ab welchem Datensatz - * @param limit wieviele Datens?tze + * @param anOffset ab welchem Datensatz + * @param aLimit wieviele Datens?tze * @return EntityList mit den gematchten Entities - * @exception StorageObjectException + * @exception StorageObjectFailure */ - public EntityList selectByWhereClause(String mainTablePrefix, List extraTables, + public EntityList selectByWhereClause( + String aMainTablePrefix, List anExtraTables, String aWhereClause, String anOrderByClause, - int offset, int limit) throws StorageObjectFailure { - - - String useTable = theTable; - String selectStar = "*"; - if (mainTablePrefix!=null && mainTablePrefix.trim().length()>0) { - useTable+=" "+mainTablePrefix; - selectStar=mainTablePrefix.trim() + ".*"; + int anOffset, int aLimit) throws StorageObjectFailure { + + // TODO get rid of emtpy Strings in anExtraTables + // make anExtraTables null, if single empty String in it + // cause StringUtil.splitString puts in emptyString + + if (anExtraTables!=null && ((String) anExtraTables.get(0)).trim().equals("")){ + anExtraTables=null; } - + + String useTable = mainTable; + String selection = "*"; + + if (aMainTablePrefix != null && aMainTablePrefix.trim().length() > 0) { + useTable += " " + aMainTablePrefix; + selection = aMainTablePrefix.trim() + ".*"; + } + // check o_store for entitylist // only if no relational select - if (extraTables==null) { - if (StoreUtil.implementsStorableObject(theEntityClass)) { - StoreIdentifier search_sid = - new StoreIdentifier( - theEntityClass, StoreContainerType.STOC_TYPE_ENTITYLIST, - StoreUtil.getEntityListUniqueIdentifierFor(useTable, aWhereClause, anOrderByClause, offset, limit)); - EntityList hit = (EntityList) o_store.use(search_sid); - - if (hit != null) { - logger.debug("CACHE (hit): " + search_sid.toString()); - - return hit; - } + if (anExtraTables==null) { + if (StoreUtil.extendsStorableEntity(entityClass)) { + StoreIdentifier searchSid = new StoreIdentifier(entityClass, + StoreContainerType.STOC_TYPE_ENTITYLIST, + StoreUtil.getEntityListUniqueIdentifierFor(mainTable, + aWhereClause, anOrderByClause, anOffset, aLimit)); + EntityList hit = (EntityList) o_store.use(searchSid); + + if (hit != null) { + return hit; + } } } // local EntityList theReturnList = null; - Connection con = null; - Statement stmt = null; - ResultSet rs; - int offsetCount = 0; - int count = 0; + Connection connection = null; + Statement statement = null; + ResultSet resultSet; // build sql-statement @@ -620,95 +550,65 @@ public class Database implements StorageObject { aWhereClause = null; } - StringBuffer countSql = - new StringBuffer("select count(*) from ").append(useTable); StringBuffer selectSql = - new StringBuffer("select "+selectStar+" from ").append(useTable); - + new StringBuffer("select "+selection+" from ").append(useTable); + // append extratables, if necessary - if (extraTables!=null) { - for (int i=0;i < extraTables.size();i++) { - countSql.append( ", " + extraTables.get(i)); - selectSql.append( ", " + extraTables.get(i)); + if (anExtraTables!=null) { + for (int i=0;i < anExtraTables.size();i++) { + if (!anExtraTables.get(i).equals("")) { + selectSql.append( ", " + anExtraTables.get(i)); + } } } - + if (aWhereClause != null) { selectSql.append(" where ").append(aWhereClause); - countSql.append(" where ").append(aWhereClause); } if ((anOrderByClause != null) && !(anOrderByClause.trim().length() == 0)) { selectSql.append(" order by ").append(anOrderByClause); } - if ((limit > -1) && (offset > -1)) { - selectSql.append(" LIMIT ").append(limit).append(" OFFSET ").append(offset); + if ((aLimit > -1) && (anOffset > -1)) { + selectSql.append(" LIMIT ").append(aLimit+1).append(" OFFSET ").append(anOffset); } // execute sql try { - con = getPooledCon(); - stmt = con.createStatement(); + connection = obtainConnection(); + statement = connection.createStatement(); + boolean hasMore = false; // selecting... - rs = executeSql(stmt, selectSql.toString()); - - if (rs != null) { - if (!evaluatedMetaData) { - evalMetaData(rs.getMetaData()); - } + resultSet = executeSql(statement, selectSql.toString()); + if (resultSet != null) { theReturnList = new EntityList(); Entity theResultEntity; - while (rs.next()) { - theResultEntity = makeEntityFromResultSet(rs); + int position = 0; + while (((aLimit == -1) || (position -1) && (offset > -1)) { - if (offsetCount == limit) { - rs = executeSql(stmt, countSql.toString()); - - if (rs != null) { - if (rs.next()) { - count = rs.getInt(1); - } - - rs.close(); - } - else { - logger.error("Could not count: " + countSql); - } - } - } - - theReturnList.setCount(count); - theReturnList.setOffset(offset); + theReturnList.setOffset(anOffset); theReturnList.setWhere(aWhereClause); theReturnList.setOrder(anOrderByClause); theReturnList.setStorage(this); - theReturnList.setLimit(limit); + theReturnList.setLimit(aLimit); - if (offset >= limit) { - theReturnList.setPrevBatch(offset - limit); + if (hasMore) { + theReturnList.setNextBatch(anOffset + aLimit); } - if ((offset + offsetCount) < count) { - theReturnList.setNextBatch(offset + limit); - } - - if (extraTables==null && StoreUtil.implementsStorableObject(theEntityClass)) { + if (anExtraTables==null && StoreUtil.extendsStorableEntity(entityClass)) { StoreIdentifier sid = theReturnList.getStoreIdentifier(); logger.debug("CACHE (add): " + sid.toString()); o_store.add(sid); @@ -720,8 +620,8 @@ public class Database implements StorageObject { } finally { try { - if (con != null) { - freeConnection(con, stmt); + if (connection != null) { + freeConnection(connection, statement); } } catch (Throwable t) { } @@ -730,28 +630,26 @@ public class Database implements StorageObject { return theReturnList; } - /** - * Bastelt aus einer Zeile der Datenbank ein EntityObjekt. - * - * @param rs Das ResultSetObjekt. - * @return Entity Die Entity. - */ private Entity makeEntityFromResultSet(ResultSet rs) throws StorageObjectFailure { - /** @todo OS: get Pkey from ResultSet and consult ObjectStore */ Map theResultHash = new HashMap(); String theResult = null; - int theType; + int type; Entity returnEntity = null; try { - int size = metadataFields.size(); + if (StoreUtil.extendsStorableEntity(entityClass)) { + StoreIdentifier searchSid = StorableObjectEntity.getStoreIdentifier(this, + entityClass, rs); + Entity hit = (Entity) o_store.use(searchSid); + if (hit != null) return hit; + } - for (int i = 0; i < size; i++) { + for (int i = 0; i < getFieldNames().size(); i++) { // alle durchlaufen bis nix mehr da - theType = metadataTypes[i]; + type = fieldTypes[i]; - if (theType == java.sql.Types.LONGVARBINARY) { + if (type == java.sql.Types.LONGVARBINARY) { InputStreamReader is = (InputStreamReader) rs.getCharacterStream(i + 1); @@ -766,40 +664,35 @@ public class Database implements StorageObject { is.close(); theResult = theResultString.toString(); - } else { + } + else { theResult = null; } - } else { - theResult = getValueAsString(rs, (i + 1), theType); + } + else { + theResult = getValueAsString(rs, (i + 1), type); } if (theResult != null) { - theResultHash.put(metadataFields.get(i), theResult); + theResultHash.put(getFieldNames().get(i), theResult); } } - if (theEntityClass != null) { - returnEntity = (Entity) theEntityClass.newInstance(); - returnEntity.setStorage(this); - returnEntity.setValues(theResultHash); + if (entityClass != null) { + returnEntity = createNewEntity(); + returnEntity.setFieldValues(theResultHash); if (returnEntity instanceof StorableObject) { - logger.debug("CACHE: ( in) " + returnEntity.getId() + " :" + theTable); + logger.debug("CACHE: ( in) " + returnEntity.getId() + " :" + mainTable); o_store.add(((StorableObject) returnEntity).getStoreIdentifier()); } } else { - throwStorageObjectException("Internal Error: theEntityClass not set!"); + throwStorageObjectException("Internal Error: entityClass not set!"); } } - catch (IllegalAccessException e) { - throwStorageObjectException("No access! -- " + e.getMessage()); - } catch (IOException e) { throwStorageObjectException("IOException! -- " + e.getMessage()); } - catch (InstantiationException e) { - throwStorageObjectException("No Instatiation! -- " + e.getMessage()); - } catch (SQLException sqe) { throwSQLException(sqe, "makeEntityFromResultSet"); @@ -812,27 +705,17 @@ public class Database implements StorageObject { /** * Inserts an entity into the database. * - * @param theEntity + * @param anEntity * @return der Wert des Primary-keys der eingef?gten Entity */ - public String insert(Entity theEntity) throws StorageObjectFailure { - //cache - invalidatePopupCache(); - - // invalidating all EntityLists corresponding with theEntityClass - if (StoreUtil.implementsStorableObject(theEntityClass)) { - StoreContainerType stoc_type = - StoreContainerType.valueOf(theEntityClass, - StoreContainerType.STOC_TYPE_ENTITYLIST); - o_store.invalidate(stoc_type); - } + public String insert(Entity anEntity) throws StorageObjectFailure { + invalidateStore(); String returnId = null; Connection con = null; PreparedStatement pstmt = null; try { - List streamedInput = theEntity.streamedInput(); StringBuffer f = new StringBuffer(); StringBuffer v = new StringBuffer(); String aField; @@ -840,29 +723,24 @@ public class Database implements StorageObject { boolean firstField = true; // make sql-string - for (int i = 0; i < getFields().size(); i++) { - aField = (String) getFields().get(i); + for (int i = 0; i < getFieldNames().size(); i++) { + aField = (String) getFieldNames().get(i); - if (!aField.equals(thePKeyName)) { + if (!aField.equals(primaryKeyField)) { aValue = null; // exceptions - if (!theEntity.hasValueForField(aField) && ( + if (!anEntity.hasFieldValue(aField) && ( aField.equals("webdb_create") || aField.equals("webdb_lastchange"))) { aValue = "NOW()"; } else { - if ((streamedInput != null) && streamedInput.contains(aField)) { - aValue = "?"; - } - else { - if (theEntity.hasValueForField(aField)) { + if (anEntity.hasFieldValue(aField)) { aValue = "'" + - JDBCStringRoutines.escapeStringLiteral((String) theEntity.getValue(aField)) + "'"; + JDBCStringRoutines.escapeStringLiteral(anEntity.getFieldValue(aField)) + "'"; } - } } // wenn Wert gegeben, dann einbauen @@ -884,23 +762,15 @@ public class Database implements StorageObject { // insert into db StringBuffer sqlBuf = - new StringBuffer("insert into ").append(theTable).append("(").append(f) + new StringBuffer("insert into ").append(mainTable).append("(").append(f) .append(") values (").append(v).append(")"); String sql = sqlBuf.toString(); - logger.debug("INSERT: " + sql); - con = getPooledCon(); + logQueryBefore(sql); + con = obtainConnection(); con.setAutoCommit(false); pstmt = con.prepareStatement(sql); - if (streamedInput != null) { - for (int i = 0; i < streamedInput.size(); i++) { - String inputString = - (String) theEntity.getValue((String) streamedInput.get(i)); - pstmt.setBytes(i + 1, inputString.getBytes()); - } - } - int ret = pstmt.executeUpdate(); if (ret == 0) { @@ -908,12 +778,10 @@ public class Database implements StorageObject { return null; } - pstmt = con.prepareStatement("select currval('" + getCoreTable() + "_id_seq')"); +// pstmt = con.prepareStatement("select currval('" + + "_id_seq')"); - ResultSet rs = pstmt.executeQuery(); - rs.next(); - returnId = rs.getString(1); - theEntity.setId(returnId); + returnId = getLatestInsertedId(con); + anEntity.setId(returnId); } catch (SQLException sqe) { throwSQLException(sqe, "insert"); @@ -928,7 +796,7 @@ public class Database implements StorageObject { freeConnection(con, pstmt); } - /** @todo store entity in o_store */ + /** todo store entity in o_store */ return returnId; } @@ -941,41 +809,34 @@ public class Database implements StorageObject { Connection con = null; PreparedStatement pstmt = null; - /** @todo this is stupid: why do we prepare statement, when we + /** todo this is stupid: why do we prepare statement, when we * throw it away afterwards. should be regular statement * update/insert could better be one routine called save() * that chooses to either insert or update depending if we * have a primary key in the entity. i don't know if we * still need the streamed input fields. // rk */ - /** @todo extension: check if Entity did change, otherwise we don't need + + /** todo extension: check if Entity did change, otherwise we don't need * the roundtrip to the database */ /** invalidating corresponding entitylists in o_store*/ - if (StoreUtil.implementsStorableObject(theEntityClass)) { - StoreContainerType stoc_type = - StoreContainerType.valueOf(theEntityClass, - StoreContainerType.STOC_TYPE_ENTITYLIST); - o_store.invalidate(stoc_type); - } - List streamedInput = theEntity.streamedInput(); + invalidateStore(); + String id = theEntity.getId(); String aField; StringBuffer fv = new StringBuffer(); boolean firstField = true; - //cache - invalidatePopupCache(); - // build sql statement - for (int i = 0; i < getFields().size(); i++) { - aField = (String) metadataFields.get(i); + for (int i = 0; i < getFieldNames().size(); i++) { + aField = (String) getFieldNames().get(i); // only normal cases - if ( !(aField.equals(thePKeyName) || + // todo if entity.hasFieldValue returns false, then the value should be stored as null + if (!(aField.equals(primaryKeyField) || aField.equals("webdb_create") || - aField.equals("webdb_lastchange") || - ((streamedInput != null) && streamedInput.contains(aField)))) { - if (theEntity.hasValueForField(aField)) { + aField.equals("webdb_lastchange"))) { + if (theEntity.hasFieldValue(aField)) { if (firstField == false) { fv.append(", "); } @@ -983,28 +844,28 @@ public class Database implements StorageObject { firstField = false; } - fv.append(aField).append("='").append(JDBCStringRoutines.escapeStringLiteral((String) theEntity.getValue(aField))).append("'"); + fv.append(aField).append("='").append(JDBCStringRoutines.escapeStringLiteral(theEntity.getFieldValue(aField))).append("'"); - // fv.append(aField).append("='").append(StringUtil.quote((String)theEntity.getValue(aField))).append("'"); + // fv.append(aField).append("='").append(StringUtil.quote((String)theEntity.getFieldValue(aField))).append("'"); } } } StringBuffer sql = - new StringBuffer("update ").append(theTable).append(" set ").append(fv); + new StringBuffer("update ").append(mainTable).append(" set ").append(fv); // exceptions - if (metadataFields.contains("webdb_lastchange")) { + if (getFieldNames().contains("webdb_lastchange")) { sql.append(",webdb_lastchange=NOW()"); } // special case: the webdb_create requires the field in yyyy-mm-dd HH:mm // format so anything extra will be ignored. -mh - if (metadataFields.contains("webdb_create") && - theEntity.hasValueForField("webdb_create")) { + if (getFieldNames().contains("webdb_create") && + theEntity.hasFieldValue("webdb_create")) { // minimum of 10 (yyyy-mm-dd)... - if (theEntity.getValue("webdb_create").length() >= 10) { - String dateString = theEntity.getValue("webdb_create"); + if (theEntity.getFieldValue("webdb_create").length() >= 10) { + String dateString = theEntity.getFieldValue("webdb_create"); // if only 10, then add 00:00 so it doesn't throw a ParseException if (dateString.length() == 10) { @@ -1023,28 +884,14 @@ public class Database implements StorageObject { } } - if (streamedInput != null) { - for (int i = 0; i < streamedInput.size(); i++) { - sql.append(",").append(streamedInput.get(i)).append("=?"); - } - } - sql.append(" where id=").append(id); - logger.debug("UPDATE: " + sql); + logQueryBefore(sql.toString()); try { - con = getPooledCon(); + con = obtainConnection(); con.setAutoCommit(false); pstmt = con.prepareStatement(sql.toString()); - if (streamedInput != null) { - for (int i = 0; i < streamedInput.size(); i++) { - String inputString = - theEntity.getValue((String) streamedInput.get(i)); - pstmt.setBytes(i + 1, inputString.getBytes()); - } - } - pstmt.executeUpdate(); } catch (SQLException sqe) { @@ -1055,7 +902,8 @@ public class Database implements StorageObject { con.setAutoCommit(true); } catch (Exception e) { - ; + + } freeConnection(con, pstmt); @@ -1068,68 +916,62 @@ public class Database implements StorageObject { * @return boolean liefert true zurueck, wenn loeschen erfolgreich war. */ public boolean delete(String id) throws StorageObjectFailure { - invalidatePopupCache(); - // ostore send notification - if (StoreUtil.implementsStorableObject(theEntityClass)) { + if (StoreUtil.extendsStorableEntity(entityClass)) { String uniqueId = id; - if (theEntityClass.equals(StorableObjectEntity.class)) { - uniqueId += ("@" + theTable); + if (entityClass.equals(StorableObjectEntity.class)) { + uniqueId += ("@" + mainTable); } logger.debug("CACHE: (del) " + id); StoreIdentifier search_sid = - new StoreIdentifier(theEntityClass, + new StoreIdentifier(entityClass, StoreContainerType.STOC_TYPE_ENTITY, uniqueId); o_store.invalidate(search_sid); } - /** @todo could be prepared Statement */ + /** todo could be prepared Statement */ Statement stmt = null; Connection con = null; int res = 0; String sql = - "delete from " + theTable + " where " + thePKeyName + "='" + id + "'"; + "delete from " + mainTable + " where " + primaryKeyField + "='" + id + "'"; - //theLog.printInfo("DELETE " + sql); + logQueryBefore(sql); try { - con = getPooledCon(); + con = obtainConnection(); stmt = con.createStatement(); res = stmt.executeUpdate(sql); - } catch (SQLException sqe) { + } + catch (SQLException sqe) { throwSQLException(sqe, "delete"); - } finally { + } + finally { freeConnection(con, stmt); } + invalidateStore(); + return (res > 0) ? true : false; } /** * Deletes entities based on a where clause - * - * @param aWhereClause - * @return - * @throws StorageObjectFailure */ public int deleteByWhereClause(String aWhereClause) throws StorageObjectFailure { - invalidatePopupCache(); - if (StoreUtil.implementsStorableObject(theEntityClass)) { - StoreContainerType stoc_type = StoreContainerType.valueOf(theEntityClass, StoreContainerType.STOC_TYPE_ENTITYLIST); - o_store.invalidate(stoc_type); - } + invalidateStore(); Statement stmt = null; Connection con = null; int res = 0; String sql = - "delete from " + theTable + " where " + aWhereClause; + "delete from " + mainTable + " where " + aWhereClause; //theLog.printInfo("DELETE " + sql); try { - con = getPooledCon(); + con = obtainConnection(); stmt = con.createStatement(); res = stmt.executeUpdate(sql); } @@ -1147,71 +989,33 @@ public class Database implements StorageObject { * @return immer false */ public boolean delete(EntityList theEntityList) { - invalidatePopupCache(); - return false; } - /* invalidates the popupCache - */ - protected void invalidatePopupCache() { - /** @todo invalidates toooo much */ - popupCache = null; - hashCache = null; - } - /** * Diese Methode fuehrt den Sqlstring sql aus und timed im Logfile. * @param stmt Statemnt * @param sql Sql-String - * @return ResultSet - * @exception StorageObjectException */ public ResultSet executeSql(Statement stmt, String sql) throws StorageObjectFailure, SQLException { ResultSet rs; + logQueryBefore(sql); long startTime = System.currentTimeMillis(); - try { rs = stmt.executeQuery(sql); - logger.debug((System.currentTimeMillis() - startTime) + "ms. for: " + sql); + logQueryAfter(sql, (System.currentTimeMillis() - startTime)); } catch (SQLException e) { - logger.error(e.getMessage() +"\n" + (System.currentTimeMillis() - startTime) + "ms. for: " + sql); + logQueryError(sql, (System.currentTimeMillis() - startTime), e); throw e; } return rs; } -/* - public ResultSet executeSql(String sql) throws StorageObjectFailure, SQLException { - long startTime = System.currentTimeMillis(); - Connection connection = null; - Statement statement = null; - try { - connection = getPooledCon(); - statement = connection.createStatement(); - ResultSet result; - - result = statement.executeQuery(sql); - - logger.debug((System.currentTimeMillis() - startTime) + "ms. for: " + sql); - return result; - } - catch (Throwable e) { - logger.error(e.getMessage() +"\n" + (System.currentTimeMillis() - startTime) + "ms. for: " + sql); - throw new StorageObjectFailure(e); - } - finally { - if (connection!=null) { - freeConnection(connection, statement); - } - } - } -*/ - private Map processRow(ResultSet aResultSet) throws StorageObjectFailure, StorageObjectExc { + private Map processRow(ResultSet aResultSet) throws StorageObjectFailure { try { Map result = new HashMap(); ResultSetMetaData metaData = aResultSet.getMetaData(); @@ -1227,12 +1031,16 @@ public class Database implements StorageObject { } } + /** + * Executes 1 sql statement and returns the results as a List of + * Maps + */ public List executeFreeSql(String sql, int aLimit) throws StorageObjectFailure, StorageObjectExc { Connection connection = null; Statement statement = null; try { - List result = new Vector(); - connection = getPooledCon(); + List result = new ArrayList(); + connection = obtainConnection(); statement = connection.createStatement(); ResultSet resultset = executeSql(statement, sql); try { @@ -1254,16 +1062,19 @@ public class Database implements StorageObject { freeConnection(connection, statement); } } - }; + } + /** + * Executes 1 sql statement and returns the first result row as a Maps + * (null if there wasn't any row) + */ public Map executeFreeSingleRowSql(String anSqlStatement) throws StorageObjectFailure, StorageObjectExc { try { List resultList = executeFreeSql(anSqlStatement, 1); try { if (resultList.size()>0) return (Map) resultList.get(0); - else - return null; + return null; } finally { } @@ -1271,8 +1082,12 @@ public class Database implements StorageObject { catch (Throwable t) { throw new StorageObjectFailure(t); } - }; + } + /** + * Executes 1 sql statement and returns the first column of the first result row as a Strings + * (null if there wasn't any row) + */ public String executeFreeSingleValueSql(String sql) throws StorageObjectFailure, StorageObjectExc { Map row = executeFreeSingleRowSql(sql); @@ -1282,30 +1097,47 @@ public class Database implements StorageObject { Iterator i = row.values().iterator(); if (i.hasNext()) return (String) i.next(); - else - return null; - }; + return null; + } + public int getSize(String where) throws SQLException, StorageObjectFailure { + return getSize("", null, where); + } /** * returns the number of rows in the table */ - public int getSize(String where) throws SQLException, StorageObjectFailure { - long startTime = System.currentTimeMillis(); - String sql = "SELECT Count(*) FROM " + theTable; + public int getSize(String mainTablePrefix, List extraTables, String where) throws SQLException, StorageObjectFailure { + + String useTable = mainTable; + if (mainTablePrefix!=null && mainTablePrefix.trim().length()>0) { + useTable+=" "+mainTablePrefix; + } + StringBuffer countSql = + new StringBuffer("select count(*) from ").append(useTable); + // append extratables, if necessary + if (extraTables!=null) { + for (int i=0;i < extraTables.size();i++) { + if (!extraTables.get(i).equals("")) { + countSql.append( ", " + extraTables.get(i)); + } + } + } if ((where != null) && (where.length() != 0)) { - sql = sql + " where " + where; + countSql.append( " where " + where); } Connection con = null; Statement stmt = null; int result = 0; + logQueryBefore(countSql.toString()); + long startTime = System.currentTimeMillis(); try { - con = getPooledCon(); + con = obtainConnection(); stmt = con.createStatement(); - ResultSet rs = executeSql(stmt, sql); + ResultSet rs = executeSql(stmt, countSql.toString()); while (rs.next()) { result = rs.getInt(1); @@ -1317,9 +1149,7 @@ public class Database implements StorageObject { finally { freeConnection(con, stmt); } - - //theLog.printInfo(theTable + " has "+ result +" rows where " + where); - logger.debug((System.currentTimeMillis() - startTime) + "ms. for: " + sql); + logQueryAfter(countSql.toString(), (System.currentTimeMillis() - startTime)); return result; } @@ -1327,15 +1157,17 @@ public class Database implements StorageObject { public int executeUpdate(Statement stmt, String sql) throws StorageObjectFailure, SQLException { int rs; + + logQueryBefore(sql); long startTime = System.currentTimeMillis(); try { rs = stmt.executeUpdate(sql); - logger.debug((System.currentTimeMillis() - startTime) + "ms. for: " + sql); + logQueryAfter(sql, (System.currentTimeMillis() - startTime)); } catch (SQLException e) { - logger.error("Failed: " + (System.currentTimeMillis() - startTime) + "ms. for: " + sql); + logQueryError(sql, (System.currentTimeMillis() - startTime), e); throw e; } @@ -1345,119 +1177,107 @@ public class Database implements StorageObject { public int executeUpdate(String sql) throws StorageObjectFailure, SQLException { int result = -1; - long startTime = System.currentTimeMillis(); Connection con = null; PreparedStatement pstmt = null; + logQueryBefore(sql); + long startTime = System.currentTimeMillis(); try { - con = getPooledCon(); + con = obtainConnection(); pstmt = con.prepareStatement(sql); result = pstmt.executeUpdate(); + logQueryAfter(sql, System.currentTimeMillis() - startTime); } catch (Throwable e) { - logger.error("Database.executeUpdate(" + sql + "): " + e.getMessage()); + logQueryError(sql, System.currentTimeMillis() - startTime, e); throw new StorageObjectFailure("Database.executeUpdate(" + sql + "): " + e.getMessage(), e); } finally { freeConnection(con, pstmt); } - - logger.debug((System.currentTimeMillis() - startTime) + "ms. for: " + sql); return result; } /** - * Wertet ResultSetMetaData aus und setzt interne Daten entsprechend - * @param md ResultSetMetaData - * @exception StorageObjectException + * Processes the metadata for the table this Database object is responsible for. */ - private void evalMetaData(ResultSetMetaData md) throws StorageObjectFailure { - this.evaluatedMetaData = true; - this.metadataFields = new ArrayList(); - this.metadataLabels = new ArrayList(); - this.metadataNotNullFields = new ArrayList(); + private void processMetaData(ResultSetMetaData aMetaData) throws StorageObjectFailure { + fieldNames = new ArrayList(); + fieldNameToType = new HashMap(); try { - int numFields = md.getColumnCount(); - this.metadataTypes = new int[numFields]; - - String aField; - int aType; + int numFields = aMetaData.getColumnCount(); + fieldTypes = new int[numFields]; for (int i = 1; i <= numFields; i++) { - aField = md.getColumnName(i); - metadataFields.add(aField); - metadataLabels.add(md.getColumnLabel(i)); - aType = md.getColumnType(i); - metadataTypes[i - 1] = aType; - - if (aField.equals(thePKeyName)) { - thePKeyType = aType; - thePKeyIndex = i; - } - - if (md.isNullable(i) == ResultSetMetaData.columnNullable) { - metadataNotNullFields.add(aField); - } + fieldNames.add(aMetaData.getColumnName(i)); + fieldTypes[i - 1] = aMetaData.getColumnType(i); + fieldNameToType.put(aMetaData.getColumnName(i), new Integer(aMetaData.getColumnType(i))); } } catch (SQLException e) { - throwSQLException(e, "evalMetaData"); + throwSQLException(e, "processMetaData"); } } /** - * Wertet die Metadaten eines Resultsets fuer eine Tabelle aus, - * um die alle Columns und Typen einer Tabelle zu ermitteln. + * Retrieves metadata from the table this Database object represents */ - private void get_meta_data() throws StorageObjectFailure { - Connection con = null; - PreparedStatement pstmt = null; - String sql = "select * from " + theTable + " where 0=1"; + private void acquireMetaData() throws StorageObjectFailure { + Connection connection = null; + PreparedStatement statement = null; + String sql = "select * from " + mainTable + " where 0=1"; try { - con = getPooledCon(); - pstmt = con.prepareStatement(sql); + connection = obtainConnection(); + statement = connection.prepareStatement(sql); logger.debug("METADATA: " + sql); - ResultSet rs = pstmt.executeQuery(); - evalMetaData(rs.getMetaData()); - rs.close(); + ResultSet resultSet = statement.executeQuery(); + try { + processMetaData(resultSet.getMetaData()); + } + finally { + resultSet.close(); + } } catch (SQLException e) { - throwSQLException(e, "get_meta_data"); + throwSQLException(e, "acquireMetaData"); } finally { - freeConnection(con, pstmt); + freeConnection(connection, statement); } } - public Connection getPooledCon() throws StorageObjectFailure { - Connection con = null; - + public Connection obtainConnection() throws StorageObjectFailure { try { - con = SQLManager.getInstance().requestConnection(); + return MirGlobal.getDatabaseEngine().obtainConnection(); } - catch (SQLException e) { - logger.error("could not connect to the database " + e.getMessage()); - - throw new StorageObjectFailure("Could not connect to the database", e); + catch (Exception e) { + throw new StorageObjectFailure(e); } - - return con; } - public void freeConnection(Connection con, Statement stmt) - throws StorageObjectFailure { - SQLManager.closeStatement(stmt); - SQLManager.getInstance().returnConnection(con); + public void freeConnection(Connection aConnection, Statement aStatement) throws StorageObjectFailure { + try { + aStatement.close(); + } + catch (Throwable t) { + logger.warn("Can't close statemnet: " + t.toString()); + } + + try { + MirGlobal.getDatabaseEngine().releaseConnection(aConnection); + } + catch (Throwable t) { + logger.warn("Can't release connection: " + t.toString()); + } } /** * Wertet SQLException aus und wirft dannach eine StorageObjectException * @param sqe SQLException - * @param wo Funktonsname, in der die SQLException geworfen wurde - * @exception StorageObjectException + * @param aFunction Funktonsname, in der die SQLException geworfen wurde */ protected void throwSQLException(SQLException sqe, String aFunction) throws StorageObjectFailure { String state = ""; @@ -1494,11 +1314,176 @@ public class Database implements StorageObject { /** * Loggt Fehlermeldung mit dem Parameter Message und wirft dannach * eine StorageObjectException - * @param message Nachricht mit dem Fehler - * @exception StorageObjectException + * @param aMessage Nachricht mit dem Fehler + * @exception StorageObjectFailure */ void throwStorageObjectException(String aMessage) throws StorageObjectFailure { logger.error(aMessage); throw new StorageObjectFailure(aMessage, null); } + + /** + * Invalidates any cached entity list + */ + private void invalidateStore() { + // invalidating all EntityLists corresponding with entityClass + if (StoreUtil.extendsStorableEntity(entityClass)) { + StoreContainerType stoc_type = + StoreContainerType.valueOf(entityClass, StoreContainerType.STOC_TYPE_ENTITYLIST); + o_store.invalidate(stoc_type); + } + } + + /** + * Retrieves a binary value + */ + public InputStream getBinaryField(String aQuery) throws StorageObjectFailure, SQLException { + Connection connection=null; + Statement statement=null; + InputStream inputStream; + InputStream imageInputStream = null; + + try { + connection = obtainConnection(); + try { + connection.setAutoCommit(false); + statement = connection.createStatement(); + ResultSet resultSet = executeSql(statement, aQuery); + + if(resultSet!=null) { + if (resultSet.next()) { + if (resultSet.getMetaData().getColumnType(1) == java.sql.Types.BINARY) { + byte[] data = resultSet.getBytes(1); + imageInputStream = new ByteArrayInputStream(data); + } + else { + inputStream = resultSet.getBlob(1).getBinaryStream(); + imageInputStream = new BinaryFieldInputStream(inputStream, connection, statement); + } + } + resultSet.close(); + } + } + finally { + } + } + catch (Throwable t) { + logger.error("EntityImages.getImage failed: " + t.toString()); + t.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE)); + + try { + connection.setAutoCommit(true); + } + catch (Throwable e) { + logger.error("EntityImages.getImage resetting transaction mode failed: " + e.toString()); + e.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE)); + } + + try { + freeConnection(connection, statement); + } + catch (Throwable e) { + logger.error("EntityImages.getImage freeing connection failed: " +e.toString()); + } + + throw new StorageObjectFailure(t); + } + + return imageInputStream; + } + + /** + * Sets a binary value for a particular field in a record specified by its identifier + */ + public void setBinaryField(String aFieldName, String anObjectId, byte aData[]) throws StorageObjectFailure, SQLException { + PreparedStatement statement = null; + Connection connection = obtainConnection(); + + try { + connection.setAutoCommit(false); + try { + // are we using bytea ? + if (getFieldType(aFieldName) == java.sql.Types.BINARY) { + statement = connection.prepareStatement( + "update " + mainTable + " set " + aFieldName + " = ? where " + getIdFieldName() + "=" + Integer.parseInt(anObjectId)); + statement.setBytes(1, aData); + statement.execute(); + connection.commit(); + } + // or the old oid's + else { + PGConnection postgresqlConnection = (org.postgresql.PGConnection) ((DelegatingConnection) connection).getDelegate(); + LargeObjectManager lobManager = postgresqlConnection.getLargeObjectAPI(); + int oid = lobManager.create(LargeObjectManager.READ | LargeObjectManager.WRITE); + LargeObject obj = lobManager.open(oid, LargeObjectManager.WRITE); // Now open the file File file = + obj.write(aData); + obj.close(); + statement = connection.prepareStatement( + "update " + mainTable + " set " + aFieldName + " = ? where " + getIdFieldName() + "=" + Integer.parseInt(anObjectId)); + statement.setInt(1, oid); + statement.execute(); + connection.commit(); + } + } + finally { + connection.setAutoCommit(true); + } + } + finally { + freeConnection(connection, statement); + } + } + + private void logQueryBefore(String aQuery) { + logger.debug("about to perform QUERY " + aQuery); +// (new Throwable()).printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE)); + } + + private void logQueryAfter(String aQuery, long aTime) { + logger.info("QUERY " + aQuery + " took " + aTime + "ms."); + } + + private void logQueryError(String aQuery, long aTime, Throwable anException) { + logger.error("QUERY " + aQuery + " took " + aTime + "ms, but threw exception " + anException.toString()); + } + + private int getFieldType(String aFieldName) { + if (fieldNameToType == null) { + acquireMetaData(); + } + + return ((Integer) fieldNameToType.get(aFieldName)).intValue(); + } + + + /** + * a small wrapper class that allows us to store the DB connection resources + * that the BlobInputStream is using and free them upon closing of the stream + */ + private class BinaryFieldInputStream extends InputStream { + InputStream inputStream; + Connection connection; + Statement statement; + + public BinaryFieldInputStream(InputStream aBlobInputStream, Connection aConnection, Statement aStatement ) { + inputStream = aBlobInputStream; + connection = aConnection; + statement = aStatement; + } + + public void close () throws IOException { + inputStream.close(); + try { + connection.setAutoCommit(true); + freeConnection(connection, statement); + } + catch (Exception e) { + throw new IOException("close(): "+e.toString()); + } + } + + public int read() throws IOException { + return inputStream.read(); + } + } }