2 * Copyright (C) 2001-2006 The Mir-coders group
4 * This file is part of Mir.
6 * Mir is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Mir is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Mir; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * In addition, as a special exception, The Mir-coders gives permission to link
21 * the code of this program with any library licensed under the Apache Software License,
22 * and distribute linked combinations including the two. You must obey the
23 * GNU General Public License in all respects for all of the code used other than
24 * the above mentioned libraries. If you modify this file, you may extend this
25 * exception to your version of the file, but you are not obligated to do so.
26 * If you do not wish to do so, delete this exception statement from your version.
30 import mir.config.MirPropertiesConfiguration;
31 import mir.entity.AbstractEntity;
32 import mir.entity.Entity;
33 import mir.entity.EntityList;
34 import mir.entity.StorableObjectEntity;
35 import mir.log.LoggerWrapper;
36 import mir.storage.store.*;
37 import mir.util.JDBCStringRoutines;
38 import mir.util.StreamCopier;
39 import mircoders.global.MirGlobal;
40 import org.apache.commons.dbcp.DelegatingConnection;
41 import org.postgresql.PGConnection;
42 import org.postgresql.largeobject.LargeObject;
43 import org.postgresql.largeobject.LargeObjectManager;
45 import java.io.ByteArrayOutputStream;
46 import java.io.InputStream;
47 import java.io.InputStreamReader;
49 import java.text.ParseException;
50 import java.text.SimpleDateFormat;
54 * Implements database access.
56 * @version $Id: Database.java,v 1.48 2007/04/08 21:46:37 idfx Exp $
61 public class Database {
62 private static final int DEFAULT_LIMIT = 20;
63 private static final Class GENERIC_ENTITY_CLASS = StorableObjectEntity.class;
64 protected static final ObjectStore o_store = ObjectStore.getInstance();
66 protected LoggerWrapper logger;
68 protected String mainTable;
69 protected String primaryKeyField = "id";
71 private List fieldNames;
72 private int[] fieldTypes;
73 private Map fieldNameToType;
75 protected Class entityClass;
78 private Set binaryFields;
80 private TimeZone timezone;
81 private SimpleDateFormat userInputDateFormat;
83 public Database() throws DatabaseFailure {
84 MirPropertiesConfiguration configuration = MirPropertiesConfiguration.instance();
85 logger = new LoggerWrapper("Database");
86 timezone = TimeZone.getTimeZone(configuration.getString("Mir.DefaultTimezone"));
88 userInputDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
89 userInputDateFormat.setTimeZone(timezone);
91 binaryFields = new HashSet();
93 String adapterName = configuration.getString("Database.Adaptor");
96 entityClass = GENERIC_ENTITY_CLASS;
99 logger.error("Error in Database() constructor with " + adapterName + " -- " + e.getMessage());
100 throw new DatabaseFailure("Error in Database() constructor.", e);
104 public Class getEntityClass() {
108 public Entity createNewEntity() throws DatabaseFailure {
110 AbstractEntity result = (AbstractEntity) entityClass.newInstance();
111 result.setStorage(this);
115 catch (Throwable t) {
116 throw new DatabaseFailure(t);
120 public String getIdFieldName() {
121 return primaryKeyField;
124 public String getTableName() {
129 * Returns a list of field names for this <code>Database</code>
131 public List getFieldNames() throws DatabaseFailure {
132 if (fieldNames == null) {
139 public boolean hasField(String aFieldName) {
140 return getFieldNames().contains(aFieldName);
144 * Gets value out of ResultSet according to type and converts to String
146 * @param aResultSet ResultSet.
147 * @param aType a type from java.sql.Types.*
148 * @param aFieldIndex index in ResultSet
149 * @return returns the value as String. If no conversion is possible
150 * /unsupported value/ is returned
152 private String getValueAsString(ResultSet aResultSet, int aFieldIndex, int aType)
153 throws DatabaseFailure {
154 String outValue = null;
156 if (aResultSet != null) {
159 case java.sql.Types.BIT:
160 outValue = (aResultSet.getBoolean(aFieldIndex) == true) ? "1" : "0";
164 case java.sql.Types.INTEGER:
165 case java.sql.Types.SMALLINT:
166 case java.sql.Types.TINYINT:
167 case java.sql.Types.BIGINT:
169 int out = aResultSet.getInt(aFieldIndex);
171 if (!aResultSet.wasNull()) {
172 outValue = new Integer(out).toString();
177 case java.sql.Types.NUMERIC:
178 long outl = aResultSet.getLong(aFieldIndex);
180 if (!aResultSet.wasNull()) {
181 outValue = new Long(outl).toString();
186 case java.sql.Types.REAL:
188 float tempf = aResultSet.getFloat(aFieldIndex);
190 if (!aResultSet.wasNull()) {
194 int tempf_int = (int) tempf;
195 tempf = (float) tempf_int;
197 outValue = "" + tempf;
198 outValue = outValue.replace('.', ',');
203 case java.sql.Types.DOUBLE:
205 double tempd = aResultSet.getDouble(aFieldIndex);
207 if (!aResultSet.wasNull()) {
211 int tempd_int = (int) tempd;
212 tempd = (double) tempd_int;
214 outValue = "" + tempd;
215 outValue = outValue.replace('.', ',');
220 case java.sql.Types.CHAR:
221 case java.sql.Types.VARCHAR:
222 case java.sql.Types.LONGVARCHAR:
223 outValue = aResultSet.getString(aFieldIndex);
227 case java.sql.Types.LONGVARBINARY:
228 outValue = aResultSet.getString(aFieldIndex);
232 case java.sql.Types.TIMESTAMP:
234 // it's important to use Timestamp here as getting it
235 // as a string is undefined and is only there for debugging
236 // according to the API. we can make it a string through formatting.
238 Timestamp timestamp = (aResultSet.getTimestamp(aFieldIndex));
240 if (!aResultSet.wasNull()) {
241 java.util.Date date = new java.util.Date(timestamp.getTime());
242 outValue = DatabaseHelper.convertDateToInternalRepresenation(date);
248 outValue = "<unsupported value>";
249 logger.warn("Unsupported Datatype: at " + aFieldIndex + " (" + aType + ")");
252 catch (SQLException e) {
253 throw new DatabaseFailure("Could not get Value out of Resultset -- ",
262 * Return an entity specified by id, or <code>null</code> if no such
265 public Entity selectById(String anId) throws DatabaseExc {
266 if ((anId == null) || anId.equals("")) {
267 throw new DatabaseExc("Database.selectById: Missing id");
270 // ask object store for object
271 if (StoreUtil.extendsStorableEntity(entityClass)) {
272 String uniqueId = anId;
274 if (entityClass.equals(StorableObjectEntity.class)) {
275 uniqueId += ("@" + mainTable);
278 StoreIdentifier search_sid = new StoreIdentifier(entityClass, uniqueId);
279 logger.debug("CACHE: (dbg) looking for sid " + search_sid.toString());
281 Entity hit = (Entity) o_store.use(search_sid);
288 Connection con = obtainConnection();
289 Entity returnEntity = null;
290 PreparedStatement statement = null;
294 String query = "select * from " + mainTable + " where " + primaryKeyField + " = ?";
296 statement = con.prepareStatement(query);
297 statement.setString(1, anId);
299 logQueryBefore(query);
301 long startTime = System.currentTimeMillis();
303 rs = statement.executeQuery();
305 logQueryAfter(query, (System.currentTimeMillis() - startTime));
307 catch (SQLException e) {
308 logQueryError(query, (System.currentTimeMillis() - startTime), e);
314 returnEntity = makeEntityFromResultSet(rs);
317 logger.warn("No data for id: " + anId + " in table " + mainTable);
323 logger.warn("No Data for Id " + anId + " in Table " + mainTable);
326 catch (Throwable e) {
327 throw new DatabaseFailure(e);
330 freeConnection(con, statement);
336 public EntityList selectByWhereClauseWithExtraTables(String mainTablePrefix, List extraTables, String aWhereClause) throws DatabaseExc, DatabaseFailure {
337 return selectByWhereClause( mainTablePrefix, extraTables, aWhereClause, "", 0, DEFAULT_LIMIT);
340 public EntityList selectByFieldValue(String aField, String aValue) throws DatabaseExc, DatabaseFailure {
341 return selectByFieldValue(aField, aValue, 0);
344 public EntityList selectByFieldValue(String aField, String aValue, int offset) throws DatabaseExc, DatabaseFailure {
345 return selectByWhereClause(aField + "='" + JDBCStringRoutines.escapeStringLiteral(aValue)+"'", offset);
348 public EntityList selectByWhereClause(String where) throws DatabaseExc, DatabaseFailure {
349 return selectByWhereClause(where, 0);
352 public EntityList selectByWhereClause(String whereClause, int offset) throws DatabaseExc, DatabaseFailure {
353 return selectByWhereClause(whereClause, null, offset);
356 public EntityList selectByWhereClause(String mainTablePrefix, List extraTables, String where, String order) throws DatabaseExc, DatabaseFailure {
357 return selectByWhereClause(mainTablePrefix, extraTables, where, order, 0, DEFAULT_LIMIT);
360 public EntityList selectByWhereClause(String whereClause, String orderBy, int offset) throws DatabaseExc, DatabaseFailure {
361 return selectByWhereClause(whereClause, orderBy, offset, DEFAULT_LIMIT);
364 public EntityList selectByWhereClause(String aWhereClause, String anOrderByClause,
365 int offset, int limit) throws DatabaseExc, DatabaseFailure {
366 return selectByWhereClause("", null, aWhereClause, anOrderByClause, offset, limit);
369 public EntityList selectByWhereClause(
370 String aMainTablePrefix, List anExtraTables,
371 String aWhereClause, String anOrderByClause,
372 int anOffset, int aLimit) throws DatabaseExc, DatabaseFailure {
374 if (anExtraTables!=null && ((String) anExtraTables.get(0)).trim().equals("")){
378 // check o_store for entitylist
379 // only if no relational select
380 if (anExtraTables==null) {
381 if (StoreUtil.extendsStorableEntity(entityClass)) {
382 StoreIdentifier searchSid = new StoreIdentifier(entityClass,
383 StoreContainerType.STOC_TYPE_ENTITYLIST,
384 StoreUtil.getEntityListUniqueIdentifierFor(mainTable,
385 aWhereClause, anOrderByClause, anOffset, aLimit));
386 EntityList hit = (EntityList) o_store.use(searchSid);
394 RecordRetriever retriever = new RecordRetriever(mainTable, aMainTablePrefix);
396 EntityList result = null;
397 Connection connection = null;
399 if (anExtraTables!=null) {
400 Iterator i = anExtraTables.iterator();
401 while (i.hasNext()) {
402 String table = (String) i.next();
403 if (!"".equals(table)) {
404 retriever.addExtraTable(table);
409 if (aWhereClause != null) {
410 retriever.appendWhereClause(aWhereClause);
413 if ((anOrderByClause != null) && !(anOrderByClause.trim().length() == 0)) {
414 retriever.appendOrderByClause(anOrderByClause);
417 if (anOffset>-1 && aLimit>-1) {
418 retriever.setLimit(aLimit+1);
419 retriever.setOffset(anOffset);
422 Iterator i = getFieldNames().iterator();
423 while (i.hasNext()) {
424 retriever.addField((String) i.next());
429 connection = obtainConnection();
430 ResultSet resultSet = retriever.execute(connection);
432 boolean hasMore = false;
434 if (resultSet != null) {
435 result = new EntityList();
439 while (((aLimit == -1) || (position<aLimit)) && resultSet.next()) {
440 entity = makeEntityFromResultSet(resultSet);
445 hasMore = resultSet.next();
449 if (result != null) {
450 result.setOffset(anOffset);
451 result.setWhere(aWhereClause);
452 result.setOrder(anOrderByClause);
453 result.setStorage(this);
454 result.setLimit(aLimit);
457 result.setNextBatch(anOffset + aLimit);
460 if (anExtraTables==null && StoreUtil.extendsStorableEntity(entityClass)) {
461 StoreIdentifier sid = result.getStoreIdentifier();
462 logger.debug("CACHE (add): " + sid.toString());
467 catch (Throwable e) {
468 throw new DatabaseFailure(e);
472 if (connection != null) {
473 freeConnection(connection);
475 } catch (Throwable t) {
482 private Entity makeEntityFromResultSet(ResultSet rs) {
483 Map fields = new HashMap();
484 String theResult = null;
486 Entity returnEntity = null;
489 if (StoreUtil.extendsStorableEntity(entityClass)) {
490 StoreIdentifier searchSid = StorableObjectEntity.getStoreIdentifier(this,
492 Entity hit = (Entity) o_store.use(searchSid);
493 if (hit != null) return hit;
496 for (int i = 0; i < getFieldNames().size(); i++) {
497 type = fieldTypes[i];
499 if (type == java.sql.Types.LONGVARBINARY) {
500 InputStreamReader is =
501 (InputStreamReader) rs.getCharacterStream(i + 1);
504 char[] data = new char[32768];
505 StringBuffer theResultString = new StringBuffer();
508 while ((len = is.read(data)) > 0) {
509 theResultString.append(data, 0, len);
513 theResult = theResultString.toString();
520 theResult = getValueAsString(rs, (i + 1), type);
523 if (theResult != null) {
524 fields.put(getFieldNames().get(i), theResult);
528 if (entityClass != null) {
529 returnEntity = createNewEntity();
530 returnEntity.setFieldValues(fields);
532 if (returnEntity instanceof StorableObject) {
533 logger.debug("CACHE: ( in) " + returnEntity.getId() + " :" + mainTable);
534 o_store.add(((StorableObject) returnEntity).getStoreIdentifier());
538 throw new DatabaseExc("Internal Error: entityClass not set!");
541 catch (Throwable e) {
542 throw new DatabaseFailure(e);
549 * Inserts an entity into the database.
552 * @return the value of the primary key of the inserted record
554 public String insert(Entity anEntity) throws DatabaseFailure {
557 RecordInserter inserter =
558 new RecordInserter(mainTable, getPrimaryKeySequence());
560 String returnId = null;
561 Connection con = null;
567 for (int i = 0; i < getFieldNames().size(); i++) {
568 fieldName = (String) getFieldNames().get(i);
570 if (!fieldName.equals(primaryKeyField)) {
572 if (!anEntity.hasFieldValue(fieldName) && (
573 fieldName.equals("webdb_create") ||
574 fieldName.equals("webdb_lastchange"))) {
575 inserter.assignVerbatim(fieldName, "now()");
578 if (anEntity.hasFieldValue(fieldName)) {
579 inserter.assignString(fieldName, anEntity.getFieldValue(fieldName));
585 con = obtainConnection();
586 returnId = inserter.execute(con);
588 anEntity.setId(returnId);
598 * Updates an entity in the database
602 public void update(Entity theEntity) throws DatabaseFailure {
605 RecordUpdater generator = new RecordUpdater(getTableName(), theEntity.getId());
607 // build sql statement
608 for (int i = 0; i < getFieldNames().size(); i++) {
609 String field = (String) getFieldNames().get(i);
611 if (!(field.equals(primaryKeyField) ||
612 "webdb_create".equals(field) ||
613 "webdb_lastchange".equals(field) ||
614 binaryFields.contains(field))) {
616 if (theEntity.hasFieldValue(field)) {
617 generator.assignString(field, theEntity.getFieldValue(field));
623 if (hasField("webdb_lastchange")) {
624 generator.assignVerbatim("webdb_lastchange", "now()");
627 // special case: the webdb_create requires the field in yyyy-mm-dd HH:mm
628 // format so anything extra will be ignored. -mh
629 if (hasField("webdb_create") &&
630 theEntity.hasFieldValue("webdb_create")) {
631 // minimum of 10 (yyyy-mm-dd)...
632 if (theEntity.getFieldValue("webdb_create").length() >= 10) {
633 String dateString = theEntity.getFieldValue("webdb_create");
635 // if only 10, then add 00:00 so it doesn't throw a ParseException
636 if (dateString.length() == 10) {
637 dateString = dateString + " 00:00";
642 java.util.Date d = userInputDateFormat.parse(dateString);
643 generator.assignDateTime("webdb_create", d);
645 catch (ParseException e) {
646 throw new DatabaseFailure(e);
650 Connection connection = null;
653 connection = obtainConnection();
654 generator.execute(connection);
657 freeConnection(connection);
661 private void invalidateObject(String anId) {
662 // ostore send notification
663 if (StoreUtil.extendsStorableEntity(entityClass)) {
664 String uniqueId = anId;
666 if (entityClass.equals(StorableObjectEntity.class)) {
667 uniqueId += ("@" + mainTable);
670 logger.debug("CACHE: (del) " + anId);
672 StoreIdentifier search_sid =
673 new StoreIdentifier(entityClass,
674 StoreContainerType.STOC_TYPE_ENTITY, uniqueId);
675 o_store.invalidate(search_sid);
681 * @param id des zu loeschenden Datensatzes
682 * @return boolean liefert true zurueck, wenn loeschen erfolgreich war.
684 public boolean delete(String id) throws DatabaseFailure {
685 invalidateObject(id);
688 Connection connection = obtainConnection();
689 PreparedStatement statement = null;
692 statement = connection.prepareStatement("delete from " + mainTable + " where " + primaryKeyField + "=?");
693 statement.setInt(1, Integer.parseInt(id));
694 logQueryBefore("delete from " + mainTable + " where " + primaryKeyField + "=" + id + "");
695 resultCode = statement.executeUpdate();
697 catch (SQLException e) {
698 logger.warn("Can't delete record", e);
701 freeConnection(connection, statement);
706 return (resultCode > 0) ? true : false;
710 * Deletes entities based on a where clause
712 public int deleteByWhereClause(String aWhereClause) throws DatabaseFailure {
715 Statement stmt = null;
716 Connection con = null;
719 "delete from " + mainTable + " where " + aWhereClause;
721 //theLog.printInfo("DELETE " + sql);
723 con = obtainConnection();
724 stmt = con.createStatement();
725 res = stmt.executeUpdate(sql);
727 catch (Throwable e) {
728 throw new DatabaseFailure(e);
731 freeConnection(con, stmt);
737 /* noch nicht implementiert.
738 * @return immer false
740 public boolean delete(EntityList theEntityList) {
744 public ResultSet executeSql(Statement stmt, String sql)
745 throws DatabaseFailure, SQLException {
748 long startTime = System.currentTimeMillis();
750 rs = stmt.executeQuery(sql);
752 logQueryAfter(sql, (System.currentTimeMillis() - startTime));
754 catch (SQLException e) {
755 logQueryError(sql, (System.currentTimeMillis() - startTime), e);
762 private Map processRow(ResultSet aResultSet) throws DatabaseFailure {
764 Map result = new HashMap();
765 ResultSetMetaData metaData = aResultSet.getMetaData();
766 int nrColumns = metaData.getColumnCount();
767 for (int i=0; i<nrColumns; i++) {
768 result.put(metaData.getColumnName(i+1), getValueAsString(aResultSet, i+1, metaData.getColumnType(i+1)));
773 catch (Throwable e) {
774 throw new DatabaseFailure(e);
779 * Executes 1 sql statement and returns the results as a <code>List</code> of
782 public List executeFreeSql(String sql, int aLimit) throws DatabaseFailure, DatabaseExc {
783 Connection connection = null;
784 Statement statement = null;
786 List result = new ArrayList();
787 connection = obtainConnection();
788 statement = connection.createStatement();
789 ResultSet resultset = executeSql(statement, sql);
791 while (resultset.next() && result.size() < aLimit) {
792 result.add(processRow(resultset));
801 catch (Throwable e) {
802 throw new DatabaseFailure(e);
805 if (connection!=null) {
806 freeConnection(connection, statement);
812 * Executes 1 sql statement and returns the first result row as a <code>Map</code>s
813 * (<code>null</code> if there wasn't any row)
815 public Map executeFreeSingleRowSql(String anSqlStatement) throws DatabaseFailure, DatabaseExc {
817 List resultList = executeFreeSql(anSqlStatement, 1);
819 if (resultList.size()>0)
820 return (Map) resultList.get(0);
826 catch (Throwable t) {
827 throw new DatabaseFailure(t);
832 * Executes 1 sql statement and returns the first column of the first result row as a <code>String</code>s
833 * (<code>null</code> if there wasn't any row)
835 public String executeFreeSingleValueSql(String sql) throws DatabaseFailure, DatabaseExc {
836 Map row = executeFreeSingleRowSql(sql);
841 Iterator i = row.values().iterator();
843 return (String) i.next();
847 public int getSize(String where) throws SQLException, DatabaseFailure {
848 return getSize("", null, where);
851 * returns the number of rows in the table
853 public int getSize(String mainTablePrefix, List extraTables, String where) throws SQLException, DatabaseFailure {
855 String useTable = mainTable;
856 if (mainTablePrefix!=null && mainTablePrefix.trim().length()>0) {
857 useTable+=" "+mainTablePrefix;
859 StringBuffer countSql =
860 new StringBuffer("select count(*) from ").append(useTable);
861 // append extratables, if necessary
862 if (extraTables!=null) {
863 for (int i=0;i < extraTables.size();i++) {
864 if (!extraTables.get(i).equals("")) {
865 countSql.append( ", " + extraTables.get(i));
870 if ((where != null) && (where.length() != 0)) {
871 countSql.append( " where " + where);
874 Connection con = null;
875 Statement stmt = null;
877 logQueryBefore(countSql.toString());
878 long startTime = System.currentTimeMillis();
881 con = obtainConnection();
882 stmt = con.createStatement();
884 ResultSet rs = executeSql(stmt, countSql.toString());
887 result = rs.getInt(1);
890 catch (SQLException e) {
891 logger.error("Database.getSize: " + e.getMessage());
894 freeConnection(con, stmt);
896 logQueryAfter(countSql.toString(), (System.currentTimeMillis() - startTime));
901 public int executeUpdate(Statement stmt, String sql)
902 throws DatabaseFailure, SQLException {
906 long startTime = System.currentTimeMillis();
909 rs = stmt.executeUpdate(sql);
911 logQueryAfter(sql, (System.currentTimeMillis() - startTime));
913 catch (SQLException e) {
914 logQueryError(sql, (System.currentTimeMillis() - startTime), e);
921 public int executeUpdate(String sql)
922 throws DatabaseFailure, SQLException {
924 Connection con = null;
925 PreparedStatement pstmt = null;
928 long startTime = System.currentTimeMillis();
930 con = obtainConnection();
931 pstmt = con.prepareStatement(sql);
932 result = pstmt.executeUpdate();
933 logQueryAfter(sql, System.currentTimeMillis() - startTime);
935 catch (Throwable e) {
936 logQueryError(sql, System.currentTimeMillis() - startTime, e);
937 throw new DatabaseFailure("Database.executeUpdate(" + sql + "): " + e.getMessage(), e);
940 freeConnection(con, pstmt);
946 * Processes the metadata for the table this Database object is responsible for.
948 private void processMetaData(ResultSetMetaData aMetaData) throws DatabaseFailure {
949 fieldNames = new ArrayList();
950 fieldNameToType = new HashMap();
953 int numFields = aMetaData.getColumnCount();
954 fieldTypes = new int[numFields];
956 for (int i = 1; i <= numFields; i++) {
957 fieldNames.add(aMetaData.getColumnName(i));
958 fieldTypes[i - 1] = aMetaData.getColumnType(i);
959 fieldNameToType.put(aMetaData.getColumnName(i), new Integer(aMetaData.getColumnType(i)));
962 catch (Throwable e) {
963 throw new DatabaseFailure(e);
968 * Retrieves metadata from the table this Database object represents
970 private void acquireMetaData() throws DatabaseFailure {
971 Connection connection = null;
972 PreparedStatement statement = null;
973 String sql = "select * from " + mainTable + " where 0=1";
976 connection = obtainConnection();
977 statement = connection.prepareStatement(sql);
979 logger.debug("METADATA: " + sql);
980 ResultSet resultSet = statement.executeQuery();
982 processMetaData(resultSet.getMetaData());
988 catch (Throwable e) {
989 throw new DatabaseFailure(e);
992 freeConnection(connection, statement);
996 public Connection obtainConnection() throws DatabaseFailure {
998 return MirGlobal.getDatabaseEngine().obtainConnection();
1000 catch (Exception e) {
1001 throw new DatabaseFailure(e);
1005 public void freeConnection(Connection aConnection) throws DatabaseFailure {
1007 MirGlobal.getDatabaseEngine().releaseConnection(aConnection);
1009 catch (Throwable t) {
1010 logger.warn("Can't release connection: " + t.toString());
1014 public void freeConnection(Connection aConnection, Statement aStatement) throws DatabaseFailure {
1018 catch (Throwable t) {
1019 logger.warn("Can't close statement", t);
1022 freeConnection(aConnection);
1025 protected void _throwStorageObjectException(Exception e, String aFunction)
1026 throws DatabaseFailure {
1029 logger.error(e.getMessage() + aFunction);
1030 throw new DatabaseFailure(aFunction, e);
1036 * Invalidates any cached entity list
1038 private void invalidateStore() {
1039 // invalidating all EntityLists corresponding with entityClass
1040 if (StoreUtil.extendsStorableEntity(entityClass)) {
1041 StoreContainerType stoc_type =
1042 StoreContainerType.valueOf(entityClass, StoreContainerType.STOC_TYPE_ENTITYLIST);
1043 o_store.invalidate(stoc_type);
1048 * Retrieves a binary value
1050 public byte[] getBinaryField(String aQuery) throws DatabaseFailure, SQLException {
1051 Connection connection=null;
1052 Statement statement=null;
1053 InputStream inputStream;
1056 connection = obtainConnection();
1058 connection.setAutoCommit(false);
1059 statement = connection.createStatement();
1060 ResultSet resultSet = executeSql(statement, aQuery);
1062 if(resultSet!=null) {
1063 if (resultSet.next()) {
1064 if (resultSet.getMetaData().getColumnType(1) == java.sql.Types.BINARY) {
1065 return resultSet.getBytes(1);
1068 inputStream = resultSet.getBlob(1).getBinaryStream();
1069 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
1070 StreamCopier.copy(inputStream, outputStream);
1071 return outputStream.toByteArray();
1079 connection.setAutoCommit(true);
1081 catch (Throwable e) {
1082 logger.error("EntityImages.getImage resetting transaction mode failed: " + e.toString());
1083 e.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
1087 freeConnection(connection, statement);
1089 catch (Throwable e) {
1090 logger.error("EntityImages.getImage freeing connection failed: " +e.toString());
1095 catch (Throwable t) {
1096 logger.error("EntityImages.getImage failed: " + t.toString());
1097 t.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
1099 throw new DatabaseFailure(t);
1106 * Sets a binary value for a particular field in a record specified by its identifier
1108 public void setBinaryField(String aFieldName, String anObjectId, byte aData[]) throws DatabaseFailure, SQLException {
1109 PreparedStatement statement = null;
1110 Connection connection = obtainConnection();
1113 connection.setAutoCommit(false);
1115 // are we using bytea ?
1116 if (getFieldType(aFieldName) == java.sql.Types.BINARY) {
1117 statement = connection.prepareStatement(
1118 "update " + mainTable + " set " + aFieldName + " = ? where " + getIdFieldName() + "=" + Integer.parseInt(anObjectId));
1119 statement.setBytes(1, aData);
1120 statement.execute();
1121 connection.commit();
1125 PGConnection postgresqlConnection = (org.postgresql.PGConnection) ((DelegatingConnection) connection).getDelegate();
1126 LargeObjectManager lobManager = postgresqlConnection.getLargeObjectAPI();
1127 int oid = lobManager.create(LargeObjectManager.READ | LargeObjectManager.WRITE);
1128 LargeObject obj = lobManager.open(oid, LargeObjectManager.WRITE); // Now open the file File file =
1131 statement = connection.prepareStatement(
1132 "update " + mainTable + " set " + aFieldName + " = ? where " + getIdFieldName() + "=" + Integer.parseInt(anObjectId));
1133 statement.setInt(1, oid);
1134 statement.execute();
1135 connection.commit();
1139 connection.setAutoCommit(true);
1143 freeConnection(connection, statement);
1148 * Can be overridden to specify a primary key sequence name not named according to
1149 * the convention (tablename _id_seq)
1151 protected String getPrimaryKeySequence() {
1152 return mainTable+"_id_seq";
1156 * Can be called by subclasses to specify fields that are binary, and that shouldn't
1157 * be updated outside of {@link #setBinaryField}
1159 * @param aBinaryField The field name of the binary field
1161 protected void markBinaryField(String aBinaryField) {
1162 binaryFields.add(aBinaryField);
1165 private void logQueryBefore(String aQuery) {
1166 logger.debug("about to perform QUERY " + aQuery);
1167 // (new Throwable()).printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
1170 private void logQueryAfter(String aQuery, long aTime) {
1171 logger.info("QUERY " + aQuery + " took " + aTime + "ms.");
1174 private void logQueryError(String aQuery, long aTime, Throwable anException) {
1175 logger.error("QUERY " + aQuery + " took " + aTime + "ms, but threw exception " + anException.toString());
1178 private int getFieldType(String aFieldName) {
1179 if (fieldNameToType == null) {
1183 return ((Integer) fieldNameToType.get(aFieldName)).intValue();