2 * Copyright (C) 2001-2005 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 * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
23 * (or with modified versions of the above that use the same license as the above),
24 * and distribute linked combinations including the two. You must obey the
25 * GNU General Public License in all respects for all of the code used other than
26 * the above mentioned libraries. If you modify this file, you may extend this
27 * exception to your version of the file, but you are not obligated to do so.
28 * If you do not wish to do so, delete this exception statement from your version.
32 import mir.config.MirPropertiesConfiguration;
33 import mir.entity.AbstractEntity;
34 import mir.entity.Entity;
35 import mir.entity.EntityList;
36 import mir.entity.StorableObjectEntity;
37 import mir.log.LoggerWrapper;
38 import mir.storage.store.*;
39 import mir.util.JDBCStringRoutines;
40 import mir.util.StreamCopier;
41 import mircoders.global.MirGlobal;
42 import org.apache.commons.dbcp.DelegatingConnection;
43 import org.postgresql.PGConnection;
44 import org.postgresql.largeobject.LargeObject;
45 import org.postgresql.largeobject.LargeObjectManager;
47 import java.io.ByteArrayOutputStream;
48 import java.io.InputStream;
49 import java.io.InputStreamReader;
51 import java.text.ParseException;
52 import java.text.SimpleDateFormat;
56 * Implements database access.
58 * @version $Id: Database.java,v 1.44.2.35 2005/12/23 21:56:27 zapata Exp $
63 public class Database {
64 private static final int DEFAULT_LIMIT = 20;
65 private static final Class GENERIC_ENTITY_CLASS = StorableObjectEntity.class;
66 protected static final ObjectStore o_store = ObjectStore.getInstance();
68 protected LoggerWrapper logger;
70 protected String mainTable;
71 protected String primaryKeyField = "id";
73 private List fieldNames;
74 private int[] fieldTypes;
75 private Map fieldNameToType;
77 protected Class entityClass;
80 private Set binaryFields;
82 private TimeZone timezone;
83 private SimpleDateFormat userInputDateFormat;
85 public Database() throws DatabaseFailure {
86 MirPropertiesConfiguration configuration = MirPropertiesConfiguration.instance();
87 logger = new LoggerWrapper("Database");
88 timezone = TimeZone.getTimeZone(configuration.getString("Mir.DefaultTimezone"));
90 userInputDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
91 userInputDateFormat.setTimeZone(timezone);
93 binaryFields = new HashSet();
95 String adapterName = configuration.getString("Database.Adaptor");
98 entityClass = GENERIC_ENTITY_CLASS;
100 catch (Throwable e) {
101 logger.error("Error in Database() constructor with " + adapterName + " -- " + e.getMessage());
102 throw new DatabaseFailure("Error in Database() constructor.", e);
106 public Class getEntityClass() {
110 public Entity createNewEntity() throws DatabaseFailure {
112 AbstractEntity result = (AbstractEntity) entityClass.newInstance();
113 result.setStorage(this);
117 catch (Throwable t) {
118 throw new DatabaseFailure(t);
122 public String getIdFieldName() {
123 return primaryKeyField;
126 public String getTableName() {
131 * Returns a list of field names for this <code>Database</code>
133 public List getFieldNames() throws DatabaseFailure {
134 if (fieldNames == null) {
141 public boolean hasField(String aFieldName) {
142 return getFieldNames().contains(aFieldName);
146 * Gets value out of ResultSet according to type and converts to String
148 * @param aResultSet ResultSet.
149 * @param aType a type from java.sql.Types.*
150 * @param aFieldIndex index in ResultSet
151 * @return returns the value as String. If no conversion is possible
152 * /unsupported value/ is returned
154 private String getValueAsString(ResultSet aResultSet, int aFieldIndex, int aType)
155 throws DatabaseFailure {
156 String outValue = null;
158 if (aResultSet != null) {
161 case java.sql.Types.BIT:
162 outValue = (aResultSet.getBoolean(aFieldIndex) == true) ? "1" : "0";
166 case java.sql.Types.INTEGER:
167 case java.sql.Types.SMALLINT:
168 case java.sql.Types.TINYINT:
169 case java.sql.Types.BIGINT:
171 int out = aResultSet.getInt(aFieldIndex);
173 if (!aResultSet.wasNull()) {
174 outValue = new Integer(out).toString();
179 case java.sql.Types.NUMERIC:
180 long outl = aResultSet.getLong(aFieldIndex);
182 if (!aResultSet.wasNull()) {
183 outValue = new Long(outl).toString();
188 case java.sql.Types.REAL:
190 float tempf = aResultSet.getFloat(aFieldIndex);
192 if (!aResultSet.wasNull()) {
196 int tempf_int = (int) tempf;
197 tempf = (float) tempf_int;
199 outValue = "" + tempf;
200 outValue = outValue.replace('.', ',');
205 case java.sql.Types.DOUBLE:
207 double tempd = aResultSet.getDouble(aFieldIndex);
209 if (!aResultSet.wasNull()) {
213 int tempd_int = (int) tempd;
214 tempd = (double) tempd_int;
216 outValue = "" + tempd;
217 outValue = outValue.replace('.', ',');
222 case java.sql.Types.CHAR:
223 case java.sql.Types.VARCHAR:
224 case java.sql.Types.LONGVARCHAR:
225 outValue = aResultSet.getString(aFieldIndex);
229 case java.sql.Types.LONGVARBINARY:
230 outValue = aResultSet.getString(aFieldIndex);
234 case java.sql.Types.TIMESTAMP:
236 // it's important to use Timestamp here as getting it
237 // as a string is undefined and is only there for debugging
238 // according to the API. we can make it a string through formatting.
240 Timestamp timestamp = (aResultSet.getTimestamp(aFieldIndex));
242 if (!aResultSet.wasNull()) {
243 java.util.Date date = new java.util.Date(timestamp.getTime());
244 outValue = DatabaseHelper.convertDateToInternalRepresenation(date);
250 outValue = "<unsupported value>";
251 logger.warn("Unsupported Datatype: at " + aFieldIndex + " (" + aType + ")");
254 catch (SQLException e) {
255 throw new DatabaseFailure("Could not get Value out of Resultset -- ",
264 * Return an entity specified by id
266 public Entity selectById(String anId) throws DatabaseExc {
267 if ((anId == null) || anId.equals("")) {
268 throw new DatabaseExc("Database.selectById: Missing id");
271 // ask object store for object
272 if (StoreUtil.extendsStorableEntity(entityClass)) {
273 String uniqueId = anId;
275 if (entityClass.equals(StorableObjectEntity.class)) {
276 uniqueId += ("@" + mainTable);
279 StoreIdentifier search_sid = new StoreIdentifier(entityClass, uniqueId);
280 logger.debug("CACHE: (dbg) looking for sid " + search_sid.toString());
282 Entity hit = (Entity) o_store.use(search_sid);
289 Connection con = obtainConnection();
290 Entity returnEntity = null;
291 PreparedStatement statement = null;
295 String query = "select * from " + mainTable + " where " + primaryKeyField + " = ?";
297 statement = con.prepareStatement(query);
298 statement.setString(1, anId);
300 logQueryBefore(query);
302 long startTime = System.currentTimeMillis();
304 rs = statement.executeQuery();
306 logQueryAfter(query, (System.currentTimeMillis() - startTime));
308 catch (SQLException e) {
309 logQueryError(query, (System.currentTimeMillis() - startTime), e);
315 returnEntity = makeEntityFromResultSet(rs);
318 logger.warn("No data for id: " + anId + " in table " + mainTable);
324 logger.warn("No Data for Id " + anId + " in Table " + mainTable);
327 catch (Throwable e) {
328 throw new DatabaseFailure(e);
331 freeConnection(con, statement);
337 public EntityList selectByWhereClauseWithExtraTables(String mainTablePrefix, List extraTables, String aWhereClause) throws DatabaseExc, DatabaseFailure {
338 return selectByWhereClause( mainTablePrefix, extraTables, aWhereClause, "", 0, DEFAULT_LIMIT);
341 public EntityList selectByFieldValue(String aField, String aValue) throws DatabaseExc, DatabaseFailure {
342 return selectByFieldValue(aField, aValue, 0);
345 public EntityList selectByFieldValue(String aField, String aValue, int offset) throws DatabaseExc, DatabaseFailure {
346 return selectByWhereClause(aField + "='" + JDBCStringRoutines.escapeStringLiteral(aValue)+"'", offset);
349 public EntityList selectByWhereClause(String where) throws DatabaseExc, DatabaseFailure {
350 return selectByWhereClause(where, 0);
353 public EntityList selectByWhereClause(String whereClause, int offset) throws DatabaseExc, DatabaseFailure {
354 return selectByWhereClause(whereClause, null, offset);
357 public EntityList selectByWhereClause(String mainTablePrefix, List extraTables, String where, String order) throws DatabaseExc, DatabaseFailure {
358 return selectByWhereClause(mainTablePrefix, extraTables, where, order, 0, DEFAULT_LIMIT);
361 public EntityList selectByWhereClause(String whereClause, String orderBy, int offset) throws DatabaseExc, DatabaseFailure {
362 return selectByWhereClause(whereClause, orderBy, offset, DEFAULT_LIMIT);
365 public EntityList selectByWhereClause(String aWhereClause, String anOrderByClause,
366 int offset, int limit) throws DatabaseExc, DatabaseFailure {
367 return selectByWhereClause("", null, aWhereClause, anOrderByClause, offset, limit);
370 public EntityList selectByWhereClause(
371 String aMainTablePrefix, List anExtraTables,
372 String aWhereClause, String anOrderByClause,
373 int anOffset, int aLimit) throws DatabaseExc, DatabaseFailure {
375 if (anExtraTables!=null && ((String) anExtraTables.get(0)).trim().equals("")){
379 // check o_store for entitylist
380 // only if no relational select
381 if (anExtraTables==null) {
382 if (StoreUtil.extendsStorableEntity(entityClass)) {
383 StoreIdentifier searchSid = new StoreIdentifier(entityClass,
384 StoreContainerType.STOC_TYPE_ENTITYLIST,
385 StoreUtil.getEntityListUniqueIdentifierFor(mainTable,
386 aWhereClause, anOrderByClause, anOffset, aLimit));
387 EntityList hit = (EntityList) o_store.use(searchSid);
395 RecordRetriever retriever = new RecordRetriever(mainTable, aMainTablePrefix);
397 EntityList result = null;
398 Connection connection = null;
400 if (anExtraTables!=null) {
401 Iterator i = anExtraTables.iterator();
402 while (i.hasNext()) {
403 String table = (String) i.next();
404 if (!"".equals(table)) {
405 retriever.addExtraTable(table);
410 if (aWhereClause != null) {
411 retriever.appendWhereClause(aWhereClause);
414 if ((anOrderByClause != null) && !(anOrderByClause.trim().length() == 0)) {
415 retriever.appendOrderByClause(anOrderByClause);
418 if (anOffset>-1 && aLimit>-1) {
419 retriever.setLimit(aLimit+1);
420 retriever.setOffset(anOffset);
423 Iterator i = getFieldNames().iterator();
424 while (i.hasNext()) {
425 retriever.addField((String) i.next());
430 connection = obtainConnection();
431 ResultSet resultSet = retriever.execute(connection);
433 boolean hasMore = false;
435 if (resultSet != null) {
436 result = new EntityList();
440 while (((aLimit == -1) || (position<aLimit)) && resultSet.next()) {
441 entity = makeEntityFromResultSet(resultSet);
446 hasMore = resultSet.next();
450 if (result != null) {
451 result.setOffset(anOffset);
452 result.setWhere(aWhereClause);
453 result.setOrder(anOrderByClause);
454 result.setStorage(this);
455 result.setLimit(aLimit);
458 result.setNextBatch(anOffset + aLimit);
461 if (anExtraTables==null && StoreUtil.extendsStorableEntity(entityClass)) {
462 StoreIdentifier sid = result.getStoreIdentifier();
463 logger.debug("CACHE (add): " + sid.toString());
468 catch (Throwable e) {
469 throw new DatabaseFailure(e);
473 if (connection != null) {
474 freeConnection(connection);
476 } catch (Throwable t) {
483 private Entity makeEntityFromResultSet(ResultSet rs) {
484 Map fields = new HashMap();
485 String theResult = null;
487 Entity returnEntity = null;
490 if (StoreUtil.extendsStorableEntity(entityClass)) {
491 StoreIdentifier searchSid = StorableObjectEntity.getStoreIdentifier(this,
493 Entity hit = (Entity) o_store.use(searchSid);
494 if (hit != null) return hit;
497 for (int i = 0; i < getFieldNames().size(); i++) {
498 type = fieldTypes[i];
500 if (type == java.sql.Types.LONGVARBINARY) {
501 InputStreamReader is =
502 (InputStreamReader) rs.getCharacterStream(i + 1);
505 char[] data = new char[32768];
506 StringBuffer theResultString = new StringBuffer();
509 while ((len = is.read(data)) > 0) {
510 theResultString.append(data, 0, len);
514 theResult = theResultString.toString();
521 theResult = getValueAsString(rs, (i + 1), type);
524 if (theResult != null) {
525 fields.put(getFieldNames().get(i), theResult);
529 if (entityClass != null) {
530 returnEntity = createNewEntity();
531 returnEntity.setFieldValues(fields);
533 if (returnEntity instanceof StorableObject) {
534 logger.debug("CACHE: ( in) " + returnEntity.getId() + " :" + mainTable);
535 o_store.add(((StorableObject) returnEntity).getStoreIdentifier());
539 throw new DatabaseExc("Internal Error: entityClass not set!");
542 catch (Throwable e) {
543 throw new DatabaseFailure(e);
550 * Inserts an entity into the database.
553 * @return the value of the primary key of the inserted record
555 public String insert(Entity anEntity) throws DatabaseFailure {
558 RecordInserter inserter =
559 new RecordInserter(mainTable, getPrimaryKeySequence());
561 String returnId = null;
562 Connection con = null;
568 for (int i = 0; i < getFieldNames().size(); i++) {
569 fieldName = (String) getFieldNames().get(i);
571 if (!fieldName.equals(primaryKeyField)) {
573 if (!anEntity.hasFieldValue(fieldName) && (
574 fieldName.equals("webdb_create") ||
575 fieldName.equals("webdb_lastchange"))) {
576 inserter.assignVerbatim(fieldName, "now()");
579 if (anEntity.hasFieldValue(fieldName)) {
580 inserter.assignString(fieldName, anEntity.getFieldValue(fieldName));
586 con = obtainConnection();
587 returnId = inserter.execute(con);
589 anEntity.setId(returnId);
599 * Updates an entity in the database
603 public void update(Entity theEntity) throws DatabaseFailure {
606 RecordUpdater generator = new RecordUpdater(getTableName(), theEntity.getId());
608 // build sql statement
609 for (int i = 0; i < getFieldNames().size(); i++) {
610 String field = (String) getFieldNames().get(i);
612 if (!(field.equals(primaryKeyField) ||
613 "webdb_create".equals(field) ||
614 "webdb_lastchange".equals(field) ||
615 binaryFields.contains(field))) {
617 if (theEntity.hasFieldValue(field)) {
618 generator.assignString(field, theEntity.getFieldValue(field));
624 if (hasField("webdb_lastchange")) {
625 generator.assignVerbatim("webdb_lastchange", "now()");
628 // special case: the webdb_create requires the field in yyyy-mm-dd HH:mm
629 // format so anything extra will be ignored. -mh
630 if (hasField("webdb_create") &&
631 theEntity.hasFieldValue("webdb_create")) {
632 // minimum of 10 (yyyy-mm-dd)...
633 if (theEntity.getFieldValue("webdb_create").length() >= 10) {
634 String dateString = theEntity.getFieldValue("webdb_create");
636 // if only 10, then add 00:00 so it doesn't throw a ParseException
637 if (dateString.length() == 10) {
638 dateString = dateString + " 00:00";
643 java.util.Date d = userInputDateFormat.parse(dateString);
644 generator.assignDateTime("webdb_create", d);
646 catch (ParseException e) {
647 throw new DatabaseFailure(e);
651 Connection connection = null;
654 connection = obtainConnection();
655 generator.execute(connection);
658 freeConnection(connection);
662 private void invalidateObject(String anId) {
663 // ostore send notification
664 if (StoreUtil.extendsStorableEntity(entityClass)) {
665 String uniqueId = anId;
667 if (entityClass.equals(StorableObjectEntity.class)) {
668 uniqueId += ("@" + mainTable);
671 logger.debug("CACHE: (del) " + anId);
673 StoreIdentifier search_sid =
674 new StoreIdentifier(entityClass,
675 StoreContainerType.STOC_TYPE_ENTITY, uniqueId);
676 o_store.invalidate(search_sid);
682 * @param id des zu loeschenden Datensatzes
683 * @return boolean liefert true zurueck, wenn loeschen erfolgreich war.
685 public boolean delete(String id) throws DatabaseFailure {
686 invalidateObject(id);
689 Connection connection = obtainConnection();
690 PreparedStatement statement = null;
693 statement = connection.prepareStatement("delete from " + mainTable + " where " + primaryKeyField + "=?");
694 statement.setInt(1, Integer.parseInt(id));
695 logQueryBefore("delete from " + mainTable + " where " + primaryKeyField + "=" + id + "");
696 resultCode = statement.executeUpdate();
698 catch (SQLException e) {
699 logger.warn("Can't delete record", e);
702 freeConnection(connection, statement);
707 return (resultCode > 0) ? true : false;
711 * Deletes entities based on a where clause
713 public int deleteByWhereClause(String aWhereClause) throws DatabaseFailure {
716 Statement stmt = null;
717 Connection con = null;
720 "delete from " + mainTable + " where " + aWhereClause;
722 //theLog.printInfo("DELETE " + sql);
724 con = obtainConnection();
725 stmt = con.createStatement();
726 res = stmt.executeUpdate(sql);
728 catch (Throwable e) {
729 throw new DatabaseFailure(e);
732 freeConnection(con, stmt);
738 /* noch nicht implementiert.
739 * @return immer false
741 public boolean delete(EntityList theEntityList) {
745 public ResultSet executeSql(Statement stmt, String sql)
746 throws DatabaseFailure, SQLException {
749 long startTime = System.currentTimeMillis();
751 rs = stmt.executeQuery(sql);
753 logQueryAfter(sql, (System.currentTimeMillis() - startTime));
755 catch (SQLException e) {
756 logQueryError(sql, (System.currentTimeMillis() - startTime), e);
763 private Map processRow(ResultSet aResultSet) throws DatabaseFailure {
765 Map result = new HashMap();
766 ResultSetMetaData metaData = aResultSet.getMetaData();
767 int nrColumns = metaData.getColumnCount();
768 for (int i=0; i<nrColumns; i++) {
769 result.put(metaData.getColumnName(i+1), getValueAsString(aResultSet, i+1, metaData.getColumnType(i+1)));
774 catch (Throwable e) {
775 throw new DatabaseFailure(e);
780 * Executes 1 sql statement and returns the results as a <code>List</code> of
783 public List executeFreeSql(String sql, int aLimit) throws DatabaseFailure, DatabaseExc {
784 Connection connection = null;
785 Statement statement = null;
787 List result = new ArrayList();
788 connection = obtainConnection();
789 statement = connection.createStatement();
790 ResultSet resultset = executeSql(statement, sql);
792 while (resultset.next() && result.size() < aLimit) {
793 result.add(processRow(resultset));
802 catch (Throwable e) {
803 throw new DatabaseFailure(e);
806 if (connection!=null) {
807 freeConnection(connection, statement);
813 * Executes 1 sql statement and returns the first result row as a <code>Map</code>s
814 * (<code>null</code> if there wasn't any row)
816 public Map executeFreeSingleRowSql(String anSqlStatement) throws DatabaseFailure, DatabaseExc {
818 List resultList = executeFreeSql(anSqlStatement, 1);
820 if (resultList.size()>0)
821 return (Map) resultList.get(0);
827 catch (Throwable t) {
828 throw new DatabaseFailure(t);
833 * Executes 1 sql statement and returns the first column of the first result row as a <code>String</code>s
834 * (<code>null</code> if there wasn't any row)
836 public String executeFreeSingleValueSql(String sql) throws DatabaseFailure, DatabaseExc {
837 Map row = executeFreeSingleRowSql(sql);
842 Iterator i = row.values().iterator();
844 return (String) i.next();
848 public int getSize(String where) throws SQLException, DatabaseFailure {
849 return getSize("", null, where);
852 * returns the number of rows in the table
854 public int getSize(String mainTablePrefix, List extraTables, String where) throws SQLException, DatabaseFailure {
856 String useTable = mainTable;
857 if (mainTablePrefix!=null && mainTablePrefix.trim().length()>0) {
858 useTable+=" "+mainTablePrefix;
860 StringBuffer countSql =
861 new StringBuffer("select count(*) from ").append(useTable);
862 // append extratables, if necessary
863 if (extraTables!=null) {
864 for (int i=0;i < extraTables.size();i++) {
865 if (!extraTables.get(i).equals("")) {
866 countSql.append( ", " + extraTables.get(i));
871 if ((where != null) && (where.length() != 0)) {
872 countSql.append( " where " + where);
875 Connection con = null;
876 Statement stmt = null;
878 logQueryBefore(countSql.toString());
879 long startTime = System.currentTimeMillis();
882 con = obtainConnection();
883 stmt = con.createStatement();
885 ResultSet rs = executeSql(stmt, countSql.toString());
888 result = rs.getInt(1);
891 catch (SQLException e) {
892 logger.error("Database.getSize: " + e.getMessage());
895 freeConnection(con, stmt);
897 logQueryAfter(countSql.toString(), (System.currentTimeMillis() - startTime));
902 public int executeUpdate(Statement stmt, String sql)
903 throws DatabaseFailure, SQLException {
907 long startTime = System.currentTimeMillis();
910 rs = stmt.executeUpdate(sql);
912 logQueryAfter(sql, (System.currentTimeMillis() - startTime));
914 catch (SQLException e) {
915 logQueryError(sql, (System.currentTimeMillis() - startTime), e);
922 public int executeUpdate(String sql)
923 throws DatabaseFailure, SQLException {
925 Connection con = null;
926 PreparedStatement pstmt = null;
929 long startTime = System.currentTimeMillis();
931 con = obtainConnection();
932 pstmt = con.prepareStatement(sql);
933 result = pstmt.executeUpdate();
934 logQueryAfter(sql, System.currentTimeMillis() - startTime);
936 catch (Throwable e) {
937 logQueryError(sql, System.currentTimeMillis() - startTime, e);
938 throw new DatabaseFailure("Database.executeUpdate(" + sql + "): " + e.getMessage(), e);
941 freeConnection(con, pstmt);
947 * Processes the metadata for the table this Database object is responsible for.
949 private void processMetaData(ResultSetMetaData aMetaData) throws DatabaseFailure {
950 fieldNames = new ArrayList();
951 fieldNameToType = new HashMap();
954 int numFields = aMetaData.getColumnCount();
955 fieldTypes = new int[numFields];
957 for (int i = 1; i <= numFields; i++) {
958 fieldNames.add(aMetaData.getColumnName(i));
959 fieldTypes[i - 1] = aMetaData.getColumnType(i);
960 fieldNameToType.put(aMetaData.getColumnName(i), new Integer(aMetaData.getColumnType(i)));
963 catch (Throwable e) {
964 throw new DatabaseFailure(e);
969 * Retrieves metadata from the table this Database object represents
971 private void acquireMetaData() throws DatabaseFailure {
972 Connection connection = null;
973 PreparedStatement statement = null;
974 String sql = "select * from " + mainTable + " where 0=1";
977 connection = obtainConnection();
978 statement = connection.prepareStatement(sql);
980 logger.debug("METADATA: " + sql);
981 ResultSet resultSet = statement.executeQuery();
983 processMetaData(resultSet.getMetaData());
989 catch (Throwable e) {
990 throw new DatabaseFailure(e);
993 freeConnection(connection, statement);
997 public Connection obtainConnection() throws DatabaseFailure {
999 return MirGlobal.getDatabaseEngine().obtainConnection();
1001 catch (Exception e) {
1002 throw new DatabaseFailure(e);
1006 public void freeConnection(Connection aConnection) throws DatabaseFailure {
1008 MirGlobal.getDatabaseEngine().releaseConnection(aConnection);
1010 catch (Throwable t) {
1011 logger.warn("Can't release connection: " + t.toString());
1015 public void freeConnection(Connection aConnection, Statement aStatement) throws DatabaseFailure {
1019 catch (Throwable t) {
1020 logger.warn("Can't close statement", t);
1023 freeConnection(aConnection);
1026 protected void _throwStorageObjectException(Exception e, String aFunction)
1027 throws DatabaseFailure {
1030 logger.error(e.getMessage() + aFunction);
1031 throw new DatabaseFailure(aFunction, e);
1037 * Invalidates any cached entity list
1039 private void invalidateStore() {
1040 // invalidating all EntityLists corresponding with entityClass
1041 if (StoreUtil.extendsStorableEntity(entityClass)) {
1042 StoreContainerType stoc_type =
1043 StoreContainerType.valueOf(entityClass, StoreContainerType.STOC_TYPE_ENTITYLIST);
1044 o_store.invalidate(stoc_type);
1049 * Retrieves a binary value
1051 public byte[] getBinaryField(String aQuery) throws DatabaseFailure, SQLException {
1052 Connection connection=null;
1053 Statement statement=null;
1054 InputStream inputStream;
1057 connection = obtainConnection();
1059 connection.setAutoCommit(false);
1060 statement = connection.createStatement();
1061 ResultSet resultSet = executeSql(statement, aQuery);
1063 if(resultSet!=null) {
1064 if (resultSet.next()) {
1065 if (resultSet.getMetaData().getColumnType(1) == java.sql.Types.BINARY) {
1066 return resultSet.getBytes(1);
1069 inputStream = resultSet.getBlob(1).getBinaryStream();
1070 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
1071 StreamCopier.copy(inputStream, outputStream);
1072 return outputStream.toByteArray();
1080 connection.setAutoCommit(true);
1082 catch (Throwable e) {
1083 logger.error("EntityImages.getImage resetting transaction mode failed: " + e.toString());
1084 e.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
1088 freeConnection(connection, statement);
1090 catch (Throwable e) {
1091 logger.error("EntityImages.getImage freeing connection failed: " +e.toString());
1096 catch (Throwable t) {
1097 logger.error("EntityImages.getImage failed: " + t.toString());
1098 t.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
1100 throw new DatabaseFailure(t);
1107 * Sets a binary value for a particular field in a record specified by its identifier
1109 public void setBinaryField(String aFieldName, String anObjectId, byte aData[]) throws DatabaseFailure, SQLException {
1110 PreparedStatement statement = null;
1111 Connection connection = obtainConnection();
1114 connection.setAutoCommit(false);
1116 // are we using bytea ?
1117 if (getFieldType(aFieldName) == java.sql.Types.BINARY) {
1118 statement = connection.prepareStatement(
1119 "update " + mainTable + " set " + aFieldName + " = ? where " + getIdFieldName() + "=" + Integer.parseInt(anObjectId));
1120 statement.setBytes(1, aData);
1121 statement.execute();
1122 connection.commit();
1126 PGConnection postgresqlConnection = (org.postgresql.PGConnection) ((DelegatingConnection) connection).getDelegate();
1127 LargeObjectManager lobManager = postgresqlConnection.getLargeObjectAPI();
1128 int oid = lobManager.create(LargeObjectManager.READ | LargeObjectManager.WRITE);
1129 LargeObject obj = lobManager.open(oid, LargeObjectManager.WRITE); // Now open the file File file =
1132 statement = connection.prepareStatement(
1133 "update " + mainTable + " set " + aFieldName + " = ? where " + getIdFieldName() + "=" + Integer.parseInt(anObjectId));
1134 statement.setInt(1, oid);
1135 statement.execute();
1136 connection.commit();
1140 connection.setAutoCommit(true);
1144 freeConnection(connection, statement);
1149 * Can be overridden to specify a primary key sequence name not named according to
1150 * the convention (tablename _id_seq)
1152 protected String getPrimaryKeySequence() {
1153 return mainTable+"_id_seq";
1157 * Can be called by subclasses to specify fields that are binary, and that shouldn't
1158 * be updated outside of {@link #setBinaryField}
1160 * @param aBinaryField The field name of the binary field
1162 protected void markBinaryField(String aBinaryField) {
1163 binaryFields.add(aBinaryField);
1166 private void logQueryBefore(String aQuery) {
1167 logger.debug("about to perform QUERY " + aQuery);
1168 // (new Throwable()).printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
1171 private void logQueryAfter(String aQuery, long aTime) {
1172 logger.info("QUERY " + aQuery + " took " + aTime + "ms.");
1175 private void logQueryError(String aQuery, long aTime, Throwable anException) {
1176 logger.error("QUERY " + aQuery + " took " + aTime + "ms, but threw exception " + anException.toString());
1179 private int getFieldType(String aFieldName) {
1180 if (fieldNameToType == null) {
1184 return ((Integer) fieldNameToType.get(aFieldName)).intValue();