*/
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;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+
import mir.config.MirPropertiesConfiguration;
+import mir.entity.AbstractEntity;
import mir.entity.Entity;
import mir.entity.EntityList;
import mir.entity.StorableObjectEntity;
import mir.log.LoggerWrapper;
import mir.misc.StringUtil;
-import mir.storage.store.*;
+import mir.storage.store.ObjectStore;
+import mir.storage.store.StorableObject;
+import mir.storage.store.StoreContainerType;
+import mir.storage.store.StoreIdentifier;
+import mir.storage.store.StoreUtil;
import mir.util.JDBCStringRoutines;
import mircoders.global.MirGlobal;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.sql.*;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.*;
+import org.apache.commons.dbcp.DelegatingConnection;
+import org.postgresql.PGConnection;
+import org.postgresql.largeobject.LargeObject;
+import org.postgresql.largeobject.LargeObjectManager;
/**
* Implements database access.
*
- * @version $Id: Database.java,v 1.44.2.23 2004/11/21 22:07:13 zapata 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;
protected static final ObjectStore o_store = ObjectStore.getInstance();
private static final int _millisPerHour = 60 * 60 * 1000;
protected List fieldNames;
protected int[] fieldTypes;
+ protected Map fieldNameToType;
- protected Class theEntityClass;
- protected boolean hasTimestamp = true;
+ protected Class entityClass;
private int defaultLimit;
TimeZone timezone;
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());
}
}
- /**
- * 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);
+ }
}
/**
return defaultLimit;
}
- /**
- * Liefert den Namen des Primary-Keys zur?ck. Wird die Variable nicht von
- * der erbenden Klasse ?berschrieben, so ist der Wert <code>PKEY</code>
- * @return Name des Primary-Keys
- */
- public String getIdName() {
+ public String getIdFieldName() {
return primaryKeyField;
}
}
/**
- * {@inheritDoc}
+ * Returns a list of field names for this <code>Database</code>
*/
public List getFieldNames() throws StorageObjectFailure {
if (fieldNames == null) {
- retrieveMetaData();
+ acquireMetaData();
}
return fieldNames;
}
// ask object store for object
- if (StoreUtil.extendsStorableEntity(theEntityClass)) {
+ if (StoreUtil.extendsStorableEntity(entityClass)) {
String uniqueId = id;
- if (theEntityClass.equals(StorableObjectEntity.class)) {
+ 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);
// cause StringUtil.splitString puts in emptyString
if (anExtraTables!=null && ((String) anExtraTables.get(0)).trim().equals("")){
- logger.debug("+++ made anExtraTables to null!");
anExtraTables=null;
}
// check o_store for entitylist
// only if no relational select
if (anExtraTables==null) {
- if (StoreUtil.extendsStorableEntity(theEntityClass)) {
- StoreIdentifier searchSid = new StoreIdentifier(theEntityClass,
+ if (StoreUtil.extendsStorableEntity(entityClass)) {
+ StoreIdentifier searchSid = new StoreIdentifier(entityClass,
StoreContainerType.STOC_TYPE_ENTITYLIST,
StoreUtil.getEntityListUniqueIdentifierFor(mainTable,
aWhereClause, anOrderByClause, anOffset, aLimit));
Statement statement = null;
ResultSet resultSet;
- int offsetCount = 0;
- int count = 0;
-
// build sql-statement
if ((aWhereClause != null) && (aWhereClause.trim().length() == 0)) {
aWhereClause = null;
}
- StringBuffer countSql =
- new StringBuffer("select count(*) from ").append(useTable);
StringBuffer selectSql =
new StringBuffer("select "+selection+" from ").append(useTable);
if (anExtraTables!=null) {
for (int i=0;i < anExtraTables.size();i++) {
if (!anExtraTables.get(i).equals("")) {
- countSql.append( ", " + anExtraTables.get(i));
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)) {
}
if ((aLimit > -1) && (anOffset > -1)) {
- selectSql.append(" LIMIT ").append(aLimit).append(" OFFSET ").append(anOffset);
+ selectSql.append(" LIMIT ").append(aLimit+1).append(" OFFSET ").append(anOffset);
}
// execute sql
try {
connection = obtainConnection();
statement = connection.createStatement();
+ boolean hasMore = false;
// selecting...
resultSet = executeSql(statement, selectSql.toString());
if (resultSet != null) {
theReturnList = new EntityList();
Entity theResultEntity;
- while (resultSet.next()) {
+ int position = 0;
+ while (((aLimit == -1) || (position<aLimit)) && resultSet.next()) {
theResultEntity = makeEntityFromResultSet(resultSet);
theReturnList.add(theResultEntity);
- offsetCount++;
+ position++;
}
+ hasMore = resultSet.next();
resultSet.close();
}
- // making entitylist infos
- count = offsetCount;
-
if (theReturnList != null) {
// now we decide if we have to know an overall count...
- count = offsetCount;
-
- if ((aLimit > -1) && (anOffset > -1)) {
- if (offsetCount == aLimit) {
- resultSet = executeSql(statement, countSql.toString());
-
- if (resultSet != null) {
- if (resultSet.next()) {
- count = resultSet.getInt(1);
- }
-
- resultSet.close();
- }
- else {
- logger.error("Could not count: " + countSql);
- }
- }
- }
-
- theReturnList.setCount(count);
theReturnList.setOffset(anOffset);
theReturnList.setWhere(aWhereClause);
theReturnList.setOrder(anOrderByClause);
theReturnList.setStorage(this);
theReturnList.setLimit(aLimit);
- if (anOffset >= aLimit) {
- theReturnList.setPrevBatch(anOffset - aLimit);
- }
-
- if ((anOffset + offsetCount) < count) {
+ if (hasMore) {
theReturnList.setNextBatch(anOffset + aLimit);
}
- if (anExtraTables==null && StoreUtil.extendsStorableEntity(theEntityClass)) {
+ if (anExtraTables==null && StoreUtil.extendsStorableEntity(entityClass)) {
StoreIdentifier sid = theReturnList.getStoreIdentifier();
logger.debug("CACHE (add): " + sid.toString());
o_store.add(sid);
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 {
Map theResultHash = new HashMap();
Entity returnEntity = null;
try {
- if (StoreUtil.extendsStorableEntity(theEntityClass)) {
+ if (StoreUtil.extendsStorableEntity(entityClass)) {
StoreIdentifier searchSid = StorableObjectEntity.getStoreIdentifier(this,
- theEntityClass, rs);
+ entityClass, rs);
Entity hit = (Entity) o_store.use(searchSid);
if (hit != null) return hit;
}
}
}
- if (theEntityClass != null) {
- returnEntity = (Entity) theEntityClass.newInstance();
- returnEntity.setStorage(this);
+ if (entityClass != null) {
+ returnEntity = createNewEntity();
returnEntity.setFieldValues(theResultHash);
if (returnEntity instanceof StorableObject) {
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");
/**
* 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 {
+ public String insert(Entity anEntity) throws StorageObjectFailure {
invalidateStore();
String returnId = null;
aValue = null;
// exceptions
- if (!theEntity.hasFieldValue(aField) && (
+ if (!anEntity.hasFieldValue(aField) && (
aField.equals("webdb_create") ||
aField.equals("webdb_lastchange"))) {
aValue = "NOW()";
}
else {
- if (theEntity.hasFieldValue(aField)) {
+ if (anEntity.hasFieldValue(aField)) {
aValue =
"'" +
- JDBCStringRoutines.escapeStringLiteral(theEntity.getFieldValue(aField)) + "'";
+ JDBCStringRoutines.escapeStringLiteral(anEntity.getFieldValue(aField)) + "'";
}
}
.append(") values (").append(v).append(")");
String sql = sqlBuf.toString();
- logger.info("INSERT: " + sql);
+ logQueryBefore(sql);
con = obtainConnection();
con.setAutoCommit(false);
pstmt = con.prepareStatement(sql);
// pstmt = con.prepareStatement("select currval('" + + "_id_seq')");
returnId = getLatestInsertedId(con);
- theEntity.setId(returnId);
+ anEntity.setId(returnId);
}
catch (SQLException sqe) {
throwSQLException(sqe, "insert");
}
sql.append(" where id=").append(id);
- logger.info("UPDATE: " + sql);
+ logQueryBefore(sql.toString());
try {
con = obtainConnection();
con.setAutoCommit(true);
}
catch (Exception e) {
- ;
+
+
}
freeConnection(con, pstmt);
*/
public boolean delete(String id) throws StorageObjectFailure {
// ostore send notification
- if (StoreUtil.extendsStorableEntity(theEntityClass)) {
+ if (StoreUtil.extendsStorableEntity(entityClass)) {
String uniqueId = id;
- if (theEntityClass.equals(StorableObjectEntity.class)) {
+ 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);
}
String sql =
"delete from " + mainTable + " where " + primaryKeyField + "='" + id + "'";
- logger.debug("DELETE " + sql);
+ logQueryBefore(sql);
try {
con = obtainConnection();
stmt = con.createStatement();
/**
* Deletes entities based on a where clause
- *
- * @param aWhereClause
- * @return
- * @throws StorageObjectFailure
*/
public int deleteByWhereClause(String aWhereClause) throws StorageObjectFailure {
invalidateStore();
public ResultSet executeSql(Statement stmt, String sql)
throws StorageObjectFailure, SQLException {
ResultSet rs;
+ logQueryBefore(sql);
long startTime = System.currentTimeMillis();
-
try {
rs = stmt.executeQuery(sql);
- logger.info((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;
}
}
}
+ /**
+ * Executes 1 sql statement and returns the results as a <code>List</code> of
+ * <code>Map</code>s
+ */
public List executeFreeSql(String sql, int aLimit) throws StorageObjectFailure, StorageObjectExc {
Connection connection = null;
Statement statement = null;
freeConnection(connection, statement);
}
}
- };
+ }
+ /**
+ * Executes 1 sql statement and returns the first result row as a <code>Map</code>s
+ * (<code>null</code> 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 {
}
catch (Throwable t) {
throw new StorageObjectFailure(t);
}
- };
+ }
+ /**
+ * Executes 1 sql statement and returns the first column of the first result row as a <code>String</code>s
+ * (<code>null</code> if there wasn't any row)
+ */
public String executeFreeSingleValueSql(String sql) throws StorageObjectFailure, StorageObjectExc {
Map row = executeFreeSingleRowSql(sql);
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);
*/
public int getSize(String mainTablePrefix, List extraTables, String where) throws SQLException, StorageObjectFailure {
- long startTime = System.currentTimeMillis();
-
String useTable = mainTable;
if (mainTablePrefix!=null && mainTablePrefix.trim().length()>0) {
useTable+=" "+mainTablePrefix;
Connection con = null;
Statement stmt = null;
int result = 0;
+ logQueryBefore(countSql.toString());
+ long startTime = System.currentTimeMillis();
try {
con = obtainConnection();
finally {
freeConnection(con, stmt);
}
- logger.info((System.currentTimeMillis() - startTime) + "ms. for: " + countSql);
+ logQueryAfter(countSql.toString(), (System.currentTimeMillis() - startTime));
return result;
}
public int executeUpdate(Statement stmt, String sql)
throws StorageObjectFailure, SQLException {
int rs;
+
+ logQueryBefore(sql);
long startTime = System.currentTimeMillis();
try {
rs = stmt.executeUpdate(sql);
- logger.info((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;
}
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 = 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.info((System.currentTimeMillis() - startTime) + "ms. for: " + sql);
return result;
}
*/
private void processMetaData(ResultSetMetaData aMetaData) throws StorageObjectFailure {
fieldNames = new ArrayList();
+ fieldNameToType = new HashMap();
try {
int numFields = aMetaData.getColumnCount();
for (int i = 1; i <= numFields; i++) {
fieldNames.add(aMetaData.getColumnName(i));
fieldTypes[i - 1] = aMetaData.getColumnType(i);
+ fieldNameToType.put(aMetaData.getColumnName(i), new Integer(aMetaData.getColumnType(i)));
}
}
catch (SQLException e) {
/**
* Retrieves metadata from the table this Database object represents
*/
- private void retrieveMetaData() throws StorageObjectFailure {
+ private void acquireMetaData() throws StorageObjectFailure {
Connection connection = null;
PreparedStatement statement = null;
String sql = "select * from " + mainTable + " where 0=1";
}
}
catch (SQLException e) {
- throwSQLException(e, "retrieveMetaData");
+ throwSQLException(e, "acquireMetaData");
}
finally {
freeConnection(connection, statement);
* Invalidates any cached entity list
*/
private void invalidateStore() {
- // invalidating all EntityLists corresponding with theEntityClass
- if (StoreUtil.extendsStorableEntity(theEntityClass)) {
+ // invalidating all EntityLists corresponding with entityClass
+ if (StoreUtil.extendsStorableEntity(entityClass)) {
StoreContainerType stoc_type =
- StoreContainerType.valueOf(theEntityClass, StoreContainerType.STOC_TYPE_ENTITYLIST);
+ StoreContainerType.valueOf(entityClass, StoreContainerType.STOC_TYPE_ENTITYLIST);
o_store.invalidate(stoc_type);
}
}
if(resultSet!=null) {
if (resultSet.next()) {
- inputStream = resultSet.getBlob(1).getBinaryStream();
- imageInputStream = new BinaryFieldInputStream(inputStream, connection, statement);
+ 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();
}
}
/**
- * Sets a binary value. The query is supposed to contain 1 ? denoting where the
- * binary value should be inserted.
- *
- * e.g. <code>update images set image_data = ? where id= 22</code>
+ * Sets a binary value for a particular field in a record specified by its identifier
*/
- public void setBinaryField(String aQuery, byte aData[]) throws StorageObjectFailure, SQLException {
+ public void setBinaryField(String aFieldName, String anObjectId, byte aData[]) throws StorageObjectFailure, SQLException {
PreparedStatement statement = null;
Connection connection = obtainConnection();
+
try {
connection.setAutoCommit(false);
try {
- statement = connection.prepareStatement(aQuery);
- statement.setBinaryStream(1, new ByteArrayInputStream(aData), aData.length);
- statement.execute();
- connection.commit();
+ // 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);
}
}
+ 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