Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.draagon.meta.manager.db.ObjectManagerDB Maven / Gradle / Ivy
Go to download
Relational Database ObjectManager libraries for MetaData-driven development.
/*
* 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;
import com.draagon.meta.*;
import com.draagon.meta.object.MetaObjectClass;
import com.draagon.meta.manager.*;
import com.draagon.meta.manager.db.driver.*;
//import com.draagon.util.InitializationException;
import com.draagon.cache.Cache;
import com.draagon.meta.manager.exp.Expression;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import javax.sql.DataSource;
/**
* The Object Manager Base is able to add, update, delete,
* and retrieve objects of those types from a datastore.
*/
public class ObjectManagerDB extends ObjectManager
{
private static Log log = LogFactory.getLog( ObjectManagerDB.class );
private final static String CREATE_MAP_ATTR = "dbCreateMap";
private final static String READ_MAP_ATTR = "dbReadMap";
private final static String UPDATE_MAP_ATTR = "dbUpdateMap";
private final static String DELETE_MAP_ATTR = "dbDeleteMap";
private final static String HAS_CREATE_MAP_ATTR = "hasDbCreateMap";
private final static String HAS_READ_MAP_ATTR = "hasDbReadMap";
private final static String HAS_UPDATE_MAP_ATTR = "hasDbUpdateMap";
private final static String HAS_DELETE_MAP_ATTR = "hasDbDeleteMap";
public final static String ALLOW_DIRTY_WRITE = "dbAllowDirtyWrite";
public final static String DIRTY_WRITE_CHECK_FIELD = "dbDirtyWriteCheckField";
public final static String POPULATE_FILE = "dbPopulateFile";
private MappingHandler mMappingHandler = null;
private DatabaseDriver mDriver = null;
private DataSource mSource = null;
private boolean enforceTransaction = false;
private static Cache templateCache = new Cache( true, 3000, 1500 );
//private ArrayList mValidatedClasses = new ArrayList();
//private boolean autoCreateTables = false;
//private HashMap mDirtyFieldCache = new HashMap();
public ObjectManagerDB()
{
}
/** Handles enforcing transactions on SQL queries */
public void setEnforceTransaction( boolean enforce ) {
enforceTransaction = enforce;
}
/** Returns whether to enforce transactions */
public boolean enforceTransaction() {
return enforceTransaction;
}
/** Checks to see if a transaction exists or not */
protected void checkTransaction( Connection c, boolean throwEx ) throws MetaException {
try {
if ( enforceTransaction() && c.getAutoCommit() ) {
MetaException me = new MetaException( "The connection retrieved is not operating under a transaction and transactions are being enforced" );
if ( throwEx ) {
throw me;
} else {
log.warn( me.getMessage(), me );
}
}
} catch( SQLException e ) {
throw new MetaException( "Error checking connection for transaction enforcement: " + e.getMessage(), e );
}
}
///////////////////////////////////////////////////////
// CONNECTION HANDLING METHODS
//
/**
* Retrieves a connection object representing the datastore
*/
public ObjectConnection getConnection()
{
DataSource ds = getDataSource();
if ( ds == null )
throw new IllegalArgumentException( "No DataSource was specified for this ObjectManager, cannot request connection" );
Connection c;
try {
c = ds.getConnection();
} catch (SQLException e) {
throw new RuntimeException( "Could not retrieve a connection from the datasource: " + e.getMessage(), e );
}
return new ObjectConnectionDB( c );
}
/** Release the Database Connection */
public void releaseConnection( ObjectConnection oc )
throws MetaException
{
oc.close();
}
/**
* Sets the Data Source to use for database connections
*/
public void setDataSource( DataSource ds )
{
mSource = ds;
}
/**
* Retrieves the data source
*/
public DataSource getDataSource()
{
return mSource;
}
/**
* Initializes the ObjectManager
*/
public void init() throws Exception
{
super.init();
if ( getDataSource() == null )
throw new IllegalStateException( "No DataSource was specified" );
}
///////////////////////////////////////////////////////
// DATABASE DRIVER METHODS
//
public void setDriverClass( String className ) throws ClassNotFoundException, InstantiationException, IllegalAccessException
{
Class> c = Class.forName( className );
setDatabaseDriver( (DatabaseDriver) c.newInstance() );
}
public void setDatabaseDriver( DatabaseDriver dd )
{
mDriver = dd;
mDriver.setManager( this );
}
public synchronized DatabaseDriver getDatabaseDriver()
{
if ( mDriver == null ) {
mDriver = new GenericSQLDriver();
mDriver.setManager( this );
}
return mDriver;
}
///////////////////////////////////////////////////////
// PERSISTENCE METHODS
//
public MappingHandler getDefaultMappingHandler() {
return new SimpleMappingHandlerDB();
}
public void setMappingHandler( MappingHandler handler ) {
mMappingHandler = handler;
}
public MappingHandler getMappingHandler() {
if ( mMappingHandler == null ) {
mMappingHandler = getDefaultMappingHandler();
}
return mMappingHandler;
}
/** Gets the create mapping */
protected ObjectMapping getCreateMapping( MetaClass mc ) {
ObjectMapping mapping = (ObjectMapping) mc.getCacheValue( CREATE_MAP_ATTR );
if ( mapping == null ) {
mapping = getMappingHandler().getCreateMapping( mc );
if ( mapping != null ) {
mc.setCacheValue( CREATE_MAP_ATTR, mapping );
}
}
return mapping;
}
/** Gets the read mapping */
protected ObjectMapping getReadMapping( MetaClass mc ) {
ObjectMapping mapping = (ObjectMapping) mc.getCacheValue( READ_MAP_ATTR );
if ( mapping == null ) {
mapping = getMappingHandler().getReadMapping( mc );
if ( mapping != null ) {
mc.setCacheValue( READ_MAP_ATTR, mapping );
mc.setCacheValue( HAS_READ_MAP_ATTR, Boolean.TRUE );
} else {
mc.setCacheValue( HAS_READ_MAP_ATTR, Boolean.FALSE );
}
}
return mapping;
}
/** Gets the update mapping */
protected ObjectMapping getUpdateMapping( MetaClass mc ) {
ObjectMapping mapping = (ObjectMapping) mc.getCacheValue( UPDATE_MAP_ATTR );
if ( mapping == null ) {
mapping = getMappingHandler().getUpdateMapping( mc );
if ( mapping != null ) {
mc.setCacheValue( UPDATE_MAP_ATTR, mapping );
mc.setCacheValue( HAS_UPDATE_MAP_ATTR, Boolean.TRUE );
} else {
mc.setCacheValue( HAS_UPDATE_MAP_ATTR, Boolean.FALSE );
}
}
return mapping;
}
/** Gets the delete mapping */
protected ObjectMapping getDeleteMapping( MetaClass mc ) {
ObjectMapping mapping = (ObjectMapping) mc.getCacheValue( DELETE_MAP_ATTR );
if ( mapping == null ) {
mapping = getMappingHandler().getUpdateMapping( mc );
if ( mapping != null ) {
mc.setCacheValue( DELETE_MAP_ATTR, mapping );
mc.setCacheValue( HAS_DELETE_MAP_ATTR, Boolean.TRUE );
} else {
mc.setCacheValue( HAS_DELETE_MAP_ATTR, Boolean.FALSE );
}
}
return mapping;
}
/** Is this a createable class */
public boolean isCreateableClass( MetaClass mc ) {
Boolean hasMapping = (Boolean) mc.getCacheValue( HAS_CREATE_MAP_ATTR );
if ( hasMapping == null ) {
if ( getCreateMapping( mc ) == null ) return false;
else return true;
}
return hasMapping.booleanValue();
}
/** Is this a readable class */
public boolean isReadableClass( MetaClass mc ) {
Boolean hasMapping = (Boolean) mc.getCacheValue( HAS_READ_MAP_ATTR );
if ( hasMapping == null ) {
if ( getReadMapping( mc ) == null ) return false;
else return true;
}
return hasMapping.booleanValue();
}
/** Gets the update mapping to the DB */
public boolean isUpdateableClass( MetaClass mc ) {
Boolean hasMapping = (Boolean) mc.getCacheValue( HAS_UPDATE_MAP_ATTR );
if ( hasMapping == null ) {
if ( getUpdateMapping( mc ) == null ) return false;
else return true;
}
return hasMapping.booleanValue();
}
/** Gets the delete mapping to the DB */
public boolean isDeleteableClass( MetaClass mc ) {
Boolean hasMapping = (Boolean) mc.getCacheValue( HAS_DELETE_MAP_ATTR );
if ( hasMapping == null ) {
if ( getDeleteMapping( mc ) == null ) return false;
else return true;
}
return hasMapping.booleanValue();
}
/**
* Breaks apart the values from the id field represented by the keys
*/
/*protected Collection getKeyValuesFromRef( MetaClass mc, Collection keys, String ref )
throws MetaException
{
ArrayList values = new ArrayList();
String tmp = ref;
// Split apart the id field
for ( Iterator i = keys.iterator(); i.hasNext(); )
{
MetaField f = (MetaField) i.next();
if ( tmp == null || tmp.length() == 0 )
throw new MetaException( "Invalid Reference [" + ref + "] for MetaClass [" + mc + "]" );
String val = null;
int j = tmp.indexOf( '-' );
if ( j >= 0 )
{
val = tmp.substring( 0, j );
tmp = tmp.substring( j + 1 );
}
else
{
val = tmp;
tmp = null;
}
values.add( val );
}
return values;
}*/
/**
* Sets the prepared statement values for the keys of a class and
* a specifed id.
*/
/* protected void setStatementValuesForRef( PreparedStatement s, Collection keys, int start, ObjectRef ref )
throws MetaException, SQLException
{
//Collection values = getKeyValuesFromRef( keys, ref );
String [] ids = ref.getIds();
int k = 0;
int j = start;
//Iterator v = values.iterator();
for ( Iterator i = keys.iterator(); i.hasNext(); j++, k++ )
{
MetaField f = (MetaField) i.next();
String value = ids[ k ];
setStatementValue( s, f, j, value );
}
}*/
/**
* Parses an Object returned from the database
*/
public void parseObject( ResultSet rs, Collection fields, MetaClass mc, Object o ) throws SQLException, MetaException
{
if ( !isReadableClass( mc )) throw new MetaException( "MetaClass [" + mc + "] is not readable" );
int j = 1;
for( MetaField f : fields ) {
parseField( o, f, rs, j++ );
}
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;
}
}
/** Reset the objects to not be new or modified */
protected void resetObjects( MetaClass mc, Collection objects ) {
if ( !( mc instanceof StatefulMetaClass )) return;
// Reset all the objects
for ( Object o : objects ) {
resetObject( mc, o );
}
}
/** Reset the object to not be new or modified */
protected void resetObject( MetaClass mc, Object o ) {
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 );
}
}
/**
* Gets the object by the id; throws exception if it did not exist
*/
@Override
public Object getObjectByRef( ObjectConnection c, String refStr )
{
ObjectRef ref = getObjectRef( refStr );
MetaClass mc = ref.getMetaClass();
if ( !isReadableClass( mc )) throw new PersistenceException( "MetaClass [" + mc + "] is not readable" );
ObjectMappingDB readMap = (ObjectMappingDB) getReadMapping( mc );
Connection conn = (Connection) c.getDatastoreConnection();
// Check for a valid transaction if enforced
checkTransaction( conn, false );
if ( log.isDebugEnabled() ) {
log.debug( "Loading object [" + mc + "] with reference [" + ref + "]" );
}
try {
// Create the Expression for the Primary Keys
Expression exp = null;
int i = 0;
for( MetaField mf : getPrimaryKeys( mc )) {
Expression e = new Expression( mf.getName(), ref.getIds()[ i ] );
if ( exp == null ) exp = e;
else exp = exp.and( e );
i++;
}
if ( exp == null ) throw new PersistenceException( "MetaClass [" + mc + "] has no primary keys get object by reference [" + ref + "]" );
// Create the QueryOptions and limit to the first 1
QueryOptions qo = new QueryOptions();
qo.setRange( 1, 1 );
// Read the objects from the database driver
Collection objects = getDatabaseDriver().readMany( conn, mc, readMap, qo );
// Reset the object persistence states
resetObjects( mc, objects );
// Return the object if found
if ( objects.size() > 0 ) return objects.iterator().next();
else throw new ObjectNotFoundException( refStr );
}
catch( SQLException e ) {
//log.error( "Unable to load object [" + mc + "] with reference [" + ref + "]: " + e.getMessage(), e );
throw new PersistenceException( "Unable to load object [" + mc + "] with reference [" + ref + "]: " + e.getMessage(), e );
}
}
/**
* Delete the objects from the datastore where the field has the specified value
*/
@Override
public int deleteObjects( ObjectConnection c, MetaClass mc, Expression exp ) {
if ( !isDeleteableClass( mc )) throw new PersistenceException( "MetaClass [" + mc + "] is not deletable" );
ObjectMappingDB mapping = (ObjectMappingDB) getDeleteMapping( mc );
Connection conn = (Connection) c.getDatastoreConnection();
// Check for a valid transaction if enforced
checkTransaction( conn, true );
if ( log.isDebugEnabled() ) {
log.debug( "Deleting Objects of Class [" + mc + "] where [" + exp + "]" );
}
//int failures = 0;
//while( true )
//{
try {
return getDatabaseDriver().deleteMany( conn, mc, mapping, exp );
}
catch( SQLException e )
{
//log.error( "Unable to delete objects of class [" + mc.getName() + "] with expression [" + exp + "]: " + e.getMessage() );
//if ( ++failures > 5 )
throw new PersistenceException( "Unable to delete objects of class [" + mc.getName() + "] with expression [" + exp + "]: " + e.getMessage(), e );
}
// Sleep on a delete failure
//try { Thread.sleep( 200 * failures ); }
//catch (InterruptedException e) {}
//}
}
/**
* Gets the total count of objects with the specified search criteria
*/
@Override
public long getObjectsCount( ObjectConnection c, MetaClass mc, Expression exp ) throws MetaException
{
if ( !isReadableClass( mc )) throw new PersistenceException( "MetaClass [" + mc + "] is not persistable" );
Connection conn = (Connection) c.getDatastoreConnection();
ObjectMappingDB mapping = (ObjectMappingDB) getReadMapping( mc );
// Check for a valid transaction if enforced
checkTransaction( conn, false );
try
{
// Read the objects
return getDatabaseDriver().getCount( conn, mc, mapping, exp );
}
catch( SQLException e )
{
throw new PersistenceException( "Unable to get objects count of class [" + mc.getName() + "] with expression [" + exp + "]: " + e.getMessage(), e );
}
}
/**
* Gets the objects by the field with the specified search criteria
*/
@Override
public Collection getObjects( ObjectConnection c, MetaClass mc, QueryOptions options ) throws MetaException
{
if ( !isReadableClass( mc )) throw new PersistenceException( "MetaClass [" + mc + "] is not persistable" );
Connection conn = (Connection) c.getDatastoreConnection();
ObjectMappingDB mapping = (ObjectMappingDB) getReadMapping( mc );
// Check for a valid transaction if enforced
checkTransaction( conn, false );
//int failures = 0;
//while( true )
//{
try
{
// Read the objects
Collection objects = getDatabaseDriver().readMany( conn, mc, mapping, options );
// Reset the object persistence states
resetObjects( mc, objects );
// Return the objects
return objects;
}
catch( SQLException e )
{
//log.error( "Unable to load objects of class [" + mc.getName() + "]: " + e.getMessage() );
//if ( ++failures > 5 )
throw new PersistenceException( "Unable to get objects of class [" + mc.getName() + "] with options [" + options + "]: " + e.getMessage(), e );
}
// Sleep on a read failure
//try { Thread.sleep( 200 * failures ); }
//catch (InterruptedException e) {}
//}
}
/**
* Load the specified object from the database
*/
public void loadObject( ObjectConnection c, Object o ) throws MetaException
{
// Verify this object was loaded by the same object manager
//verifyObjectManager( o );
// Get the MetaClass for the object
MetaClass mc = getClassForObject( o );
// If it's not a readable class throw an exception
if ( !isReadableClass( mc )) throw new PersistenceException( "MetaClass [" + mc + "] is not persistable" );
// Get the read mapping
ObjectMappingDB mapping = (ObjectMappingDB) getReadMapping( mc );
// Get the connection
Connection conn = (Connection) c.getDatastoreConnection();
// Check for a valid transaction if enforced
checkTransaction( conn, false );
if ( log.isDebugEnabled() ) {
log.debug( "Loading object [" + o + "] of class [" + mc + "]" );
}
// Create the Expression for the Primary Keys
Expression exp = null;
for( MetaField mf : getPrimaryKeys( mc )) {
Expression e = new Expression( mf.getName(), mf.getObject( o ));
if ( exp == null ) exp = e;
else exp = exp.and( e );
}
if ( exp == null ) throw new PersistenceException( "MetaClass [" + mc + "] has no primary keys defined to load object [" + o + "]" );
// Try to read the object
try {
// Read the object from the mapping
boolean found = getDatabaseDriver().read( conn, mc, mapping, o, exp );
// If not found throw an exception
if ( !found ) throw new ObjectNotFoundException( o );
// Reset the object after it's loaded
resetObject( mc, o );
}
catch( SQLException e )
{
//log.error( "Unable to load object [" + o + "]: " + e.getMessage(), e );
throw new PersistenceException( "Unable to load object [" + o + "]: " + e.getMessage(), e );
}
}
/**
* Add the specified object to the datastore
*/
@Override
public void createObject( ObjectConnection c, Object obj ) throws PersistenceException
{
Connection conn = (Connection) c.getDatastoreConnection();
// Check for a valid transaction if enforced
checkTransaction( conn, true );
MetaClass mc = getClassForObject( obj );
if ( !isCreateableClass( mc )) throw new PersistenceException( "Object of class [" + mc + "] is not createable" );
// Get the create mapping
ObjectMappingDB mapping = (ObjectMappingDB) getCreateMapping( mc );
//verifyObjectManager( obj );
prePersistence( c, mc, obj, CREATE );
if ( log.isDebugEnabled() ) {
log.debug( "Adding object [" + obj + "] of class [" + mc + "]" );
}
try
{
if ( !getDatabaseDriver().create( conn, mc, mapping, obj )) {
throw new PersistenceException( "Now rows created for object [" + obj + "] of class [" + mc + "]" );
}
postPersistence( c, mc, obj, CREATE );
}
catch( SQLException e )
{
//log.error( "Unable to add object of class [" + mc + "]: " + e.getMessage() );
throw new PersistenceException( "Unable to add object of class [" + mc + "]:" + e.getMessage(), e );
}
}
/**
* Update the specified object in the datastore
*/
public void updateObject( ObjectConnection c, Object obj ) throws PersistenceException
{
Connection conn = (Connection) c.getDatastoreConnection();
// Check for a valid transaction if enforced
checkTransaction( conn, true );
// Get the metaclass and make sure it is updateable
MetaClass mc = getClassForObject( obj );
if ( !isUpdateableClass( mc )) throw new PersistenceException( "Object of class [" + mc + "] is not writeable" );
// check the object manager
//verifyObjectManager( obj );
// Get the update mapping
ObjectMappingDB mapping = (ObjectMappingDB) getUpdateMapping( mc );
// Check whether there are dirty writes
boolean allowsDirtyWrites = allowsDirtyWrites( mc );
MetaField dirtyField = null;
Object dirtyFieldValue = null;
// If we don't allow dirty writes then get the field we're filtering from
if ( !allowsDirtyWrites ) {
dirtyField = getDirtyField( mc );
dirtyFieldValue = dirtyField.getObject( obj );
}
if ( log.isDebugEnabled() ) {
log.debug( "Updating object [" + obj + "] of class [" + mc + "]" );
}
// Run the pre-peristence methods
prePersistence( c, mc, obj, UPDATE );
// Get the modified fields
Collection fields = mc.getMetaFields(); //mapping.getMetaFields();
if ( mc instanceof StatefulMetaClass ) {
fields = getModifiedPersistableFields( (StatefulMetaClass) mc, fields, obj );
}
// If nothing needs to be persisted, then don't bother
if ( fields.size() == 0 ) {
if ( log.isDebugEnabled() ) {
log.debug( "No need to update object of class [" + mc + "]" );
}
return;
}
try {
// Update the object
if ( !getDatabaseDriver().update( conn, mc, mapping, obj, fields, getPrimaryKeys( mc ), dirtyField, dirtyFieldValue )) {
// If no dirty writes, see if that was the issue
if ( !allowsDirtyWrites ) {
mapping = (ObjectMappingDB) getReadMapping( mc );
// Create the Expression for the Primary Keys
Expression exp = null;
for( MetaField mf : getPrimaryKeys( mc )) {
Expression e = new Expression( mf.getName(), mf.getObject( obj ));
if ( exp == null ) exp = e;
else exp = exp.and( e );
}
if ( exp == null ) throw new PersistenceException( "MetaClass [" + mc + "] has no primary keys defined to update object [" + obj + "]" );
Collection results = getDatabaseDriver().readMany( conn, mc, mapping, new QueryOptions( exp ));
if ( results.size() > 0 ) {
throw new DirtyWriteException( obj );
}
}
throw new ObjectNotFoundException( obj );
}
postPersistence( c, mc, obj, UPDATE );
}
catch( SQLException e ) {
//log.error( "Unable to update object of class [" + mc + "]: " + e.getMessage() );
throw new PersistenceException( "Unable to update object [" + obj + "] of class [" + mc + "]: " + e.getMessage(), e );
}
}
/**
* Delete the specified object from the datastore
*/
public void deleteObject( ObjectConnection c, Object obj ) throws PersistenceException
{
Connection conn = (Connection) c.getDatastoreConnection();
// Check for a valid transaction if enforced
checkTransaction( conn, true );
MetaClass mc = getClassForObject( obj );
if ( !isDeleteableClass( mc )) {
throw new PersistenceException( "Object [" + obj + "] of class [" + mc + "] is not deleteable" );
}
//verifyObjectManager( obj );
// Get the update mapping
ObjectMappingDB mapping = (ObjectMappingDB) getDeleteMapping( mc );
// Check whether there are dirty writes
boolean allowsDirtyWrites = allowsDirtyWrites( mc );
//MetaField dirtyField = null;
//Object dirtyFieldValue = null;
// If we don't allow dirty writes then get the field we're filtering from
//if ( !allowsDirtyWrites ) {
//dirtyField = getDirtyField( mc );
//dirtyFieldValue = dirtyField.getObject( obj );
//}
prePersistence( c, mc, obj, DELETE );
if ( log.isDebugEnabled() ) {
log.debug( "Deleting object [" + obj + "] of class [" + mc + "]" );
}
try
{
boolean success = getDatabaseDriver().delete( conn, mc, mapping, obj, getPrimaryKeys( mc ));
if ( !success ) {
// If no dirty writes, see if that was the issue
if ( !allowsDirtyWrites ) {
// Create the Expression for the Primary Keys
Expression exp = null;
for( MetaField mf : getPrimaryKeys( mc )) {
Expression e = new Expression( mf.getName(), mf.getObject( obj ));
if ( exp == null ) exp = e;
else exp = exp.and( e );
}
if ( exp == null ) throw new PersistenceException( "MetaClass [" + mc + "] has no primary keys defined to delete object [" + obj + "]" );
Collection results = getDatabaseDriver().readMany( conn, mc, mapping, new QueryOptions( exp ));
if ( results.size() > 0 ) {
throw new DirtyWriteException( obj );
}
}
throw new ObjectNotFoundException( obj );
}
postPersistence( c, mc, obj, DELETE );
}
catch( SQLException e )
{
//log.error( "Unable to delete object of class [" + mc + "]: " + e.getMessage() );
throw new PersistenceException( "Unable to delete object [" + obj + "] of class [" + mc + "]: " + e.getMessage(), e );
}
}
/*public boolean isAutoCreateTables() {
return autoCreateTables;
}
public void setAutoCreateTables(boolean autoCreateTables) {
this.autoCreateTables = autoCreateTables;
}*/
///////////////////////////////////////////////////////
// OBJECT QUERY LANGUAGE METHODS
//
//private final static String ROOT_CLASS_KEY = "*ROOT_CLASS_KEY*";
/*protected Map getMetaClassMap( String query ) throws MetaException
{
Map m = new HashMap();
int i = -1;
while( ( i = query.indexOf( '[', i + 1 )) > 0 )
{
int j = query.indexOf( ']', i );
int k = query.indexOf( '=', i );
if ( j <= 0 )
throw new IllegalArgumentException( "Malformed OQL, missing closing '}': [" + query + "]" );
if ( k >= 0 && k < j )
{
String cn = query.substring( i + 1, k );
String var = query.substring( k + 1, j );
m.put( var, MetaClass.forName( cn ));
}
else
{
String cn = query.substring( i + 1, j );
m.put( ROOT_CLASS_KEY, MetaClass.forName( cn ));
}
i = j;
}
//ystem.out.println( "MAP: " + m );
return m;
}*/
/* private String convertToSQL( String query, Map m ) throws MetaException
{
//StringBuffer b = new StringBuffer();
//ystem.out.println( "IN: " + query );
int i = -1;
// Replace tables
while( ( i = query.indexOf( '{', i + 1 )) > 0 )
{
int j = query.indexOf( '}', i );
if ( j <= 0 )
throw new IllegalArgumentException( "Malformed OQL, missing closing '}': [" + query + "]" );
String field = null;
String var = null;
MetaClass mc = null;
int k = query.indexOf( '.', i );
if ( k >= 0 && k < j )
{
var = query.substring( i + 1, k );
field = query.substring( k + 1, j );
mc = (MetaClass) m.get( var );
if ( mc == null )
throw new IllegalArgumentException( "Malformed OQL, unmapped metaclass variable '" + var + "': [" + query + "]" );
var += ".";
}
else
{
field = query.substring( i + 1, j );
mc = (MetaClass) m.get( ROOT_CLASS_KEY );
var = "";
if ( mc == null )
throw new IllegalArgumentException( "Malformed OQL, no default metaclass defined: [" + query + "]" );
}
String colName = null;
if ( field.equals( "*" ))
colName = "*";
else
colName = getColumnName( mc.getMetaField( field ));
query = query.substring( 0, i ) +
var + colName +
query.substring( j + 1 );
}
// Replace fields
i = -1;
while( ( i = query.indexOf( '[', i + 1 )) > 0 )
{
int j = query.indexOf( ']', i );
if ( j <= 0 )
throw new IllegalArgumentException( "Malformed OQL, missing closing '}': [" + query + "]" );
String var = null;
MetaClass mc = null;
int k = query.indexOf( '=', i );
if ( k >= 0 && k < j )
{
var = query.substring( k + 1, j );
mc = (MetaClass) m.get( var );
if ( mc == null )
throw new IllegalArgumentException( "Malformed OQL, unmapped metaclass variable '" + var + "': [" + query + "]" );
var = " " + var;
}
else
{
mc = (MetaClass) m.get( ROOT_CLASS_KEY );
if ( mc == null )
throw new IllegalArgumentException( "Malformed OQL, no default metaclass defined: [" + query + "]" );
var = "";
}
query = query.substring( 0, i ) +
getViewName( mc ) + var +
query.substring( j + 1 );
}
//ystem.out.println( "OUT: " + query );
//return b.toString();
return query;
}*/
/**
* Returns whether a MetaClass allows dirty writes
*/
public boolean allowsDirtyWrites( MetaClass mc )
{
if ( !mc.hasAttribute( ALLOW_DIRTY_WRITE )
|| !("false".equals( mc.getAttribute( ALLOW_DIRTY_WRITE ))))
return true;
else
return false;
}
/**
* Gets the name of the dirty field of a metaclass
*/
protected MetaField getDirtyField( MetaClass mc )
{
final String KEY = "getDirtyField()";
MetaField field = (MetaField) mc.getCacheValue( KEY );
if ( field == null )
{
if ( mc.hasAttribute( DIRTY_WRITE_CHECK_FIELD ))
field = mc.getMetaField( mc.getAttribute( DIRTY_WRITE_CHECK_FIELD ).toString() );
else
{
for ( MetaField f : getAutoFields( mc ))
{
if ( AUTO_UPDATE.equals( f.getAttribute( AUTO )))
{
field = f;
break;
}
}
}
if ( field == null )
throw new MetaFieldNotFoundException( "No MetaField that is useable to prevent dirty writes was found" );
mc.setCacheValue( KEY, field );
}
return field;
}
///**
// * Retrieves the value of the dirty field for an object
// */
//protected Object getDirtyFieldValue( MetaClass mc, Object obj ) {
// return getDirtyField( mc ).getObject( obj );
//}
//private Cache mOQLCache = new Cache( true, 900, 3600 );
protected PreparedStatement getPreparedStatement( Connection c, String query, Collection> args ) throws MetaException, SQLException
{
String sql = query; // (String) mOQLCache.get( query );
// If it's not in the cache, then parse it and put it there
//if ( sql == null )
//{
//Map m = getMetaClassMap( query );
//if ( m.size() > 0 ) {
// sql = convertToSQL( query, m );
//}
//else sql = query;
//mOQLCache.put( query, sql );
//}
PreparedStatement s = c.prepareStatement( sql );
if ( args != null )
{
int i = 1;
for( Object o : args )
{
if ( o instanceof Boolean )
s.setBoolean( i, (Boolean) o );
else if ( o instanceof Byte )
s.setByte( i, (Byte) o );
else if ( o instanceof Short )
s.setShort( i, (Short) o );
else if ( o instanceof Integer )
s.setInt( i, (Integer) o );
else if ( o instanceof Long )
s.setLong( i, (Long) o );
else if ( o instanceof Float )
s.setFloat( i, (Float) o );
else if ( o instanceof Double )
s.setDouble( i, (Double) o );
else if ( o instanceof Date )
s.setTimestamp( i, new Timestamp( ((Date)o).getTime() ));
else if ( o == null )
s.setString( i, null );
else
s.setString( i, o.toString() );
// Increment the i
i++;
}
}
return s;
}
public int execute( ObjectConnection c, String query, Collection> arguments ) throws MetaException
{
Connection conn = (Connection) c.getDatastoreConnection();
// Check for a valid transaction if enforced
checkTransaction( conn, true );
try
{
PreparedStatement s = getPreparedStatement( conn, query, arguments );
try {
if ( log.isDebugEnabled() ) {
log.debug( "SQL (" + conn.hashCode() + ") - execute: [" + query + " " + arguments + "]" );
}
return s.executeUpdate();
}
finally { s.close(); }
}
catch( SQLException e )
{
log.error( "Unable to execute object query [" + query + "]: " + e.getMessage() );
throw new MetaException( "Unable to execute object query [" + query + "]", e );
}
}
protected MetaField getFieldForColumn( MetaClass resultClass, ObjectMapping mapping, String col ) throws MetaException
{
// Generate a cache key
//final String KEY = ( new StringBuilder( "getFieldForColumn(" ) ).append( col ).append( ")" ).toString();
MetaField rc = null; //(MetaField) resultClass.getCacheValue( KEY );
if ( rc == null ) {
// First check against the read mapping
//ObjectMapping om = getReadMapping( resultClass );
if ( mapping != null ) {
rc = mapping.getField( col );
}
// Next try to match by the metafield name
if ( rc == null ) {
try {
rc = resultClass.getMetaField( col );
} catch( MetaFieldNotFoundException e ) {}
}
// Add it to the cache to speed things up
//if ( rc != null ) resultClass.setCacheValue( KEY, rc );
}
return rc;
}
/**
* Executes the specified query and maps it to the given object.
*
* String oql = "[" + Product.CLASSNAME + "]" +
* " SELECT {P.*}, {M.name} AS manuName" +
* " FROM [" + Product.CLASSNAME + "=P]," +
* " [" + Manufacturer.CLASSNAME + "=M]" +
* " WHERE {M.id}={P.manuId} AND {M.id} > ?";
*
* String oql = "[{min:int,max:int,num:int}]" +
* " SELECT MIN({extra2}) AS min, MAX({extra2}) AS max, COUNT(1) AS num" +
* " FROM [" + Product.CLASSNAME + "]";
*/
public Collection> executeQuery( ObjectConnection c, String query, Collection> arguments ) throws MetaException
{
Connection conn = (Connection) c.getDatastoreConnection();
// Check for a valid transaction if enforced
checkTransaction( conn, false );
try
{
MetaClass resultClass = null;
query = query.trim();
if ( query.startsWith( "[{" ))
{
int i = query.indexOf( "}]" );
if ( i<= 0 )
throw new MetaException( "OQL does not contain a closing '}]': [" + query + "]" );
String classTemplate = query.substring( 2, i ).trim();
query = query.substring( i + 2 ).trim();
String templateClassname = "draagon::meta::manager::db::OQL" + classTemplate.hashCode();
// Get the result class, try it from the cache first
resultClass = templateCache.get( templateClassname );
if ( resultClass == null ) {
resultClass = MetaObjectClass.createFromTemplate( templateClassname, classTemplate );
templateCache.put( templateClassname, resultClass );
}
}
else if ( query.startsWith( "[" ))
{
int i = query.indexOf( "]" );
if ( i<= 0 )
throw new MetaException( "OQL does not contain a closing ']': [" + query + "]" );
String className = query.substring( 1, i ).trim();
query = query.substring( i + 1 ).trim();
resultClass = MetaClass.forName( className );
}
else
throw new MetaException( "OQL does not contain a result set definition using []'s or {}'s: [" + query + "]" );
PreparedStatement s = getPreparedStatement( conn, query, arguments );
try
{
if ( log.isDebugEnabled() ) {
log.debug( "SQL (" + conn.hashCode() + ") - executeQuery: [" + query + " " + arguments + "]" );
}
ResultSet rs = s.executeQuery();
LinkedList data = new LinkedList();
try
{
ObjectMappingDB mapping = (ObjectMappingDB) getReadMapping( resultClass );
while( rs.next() )
{
Object o = resultClass.newInstance();
for( int i = 1; i <= rs.getMetaData().getColumnCount(); i++ )
{
String col = rs.getMetaData().getColumnName( i );
MetaField mf = getFieldForColumn( resultClass, mapping, col );
if ( mf != null ) parseField( o, mf, rs, i );
}
data.add( o );
}
return data;
}
finally { rs.close(); }
}
finally { s.close(); }
}
catch( SQLException e )
{
log.error( "Unable to execute object query [" + query + " (" + arguments + ")]: " + e.getMessage() );
throw new MetaException( "Unable to execute object query [" + query + " (" + arguments + ")]: " + e.getMessage(), e );
}
}
///////////////////////////////////////////////////////
// TO STRING METHOD
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( getClass().getSimpleName() )
.append( "[" )
.append( getMappingHandler().getClass().getSimpleName() )
.append( "][" )
.append( getDatabaseDriver().getClass().getSimpleName() )
.append( "]" );
return sb.toString();
}
}