All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.draagon.meta.manager.db.driver.GenericSQLDriver Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2003 Draagon Software LLC. All Rights Reserved.
 *
 * This software is the proprietary information of Draagon Software LLC.
 * Use is subject to license terms.
 */

package com.draagon.meta.manager.db.driver;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.draagon.meta.MetaClass;
import com.draagon.meta.MetaException;
import com.draagon.meta.MetaField;
import com.draagon.meta.manager.QueryOptions;
import com.draagon.meta.manager.db.DatabaseDriver;
import com.draagon.meta.manager.db.ObjectManagerDB;
import com.draagon.meta.manager.db.ObjectMappingDB;
import com.draagon.meta.manager.db.SubSelectValue;
import com.draagon.meta.manager.db.defs.BaseDef;
import com.draagon.meta.manager.db.defs.BaseTableDef;
import com.draagon.meta.manager.db.defs.ColumnDef;
import com.draagon.meta.manager.db.defs.ForeignKeyDef;
import com.draagon.meta.manager.db.defs.IndexDef;
import com.draagon.meta.manager.db.defs.InheritenceDef;
import com.draagon.meta.manager.db.defs.NameDef;
import com.draagon.meta.manager.db.defs.SequenceDef;
import com.draagon.meta.manager.db.defs.TableDef;
import com.draagon.meta.manager.db.defs.ViewDef;
import com.draagon.meta.manager.exp.Expression;
import com.draagon.meta.manager.exp.ExpressionGroup;
import com.draagon.meta.manager.exp.ExpressionOperator;
import com.draagon.meta.manager.exp.Range;
import com.draagon.meta.manager.exp.SortOrder;

/**
 * The Object Manager Base is able to add, update, delete, and retrieve objects
 * of those types from a datastore.
 */
public class GenericSQLDriver implements DatabaseDriver {
	private static Log log = LogFactory.getLog(GenericSQLDriver.class);

	private ObjectManagerDB mManager = null;

	/**
	 * This is to handle arguments for the constructed SQL query to ensure proper type conversion
	 * @author Doug
	 */
	public class SQLArg {
		private MetaField metaField = null;
		private Object value = null;
		public SQLArg( MetaField f, Object value ) {
			this.metaField = f;
			this.value = value;
		}
		public MetaField getMetaField() { return metaField; }
		public Object getValue() { return value; }
	}

	public GenericSQLDriver() {
	}

	public void setManager(ObjectManagerDB man) {
		mManager = man;
	}

	/**
	 * Returns the Object Manager
	 */
	public ObjectManagerDB getManager() {
		return mManager;
	}

	// /**
	// * Returns whether the auto id is retrieved prior to creation
	// */
	// @Override
	// public int getAutoType() {
	// return AUTO_PRIOR;
	// }

	@Override
	public boolean checkTable(Connection c, TableDef table) throws SQLException {
		return checkBaseTable(c, table);
	}

	@Override
	public boolean checkView(Connection c, ViewDef view) throws SQLException {
		return checkBaseTable(c, view);
	}

	/**
	 * Checks for the existence of the base table and optionally creates it if
	 * it doesn't exist
	 * 
	 * @param c
	 *            Database connection to use
	 * @param baseTable
	 *            Base Table Definition (Table or View)
	 * @param autoCreate
	 *            Whether to auto create the table or view
	 * @return Whether the table or view exists
	 * @throws SQLException
	 *             Exception if it exists in an invalid format
	 */
	protected boolean checkBaseTable(Connection c, BaseTableDef baseTable)
			throws SQLException {
		String schema = baseTable.getNameDef().getSchema();
		String name = baseTable.getNameDef().getName();

		// VALIDATE TABLE OR VIEW
		ResultSet rs = c.getMetaData().getTables(null, schema, name, null);
		try {
			boolean found = false;
			while (rs.next()) {
				if (name.equalsIgnoreCase(rs.getString(3))) {
					found = true;
					break;
				}
			}

			if (!found) {
				// throw new TableDoesNotExistException( "Table or View [" +
				// baseTable.getNameDef() + "] does not exist" );
				return false;
			}
		} finally {
			rs.close();
		}

		// VALIDATE THE COLUMNS
		for (ColumnDef col : baseTable.getColumns()) {
			rs = c.getMetaData().getColumns(null, schema, name, col.getName());
			try {
				boolean found = false;
				while (rs.next()) {
					if (col.getName().equalsIgnoreCase(rs.getString(4))) {
						found = true;
						break;
					}
				}

				if (!found)
					throw new SQLException("Table or View ["
							+ baseTable.getNameDef()
							+ "] does not have a Column [" + col.getName()
							+ "]");
			} finally {
				rs.close();
			}
		}

		return true;
	}

	/**
	 * Gets the next sequence for a given MetaClass
	 */
	protected String getNextAutoId(Connection conn, ColumnDef col) throws SQLException {
		// String table = getManager().getTableName( mc );
		// if ( table == null )
		// throw new MetaException( "MetaClass [" + mc + "] has no table
		// defined" );

		// String col = getManager().getColumnName( mf );
		// if ( col == null )
		// throw new MetaException( "MetaField [" + mf + "] has no column
		// defined" );

		StringBuilder query = new StringBuilder();
		try {
			// Get next next MAX() sequence
			Statement s = conn.createStatement();
			try {
				query.append("SELECT MAX( ").append(col.getName()).append(" )")
						.append("FROM ").append(
								getProperName(col.getBaseTable().getNameDef()));

				ResultSet rs = s.executeQuery(query.toString());

				try {
					if (!rs.next())
						return "1";

					String tmp = rs.getString(1);

					if (tmp == null)
						return "1";

					int i = Integer.parseInt(tmp) + 1;

					return "" + i;
				} finally {
					rs.close();
				}
			} finally {
				s.close();
			}
		} catch (SQLException e) {
			log.error("Unable to get next id for column [" + col
					+ "] with query [" + query + "]: " + e.getMessage(), e);
			throw new SQLException("Unable to get next id for column [" + col
					+ "]: " + e.getMessage(), e);
		}
	}

	/**
	 * Creates a table in the database
	 */
	@Override
	public void createTable(Connection c, TableDef tableDef)
			throws SQLException {
		throw new UnsupportedOperationException("CREATE TABLE NOT IMPLEMENTED!");
	}

	/**
	 * Deletes a table from the database
	 */
	@Override
	public void deleteTable(Connection c, TableDef tableDef)
			throws SQLException {
		throw new UnsupportedOperationException("DELETE TABLE NOT IMPLEMENTED!");
	}

	/**
	 * Creates a view in the database
	 */
	@Override
	public void createView(Connection c, ViewDef viewDef) throws SQLException {
		throw new UnsupportedOperationException("CREATE VIEW NOT IMPLEMENTED!");
	}

	/**
	 * Creates the sequence in the database
	 */
	public void createSequence(Connection c, SequenceDef sequenceDef)
			throws SQLException {
		throw new UnsupportedOperationException(
				"CREATE SEQUENCE NOT IMPLEMENTED!");
	}

	/**
	 * Creates the index in the database
	 */
	public void createIndex(Connection c, IndexDef indexDef)
			throws SQLException {
		// Do Nothing
	}

	/**
	 * Creates the foreign keys for the table in the database
	 */
	@Override
	public void createForeignKey(Connection c, ForeignKeyDef foreignKeyDef)
			throws SQLException {
		// DO Nothing
	}

	/**
	 * Returns the proper name of the table or view
	 * 
	 * @param nameDef
	 * @return
	 */
	public String getProperName(NameDef nameDef) {
		return nameDef.getFullname();
	}

	@Override
	public boolean create(Connection c, MetaClass mc, ObjectMappingDB omdb,
			Object o) throws SQLException {
		
		// Check if there is table inheritence going on, and if so create the super table first
		BaseDef base = omdb.getDBDef();
		if ( base instanceof TableDef ) {
			TableDef table = (TableDef) base;
			
			InheritenceDef inheritence = table.getInheritence(); 
			if ( inheritence != null ) {
				ObjectMappingDB smom = (ObjectMappingDB) omdb.getSuperMapping();
				
				// Set the discriminator values
				if ( inheritence.getDiscriminatorName() != null ) {
					MetaField df = omdb.getField( inheritence.getDiscriminatorName() );
					df.setString( o, inheritence.getDiscriminatorValue() );
				}
				
				// Create the super classes table entry
				if ( !create( c, mc, smom, o )) {
					throw new SQLException( "Super table entry could not be created for mapping [" + smom + "]" );
				}
				
				// Set the mapping field
				MetaField rf = omdb.getField( inheritence.getRefColumn() );
				MetaField f = omdb.getField( inheritence.getColumnName() );
				f.setObject( o, rf.getObject( o ));
			}
		}
		
		// Now create the entry for this object
		PreparedStatement s = getInsertStatement(c, mc, omdb, o);

		try {
			s.execute();

			if (s.getUpdateCount() < 1) {
				return false;
			} else {
				return true;
			}
		} finally {
			s.close();
		}
	}

	@Override
	public boolean read(Connection c, MetaClass mc, ObjectMappingDB mapping,
			Object o, Expression exp) throws SQLException {

		// Get the fields to read
		Collection fields = mapping.getMetaFields();

		QueryOptions qo = new QueryOptions(exp);
		qo.setRange(1, 1);

		PreparedStatement s = getSelectStatementWhere(c, mc, mapping, fields, qo);

		try {
			ResultSet rs = s.executeQuery();

			try {
				if (rs.next()) {

					// Parse the Object
					parseObject(rs, fields, mc, o);

					return true;
				}

				return false;
			} finally {
				rs.close();
			}
		} finally {
			s.close();
		}
	}

	@Override
	public boolean update( Connection c, MetaClass mc, ObjectMappingDB omdb,
			Object o, Collection fields, Collection keys, 
			MetaField dirtyField, Object dirtyValue ) throws SQLException {
		
		Expression exp = null;
		
		// Check if there is table inheritence going on, and if so delete the super table first
		BaseDef base = omdb.getDBDef();
		if ( base instanceof TableDef ) {
			TableDef table = (TableDef) base;
			
			InheritenceDef inheritence = table.getInheritence(); 
			if ( inheritence != null ) {

				ObjectMappingDB smom = (ObjectMappingDB) omdb.getSuperMapping();

				// Setup the key
				MetaField rf = omdb.getField( inheritence.getRefColumn() );
				Collection pkeys = new ArrayList();
				pkeys.add( rf );
				
				// Create the super classes table entry
				if ( !update( c, mc, smom, o, fields, pkeys, dirtyField, dirtyValue )) {
					return false;
					// throw new SQLException( "Super table entry could not be updated for mapping [" + smom + "]" );
				}
				
				// Set the mapping field
				MetaField f = omdb.getField( inheritence.getColumnName() );
				
				// Set the expression to delete
				exp = new Expression( f.getName(), rf.getObject( o ));
				
				// Can only have dirty fields on the highest level of inheritence
				dirtyField = null;
				dirtyValue = null;
			}
		}
		
		// If there wasn't inheritence, then generate the delete where clause
		if ( exp == null ) {			
			// Generate the keys expression
			for( MetaField mf : keys ) {
				//if ( omdb.isInThisMap( mf )) {
					Expression e = new Expression( mf.getName(), mf.getObject( o ));
					if ( exp == null ) exp = e;
					else exp = exp.and( e );
				//}
			}
		}
				
		// Add the dirty field expression
		if ( dirtyField != null ) {
			
			Expression e = new Expression( dirtyField.getName(), dirtyValue );
			if ( exp == null ) exp = e;
			else exp = exp.and( e );
		} 
		
		//setAutoFields( c, mc, omdb, fields, o, ObjectManager.UPDATE );
		
		Collection fieldsInMap = new ArrayList();
		for ( MetaField mf : fields ) {
			if ( omdb.isInThisMap( mf )) {
				fieldsInMap.add( mf );
			}
		}

		if ( fieldsInMap.size() == 0 ) return true;
			
		PreparedStatement s = getUpdateStatement(c, omdb, fieldsInMap, mc, o, exp);

		try {
			int rc = s.executeUpdate(); 
			if ( rc > 0 ) return true;
			else return false;
		} finally {
			s.close();
		}
	}

	@Override
	public boolean delete( Connection c, MetaClass mc, ObjectMappingDB omdb,
			Object o, Collection keys ) 
		throws SQLException {
				
		// Check if there is table inheritence going on, and if so delete the super table first
		BaseDef base = omdb.getDBDef();
		if ( base instanceof TableDef ) {
			TableDef table = (TableDef) base;
			
			InheritenceDef inheritence = table.getInheritence(); 
			if ( inheritence != null ) {
				ObjectMappingDB smom = (ObjectMappingDB) omdb.getSuperMapping();

				MetaField rf = omdb.getField( inheritence.getRefColumn() );
				Collection pkeys = new ArrayList();
				pkeys.add( rf );

				// Set the mapping field
				MetaField f = omdb.getField( inheritence.getColumnName() );
				
				// Set the expression to delete
				Expression exp = new Expression( f.getName(), rf.getObject( o ));

				// Delete the higher level table first
				boolean result = executeDelete( c, mc, omdb, exp );
				
				// Return a false if it was not deleteable
				if ( result == false ) return false;
				
				// Delete the super classes table entry
				if ( !delete( c, mc, smom, o, pkeys )) {
					throw new SQLException( "Super table entry could not be deleted for mapping [" + smom + "]" );
				}
				
				return result;
			}
		}
		
		// If there wasn't inheritence, then generate the delete where clause
		
		// Generate the keys expression
		Expression exp = null;
		for( MetaField mf : keys ) {
			Expression e = new Expression( mf.getName(), mf.getObject( o ));
			if ( exp == null ) exp = e;
			else exp = exp.and( e );
		}
		
		//setAutoFields( c, mc, omdb, fields, o, ObjectManager.UPDATE );
		
		return executeDelete( c, mc, omdb, exp );
	}
	
	/** Perform the actual delete call */
	protected boolean executeDelete( Connection c, MetaClass mc, ObjectMappingDB omdb, Expression exp ) throws MetaException, SQLException {
		
		PreparedStatement s = getDeleteStatementWhere(c, mc, omdb, exp);

		try {
			int rc = s.executeUpdate(); 
			if ( rc == 1 ) return true;
			else return false;
		} finally {
			s.close();
		}		
	}

	@Override
	public long getCount(Connection c, MetaClass mc,
			ObjectMappingDB mapping, Expression exp ) throws SQLException {
		
		PreparedStatement s = getCountStatementWhere(c, mc, mapping, exp );
		
		try {
			ResultSet rs = s.executeQuery();
			try {
				if (rs.next()) {
					return rs.getLong( 1 );
				} else {
					return 0;
				}
			} finally {
				rs.close();
			}
		} finally {
			s.close();
		}		
	}
	
	@Override
	public Collection readMany(Connection c, MetaClass mc,
			ObjectMappingDB mapping, QueryOptions options) throws SQLException {

		if ( options.withLock() && !( mapping.getDBDef() instanceof TableDef )) {
			throw new SQLException( "Can only lock when reading from a Table, not a [" + mapping.getDBDef() + "]" );
		}
		
		// Get the fields to read
		Collection fields = options.getFields();
		if (fields == null || fields.size() == 0) {
			fields = mapping.getMetaFields();
		}

		PreparedStatement s = getSelectStatementWhere(c, mc, mapping, fields, options);

		ArrayList objects = new ArrayList();

		try {
			ResultSet rs = s.executeQuery();

			// If the range is not supported in the query, then we need to do it manually
			Range range = null;
			if ( !supportsRangeInQuery() ) options.getRange();
			
			int index = 1;

			try {
				while (rs.next()) {
					if (range == null
							|| (index >= range.getStart() && index <= range
									.getEnd())) {
						// Get a new Object
						Object o = getManager().getNewObject(mc);

						// Parse the Object
						parseObject(rs, fields, mc, o);

						// Add the object to the returned array
						objects.add(o);
					}

					index++;
				}
			} finally {
				rs.close();
			}
		} finally {
			s.close();
		}

		return objects;
	}

	@Override
	public int updateMany( Connection c, MetaClass mc, ObjectMappingDB mapping,
			Map values, Expression exp ) throws SQLException {
		
		throw new UnsupportedOperationException( "UpdateMany not supported by this Driver" );
		
		/*PreparedStatement s = getUpdateStatement( c, mapping, fields, mc, o, exp);

		try {
			return s.executeUpdate();
		} finally {
			s.close();
		}*/
	}

	@Override
	public int deleteMany(Connection c, MetaClass mc, ObjectMappingDB mapping,
			Expression exp) throws SQLException {

		PreparedStatement s = getDeleteStatementWhere( c, mc, mapping, exp );

		try {
			return s.executeUpdate();
		} finally {
			s.close();
		}
	}

	/**
	 * Gets the string to append to the INSERT call to request the generated ids
	 */
	// public String getInsertAppendString( MetaClass mc )
	// {
	// throw new UnsupportedOperationException( "The GenericDriver cannot
	// retrieve field ids" );
	// }
	/**
	 * Used if AUTO_DURING to retrieve the ids for the specified class
	 */
	// @Override
	// public void getAutoIdentifiers( PreparedStatement s, MetaClass mc, Object
	// o ) throws SQLException {
	// throw new UnsupportedOperationException( "The GenericDriver cannot
	// retrieve field ids" );
	// }
	/**
	 * Gets the next sequence for a given MetaClass
	 */
	// @Override
	// public String getLastAutoId( Connection c, ColumnDef col ) throws
	// SQLException {
	// throw new UnsupportedOperationException( "The GenericDriver cannot
	// retrieve field ids" );
	// }
	/**
	 * The SQL query to append to a SQL SELECT to lock the returned rows
	 */
	public String getLockString() {
		throw new UnsupportedOperationException(
				"The GenericDriver cannot lock rows for updating");
	}

	public String getDateFormat() {
		return "yyyy-MM-dd HH:mm:ss";
	}
	
	
	/** Returns whether the drive supports the Range within the query, i.e. LIMIT */
	protected boolean supportsRangeInQuery() {
		return false;
	}
	
	/** Returns the SQL portion of the range string */
	public String getRangeString( Range range ) {
		throw new UnsupportedOperationException(
			"The GenericDriver cannot perform a range during select");
	}	

	/*private void setAutoFields(Connection c, MetaClass mc,
			ObjectMappingDB omdb, Collection fields, Object o,
			int mode ) throws SQLException {
		
		// Iterate the fields looking for auto creation fields
		for ( MetaField f : fields ) {
			
			// Switch off the column's auto type field
			ColumnDef colDef = (ColumnDef) omdb.getArgDef( f );
			switch( colDef.getAutoType() ) {
			
			// If an id, get the next sequence 
			case ColumnDef.AUTO_ID:
				if ( ObjectManager.CREATE == mode ) f.setString( o, getNextAutoId( c , colDef ));
				break;
				
			// If the create date, then set it on creates
			case ColumnDef.AUTO_DATE_CREATE:
				if ( ObjectManager.CREATE == mode ) f.setDate( o, new Date() );
				break;
				
			// If it's the update date, then set that on create and update
			case ColumnDef.AUTO_DATE_UPDATE:
				f.setDate( o, new Date() );
				break;
			}
		}
	}*/
	
	/**
	 * Concatonates the list of field names together
	 */
	protected String getFieldString( ObjectMappingDB omdb, Collection fields, String prefix ) {
		return getFieldString( omdb, fields, true, prefix );
	}

	/**
	 * Concatonates the list of field names together
	 */
	protected String getFieldString( ObjectMappingDB omdb, Collection fields, boolean selectOnly, String prefix ) {

		// Setup the select fields string
		StringBuilder buf = new StringBuilder();
		int j = 0;
		for ( MetaField mf : fields ) {
			
			//BaseTableDef base = (BaseTableDef) omdb.getDBDef();
			
			ColumnDef colDef = (ColumnDef) omdb.getArgDef(mf);
			//while ( colDef == null ) {
			//	if ( base instanceof TableDef ) {					
			//	}
			//}
			
			// Do not use the field if it's an auto id that is set after after
			// creation/update -- only relevant on some drivers (MSSQL)
			//if (!selectOnly && colDef.isAutoIncrementor() )
			//	continue;

			if (j > 0) buf.append(",");

			if ( prefix != null ) {
				buf.append( prefix ).append( "." );
			}
			buf.append( colDef.getName() );

			j++;
		}

		return buf.toString();
	}

	/**
	 * 

* Concatonates the list of ?'s for each field in the collection *

*/ protected String getValueStringForFields( ObjectMappingDB omdb, Collection fields ) { return getQuestionCommaString( fields.size() ); /*int j = 0; for ( MetaField mf : fields ) { ColumnDef colDef = (ColumnDef) omdb.getArgDef(mf); // Do not use the field if it's an auto id that is set after after // creation/update if ( colDef.isAutoIncrementor() ) j++; } return getQuestionCommaString(j);*/ } /** *

* Concatonates the list of ?'s for each field in the collection *

*/ protected String getQuestionCommaString(int size) throws MetaException { // Setup the select fields string StringBuilder buf = new StringBuilder(); for (int j = 0; j < size; j++) { if (j > 0) buf.append(","); buf.append('?'); } return buf.toString(); } /** * Gets the SQL SET clause for the fields of a class */ protected String getSetString( ObjectMappingDB omdb, Collection fields ) { StringBuilder set = new StringBuilder(); // Setup the SET clause for ( MetaField mf : fields ) { ColumnDef colDef = (ColumnDef) omdb.getArgDef(mf); // Don't try to set the id field if ( colDef.isAutoIncrementor() ) continue; if (set.length() > 0) set.append(", "); set.append( colDef.getName() ); set.append( "=?" ); } return set.toString(); } /** * Gets the ORDER BY clause */ protected String getOrderString( MetaClass mc, ObjectMappingDB omdb, SortOrder order ) throws MetaException { StringBuilder b = new StringBuilder(); boolean first = true; while (order != null) { if (first) first = false; else b.append(", "); MetaField mf = mc.getMetaField(order.getField()); char prefix = 'A'; ColumnDef colDef = (ColumnDef) omdb.getArgDef(mf); if (colDef == null) { throw new MetaException( "MetaField [" + mf + "] has no column mapping for [" + omdb + "]" ); } if (mf.getType() == MetaField.STRING) { b.append("UPPER("); b.append( prefix ).append( "." ); b.append(colDef.getName()); b.append(')'); } else { b.append( prefix ).append( "." ); b.append(colDef.getName()); } if (order.getOrder() == SortOrder.DESC) b.append(" DESC"); order = order.getNext(); } return b.toString(); } /** * Gets the SQL WHERE clause for the fields of a class */ protected String getExpressionString(MetaClass mc, ObjectMappingDB omdb, Expression exp, ArrayList args, String prefix ) throws MetaException { StringBuilder set = new StringBuilder(); if (exp instanceof ExpressionGroup) { set.append("( "); set.append(getExpressionString(mc, omdb, ((ExpressionGroup) exp ) .getGroup(), args, prefix )); set.append(" )"); } else if (exp instanceof ExpressionOperator) { ExpressionOperator oper = (ExpressionOperator) exp; set.append(getExpressionString(mc, omdb, oper.getExpressionA(), args, prefix )); if (oper.getOperator() == ExpressionOperator.AND) set.append(" AND "); else set.append(" OR "); set.append(getExpressionString(mc, omdb, oper.getExpressionB(), args, prefix )); } else if (exp.isSpecial()) { throw new IllegalArgumentException( "Unsupported Special Expression [" + exp + "]"); } else { MetaField f = mc.getMetaField(exp.getField()); ColumnDef colDef = (ColumnDef) omdb.getArgDef(f); if (colDef == null) { throw new MetaException( "MetaField [" + f + "] has no column mapping defined for [" + omdb + "]" ); } int c = exp.getCondition(); if (c == Expression.CONTAIN || c == Expression.NOT_CONTAIN || c == Expression.START_WITH || c == Expression.NOT_START_WITH || c == Expression.END_WITH || c == Expression.NOT_END_WITH || c == Expression.EQUALS_IGNORE_CASE ) { set.append("UPPER("); if ( prefix != null ) { set.append( prefix ).append( "." ); } set.append(colDef.getName()); set.append(')'); } else { if ( prefix != null ) { set.append( prefix ).append( "." ); } set.append(colDef.getName()); } set.append(' '); if (exp.getValue() == null) { switch (c) { case Expression.EQUAL: set.append("IS NULL"); break; case Expression.NOT_EQUAL: set.append("IS NOT NULL"); break; default: throw new IllegalArgumentException( "Null values not allowed for condition [" + Expression.condStr(c) + "]"); } } else if ( exp.getValue() instanceof SubSelectValue ) { if ( c != Expression.EQUAL && c != Expression.NOT_EQUAL ) { throw new IllegalArgumentException( "Can only have a value of type Collection for EQUAL or NOT_EQUAL comparisons" ); } if ( c == Expression.NOT_EQUAL ) set.append( "NOT " ); set.append( "IN (" ); set.append( ((SubSelectValue) exp.getValue() ).getSql() ); set.append( ")" ); } else if ( exp.getValue() instanceof Collection ) { if ( c != Expression.EQUAL && c != Expression.NOT_EQUAL ) { throw new IllegalArgumentException( "Can only have a value of type Collection for EQUAL or NOT_EQUAL comparisons" ); } if ( c == Expression.NOT_EQUAL ) set.append( "NOT " ); set.append( "IN (" ); boolean first = true; for( Object o : ((Collection) exp.getValue())) { if ( first ) { first = false; } else { set.append( "," ); } set.append( "?" ); args.add( new SQLArg( f, o )); } set.append( ")" ); } else { // Add these on as arguments switch (c) { case Expression.EQUAL: set.append("= ?"); break; case Expression.NOT_EQUAL: set.append("<> ?"); break; case Expression.GREATER: set.append("> ?"); break; case Expression.LESSER: set.append("< ?"); break; case Expression.EQUAL_GREATER: set.append(">= ?"); break; case Expression.EQUAL_LESSER: set.append("<= ?"); break; case Expression.EQUALS_IGNORE_CASE: case Expression.CONTAIN: case Expression.START_WITH: case Expression.END_WITH: set.append("LIKE UPPER(?)"); break; case Expression.NOT_CONTAIN: case Expression.NOT_START_WITH: case Expression.NOT_END_WITH: set.append("NOT LIKE UPPER(?)"); break; default: throw new IllegalArgumentException( "Unsupported Expression Conditional (" + exp.getCondition() + ") for Expression [" + exp + "]"); } // Only add an argument if it's not null if (c == Expression.EQUAL || c == Expression.NOT_EQUAL || c == Expression.GREATER || c == Expression.LESSER || c == Expression.EQUAL_GREATER || c == Expression.EQUAL_LESSER) { args.add( new SQLArg( f, exp.getValue() )); } else if (c == Expression.CONTAIN || c == Expression.NOT_CONTAIN || c == Expression.START_WITH || c == Expression.NOT_START_WITH || c == Expression.END_WITH || c == Expression.NOT_END_WITH || c == Expression.EQUALS_IGNORE_CASE ) { String val = (exp.getValue() == null) ? null : exp .getValue().toString(); switch (c) { case Expression.CONTAIN: case Expression.NOT_CONTAIN: val = "%" + val + "%"; break; case Expression.END_WITH: case Expression.NOT_END_WITH: val = "%" + val; break; case Expression.START_WITH: case Expression.NOT_START_WITH: val = val + "%"; break; case Expression.EQUALS_IGNORE_CASE: // val = val; break; } args.add( new SQLArg( f, val )); } else throw new IllegalStateException( "Invalud Expression Condition [" + Expression.condStr(c) + "]"); } // set.append( " ?" ); } return set.toString(); } /** * Gets the SQL WHERE clause for the keys of a class */ protected String getWhereStringForKeys( ObjectMappingDB omdb, Collection keys ) throws MetaException { StringBuilder where = new StringBuilder(); // Setup the WHERE clause for (MetaField f : keys) { ColumnDef colDef = (ColumnDef) omdb.getArgDef(f); if (colDef == null) throw new MetaException("MetaField [" + f + "] has no column mapping defined for [" + omdb + "]" ); if (where.length() > 0) where.append(" AND "); where.append(colDef.getName()); where.append("=?"); } return where.toString(); } /** * Gets the id clause for a unique transaction */ /*protected PreparedStatement getSelectStatementForRef(Connection c, MetaClass mc, Collection fields, ObjectRef ref) throws SQLException, MetaException { // TODO: The query construction should be cached for each MetaClass & // field combo if (!isReadableClass(mc)) throw new MetaException("MetaClass [" + mc + "] is not readable"); // validateMetaClass( c, mc ); Collection keys = getPrimaryKeys(mc); // Get the components of the SELECT query String tableStr = getViewName(mc); if (tableStr == null) throw new MetaException("MetaClass [" + mc + "] has no table or view defined"); String whereStr = getWhereStringForKeys(keys); String fieldStr = getFieldString(fields); // Construct the SELECT query String query = "SELECT " + fieldStr + " FROM " + tableStr + " WHERE " + whereStr; PreparedStatement s = c.prepareStatement(query); // ystem.out.println( ">>> QUERY: " + query ); StringBuilder valStr = new StringBuilder(" "); try { // Set the statement values form the id // setStatementValuesForRef( s, keys, 1, ref ); String[] ids = ref.getIds(); int k = 0; int j = 1; // Iterator v = values.iterator(); for (MetaField f : keys) { String value = ids[k++]; setStatementValue(s, f, j++, value); valStr.append("(" + value + ")"); } if (log.isDebugEnabled()) log.debug("SQL (" + c.hashCode() + ") - getSelectStatementForRef: [" + query + valStr.toString() + "]"); // Return the prepared statement return s; } catch (SQLException e) { s.close(); throw e; } }*/ /** * Gets the id clause for a unique transaction */ /* protected PreparedStatement getSelectStatementForObject(Connection c, MetaClass mc, Collection fields, Object obj) throws SQLException, MetaException { // TODO: The query construction should be cached for each MetaClass & // field combo if (!isReadableClass(mc)) throw new MetaException("MetaClass [" + mc + "] is not persistable"); // PersistableMetaClass pmc = (PersistableMetaClass) mc; // validateMetaClass( c, mc ); Collection keys = getPrimaryKeys(mc); // Get the components of the SELECT query String tableStr = getViewName(mc); if (tableStr == null) throw new MetaException("MetaClass [" + mc + "] has no table or view defined"); String whereStr = getWhereStringForKeys(keys); String fieldStr = getFieldString(fields); // Construct the SELECT query String query = "SELECT " + fieldStr + " FROM " + tableStr + " WHERE " + whereStr; PreparedStatement s = c.prepareStatement(query); // ystem.out.println( ">>> QUERY: " + query ); StringBuilder valStr = new StringBuilder(" "); try { int j = 1; // Set the statement values form the id for (MetaField f : keys) { setStatementValue(s, f, j, f.getObject(obj)); valStr.append("(" + f.getString(obj) + ")"); j++; } if (log.isDebugEnabled()) log.debug("SQL (" + c.hashCode() + ") - getSelectStatementforObject: [" + query + valStr.toString() + "]"); // Return the prepared statement return s; } catch (SQLException e) { s.close(); throw e; } } */ /** * Gets the id clause for a unique transaction */ protected PreparedStatement getCountStatementWhere( Connection c, MetaClass mc, ObjectMappingDB omdb, Expression where ) throws SQLException, MetaException { // Construct the SELECT query StringBuilder query = new StringBuilder(); query.append("SELECT COUNT(*) FROM "); char prefix = 'A'; BaseTableDef base = (BaseTableDef) omdb.getDBDef(); while ( base != null ) { // Get the components of the SELECT query String tableStr = getProperName( base.getNameDef() ); query.append(tableStr).append( ' ' ).append( prefix ); if ( base instanceof TableDef ) { TableDef table = (TableDef) base; base = null; InheritenceDef idef = table.getInheritence(); if ( idef != null ) { base = idef.getRefTable(); prefix++; query.append( " LEFT JOIN " ); tableStr = getProperName( base.getNameDef() ); query.append(tableStr).append( ' ' ).append( prefix ); query.append( " ON " ); query.append( prefix-- ).append( idef.getColumnName() ) .append( "=" ).append( prefix ).append( idef.getRefColumn().getName() ); } } else { break; } } ArrayList args = new ArrayList(); if (where != null) { query.append(" WHERE ").append(getExpressionString(mc, omdb, where, args, "A" )); } PreparedStatement s = c.prepareStatement(query.toString()); int index = 1; StringBuilder valStr = null; if (log.isDebugEnabled()) { valStr = new StringBuilder(" "); } if (where != null) { for ( SQLArg arg : args ) { MetaField f = arg.getMetaField(); Object value = arg.getValue(); if ( log.isDebugEnabled() && index > 1 ) { valStr.append(", "); } setStatementValue(s, f, index++, value); if (log.isDebugEnabled()) { valStr.append("(" + value + ")"); } } } if (log.isDebugEnabled()) { log.debug("SQL (" + c.hashCode() + ") - getCountStatementWhere: [ " + query.toString() + valStr.toString() + " ]"); } // ystem.out.println( ">>> QUERY: " + query.toString() + " " + // valStr.toString() ); return s; } /** * Gets the id clause for a unique transaction */ protected PreparedStatement getSelectStatementWhere( Connection c, MetaClass mc, ObjectMappingDB omdb, Collection fields, QueryOptions options ) throws SQLException, MetaException { Expression where = options.getExpression(); SortOrder order = options.getSortOrder(); // Construct the SELECT query StringBuilder query = new StringBuilder(); query.append("SELECT "); if (options.isDistinct()) { query.append("DISTINCT "); } if (options.getRange() != null) { // TODO: Make this a little smarter for christ's sake! // Recommendation: Have the driver form the final SELECT call, then // it can insert the limit where needed //if (getDatabaseDriver() instanceof MSSQLDriver) { // query.append("TOP ").append(options.getRange().getEnd()) // .append(' '); //} } String fieldStr = getFieldString( omdb, fields, "A" ); query.append(fieldStr); query.append(" FROM "); char prefix = 'A'; BaseTableDef base = (BaseTableDef) omdb.getDBDef(); while ( base != null ) { // Get the components of the SELECT query String tableStr = getProperName( base.getNameDef() ); query.append(tableStr).append( ' ' ).append( prefix ); if ( base instanceof TableDef ) { TableDef table = (TableDef) base; base = null; InheritenceDef idef = table.getInheritence(); if ( idef != null ) { base = idef.getRefTable(); prefix++; query.append( " LEFT JOIN " ); tableStr = getProperName( base.getNameDef() ); query.append(tableStr).append( ' ' ).append( prefix ); query.append( " ON " ); query.append( prefix-- ).append( idef.getColumnName() ) .append( "=" ).append( prefix ).append( idef.getRefColumn().getName() ); } } else { break; } } ArrayList args = new ArrayList(); if (where != null) { query.append(" WHERE ").append(getExpressionString(mc, omdb, where, args, "A" )); } if (order != null) { query.append(" ORDER BY ").append(getOrderString(mc, omdb, order)); } if (options.withLock()) { query.append(" ").append(getLockString()); } // Add on the range Range range = options.getRange(); if ( supportsRangeInQuery() && options.getRange() != null && range.getStart() > 0 && range.getEnd() > 0 ) { if ( range.getStart() > range.getEnd() ) { throw new IllegalArgumentException( "The range end (" + range.getEnd() + ") cannot be greater than the start value (" + range.getStart() + ")" ); } query.append( " " ).append( getRangeString( range )); } PreparedStatement s = c.prepareStatement(query.toString()); int index = 1; StringBuilder valStr = null; if (log.isDebugEnabled()) { valStr = new StringBuilder(" "); } if (where != null) { for ( SQLArg arg : args ) { MetaField f = arg.getMetaField(); Object value = arg.getValue(); if ( log.isDebugEnabled() && index > 1 ) { valStr.append(", "); } setStatementValue(s, f, index++, value); if (log.isDebugEnabled()) { valStr.append("(" + value + ")"); } } } if (log.isDebugEnabled()) { log.debug("SQL (" + c.hashCode() + ") - getSelectStatementWhere: [ " + query.toString() + valStr.toString() + " ]"); } // ystem.out.println( ">>> QUERY: " + query.toString() + " " + // valStr.toString() ); return s; } /** * Gets the id clause for a unique transaction */ protected PreparedStatement getDeleteStatementWhere(Connection c, MetaClass mc, ObjectMappingDB omdb, Expression where ) throws SQLException, MetaException { // Get the components of the SELECT query String tableStr = getProperName( omdb.getDBDef().getNameDef() ); // Construct the SELECT query StringBuilder query = new StringBuilder("DELETE FROM "); query.append(tableStr); //.append( " A"); ArrayList args = new ArrayList(); if (where != null) { query.append(" WHERE "); query.append(getExpressionString(mc, omdb, where, args, null )); } PreparedStatement s = c.prepareStatement(query.toString()); int index = 1; StringBuilder valStr = new StringBuilder(" "); if (where != null) { for ( SQLArg arg : args ) { MetaField f = arg.getMetaField(); Object value = arg.getValue(); if (index > 1) valStr.append(", "); setStatementValue(s, f, index++, value); valStr.append("(" + value + ")"); } } if (log.isDebugEnabled()) log.debug("SQL (" + c.hashCode() + ") - getDeleteStatementWhere: [ " + query.toString() + valStr.toString() + " ]"); // ystem.out.println( ">>> QUERY: " + query.toString() + " " + // valStr.toString() ); return s; } /** * Sets a specific value on a prepared statement */ protected void setStatementValue( PreparedStatement s, MetaField f, int index, Object value ) throws SQLException { int j = index; switch( f.getType() ) { case MetaField.BOOLEAN: { if ( value == null ) s.setNull( j, Types.BIT ); else if ( value instanceof Boolean ) s.setBoolean( j, ((Boolean) value ).booleanValue() ); else s.setBoolean( j, Boolean.valueOf( value.toString() ).booleanValue() ); } break; case MetaField.BYTE: { if ( value == null ) s.setNull( j, Types.TINYINT ); else if ( value instanceof Byte ) s.setByte( j, ((Byte) value ).byteValue() ); else s.setByte( j, Byte.valueOf( value.toString() ).byteValue() ); } break; case MetaField.SHORT: { if ( value == null ) s.setNull( j, Types.SMALLINT ); else if ( value instanceof Short ) s.setShort( j, ((Short) value ).shortValue() ); else s.setShort( j, Short.valueOf( value.toString() ).shortValue() ); } break; case MetaField.INT: { if ( value == null ) s.setNull( j, Types.INTEGER ); else if ( value instanceof Integer ) s.setInt( j, ((Integer) value ).intValue() ); else s.setInt( j, Integer.valueOf( value.toString() ).intValue() ); } break; case MetaField.DATE: // NOTE DATE IS TREATED AS LONG! { if ( value == null ) s.setNull( j, Types.TIMESTAMP ); else if ( value instanceof java.util.Date ) s.setTimestamp( j, new Timestamp( ((java.util.Date) value ).getTime() )); else s.setTimestamp( j, new Timestamp( Long.valueOf( value.toString() ).longValue() )); } break; case MetaField.LONG: { if ( value == null ) s.setNull( j, Types.BIGINT ); else if ( value instanceof Long ) s.setLong( j, ((Long) value ).longValue() ); else s.setLong( j, Long.valueOf( value.toString() ).longValue() ); } break; // WARNING: This should not be a valid key case MetaField.FLOAT: { if ( value == null ) s.setNull( j, Types.FLOAT ); else if ( value instanceof Float ) s.setFloat( j, ((Float) value ).floatValue() ); else s.setFloat( j, Float.valueOf( value.toString() ).floatValue() ); } break; // WARNING: This should not be a valid key case MetaField.DOUBLE: { if ( value == null ) s.setNull( j, Types.DOUBLE ); else if ( value instanceof Double ) s.setDouble( j, ((Double) value ).doubleValue() ); else s.setDouble( j, Double.valueOf( value.toString() ).doubleValue() ); } break; case MetaField.STRING: if ( value == null ) s.setNull( j, Types.VARCHAR ); else s.setString( j, value.toString() ); break; case MetaField.OBJECT: //if ( value == null ) // s.setNull( j, Types.BLOB ); //else s.setObject( j, value ); break; } } /** * Gets the id clause for a unique transaction */ protected PreparedStatement getInsertStatement(Connection c, MetaClass mc, ObjectMappingDB omdb, Object o) throws SQLException, MetaException { Collection fields = omdb.getMetaFields(); // Get the components of the SELECT query String tableStr = getProperName( omdb.getDBDef().getNameDef() ); String fieldStr = getFieldString( omdb, fields, false, null ); String valueStr = getValueStringForFields( omdb, fields ); // Construct the SELECT query String query = "INSERT INTO " + tableStr + " (" + fieldStr + ")" + " VALUES (" + valueStr + ")"; PreparedStatement s = null; // Tack on any needed queries to get the keys // if ( getDatabaseDriver().getAutoType() == AUTO_DURING ) // { // String add = getDatabaseDriver().getInsertAppendString( mc ); // if ( add != null && add.length() > 0 ) query += add; // s = c.prepareCall( query ); // } // else // { s = c.prepareStatement(query); // } StringBuilder valStr = new StringBuilder(" "); try { int j = 1; for (MetaField f : fields) { ColumnDef colDef = (ColumnDef) omdb.getArgDef(f); // Do not use the field if it's an auto id that is set after // after creation/update -- only valid on some drivers (MSSQL) //if ( colDef.isAutoIncrementor() ) // continue; // Set the statement id value if ( colDef.isAutoIncrementor() ) { f.setString( o, getNextAutoId( c , colDef )); } // Set the create date (if auto) //else if ( colDef.getAutoType() == ColumnDef.AUTO_DATE_CREATE ) { // f.setDate( o, new Date() ); //} setStatementValue(s, f, j, f.getObject(o)); valStr.append("(" + f.getString(o) + ")"); j++; } if (log.isDebugEnabled()) log.debug("SQL (" + c.hashCode() + ") - getInsertStatement: [" + query + valStr.toString() + "]"); // ystem.out.println( ">>> QUERY: [" + query + "] [" + // valStr.toString() + "]" ); // Return the prepared statement return s; } catch (SQLException e) { s.close(); throw e; } } /** * Gets the update statement for a unique object */ protected PreparedStatement getUpdateStatement(Connection c, ObjectMappingDB omdb, Collection fields, MetaClass mc, Object o, Expression exp ) throws SQLException { // WARNING: The query construction should be cached for each MetaClass & // field combo // validateMetaClass( c, mc ); // String id = pmc.getId( o ); //Collection keys = getPrimaryKeys(mc); // Get the components of the SELECT query String tableStr = getProperName( omdb.getDBDef().getNameDef() ); ArrayList args = new ArrayList(); String setStr = getSetString( omdb, fields ); String whereStr = getExpressionString( mc, omdb, exp, args, null ); // Construct the SELECT query String query = "UPDATE " + tableStr + " SET " + setStr + " WHERE " + whereStr; // Used to prevent dirty writes //if (dirtyField != null) { // query += " AND ( " + dirtyField.getAttribute(COL_REF) + "=? )"; //} PreparedStatement s = c.prepareStatement(query); // ystem.out.println( ">>> QUERY: " + query ); StringBuilder valStr = new StringBuilder(" "); try { // Set the update values int j = 1; for (MetaField f : fields) { ColumnDef colDef = (ColumnDef) omdb.getArgDef(f); if ( colDef.isAutoIncrementor() ) continue; // Set the create date (if auto) //if ( colDef.getAutoType() == ColumnDef.AUTO_DATE_CREATE || // colDef.getAutoType() == ColumnDef.AUTO_DATE_UPDATE ) { // f.setDate( o, new Date() ); //} setStatementValue(s, f, j++, f.getObject(o)); valStr.append("(" + f.getString(o) + ")"); } // Set the WHERE clause arguments for ( SQLArg arg : args ) { MetaField f = arg.getMetaField(); Object value = arg.getValue(); if (j > 1) valStr.append(", "); setStatementValue(s, f, j++, value); // If we're logging, then get the values to log if (log.isDebugEnabled()) { String v = null; if ( value != null && value instanceof Date ) { v = "" + ((Date) value ).getTime(); } else { v = "" + value; } valStr.append("(" + v + ")"); } } // Set the key values - replaced by WHERE clause //for (MetaField f : keys) { // setStatementValue(s, f, j++, f.getObject(o)); // valStr.append("{" + f.getString(o) + "}"); //} // Used to prevent dirty writes //if (dirtyField != null) // setStatementValue(s, dirtyField, j, dirtyFieldValue); if (log.isDebugEnabled()) log.debug("SQL (" + c.hashCode() + ") - getUpdateStatement: [" + query + valStr + "]"); // Return the prepared statement return s; } catch (SQLException e) { s.close(); throw e; } } /** * Gets the delete statement for a specific id */ protected PreparedStatement getDeleteStatement(Connection c, MetaClass mc, ObjectMappingDB omdb, Collection keys, Object obj) throws SQLException, MetaException { // Get the components of the SELECT query String tableStr = getProperName( omdb.getDBDef().getNameDef() ); String whereStr = null;//getWhereStringForKeys(keys); // Construct the SELECT query String query = "DELETE FROM " + tableStr + " WHERE " + whereStr; PreparedStatement s = c.prepareStatement(query); // ystem.out.println( ">>> QUERY: " + query ); StringBuilder valStr = new StringBuilder(" "); try { // Set the key values int j = 1; for (Iterator i = keys.iterator(); i.hasNext(); j++) { MetaField f = i.next(); setStatementValue(s, f, j, f.getObject(obj)); valStr.append("(" + f.getString(obj) + ")"); } if (log.isDebugEnabled()) log.debug("SQL (" + c.hashCode() + ") - getDeleteStatement: [" + query + valStr + "]"); // Return the prepared statement return s; } catch (SQLException e) { s.close(); throw e; } } /** * Parses an Object returned from the database */ protected void parseObject(ResultSet rs, Collection fields, MetaClass mc, Object o) throws SQLException, MetaException { int j = 1; for (MetaField f : fields) { parseField(o, f, rs, j++); } // This should be done in the ObjectManager // Update the state of the object /* * if ( mc instanceof StatefulMetaClass ) { // It was pulled from the * database, so it doesn't need to be flagged as modified * ((StatefulMetaClass) mc ).setModified( o, false ); * // It is also no longer a new item ((StatefulMetaClass) mc ).setNew( * o, false ); } */ } protected void parseField(Object o, MetaField f, ResultSet rs, int j) throws SQLException { switch (f.getType()) { case MetaField.BOOLEAN: { boolean bv = rs.getBoolean(j); if (rs.wasNull()) f.setBoolean(o, null); else f.setBoolean(o, new Boolean(bv)); } break; case MetaField.BYTE: { byte bv = rs.getByte(j); if (rs.wasNull()) f.setByte(o, null); else f.setByte(o, new Byte(bv)); } break; case MetaField.SHORT: { short sv = rs.getShort(j); if (rs.wasNull()) f.setShort(o, null); else f.setShort(o, new Short(sv)); } break; case MetaField.INT: { int iv = rs.getInt(j); if (rs.wasNull()) f.setInt(o, null); else f.setInt(o, new Integer(iv)); } break; case MetaField.DATE: { Timestamp tv = rs.getTimestamp(j); if (rs.wasNull()) f.setDate(o, null); else f.setDate(o, new java.util.Date(tv.getTime())); } break; case MetaField.LONG: { long lv = rs.getLong(j); if (rs.wasNull()) f.setLong(o, null); else f.setLong(o, new Long(lv)); } break; case MetaField.FLOAT: { float fv = rs.getFloat(j); if (rs.wasNull()) f.setFloat(o, null); else f.setFloat(o, new Float(fv)); } break; case MetaField.DOUBLE: { double dv = rs.getDouble(j); if (rs.wasNull()) f.setDouble(o, null); else f.setDouble(o, new Double(dv)); } break; case MetaField.STRING: f.setString(o, rs.getString(j)); break; case MetaField.OBJECT: f.setObject(o, rs.getObject(j)); break; } } // ///////////////////////////////////////////////////// // TO STRING METHOD public String toString() { return getClass().getSimpleName(); } }