[hibernate-commits] Hibernate SVN: r17821 - in core/trunk: core/src/main/java/org/hibernate/dialect/lock and 19 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Thu Oct 22 16:02:11 EDT 2009


Author: steve.ebersole at jboss.com
Date: 2009-10-22 16:02:10 -0400 (Thu, 22 Oct 2009)
New Revision: 17821

Added:
   core/trunk/testsuite/src/test/java/org/hibernate/test/cut/MutualFund.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Image.hbm.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Image.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestCustomColumnReadAndWrite.java
Modified:
   core/trunk/core/src/main/java/org/hibernate/cfg/HbmBinder.java
   core/trunk/core/src/main/java/org/hibernate/dialect/lock/UpdateLockingStrategy.java
   core/trunk/core/src/main/java/org/hibernate/mapping/Column.java
   core/trunk/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java
   core/trunk/core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java
   core/trunk/core/src/main/java/org/hibernate/persister/collection/CompositeElementPropertyMapping.java
   core/trunk/core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java
   core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
   core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java
   core/trunk/core/src/main/java/org/hibernate/persister/entity/BasicEntityPropertyMapping.java
   core/trunk/core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java
   core/trunk/core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java
   core/trunk/core/src/main/java/org/hibernate/sql/Delete.java
   core/trunk/core/src/main/java/org/hibernate/sql/Insert.java
   core/trunk/core/src/main/java/org/hibernate/sql/SelectFragment.java
   core/trunk/core/src/main/java/org/hibernate/sql/Update.java
   core/trunk/core/src/main/resources/org/hibernate/hibernate-mapping-3.0.dtd
   core/trunk/documentation/manual/src/main/docbook/en-US/content/basic_mapping.xml
   core/trunk/documentation/manual/src/main/docbook/en-US/content/query_sql.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/component/basic/ComponentTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/component/basic/Person.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/component/basic/User.hbm.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/compositeelement/Child.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/compositeelement/CompositeElementTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/compositeelement/Parent.hbm.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/cut/CompositeUserTypeTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/cut/Transaction.hbm.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/filter/DynamicFilterTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/filter/Product.hbm.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/filter/Product.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/filter/defs.hbm.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Animal.hbm.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/hql/CriteriaClassicAggregationReturnTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/hql/CriteriaHQLAlignmentTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Human.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.hbm.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/buildtime/InstrumentTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/domain/Document.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/domain/Documents.hbm.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/runtime/AbstractTransformingClassLoaderInstrumentTestCase.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/runtime/CGLIBInstrumentationTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/runtime/JavassistInstrumentationTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/join/JoinTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/join/Person.hbm.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/join/Person.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/join/User.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Employee.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/JoinedSubclassTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.hbm.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Alien.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Being.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Beings.hbm.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Human.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/SubselectTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Employee.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.hbm.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/UnionSubclassTest.java
Log:
HHH-4440 : column-level read/write fragment support

Modified: core/trunk/core/src/main/java/org/hibernate/cfg/HbmBinder.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/cfg/HbmBinder.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/cfg/HbmBinder.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -25,15 +25,12 @@
 package org.hibernate.cfg;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Properties;
 import java.util.StringTokenizer;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.dom4j.Attribute;
 import org.dom4j.Document;
 import org.dom4j.Element;
@@ -42,10 +39,10 @@
 import org.hibernate.FetchMode;
 import org.hibernate.FlushMode;
 import org.hibernate.MappingException;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
 import org.hibernate.engine.FilterDefinition;
 import org.hibernate.engine.NamedQueryDefinition;
 import org.hibernate.engine.Versioning;
-import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
 import org.hibernate.id.PersistentIdentifierGenerator;
 import org.hibernate.mapping.Any;
 import org.hibernate.mapping.Array;
@@ -100,6 +97,8 @@
 import org.hibernate.util.JoinedIterator;
 import org.hibernate.util.ReflectHelper;
 import org.hibernate.util.StringHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Walks an XML mapping document and produces the Hibernate configuration-time metamodel (the
@@ -1705,7 +1704,7 @@
 
 	}
 
-	public static void bindColumn(Element node, Column column, boolean isNullable) {
+	public static void bindColumn(Element node, Column column, boolean isNullable) throws MappingException {
 		Attribute lengthNode = node.attribute( "length" );
 		if ( lengthNode != null ) column.setLength( Integer.parseInt( lengthNode.getValue() ) );
 		Attribute scalNode = node.attribute( "scale" );
@@ -1725,6 +1724,13 @@
 		Attribute typeNode = node.attribute( "sql-type" );
 		if ( typeNode != null ) column.setSqlType( typeNode.getValue() );
 
+		String customWrite = node.attributeValue( "write" );
+		if(customWrite != null && !customWrite.matches("[^?]*\\?[^?]*")) {
+			throw new MappingException("write expression must contain exactly one value placeholder ('?') character");
+		}
+		column.setCustomWrite( customWrite );
+		column.setCustomRead( node.attributeValue( "read" ) );
+		
 		Element comment = node.element("comment");
 		if (comment!=null) column.setComment( comment.getTextTrim() );
 

Modified: core/trunk/core/src/main/java/org/hibernate/dialect/lock/UpdateLockingStrategy.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/dialect/lock/UpdateLockingStrategy.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/dialect/lock/UpdateLockingStrategy.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -24,23 +24,23 @@
  */
 package org.hibernate.dialect.lock;
 
-import org.hibernate.persister.entity.Lockable;
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.hibernate.HibernateException;
+import org.hibernate.JDBCException;
 import org.hibernate.LockMode;
-import org.hibernate.HibernateException;
 import org.hibernate.StaleObjectStateException;
-import org.hibernate.JDBCException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.persister.entity.Lockable;
 import org.hibernate.pretty.MessageHelper;
-import org.hibernate.exception.JDBCExceptionHelper;
 import org.hibernate.sql.Update;
-import org.hibernate.engine.SessionImplementor;
-import org.hibernate.engine.SessionFactoryImplementor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.Serializable;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-
 /**
  * A locking strategy where the locks are obtained through update statements.
  * <p/>
@@ -131,7 +131,7 @@
 		SessionFactoryImplementor factory = lockable.getFactory();
 		Update update = new Update( factory.getDialect() );
 		update.setTableName( lockable.getRootTableName() );
-		update.setPrimaryKeyColumnNames( lockable.getRootTableIdentifierColumnNames() );
+		update.addPrimaryKeyColumns( lockable.getRootTableIdentifierColumnNames() );
 		update.setVersionColumnName( lockable.getVersionColumnName() );
 		update.addColumn( lockable.getVersionColumnName() );
 		if ( factory.getSettings().isCommentsEnabled() ) {

Modified: core/trunk/core/src/main/java/org/hibernate/mapping/Column.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/mapping/Column.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/mapping/Column.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -31,6 +31,7 @@
 import org.hibernate.dialect.Dialect;
 import org.hibernate.dialect.function.SQLFunctionRegistry;
 import org.hibernate.engine.Mapping;
+import org.hibernate.sql.Template;
 import org.hibernate.util.StringHelper;
 
 /**
@@ -58,6 +59,8 @@
 	private String checkConstraint;
 	private String comment;
 	private String defaultValue;
+	private String customWrite;
+	private String customRead;
 
 	public Column() { };
 
@@ -260,9 +263,18 @@
 	}
 
 	public String getTemplate(Dialect dialect, SQLFunctionRegistry functionRegistry) {
-		return getQuotedName(dialect);
+		String expr = getReadExpr(dialect);
+		return Template.renderWhereStringTemplate(expr, dialect, functionRegistry);
 	}
 
+	public String getReadExpr(Dialect dialect) {
+		return ( customRead != null && customRead.length() > 0 ) ? customRead : getQuotedName(dialect);
+	}
+	
+	public String getWriteExpr() {
+		return ( customWrite != null && customWrite.length() > 0 ) ? customWrite : "?";
+	}
+	
 	public boolean isFormula() {
 		return false;
 	}
@@ -304,6 +316,22 @@
 		this.defaultValue = defaultValue;
 	}
 
+	public String getCustomWrite() {
+		return customWrite;
+	}
+
+	public void setCustomWrite(String customWrite) {
+		this.customWrite = customWrite;
+	}
+
+	public String getCustomRead() {
+		return customRead;
+	}
+
+	public void setCustomRead(String customRead) {
+		this.customRead = customRead;
+	}
+
 	public String getCanonicalName() {
 		return quoted ? name : name.toLowerCase();
 	}
@@ -327,6 +355,8 @@
 		copy.setCheckConstraint( checkConstraint );
 		copy.setComment( comment );
 		copy.setDefaultValue( defaultValue );
+		copy.setCustomRead( customRead );
+		copy.setCustomWrite( customWrite );
 		return copy;
 	}
 

Modified: core/trunk/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -33,16 +33,12 @@
 import java.util.Iterator;
 import java.util.Map;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.hibernate.AssertionFailure;
 import org.hibernate.FetchMode;
 import org.hibernate.HibernateException;
 import org.hibernate.MappingException;
 import org.hibernate.QueryException;
 import org.hibernate.TransientObjectException;
-import org.hibernate.jdbc.Expectation;
-import org.hibernate.jdbc.Expectations;
 import org.hibernate.cache.CacheException;
 import org.hibernate.cache.access.CollectionRegionAccessStrategy;
 import org.hibernate.cache.entry.CacheEntryStructure;
@@ -53,15 +49,17 @@
 import org.hibernate.collection.PersistentCollection;
 import org.hibernate.dialect.Dialect;
 import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
+import org.hibernate.engine.LoadQueryInfluencers;
 import org.hibernate.engine.PersistenceContext;
 import org.hibernate.engine.SessionFactoryImplementor;
 import org.hibernate.engine.SessionImplementor;
 import org.hibernate.engine.SubselectFetch;
-import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
-import org.hibernate.engine.LoadQueryInfluencers;
 import org.hibernate.exception.JDBCExceptionHelper;
 import org.hibernate.exception.SQLExceptionConverter;
 import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.jdbc.Expectation;
+import org.hibernate.jdbc.Expectations;
 import org.hibernate.loader.collection.CollectionInitializer;
 import org.hibernate.mapping.Collection;
 import org.hibernate.mapping.Column;
@@ -88,6 +86,8 @@
 import org.hibernate.util.ArrayHelper;
 import org.hibernate.util.FilterHelper;
 import org.hibernate.util.StringHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 
 /**
@@ -140,6 +140,9 @@
 	protected final String[] indexFormulas;
 	protected final boolean[] indexColumnIsSettable;
 	protected final String[] elementColumnNames;
+	protected final String[] elementColumnWriters;
+	protected final String[] elementColumnReaders;
+	protected final String[] elementColumnReaderTemplates;
 	protected final String[] elementFormulaTemplates;
 	protected final String[] elementFormulas;
 	protected final boolean[] elementColumnIsSettable;
@@ -320,6 +323,9 @@
 		int elementSpan = collection.getElement().getColumnSpan();
 		elementColumnAliases = new String[elementSpan];
 		elementColumnNames = new String[elementSpan];
+		elementColumnWriters = new String[elementSpan];
+		elementColumnReaders = new String[elementSpan];
+		elementColumnReaderTemplates = new String[elementSpan];
 		elementFormulaTemplates = new String[elementSpan];
 		elementFormulas = new String[elementSpan];
 		elementColumnIsSettable = new boolean[elementSpan];
@@ -339,6 +345,9 @@
 			else {
 				Column col = (Column) selectable;
 				elementColumnNames[j] = col.getQuotedName(dialect);
+				elementColumnWriters[j] = col.getWriteExpr();
+				elementColumnReaders[j] = col.getReadExpr(dialect);
+				elementColumnReaderTemplates[j] = col.getTemplate(dialect, factory.getSqlFunctionRegistry());
 				elementColumnIsSettable[j] = true;
 				elementColumnIsInPrimaryKey[j] = !col.isNullable();
 				if ( !col.isNullable() ) {
@@ -513,6 +522,8 @@
 		if ( elementType.isComponentType() ) {
 			elementPropertyMapping = new CompositeElementPropertyMapping( 
 					elementColumnNames,
+					elementColumnReaders,
+					elementColumnReaderTemplates,
 					elementFormulaTemplates,
 					(AbstractComponentType) elementType,
 					factory 
@@ -970,7 +981,7 @@
 	protected void appendElementColumns(SelectFragment frag, String elemAlias) {
 		for ( int i=0; i<elementColumnIsSettable.length; i++ ) {
 			if ( elementColumnIsSettable[i] ) {
-				frag.addColumn( elemAlias, elementColumnNames[i], elementColumnAliases[i] );
+				frag.addColumnTemplate( elemAlias, elementColumnReaderTemplates[i], elementColumnAliases[i] );
 			}
 			else {
 				frag.addFormula( elemAlias, elementFormulaTemplates[i], elementColumnAliases[i] );

Modified: core/trunk/core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -31,10 +31,6 @@
 
 import org.hibernate.HibernateException;
 import org.hibernate.MappingException;
-import org.hibernate.jdbc.Expectations;
-import org.hibernate.jdbc.Expectation;
-import org.hibernate.type.AssociationType;
-import org.hibernate.persister.entity.Joinable;
 import org.hibernate.cache.CacheException;
 import org.hibernate.cache.access.CollectionRegionAccessStrategy;
 import org.hibernate.cfg.Configuration;
@@ -44,15 +40,19 @@
 import org.hibernate.engine.SubselectFetch;
 import org.hibernate.engine.LoadQueryInfluencers;
 import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.jdbc.Expectation;
+import org.hibernate.jdbc.Expectations;
 import org.hibernate.loader.collection.BatchingCollectionInitializer;
 import org.hibernate.loader.collection.CollectionInitializer;
 import org.hibernate.loader.collection.SubselectCollectionLoader;
 import org.hibernate.mapping.Collection;
+import org.hibernate.persister.entity.Joinable;
 import org.hibernate.pretty.MessageHelper;
 import org.hibernate.sql.Delete;
 import org.hibernate.sql.Insert;
+import org.hibernate.sql.SelectFragment;
 import org.hibernate.sql.Update;
-import org.hibernate.sql.SelectFragment;
+import org.hibernate.type.AssociationType;
 import org.hibernate.util.ArrayHelper;
 
 /**
@@ -81,7 +81,7 @@
 		
 		Delete delete = new Delete()
 				.setTableName( qualifiedTableName )
-				.setPrimaryKeyColumnNames( keyColumnNames );
+				.addPrimaryKeyColumns( keyColumnNames );
 		
 		if ( hasWhere ) delete.setWhere( sqlWhereString );
 		
@@ -112,7 +112,7 @@
 		}
 		
 		//if ( !elementIsFormula ) {
-			insert.addColumns( elementColumnNames, elementColumnIsSettable );
+			insert.addColumns( elementColumnNames, elementColumnIsSettable, elementColumnWriters );
 		//}
 		
 		return insert.toStatementString();
@@ -127,17 +127,18 @@
 			.setTableName( qualifiedTableName );
 		
 		//if ( !elementIsFormula ) {
-			update.addColumns( elementColumnNames, elementColumnIsSettable );
+			update.addColumns( elementColumnNames, elementColumnIsSettable, elementColumnWriters );
 		//}
 		
 		if ( hasIdentifier ) {
-			update.setPrimaryKeyColumnNames( new String[]{ identifierColumnName } );
+			update.addPrimaryKeyColumns( new String[]{ identifierColumnName } );
 		}
 		else if ( hasIndex && !indexContainsFormula ) {
-			update.setPrimaryKeyColumnNames( ArrayHelper.join( keyColumnNames, indexColumnNames ) );
+			update.addPrimaryKeyColumns( ArrayHelper.join( keyColumnNames, indexColumnNames ) );
 		}
 		else {
-			update.setPrimaryKeyColumnNames( ArrayHelper.join( keyColumnNames, elementColumnNames, elementColumnIsInPrimaryKey ) );
+			update.addPrimaryKeyColumns( keyColumnNames );
+			update.addPrimaryKeyColumns( elementColumnNames, elementColumnIsInPrimaryKey, elementColumnWriters );
 		}
 		
 		if ( getFactory().getSettings().isCommentsEnabled() ) {
@@ -156,13 +157,14 @@
 			.setTableName( qualifiedTableName );
 		
 		if ( hasIdentifier ) {
-			delete.setPrimaryKeyColumnNames( new String[]{ identifierColumnName } );
+			delete.addPrimaryKeyColumns( new String[]{ identifierColumnName } );
 		}
 		else if ( hasIndex && !indexContainsFormula ) {
-			delete.setPrimaryKeyColumnNames( ArrayHelper.join( keyColumnNames, indexColumnNames ) );
+			delete.addPrimaryKeyColumns( ArrayHelper.join( keyColumnNames, indexColumnNames ) );
 		}
 		else {
-			delete.setPrimaryKeyColumnNames( ArrayHelper.join( keyColumnNames, elementColumnNames, elementColumnIsInPrimaryKey ) );
+			delete.addPrimaryKeyColumns( keyColumnNames );
+			delete.addPrimaryKeyColumns( elementColumnNames, elementColumnIsInPrimaryKey, elementColumnWriters );
 		}
 		
 		if ( getFactory().getSettings().isCommentsEnabled() ) {

Modified: core/trunk/core/src/main/java/org/hibernate/persister/collection/CompositeElementPropertyMapping.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/persister/collection/CompositeElementPropertyMapping.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/persister/collection/CompositeElementPropertyMapping.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -38,7 +38,9 @@
 	private final AbstractComponentType compositeType;
 	
 	public CompositeElementPropertyMapping(
-			String[] elementColumns, 
+			String[] elementColumns,
+			String[] elementColumnReaders,
+			String[] elementColumnReaderTemplates, 
 			String[] elementFormulaTemplates, 
 			AbstractComponentType compositeType, 
 			Mapping factory)
@@ -46,7 +48,8 @@
 
 		this.compositeType = compositeType;
 
-		initComponentPropertyPaths(null, compositeType, elementColumns, elementFormulaTemplates, factory);
+		initComponentPropertyPaths(null, compositeType, elementColumns, elementColumnReaders,
+				elementColumnReaderTemplates, elementFormulaTemplates, factory);
 
 	}
 
@@ -58,4 +61,4 @@
 		return compositeType.getName();
 	}
 
-}
\ No newline at end of file
+}

Modified: core/trunk/core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -31,8 +31,6 @@
 
 import org.hibernate.HibernateException;
 import org.hibernate.MappingException;
-import org.hibernate.jdbc.Expectation;
-import org.hibernate.jdbc.Expectations;
 import org.hibernate.cache.CacheException;
 import org.hibernate.cache.access.CollectionRegionAccessStrategy;
 import org.hibernate.cfg.Configuration;
@@ -42,6 +40,8 @@
 import org.hibernate.engine.SubselectFetch;
 import org.hibernate.engine.LoadQueryInfluencers;
 import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.jdbc.Expectation;
+import org.hibernate.jdbc.Expectations;
 import org.hibernate.loader.collection.BatchingCollectionInitializer;
 import org.hibernate.loader.collection.CollectionInitializer;
 import org.hibernate.loader.collection.SubselectOneToManyLoader;
@@ -96,7 +96,7 @@
 		Update update = new Update( getDialect() )
 				.setTableName( qualifiedTableName )
 				.addColumns( keyColumnNames, "null" )
-				.setPrimaryKeyColumnNames( keyColumnNames );
+				.addPrimaryKeyColumns( keyColumnNames );
 		
 		if ( hasIndex && !indexContainsFormula ) update.addColumns( indexColumnNames, "null" );
 		
@@ -125,7 +125,7 @@
 			update.setComment( "create one-to-many row " + getRole() );
 		}
 		
-		return update.setPrimaryKeyColumnNames( elementColumnNames )
+		return update.addPrimaryKeyColumns( elementColumnNames, elementColumnWriters )
 				.toStatementString();
 	}
 
@@ -156,7 +156,7 @@
 		//the ordering of removal and addition is not guaranteed when
 		//a child moves from one parent to another
 		String[] rowSelectColumnNames = ArrayHelper.join(keyColumnNames, elementColumnNames);
-		return update.setPrimaryKeyColumnNames( rowSelectColumnNames )
+		return update.addPrimaryKeyColumns( rowSelectColumnNames )
 				.toStatementString();
 	}
 

Modified: core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -30,15 +30,13 @@
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
-import java.util.Comparator;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.hibernate.AssertionFailure;
 import org.hibernate.EntityMode;
 import org.hibernate.FetchMode;
@@ -48,36 +46,36 @@
 import org.hibernate.QueryException;
 import org.hibernate.StaleObjectStateException;
 import org.hibernate.StaleStateException;
-import org.hibernate.jdbc.Expectation;
-import org.hibernate.jdbc.Expectations;
-import org.hibernate.jdbc.TooManyRowsAffectedException;
-import org.hibernate.dialect.lock.LockingStrategy;
 import org.hibernate.cache.CacheKey;
 import org.hibernate.cache.access.EntityRegionAccessStrategy;
 import org.hibernate.cache.entry.CacheEntry;
 import org.hibernate.cache.entry.CacheEntryStructure;
 import org.hibernate.cache.entry.StructuredCacheEntry;
 import org.hibernate.cache.entry.UnstructuredCacheEntry;
+import org.hibernate.dialect.lock.LockingStrategy;
 import org.hibernate.engine.CascadeStyle;
 import org.hibernate.engine.CascadingAction;
 import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
+import org.hibernate.engine.LoadQueryInfluencers;
 import org.hibernate.engine.Mapping;
 import org.hibernate.engine.SessionFactoryImplementor;
 import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.ValueInclusion;
 import org.hibernate.engine.Versioning;
-import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
-import org.hibernate.engine.EntityKey;
-import org.hibernate.engine.ValueInclusion;
-import org.hibernate.engine.LoadQueryInfluencers;
 import org.hibernate.exception.JDBCExceptionHelper;
 import org.hibernate.id.IdentifierGenerator;
 import org.hibernate.id.PostInsertIdentifierGenerator;
 import org.hibernate.id.PostInsertIdentityPersister;
+import org.hibernate.id.insert.Binder;
 import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
-import org.hibernate.id.insert.Binder;
-import org.hibernate.intercept.LazyPropertyInitializer;
 import org.hibernate.intercept.FieldInterceptionHelper;
 import org.hibernate.intercept.FieldInterceptor;
+import org.hibernate.intercept.LazyPropertyInitializer;
+import org.hibernate.jdbc.Expectation;
+import org.hibernate.jdbc.Expectations;
+import org.hibernate.jdbc.TooManyRowsAffectedException;
 import org.hibernate.loader.entity.BatchingEntityLoader;
 import org.hibernate.loader.entity.CascadeEntityLoader;
 import org.hibernate.loader.entity.EntityLoader;
@@ -99,10 +97,9 @@
 import org.hibernate.sql.SimpleSelect;
 import org.hibernate.sql.Template;
 import org.hibernate.sql.Update;
-import org.hibernate.sql.AliasGenerator;
+import org.hibernate.tuple.Tuplizer;
 import org.hibernate.tuple.entity.EntityMetamodel;
 import org.hibernate.tuple.entity.EntityTuplizer;
-import org.hibernate.tuple.Tuplizer;
 import org.hibernate.type.AbstractComponentType;
 import org.hibernate.type.AssociationType;
 import org.hibernate.type.EntityType;
@@ -112,6 +109,8 @@
 import org.hibernate.util.ArrayHelper;
 import org.hibernate.util.FilterHelper;
 import org.hibernate.util.StringHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Basic functionality for persisting an entity via JDBC
@@ -136,6 +135,8 @@
 	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 	private final String[] rootTableKeyColumnNames;
+	private final String[] rootTableKeyColumnReaders;
+	private final String[] rootTableKeyColumnReaderTemplates;
 	private final String[] identifierAliases;
 	private final int identifierColumnSpan;
 	private final String versionColumnName;
@@ -158,6 +159,8 @@
 	private final String[][] propertyColumnAliases;
 	private final String[][] propertyColumnNames;
 	private final String[][] propertyColumnFormulaTemplates;
+	private final String[][] propertyColumnReaderTemplates;
+	private final String[][] propertyColumnWriters;
 	private final boolean[][] propertyColumnUpdateable;
 	private final boolean[][] propertyColumnInsertable;
 	private final boolean[] propertyUniqueness;
@@ -175,6 +178,8 @@
 	private final Type[] subclassPropertyTypeClosure;
 	private final String[][] subclassPropertyFormulaTemplateClosure;
 	private final String[][] subclassPropertyColumnNameClosure;
+	private final String[][] subclassPropertyColumnReaderClosure;
+	private final String[][] subclassPropertyColumnReaderTemplateClosure;
 	private final FetchMode[] subclassPropertyFetchModeClosure;
 	private final boolean[] subclassPropertyNullabilityClosure;
 	private final boolean[] propertyDefinedOnSubclass;
@@ -187,6 +192,7 @@
 	private final boolean[] subclassColumnLazyClosure;
 	private final String[] subclassColumnAliasClosure;
 	private final boolean[] subclassColumnSelectableClosure;
+	private final String[] subclassColumnReaderTemplateClosure;
 	private final String[] subclassFormulaClosure;
 	private final String[] subclassFormulaTemplateClosure;
 	private final String[] subclassFormulaAliasClosure;
@@ -285,6 +291,14 @@
 		return DISCRIMINATOR_ALIAS;
 	}
 
+	public String getDiscriminatorColumnReaders() {
+		return DISCRIMINATOR_ALIAS;
+	}	
+	
+	public String getDiscriminatorColumnReaderTemplate() {
+		return DISCRIMINATOR_ALIAS;
+	}	
+	
 	protected String getDiscriminatorAlias() {
 		return DISCRIMINATOR_ALIAS;
 	}
@@ -471,6 +485,8 @@
 
 		identifierColumnSpan = persistentClass.getIdentifier().getColumnSpan();
 		rootTableKeyColumnNames = new String[identifierColumnSpan];
+		rootTableKeyColumnReaders = new String[identifierColumnSpan];
+		rootTableKeyColumnReaderTemplates = new String[identifierColumnSpan];
 		identifierAliases = new String[identifierColumnSpan];
 
 		rowIdName = persistentClass.getRootTable().getRowId();
@@ -482,6 +498,8 @@
 		while ( iter.hasNext() ) {
 			Column col = ( Column ) iter.next();
 			rootTableKeyColumnNames[i] = col.getQuotedName( factory.getDialect() );
+			rootTableKeyColumnReaders[i] = col.getReadExpr( factory.getDialect() );
+			rootTableKeyColumnReaderTemplates[i] = col.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
 			identifierAliases[i] = col.getAlias( factory.getDialect(), persistentClass.getRootTable() );
 			i++;
 		}
@@ -512,6 +530,8 @@
 		propertyColumnAliases = new String[hydrateSpan][];
 		propertyColumnNames = new String[hydrateSpan][];
 		propertyColumnFormulaTemplates = new String[hydrateSpan][];
+		propertyColumnReaderTemplates = new String[hydrateSpan][];
+		propertyColumnWriters = new String[hydrateSpan][];
 		propertyUniqueness = new boolean[hydrateSpan];
 		propertySelectable = new boolean[hydrateSpan];
 		propertyColumnUpdateable = new boolean[hydrateSpan][];
@@ -536,7 +556,9 @@
 			propertySubclassNames[i] = prop.getPersistentClass().getEntityName();
 			String[] colNames = new String[span];
 			String[] colAliases = new String[span];
-			String[] templates = new String[span];
+			String[] colReaderTemplates = new String[span];
+			String[] colWriters = new String[span];
+			String[] formulaTemplates = new String[span];
 			Iterator colIter = prop.getColumnIterator();
 			int k = 0;
 			while ( colIter.hasNext() ) {
@@ -544,15 +566,20 @@
 				colAliases[k] = thing.getAlias( factory.getDialect() , prop.getValue().getTable() );
 				if ( thing.isFormula() ) {
 					foundFormula = true;
-					templates[k] = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
+					formulaTemplates[k] = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
 				}
 				else {
-					colNames[k] = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
+					Column col = (Column)thing;
+					colNames[k] = col.getQuotedName( factory.getDialect() );
+					colReaderTemplates[k] = col.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
+					colWriters[k] = col.getWriteExpr();
 				}
 				k++;
 			}
 			propertyColumnNames[i] = colNames;
-			propertyColumnFormulaTemplates[i] = templates;
+			propertyColumnFormulaTemplates[i] = formulaTemplates;
+			propertyColumnReaderTemplates[i] = colReaderTemplates;
+			propertyColumnWriters[i] = colWriters;
 			propertyColumnAliases[i] = colAliases;
 
 			if ( lazyAvailable && prop.isLazy() ) {
@@ -583,6 +610,7 @@
 
 		ArrayList columns = new ArrayList();
 		ArrayList columnsLazy = new ArrayList();
+		ArrayList columnReaderTemplates = new ArrayList();
 		ArrayList aliases = new ArrayList();
 		ArrayList formulas = new ArrayList();
 		ArrayList formulaAliases = new ArrayList();
@@ -593,6 +621,8 @@
 		ArrayList classes = new ArrayList();
 		ArrayList templates = new ArrayList();
 		ArrayList propColumns = new ArrayList();
+		ArrayList propColumnReaders = new ArrayList();
+		ArrayList propColumnReaderTemplates = new ArrayList();
 		ArrayList joinedFetchesList = new ArrayList();
 		ArrayList cascades = new ArrayList();
 		ArrayList definedBySubclass = new ArrayList();
@@ -613,6 +643,8 @@
 
 			Iterator colIter = prop.getColumnIterator();
 			String[] cols = new String[prop.getColumnSpan()];
+			String[] readers = new String[prop.getColumnSpan()];
+			String[] readerTemplates = new String[prop.getColumnSpan()];
 			String[] forms = new String[prop.getColumnSpan()];
 			int[] colnos = new int[prop.getColumnSpan()];
 			int[] formnos = new int[prop.getColumnSpan()];
@@ -631,7 +663,8 @@
 					formulasLazy.add( lazy );
 				}
 				else {
-					String colName = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
+					Column col = (Column)thing;
+					String colName = col.getQuotedName( factory.getDialect() );
 					colnos[l] = columns.size(); //before add :-)
 					formnos[l] = -1;
 					columns.add( colName );
@@ -639,10 +672,17 @@
 					aliases.add( thing.getAlias( factory.getDialect(), prop.getValue().getTable() ) );
 					columnsLazy.add( lazy );
 					columnSelectables.add( Boolean.valueOf( prop.isSelectable() ) );
+					
+					readers[l] = col.getReadExpr( factory.getDialect() );
+					String readerTemplate = col.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
+					readerTemplates[l] = readerTemplate;
+					columnReaderTemplates.add( readerTemplate );
 				}
 				l++;
 			}
 			propColumns.add( cols );
+			propColumnReaders.add( readers );
+			propColumnReaderTemplates.add( readerTemplates );
 			templates.add( forms );
 			propColumnNumbers.add( colnos );
 			propFormulaNumbers.add( formnos );
@@ -654,6 +694,7 @@
 		subclassColumnAliasClosure = ArrayHelper.toStringArray( aliases );
 		subclassColumnLazyClosure = ArrayHelper.toBooleanArray( columnsLazy );
 		subclassColumnSelectableClosure = ArrayHelper.toBooleanArray( columnSelectables );
+		subclassColumnReaderTemplateClosure = ArrayHelper.toStringArray( columnReaderTemplates );
 
 		subclassFormulaClosure = ArrayHelper.toStringArray( formulas );
 		subclassFormulaTemplateClosure = ArrayHelper.toStringArray( formulaTemplates );
@@ -666,6 +707,8 @@
 		subclassPropertyNullabilityClosure = ArrayHelper.toBooleanArray( propNullables );
 		subclassPropertyFormulaTemplateClosure = ArrayHelper.to2DStringArray( templates );
 		subclassPropertyColumnNameClosure = ArrayHelper.to2DStringArray( propColumns );
+		subclassPropertyColumnReaderClosure = ArrayHelper.to2DStringArray( propColumnReaders );
+		subclassPropertyColumnReaderTemplateClosure = ArrayHelper.to2DStringArray( propColumnReaderTemplates );
 		subclassPropertyColumnNumberClosure = ArrayHelper.to2DIntArray( propColumnNumbers );
 		subclassPropertyFormulaNumberClosure = ArrayHelper.to2DIntArray( propFormulaNumbers );
 
@@ -906,6 +949,14 @@
 		return rootTableKeyColumnNames;
 	}
 
+	public String[] getIdentifierColumnReaders() {
+		return rootTableKeyColumnReaders;
+	}	
+
+	public String[] getIdentifierColumnReaderTemplates() {
+		return rootTableKeyColumnReaderTemplates;
+	}	
+	
 	protected int getIdentifierColumnSpan() {
 		return identifierColumnSpan;
 	}
@@ -997,14 +1048,14 @@
 
 		int[] columnTableNumbers = getSubclassColumnTableNumberClosure();
 		String[] columnAliases = getSubclassColumnAliasClosure();
-		String[] columns = getSubclassColumnClosure();
+		String[] columnReaderTemplates = getSubclassColumnReaderTemplateClosure();
 		for ( int i = 0; i < getSubclassColumnClosure().length; i++ ) {
 			boolean selectable = ( allProperties || !subclassColumnLazyClosure[i] ) &&
 				!isSubclassTableSequentialSelect( columnTableNumbers[i] ) &&
 				subclassColumnSelectableClosure[i];
 			if ( selectable ) {
 				String subalias = generateTableAlias( tableAlias, columnTableNumbers[i] );
-				select.addColumn( subalias, columns[i], columnAliases[i] );
+				select.addColumnTemplate( subalias, columnReaderTemplates[i], columnAliases[i] );
 			}
 		}
 
@@ -1174,9 +1225,9 @@
 		SelectFragment frag = new SelectFragment();
 		for ( int i = 0; i < propertyCount; i++ ) {
 			if ( inclusionChecker.includeProperty( i ) ) {
-				frag.addColumns(
+				frag.addColumnTemplates(
 						generateTableAlias( alias, propertyTableNumbers[i] ),
-						propertyColumnNames[i],
+						propertyColumnReaderTemplates[i],
 						propertyColumnAliases[i]
 				);
 				frag.addFormulas(
@@ -1284,7 +1335,7 @@
 			update.setComment( "forced version increment" );
 		}
 		update.addColumn( getVersionColumnName() );
-		update.setPrimaryKeyColumnNames( getIdentifierColumnNames() );
+		update.addPrimaryKeyColumns( getIdentifierColumnNames() );
 		update.setVersionColumnName( getVersionColumnName() );
 		return update.toStatementString();
 	}
@@ -1472,6 +1523,10 @@
 	public String[] getPropertyColumnNames(int i) {
 		return propertyColumnNames[i];
 	}
+	
+	public String[] getPropertyColumnWriters(int i) {
+		return propertyColumnWriters[i];
+	}	
 
 	protected int getPropertyColumnSpan(int i) {
 		return propertyColumnSpans[i];
@@ -1520,7 +1575,15 @@
 	protected String[][] getSubclassPropertyColumnNameClosure() {
 		return subclassPropertyColumnNameClosure;
 	}
+	
+	public String[][] getSubclassPropertyColumnReaderClosure() {
+		return subclassPropertyColumnReaderClosure;
+	}
 
+	public String[][] getSubclassPropertyColumnReaderTemplateClosure() {
+		return subclassPropertyColumnReaderTemplateClosure;
+	}
+
 	protected String[] getSubclassPropertyNameClosure() {
 		return subclassPropertyNameClosure;
 	}
@@ -1537,6 +1600,10 @@
 		return subclassColumnAliasClosure;
 	}
 
+	public String[] getSubclassColumnReaderTemplateClosure() {
+		return subclassColumnReaderTemplateClosure;
+	}
+
 	protected String[] getSubclassFormulaClosure() {
 		return subclassFormulaClosure;
 	}
@@ -1744,6 +1811,8 @@
 			propertyMapping.initPropertyPaths( getSubclassPropertyNameClosure()[i],
 					getSubclassPropertyTypeClosure()[i],
 					getSubclassPropertyColumnNameClosure()[i],
+					getSubclassPropertyColumnReaderClosure()[i],
+					getSubclassPropertyColumnReaderTemplateClosure()[i],
 					getSubclassPropertyFormulaTemplateClosure()[i],
 					mapping );
 		}
@@ -1752,13 +1821,16 @@
 	private void initIdentifierPropertyPaths(Mapping mapping) throws MappingException {
 		String idProp = getIdentifierPropertyName();
 		if ( idProp != null ) {
-			propertyMapping.initPropertyPaths( idProp, getIdentifierType(), getIdentifierColumnNames(), null, mapping );
+			propertyMapping.initPropertyPaths( idProp, getIdentifierType(), getIdentifierColumnNames(),
+					getIdentifierColumnReaders(), getIdentifierColumnReaderTemplates(), null, mapping );
 		}
 		if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) {
-			propertyMapping.initPropertyPaths( null, getIdentifierType(), getIdentifierColumnNames(), null, mapping );
+			propertyMapping.initPropertyPaths( null, getIdentifierType(), getIdentifierColumnNames(),
+					getIdentifierColumnReaders(), getIdentifierColumnReaderTemplates(), null, mapping );
 		}
 		if ( ! entityMetamodel.hasNonIdentifierPropertyNamedId() ) {
-			propertyMapping.initPropertyPaths( ENTITY_ID, getIdentifierType(), getIdentifierColumnNames(), null, mapping );
+			propertyMapping.initPropertyPaths( ENTITY_ID, getIdentifierType(), getIdentifierColumnNames(),
+					getIdentifierColumnReaders(), getIdentifierColumnReaderTemplates(), null, mapping );
 		}
 	}
 
@@ -1766,6 +1838,8 @@
 		propertyMapping.initPropertyPaths( ENTITY_CLASS,
 				getDiscriminatorType(),
 				new String[]{getDiscriminatorColumnName()},
+				new String[]{getDiscriminatorColumnReaders()},
+				new String[]{getDiscriminatorColumnReaderTemplate()},
 				new String[]{getDiscriminatorFormulaTemplate()},
 				getFactory() );
 	}
@@ -1838,17 +1912,17 @@
 
 		// select the correct row by either pk or rowid
 		if ( useRowId ) {
-			update.setPrimaryKeyColumnNames( new String[]{rowIdName} ); //TODO: eventually, rowIdName[j]
+			update.addPrimaryKeyColumns( new String[]{rowIdName} ); //TODO: eventually, rowIdName[j]
 		}
 		else {
-			update.setPrimaryKeyColumnNames( getKeyColumns( j ) );
+			update.addPrimaryKeyColumns( getKeyColumns( j ) );
 		}
 
 		boolean hasColumns = false;
 		for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
 			if ( includeProperty[i] && isPropertyOfTable( i, j ) ) {
 				// this is a property of the table, which we are updating
-				update.addColumns( getPropertyColumnNames(i), propertyColumnUpdateable[i] );
+				update.addColumns( getPropertyColumnNames(i), propertyColumnUpdateable[i], propertyColumnWriters[i] );
 				hasColumns = hasColumns || getPropertyColumnSpan( i ) > 0;
 			}
 		}
@@ -1879,10 +1953,11 @@
 					// this property belongs to the table, and it is not specifically
 					// excluded from optimistic locking by optimistic-lock="false"
 					String[] propertyColumnNames = getPropertyColumnNames( i );
+					String[] propertyColumnWriters = getPropertyColumnWriters( i );
 					boolean[] propertyNullness = types[i].toColumnNullness( oldFields[i], getFactory() );
 					for ( int k=0; k<propertyNullness.length; k++ ) {
 						if ( propertyNullness[k] ) {
-							update.addWhereColumn( propertyColumnNames[k] );
+							update.addWhereColumn( propertyColumnNames[k], "=" + propertyColumnWriters[k] );
 						}
 						else {
 							update.addWhereColumn( propertyColumnNames[k], " is null" );
@@ -1928,7 +2003,7 @@
 		for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
 			if ( includeProperty[i] && isPropertyOfTable( i, j ) ) {
 				// this property belongs on the table and is to be inserted
-				insert.addColumns( getPropertyColumnNames(i), propertyColumnInsertable[i] );
+				insert.addColumns( getPropertyColumnNames(i), propertyColumnInsertable[i], propertyColumnWriters[i] );
 			}
 		}
 
@@ -1976,7 +2051,7 @@
 		for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
 			if ( includeProperty[i] && isPropertyOfTable( i, 0 ) ) {
 				// this property belongs on the table and is to be inserted
-				insert.addColumns( getPropertyColumnNames(i), propertyColumnInsertable[i] );
+				insert.addColumns( getPropertyColumnNames(i), propertyColumnInsertable[i], propertyColumnWriters[i] );
 			}
 		}
 
@@ -1998,7 +2073,7 @@
 	protected String generateDeleteString(int j) {
 		Delete delete = new Delete()
 				.setTableName( getTableName( j ) )
-				.setPrimaryKeyColumnNames( getKeyColumns( j ) );
+				.addPrimaryKeyColumns( getKeyColumns( j ) );
 		if ( j == 0 ) {
 			delete.setVersionColumnName( getVersionColumnName() );
 		}
@@ -2750,7 +2825,7 @@
 		for ( int j = span - 1; j >= 0; j-- ) {
 			Delete delete = new Delete()
 					.setTableName( getTableName( j ) )
-					.setPrimaryKeyColumnNames( getKeyColumns( j ) );
+					.addPrimaryKeyColumns( getKeyColumns( j ) );
 			if ( getFactory().getSettings().isCommentsEnabled() ) {
 				delete.setComment( "delete " + getEntityName() + " [" + j + "]" );
 			}
@@ -2889,12 +2964,12 @@
 
 		int[] columnTableNumbers = getSubclassColumnTableNumberClosure();
 		String[] columnAliases = getSubclassColumnAliasClosure();
-		String[] columns = getSubclassColumnClosure();
+		String[] columnReaderTemplates = getSubclassColumnReaderTemplateClosure();
 		for ( int i = 0; i < subclassColumnNumbers.length; i++ ) {
 			int columnNumber = subclassColumnNumbers[i];
 			if ( subclassColumnSelectableClosure[columnNumber] ) {
 				final String subalias = generateTableAlias( getRootAlias(), columnTableNumbers[columnNumber] );
-				selectFragment.addColumn( subalias, columns[columnNumber], columnAliases[columnNumber] );
+				selectFragment.addColumnTemplate( subalias, columnReaderTemplates[columnNumber], columnAliases[columnNumber] );
 			}
 		}
 

Modified: core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -47,12 +47,22 @@
 
 	private final Map typesByPropertyPath = new HashMap();
 	private final Map columnsByPropertyPath = new HashMap();
+	private final Map columnReadersByPropertyPath = new HashMap();
+	private final Map columnReaderTemplatesByPropertyPath = new HashMap();
 	private final Map formulaTemplatesByPropertyPath = new HashMap();
 
 	public String[] getIdentifierColumnNames() {
 		throw new UnsupportedOperationException("one-to-one is not supported here");
 	}
 
+	public String[] getIdentifierColumnReaderTemplates() {
+		throw new UnsupportedOperationException("one-to-one is not supported here");
+	}
+
+	public String[] getIdentifierColumnReaders() {
+		throw new UnsupportedOperationException("one-to-one is not supported here");
+	}
+	
 	protected abstract String getEntityName();
 
 	public Type toType(String propertyName) throws QueryException {
@@ -81,14 +91,15 @@
 		if ( columns == null ) {
 			throw propertyException( propertyName );
 		}
-		String[] templates = (String[]) formulaTemplatesByPropertyPath.get(propertyName);
+		String[] formulaTemplates = (String[]) formulaTemplatesByPropertyPath.get(propertyName);
+		String[] columnReaderTemplates = (String[]) columnReaderTemplatesByPropertyPath.get(propertyName);		
 		String[] result = new String[columns.length];
 		for ( int i=0; i<columns.length; i++ ) {
-			if ( columns[i]==null ) {
-				result[i] = StringHelper.replace( templates[i], Template.TEMPLATE, alias );
+			if ( columnReaderTemplates[i]==null ) {
+				result[i] = StringHelper.replace( formulaTemplates[i], Template.TEMPLATE, alias );
 			}
 			else {
-				result[i] = StringHelper.qualify( alias, columns[i] );
+				result[i] = StringHelper.replace( columnReaderTemplates[i], Template.TEMPLATE, alias );
 			}
 		}
 		return result;
@@ -99,22 +110,27 @@
 		if ( columns == null ) {
 			throw propertyException( propertyName );
 		}
-		String[] templates = (String[]) formulaTemplatesByPropertyPath.get(propertyName);
+		String[] formulaTemplates = (String[]) formulaTemplatesByPropertyPath.get(propertyName);
+		String[] columnReaders = (String[]) columnReadersByPropertyPath.get(propertyName);
 		String[] result = new String[columns.length];
 		for ( int i=0; i<columns.length; i++ ) {
-			if ( columns[i]==null ) {
-				result[i] = StringHelper.replace( templates[i], Template.TEMPLATE, "" );
+			if ( columnReaders[i]==null ) {
+				result[i] = StringHelper.replace( formulaTemplates[i], Template.TEMPLATE, "" );
 			}
 			else {
-				result[i] = columns[i];
+				result[i] = columnReaders[i];
 			}
 		}
 		return result;
 	}
 
-	protected void addPropertyPath(String path, Type type, String[] columns, String[] formulaTemplates) {
+	protected void addPropertyPath(String path, Type type, String[] columns,
+			String[] columnReaders, String[] columnReaderTemplates,
+			String[] formulaTemplates) {
 		typesByPropertyPath.put(path, type);
 		columnsByPropertyPath.put(path, columns);
+		columnReadersByPropertyPath.put(path, columnReaders);
+		columnReaderTemplatesByPropertyPath.put(path, columnReaderTemplates);
 		if (formulaTemplates!=null) {
 			formulaTemplatesByPropertyPath.put(path, formulaTemplates);
 		}
@@ -135,6 +151,8 @@
 			final String path,
 			final Type type,
 			String[] columns,
+			String[] columnReaders,
+			String[] columnReaderTemplates,
 			final String[] formulaTemplates,
 			final Mapping factory)
 	throws MappingException {
@@ -150,6 +168,8 @@
 			AssociationType actype = (AssociationType) type;
 			if ( actype.useLHSPrimaryKey() ) {
 				columns = getIdentifierColumnNames();
+				columnReaders = getIdentifierColumnReaders();
+				columnReaderTemplates = getIdentifierColumnReaderTemplates();
 			}
 			else {
 				String foreignKeyProperty = actype.getLHSPropertyName();
@@ -158,27 +178,31 @@
 					//      referenced property in the mapping file (ok?)
 					columns = (String[]) columnsByPropertyPath.get(foreignKeyProperty);
 					if (columns==null) return; //get em on the second pass!
+					columnReaders = (String[]) columnReadersByPropertyPath.get(foreignKeyProperty);
+					columnReaderTemplates = (String[]) columnReaderTemplatesByPropertyPath.get(foreignKeyProperty);
 				}
 			}
 		}
 
-		if (path!=null) addPropertyPath(path, type, columns, formulaTemplates);
+		if (path!=null) addPropertyPath(path, type, columns, columnReaders, columnReaderTemplates, formulaTemplates);
 
 		if ( type.isComponentType() ) {
 			AbstractComponentType actype = (AbstractComponentType) type;
-			initComponentPropertyPaths( path, actype, columns, formulaTemplates, factory );
+			initComponentPropertyPaths( path, actype, columns, columnReaders, columnReaderTemplates, formulaTemplates, factory );
 			if ( actype.isEmbedded() ) {
 				initComponentPropertyPaths(
 						path==null ? null : StringHelper.qualifier(path),
 						actype,
 						columns,
+						columnReaders,
+						columnReaderTemplates,
 						formulaTemplates,
 						factory
 					);
 			}
 		}
 		else if ( type.isEntityType() ) {
-			initIdentifierPropertyPaths( path, (EntityType) type, columns, factory );
+			initIdentifierPropertyPaths( path, (EntityType) type, columns, columnReaders, columnReaderTemplates, factory );
 		}
 	}
 
@@ -186,6 +210,8 @@
 			final String path,
 			final EntityType etype,
 			final String[] columns,
+			final String[] columnReaders,
+			final String[] columnReaderTemplates,
 			final Mapping factory) throws MappingException {
 
 		Type idtype = etype.getIdentifierOrUniqueKeyType( factory );
@@ -195,15 +221,15 @@
 		if ( etype.isReferenceToPrimaryKey() ) {
 			if ( !hasNonIdentifierPropertyNamedId ) {
 				String idpath1 = extendPath(path, EntityPersister.ENTITY_ID);
-				addPropertyPath(idpath1, idtype, columns, null);
-				initPropertyPaths(idpath1, idtype, columns, null, factory);
+				addPropertyPath(idpath1, idtype, columns, columnReaders, columnReaderTemplates, null);
+				initPropertyPaths(idpath1, idtype, columns, columnReaders, columnReaderTemplates, null, factory);
 			}
 		}
 
 		if (idPropName!=null) {
 			String idpath2 = extendPath(path, idPropName);
-			addPropertyPath(idpath2, idtype, columns, null);
-			initPropertyPaths(idpath2, idtype, columns, null, factory);
+			addPropertyPath(idpath2, idtype, columns, columnReaders, columnReaderTemplates, null);
+			initPropertyPaths(idpath2, idtype, columns, columnReaders, columnReaderTemplates, null, factory);
 		}
 	}
 
@@ -223,6 +249,8 @@
 			final String path,
 			final AbstractComponentType type,
 			final String[] columns,
+			final String[] columnReaders,
+			final String[] columnReaderTemplates,
 			String[] formulaTemplates, final Mapping factory)
 	throws MappingException {
 
@@ -234,9 +262,11 @@
 			try {
 				int length = types[i].getColumnSpan(factory);
 				String[] columnSlice = ArrayHelper.slice(columns, begin, length);
+				String[] columnReaderSlice = ArrayHelper.slice(columnReaders, begin, length);
+				String[] columnReaderTemplateSlice = ArrayHelper.slice(columnReaderTemplates, begin, length);
 				String[] formulaSlice = formulaTemplates==null ?
 						null : ArrayHelper.slice(formulaTemplates, begin, length);
-				initPropertyPaths(subpath, types[i], columnSlice, formulaSlice, factory);
+				initPropertyPaths(subpath, types[i], columnSlice, columnReaderSlice, columnReaderTemplateSlice, formulaSlice, factory);
 				begin+=length;
 			}
 			catch (Exception e) {

Modified: core/trunk/core/src/main/java/org/hibernate/persister/entity/BasicEntityPropertyMapping.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/persister/entity/BasicEntityPropertyMapping.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/persister/entity/BasicEntityPropertyMapping.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -41,6 +41,14 @@
 	public String[] getIdentifierColumnNames() {
 		return persister.getIdentifierColumnNames();
 	}
+	
+	public String[] getIdentifierColumnReaders() {
+		return persister.getIdentifierColumnReaders();
+	}
+	
+	public String[] getIdentifierColumnReaderTemplates() {
+		return persister.getIdentifierColumnReaderTemplates();
+	}
 
 	protected String getEntityName() {
 		return persister.getEntityName();

Modified: core/trunk/core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -36,10 +36,10 @@
 import org.hibernate.MappingException;
 import org.hibernate.QueryException;
 import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
 import org.hibernate.engine.Mapping;
 import org.hibernate.engine.SessionFactoryImplementor;
 import org.hibernate.engine.Versioning;
-import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
 import org.hibernate.mapping.Column;
 import org.hibernate.mapping.KeyValue;
 import org.hibernate.mapping.PersistentClass;
@@ -65,7 +65,11 @@
 	private final String[] tableNames;
 	private final String[] naturalOrderTableNames;
 	private final String[][] tableKeyColumns;
+	private final String[][] tableKeyColumnReaders;
+	private final String[][] tableKeyColumnReaderTemplates;
 	private final String[][] naturalOrderTableKeyColumns;
+	private final String[][] naturalOrderTableKeyColumnReaders;
+	private final String[][] naturalOrderTableKeyColumnReaderTemplates;
 	private final boolean[] naturalOrderCascadeDeleteEnabled;
 
 	private final String[] spaces;
@@ -139,6 +143,8 @@
 
 		ArrayList tables = new ArrayList();
 		ArrayList keyColumns = new ArrayList();
+		ArrayList keyColumnReaders = new ArrayList();
+		ArrayList keyColumnReaderTemplates = new ArrayList();
 		ArrayList cascadeDeletes = new ArrayList();
 		Iterator titer = persistentClass.getTableClosureIterator();
 		Iterator kiter = persistentClass.getKeyClosureIterator();
@@ -152,15 +158,24 @@
 			);
 			tables.add(tabname);
 			String[] keyCols = new String[idColumnSpan];
+			String[] keyColReaders = new String[idColumnSpan];
+			String[] keyColReaderTemplates = new String[idColumnSpan];
 			Iterator citer = key.getColumnIterator();
 			for ( int k=0; k<idColumnSpan; k++ ) {
-				keyCols[k] = ( (Column) citer.next() ).getQuotedName( factory.getDialect() );
+				Column column = (Column) citer.next();
+				keyCols[k] = column.getQuotedName( factory.getDialect() );
+				keyColReaders[k] = column.getReadExpr( factory.getDialect() );
+				keyColReaderTemplates[k] = column.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
 			}
 			keyColumns.add(keyCols);
+			keyColumnReaders.add(keyColReaders);
+			keyColumnReaderTemplates.add(keyColReaderTemplates);
 			cascadeDeletes.add( new Boolean( key.isCascadeDeleteEnabled() && factory.getDialect().supportsCascadeDelete() ) );
 		}
 		naturalOrderTableNames = ArrayHelper.toStringArray(tables);
 		naturalOrderTableKeyColumns = ArrayHelper.to2DStringArray(keyColumns);
+		naturalOrderTableKeyColumnReaders = ArrayHelper.to2DStringArray(keyColumnReaders);
+		naturalOrderTableKeyColumnReaderTemplates = ArrayHelper.to2DStringArray(keyColumnReaderTemplates);
 		naturalOrderCascadeDeleteEnabled = ArrayHelper.toBooleanArray(cascadeDeletes);
 
 		ArrayList subtables = new ArrayList();
@@ -198,6 +213,8 @@
 		tableSpan = naturalOrderTableNames.length;
 		tableNames = reverse(naturalOrderTableNames);
 		tableKeyColumns = reverse(naturalOrderTableKeyColumns);
+		tableKeyColumnReaders = reverse(naturalOrderTableKeyColumnReaders);
+		tableKeyColumnReaderTemplates = reverse(naturalOrderTableKeyColumnReaderTemplates);
 		reverse(subclassTableNameClosure, tableSpan);
 		reverse(subclassTableKeyColumnClosure, tableSpan);
 
@@ -514,6 +531,14 @@
 		return tableKeyColumns[0];
 	}
 
+	public String[] getIdentifierColumnReaderTemplates() {
+		return tableKeyColumnReaderTemplates[0];
+	}
+
+	public String[] getIdentifierColumnReaders() {
+		return tableKeyColumnReaders[0];
+	}		
+	
 	public String[] toColumns(String alias, String propertyName) throws QueryException {
 
 		if ( ENTITY_CLASS.equals(propertyName) ) {

Modified: core/trunk/core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -35,9 +35,9 @@
 import org.hibernate.HibernateException;
 import org.hibernate.MappingException;
 import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
 import org.hibernate.engine.Mapping;
 import org.hibernate.engine.SessionFactoryImplementor;
-import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
 import org.hibernate.mapping.Column;
 import org.hibernate.mapping.Formula;
 import org.hibernate.mapping.Join;
@@ -102,6 +102,8 @@
 	private final Map subclassesByDiscriminatorValue = new HashMap();
 	private final boolean forceDiscriminator;
 	private final String discriminatorColumnName;
+	private final String discriminatorColumnReaders;
+	private final String discriminatorColumnReaderTemplate;
 	private final String discriminatorFormula;
 	private final String discriminatorFormulaTemplate;
 	private final String discriminatorAlias;
@@ -295,11 +297,15 @@
 				discriminatorFormula = formula.getFormula();
 				discriminatorFormulaTemplate = formula.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
 				discriminatorColumnName = null;
+				discriminatorColumnReaders = null;
+				discriminatorColumnReaderTemplate = null;
 				discriminatorAlias = "clazz_";
 			}
 			else {
 				Column column = (Column) selectable;
 				discriminatorColumnName = column.getQuotedName( factory.getDialect() );
+				discriminatorColumnReaders = column.getReadExpr( factory.getDialect() );
+				discriminatorColumnReaderTemplate = column.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
 				discriminatorAlias = column.getAlias( factory.getDialect(), persistentClass.getRootTable() );
 				discriminatorFormula = null;
 				discriminatorFormulaTemplate = null;
@@ -334,6 +340,8 @@
 			forceDiscriminator = false;
 			discriminatorInsertable = false;
 			discriminatorColumnName = null;
+			discriminatorColumnReaders = null;
+			discriminatorColumnReaderTemplate = null;
 			discriminatorAlias = null;
 			discriminatorType = null;
 			discriminatorValue = null;
@@ -444,6 +452,14 @@
 		return discriminatorColumnName;
 	}
 
+	public String getDiscriminatorColumnReaders() {
+		return discriminatorColumnReaders;
+	}			
+	
+	public String getDiscriminatorColumnReaderTemplate() {
+		return discriminatorColumnReaderTemplate;
+	}	
+	
 	protected String getDiscriminatorAlias() {
 		return discriminatorAlias;
 	}

Modified: core/trunk/core/src/main/java/org/hibernate/sql/Delete.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/sql/Delete.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/sql/Delete.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -24,8 +24,12 @@
  */
 package org.hibernate.sql;
 
-import org.hibernate.util.StringHelper;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
 
+import net.sf.cglib.transform.impl.AddPropertyTransformer;
+
 /**
  * An SQL <tt>DELETE</tt> statement
  *
@@ -34,10 +38,11 @@
 public class Delete {
 
 	private String tableName;
-	private String[] primaryKeyColumnNames;
 	private String versionColumnName;
 	private String where;
 
+	private Map primaryKeyColumns = new LinkedHashMap();	
+	
 	private String comment;
 	public Delete setComment(String comment) {
 		this.comment = comment;
@@ -55,12 +60,17 @@
 			buf.append( "/* " ).append(comment).append( " */ " );
 		}
 		buf.append( "delete from " ).append(tableName);
-		if ( where != null || primaryKeyColumnNames != null || versionColumnName != null ) {
+		if ( where != null || !primaryKeyColumns.isEmpty() || versionColumnName != null ) {
 			buf.append( " where " );
 		}
 		boolean conditionsAppended = false;
-		if ( primaryKeyColumnNames != null ) {
-			buf.append( StringHelper.join( "=? and ", primaryKeyColumnNames ) ).append( "=?" );
+		Iterator iter = primaryKeyColumns.entrySet().iterator();
+		while ( iter.hasNext() ) {
+			Map.Entry e = (Map.Entry) iter.next();
+			buf.append( e.getKey() ).append( '=' ).append( e.getValue() );
+			if ( iter.hasNext() ) {
+				buf.append( " and " );
+			}
 			conditionsAppended = true;
 		}
 		if ( where!=null ) {
@@ -94,11 +104,38 @@
 		return this;
 	}
 
-	public Delete setPrimaryKeyColumnNames(String[] primaryKeyColumnNames) {
-		this.primaryKeyColumnNames = primaryKeyColumnNames;
+	public Delete setPrimaryKeyColumnNames(String[] columnNames) {
+		this.primaryKeyColumns.clear();
+		addPrimaryKeyColumns(columnNames);
 		return this;
+	}	
+
+	public Delete addPrimaryKeyColumns(String[] columnNames) {
+		for ( int i=0; i<columnNames.length; i++ ) {
+			addPrimaryKeyColumn( columnNames[i], "?" );
+		}
+		return this;
 	}
+	
+	public Delete addPrimaryKeyColumns(String[] columnNames, boolean[] includeColumns, String[] valueExpressions) {
+		for ( int i=0; i<columnNames.length; i++ ) {
+			if( includeColumns[i] ) addPrimaryKeyColumn( columnNames[i], valueExpressions[i] );
+		}
+		return this;
+	}
+	
+	public Delete addPrimaryKeyColumns(String[] columnNames, String[] valueExpressions) {
+		for ( int i=0; i<columnNames.length; i++ ) {
+			addPrimaryKeyColumn( columnNames[i], valueExpressions[i] );
+		}
+		return this;
+	}	
 
+	public Delete addPrimaryKeyColumn(String columnName, String valueExpression) {
+		this.primaryKeyColumns.put(columnName, valueExpression);
+		return this;
+	}
+
 	public Delete setVersionColumnName(String versionColumnName) {
 		this.versionColumnName = versionColumnName;
 		return this;

Modified: core/trunk/core/src/main/java/org/hibernate/sql/Insert.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/sql/Insert.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/sql/Insert.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -25,8 +25,8 @@
 package org.hibernate.sql;
 
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.Map;
-import java.util.LinkedHashMap;
 
 import org.hibernate.dialect.Dialect;
 import org.hibernate.type.LiteralType;
@@ -75,11 +75,20 @@
 		return this;
 	}
 
-	public Insert addColumn(String columnName, String value) {
-		columns.put(columnName, value);
+	public Insert addColumns(String[] columnNames, boolean[] insertable, String[] valueExpressions) {
+		for ( int i=0; i<columnNames.length; i++ ) {
+			if ( insertable[i] ) {
+				addColumn( columnNames[i], valueExpressions[i] );
+			}
+		}
 		return this;
 	}
 
+	public Insert addColumn(String columnName, String valueExpression) {
+		columns.put(columnName, valueExpression);
+		return this;
+	}
+
 	public Insert addColumn(String columnName, Object value, LiteralType type) throws Exception {
 		return addColumn( columnName, type.objectToSQLString(value, dialect) );
 	}

Modified: core/trunk/core/src/main/java/org/hibernate/sql/SelectFragment.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/sql/SelectFragment.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/sql/SelectFragment.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -121,7 +121,17 @@
 		columnAliases.add(formulaAlias);
 		return this;
 	}
+	
+	public SelectFragment addColumnTemplate(String tableAlias, String columnTemplate, String columnAlias) {
+		// In this context, there's no difference between a column template and a formula.
+		return addFormula( tableAlias, columnTemplate, columnAlias );
+	}
 
+	public SelectFragment addColumnTemplates(String tableAlias, String[] columnTemplates, String columnAliases[]) {
+		// In this context, there's no difference between a column template and a formula.
+		return addFormulas( tableAlias, columnTemplates, columnAliases );
+	}	
+	
 	public String toFragmentString() {
 		StringBuffer buf = new StringBuffer( columns.size() * 10 );
 		Iterator iter = columns.iterator();

Modified: core/trunk/core/src/main/java/org/hibernate/sql/Update.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/sql/Update.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/java/org/hibernate/sql/Update.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -25,12 +25,11 @@
 package org.hibernate.sql;
 
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.Map;
-import java.util.LinkedHashMap;
 
 import org.hibernate.dialect.Dialect;
 import org.hibernate.type.LiteralType;
-import org.hibernate.util.StringHelper;
 
 /**
  * An SQL <tt>UPDATE</tt> statement
@@ -40,12 +39,12 @@
 public class Update {
 
 	private String tableName;
-	private String[] primaryKeyColumnNames;
 	private String versionColumnName;
 	private String where;
 	private String assignments;
 	private String comment;
 
+	private Map primaryKeyColumns = new LinkedHashMap();
 	private Map columns = new LinkedHashMap();
 	private Map whereColumns = new LinkedHashMap();
 	
@@ -74,11 +73,38 @@
 		return this;
 	}
 
-	public Update setPrimaryKeyColumnNames(String[] primaryKeyColumnNames) {
-		this.primaryKeyColumnNames = primaryKeyColumnNames;
+	public Update setPrimaryKeyColumnNames(String[] columnNames) {
+		this.primaryKeyColumns.clear();
+		addPrimaryKeyColumns(columnNames);
 		return this;
+	}	
+	
+	public Update addPrimaryKeyColumns(String[] columnNames) {
+		for ( int i=0; i<columnNames.length; i++ ) {
+			addPrimaryKeyColumn( columnNames[i], "?" );
+		}
+		return this;
 	}
+	
+	public Update addPrimaryKeyColumns(String[] columnNames, boolean[] includeColumns, String[] valueExpressions) {
+		for ( int i=0; i<columnNames.length; i++ ) {
+			if( includeColumns[i] ) addPrimaryKeyColumn( columnNames[i], valueExpressions[i] );
+		}
+		return this;
+	}
+	
+	public Update addPrimaryKeyColumns(String[] columnNames, String[] valueExpressions) {
+		for ( int i=0; i<columnNames.length; i++ ) {
+			addPrimaryKeyColumn( columnNames[i], valueExpressions[i] );
+		}
+		return this;
+	}	
 
+	public Update addPrimaryKeyColumn(String columnName, String valueExpression) {
+		this.primaryKeyColumns.put(columnName, valueExpression);
+		return this;
+	}
+	
 	public Update setVersionColumnName(String versionColumnName) {
 		this.versionColumnName = versionColumnName;
 		return this;
@@ -89,7 +115,7 @@
 		this.comment = comment;
 		return this;
 	}
-
+	
 	public Update addColumns(String[] columnNames) {
 		for ( int i=0; i<columnNames.length; i++ ) {
 			addColumn( columnNames[i] );
@@ -97,16 +123,16 @@
 		return this;
 	}
 
-	public Update addColumns(String[] columnNames, boolean[] updateable) {
+	public Update addColumns(String[] columnNames, boolean[] updateable, String[] valueExpressions) {
 		for ( int i=0; i<columnNames.length; i++ ) {
-			if ( updateable[i] ) addColumn( columnNames[i] );
+			if ( updateable[i] ) addColumn( columnNames[i], valueExpressions[i] );
 		}
 		return this;
 	}
 
-	public Update addColumns(String[] columnNames, String value) {
+	public Update addColumns(String[] columnNames, String valueExpression) {
 		for ( int i=0; i<columnNames.length; i++ ) {
-			addColumn( columnNames[i], value );
+			addColumn( columnNames[i], valueExpression );
 		}
 		return this;
 	}
@@ -115,8 +141,8 @@
 		return addColumn(columnName, "?");
 	}
 
-	public Update addColumn(String columnName, String value) {
-		columns.put(columnName, value);
+	public Update addColumn(String columnName, String valueExpression) {
+		columns.put(columnName, valueExpression);
 		return this;
 	}
 
@@ -131,9 +157,9 @@
 		return this;
 	}
 
-	public Update addWhereColumns(String[] columnNames, String value) {
+	public Update addWhereColumns(String[] columnNames, String valueExpression) {
 		for ( int i=0; i<columnNames.length; i++ ) {
-			addWhereColumn( columnNames[i], value );
+			addWhereColumn( columnNames[i], valueExpression );
 		}
 		return this;
 	}
@@ -142,8 +168,8 @@
 		return addWhereColumn(columnName, "=?");
 	}
 
-	public Update addWhereColumn(String columnName, String value) {
-		whereColumns.put(columnName, value);
+	public Update addWhereColumn(String columnName, String valueExpression) {
+		whereColumns.put(columnName, valueExpression);
 		return this;
 	}
 
@@ -176,11 +202,16 @@
 		}
 
 		boolean conditionsAppended = false;
-		if ( primaryKeyColumnNames != null || where != null || !whereColumns.isEmpty() || versionColumnName != null ) {
+		if ( !primaryKeyColumns.isEmpty() || where != null || !whereColumns.isEmpty() || versionColumnName != null ) {
 			buf.append( " where " );
 		}
-		if ( primaryKeyColumnNames != null ) {
-			buf.append( StringHelper.join( "=? and ", primaryKeyColumnNames ) ).append( "=?" );
+		iter = primaryKeyColumns.entrySet().iterator();
+		while ( iter.hasNext() ) {
+			Map.Entry e = (Map.Entry) iter.next();
+			buf.append( e.getKey() ).append( '=' ).append( e.getValue() );
+			if ( iter.hasNext() ) {
+				buf.append( " and " );
+			}
 			conditionsAppended = true;
 		}
 		if ( where != null ) {

Modified: core/trunk/core/src/main/resources/org/hibernate/hibernate-mapping-3.0.dtd
===================================================================
--- core/trunk/core/src/main/resources/org/hibernate/hibernate-mapping-3.0.dtd	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/core/src/main/resources/org/hibernate/hibernate-mapping-3.0.dtd	2009-10-22 20:02:10 UTC (rev 17821)
@@ -916,6 +916,8 @@
 	<!ATTLIST column index CDATA #IMPLIED>
 	<!ATTLIST column check CDATA #IMPLIED>						<!-- default: no check constraint -->
     <!ATTLIST column default CDATA #IMPLIED>                    <!-- default: no default value -->
+    <!ATTLIST column read CDATA #IMPLIED>                       <!-- default: column name -->
+    <!ATTLIST column write CDATA #IMPLIED>                      <!-- default: parameter placeholder ('?') -->
 
 <!-- The formula and subselect elements allow us to map derived properties and 
 entities. -->

Modified: core/trunk/documentation/manual/src/main/docbook/en-US/content/basic_mapping.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/content/basic_mapping.xml	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/content/basic_mapping.xml	2009-10-22 20:02:10 UTC (rev 17821)
@@ -2788,7 +2788,7 @@
 
         </sect2>
 
-        <sect2 id="mapping-column" revision="4">
+        <sect2 id="mapping-column" revision="5">
            <title>Column and formula elements</title>
            <para>
                Mapping elements which accept a <literal>column</literal> attribute will alternatively
@@ -2807,12 +2807,22 @@
         index="index_name"
         sql-type="sql_type_name"
         check="SQL expression"
-        default="SQL expression"/>]]></programlisting>
+        default="SQL expression"
+        read="SQL expression"
+        write="SQL expression"/>]]></programlisting>
 
             <programlisting><![CDATA[<formula>SQL expression</formula>]]></programlisting>
 
             <para>
-                <literal>column</literal> and <literal>formula</literal> attributes can even be combined
+                Most of the attributes on <literal>column</literal> provide a means of tailoring the
+                DDL during automatic schema generation. The <literal>read</literal> and <literal>write</literal>
+                attributes allow you to specify custom SQL that Hibernate will use to access the column's value.
+                For more on this, see the discussion of 
+                <link linkend="mapping-column-read-and-write">column read and write expressions</link>.
+            </para>
+
+            <para>
+                The <literal>column</literal> and <literal>formula</literal> elements can even be combined
                 within the same property or association mapping to express, for example, exotic join
                 conditions.
             </para>
@@ -3544,6 +3554,44 @@
 	    </para>
     </sect1>
 
+    <sect1 id="mapping-column-read-and-write" revision="1">
+        <title>Column read and write expressions</title>
+        <para>
+            Hibernate allows you to customize the SQL it uses to read and write the values
+            of columns mapped to <link linkend="mapping-declaration-property">simple properties</link>.
+            For example, if your database provides a set of data encryption functions, you can
+            invoke them for individual columns like this:
+            <programlisting><![CDATA[<property name="creditCardNumber">
+        <column 
+          name="credit_card_num"
+          read="decrypt(credit_card_num)"
+          write="encrypt(?)"/>
+</property>]]></programlisting>
+        </para>
+        <para>
+            Hibernate applies the custom expressions automatically whenever the property is
+            referenced in a query. This functionality is similar to a derived-property
+            <literal>formula</literal> with two differences:
+            <itemizedlist spacing="compact">
+                <listitem>
+                    <para>
+                        The property is backed by one or more columns that are exported as part of automatic
+                        schema generation.
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        The property is read-write, not read-only.
+                    </para>
+                </listitem>
+            </itemizedlist>
+        </para>
+        <para>
+            The <literal>write</literal> expression, if specified, must contain exactly one '?' placeholder
+            for the value.
+        </para>
+    </sect1>
+
     <sect1 id="mapping-database-object">
         <title>Auxiliary database objects</title>
         <para>

Modified: core/trunk/documentation/manual/src/main/docbook/en-US/content/query_sql.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/content/query_sql.xml	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/content/query_sql.xml	2009-10-22 20:02:10 UTC (rev 17821)
@@ -662,10 +662,15 @@
   <sect1 id="querysql-cud">
     <title>Custom SQL for create, update and delete</title>
 
-    <para>Hibernate3 can use custom SQL statements for create, update, and
-    delete operations. The class and collection persisters in Hibernate
-    already contain a set of configuration time generated strings (insertsql,
-    deletesql, updatesql etc.). The mapping tags
+    <para>Hibernate3 can use custom SQL for create, update, and delete operations.
+    The SQL can be overridden at the statement level or inidividual column level. This
+    section describes statement overrides. For columns, see
+    <xref linkend="mapping-column-read-and-write"/>.
+    </para>
+    <para>
+    The class and collection persisters in Hibernate already contain a set of 
+    configuration time generated strings (insertsql, deletesql, updatesql etc.). 
+    The mapping tags
     <literal>&lt;sql-insert&gt;</literal>,
     <literal>&lt;sql-delete&gt;</literal>, and
     <literal>&lt;sql-update&gt;</literal> override these strings:</para>
@@ -732,7 +737,11 @@
     <title>Custom SQL for loading</title>
 
     <para>You can also declare your own SQL (or HQL) queries for entity
-    loading:</para>
+    loading. As with inserts, updates, and deletes, this can be done at the
+    individual column level as described in
+    <xref linkend="mapping-column-read-and-write"/>
+    or at the statement level. Here is an example of a statement level override:
+    </para>
 
     <programlisting><![CDATA[<sql-query name="person">
     <return alias="pers" class="Person" lock-mode="upgrade"/>

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/component/basic/ComponentTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/component/basic/ComponentTest.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/component/basic/ComponentTest.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -7,16 +7,17 @@
 
 import junit.framework.Test;
 
+import org.hibernate.Hibernate;
 import org.hibernate.Session;
 import org.hibernate.Transaction;
-import org.hibernate.Hibernate;
 import org.hibernate.cfg.Configuration;
 import org.hibernate.cfg.Environment;
 import org.hibernate.cfg.Mappings;
 import org.hibernate.criterion.Property;
+import org.hibernate.criterion.Restrictions;
 import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.SybaseASE15Dialect;
 import org.hibernate.dialect.function.SQLFunction;
-import org.hibernate.dialect.SybaseASE15Dialect;
 import org.hibernate.junit.functional.FunctionalTestCase;
 import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
 import org.hibernate.mapping.Component;
@@ -207,7 +208,49 @@
 		t.commit();
 		s.close();
 	}
+	
+	public void testCustomColumnReadAndWrite() {
+		Session s = openSession();
+		Transaction t = s.beginTransaction();
+		User u = new User( "steve", "hibernater", new Person( "Steve Ebersole", new Date(), "Main St") );
+		final double HEIGHT_INCHES = 73;
+		final double HEIGHT_CENTIMETERS = HEIGHT_INCHES * 2.54d;
+		u.getPerson().setHeightInches(HEIGHT_INCHES);
+		s.persist( u );
+		s.flush();
+		
+		// Test value conversion during insert
+		Double heightViaSql = (Double)s.createSQLQuery("select height_centimeters from t_user where t_user.username='steve'").uniqueResult();
+		assertEquals(HEIGHT_CENTIMETERS, heightViaSql, 0.01d);
 
+		// Test projection
+		Double heightViaHql = (Double)s.createQuery("select u.person.heightInches from User u where u.id = 'steve'").uniqueResult();
+		assertEquals(HEIGHT_INCHES, heightViaHql, 0.01d);
+		
+		// Test restriction and entity load via criteria
+		u = (User)s.createCriteria(User.class)
+			.add(Restrictions.between("person.heightInches", HEIGHT_INCHES - 0.01d, HEIGHT_INCHES + 0.01d))
+			.uniqueResult();
+		assertEquals(HEIGHT_INCHES, u.getPerson().getHeightInches(), 0.01d);
+		
+		// Test predicate and entity load via HQL
+		u = (User)s.createQuery("from User u where u.person.heightInches between ? and ?")
+			.setDouble(0, HEIGHT_INCHES - 0.01d)
+			.setDouble(1, HEIGHT_INCHES + 0.01d)
+			.uniqueResult();
+		assertEquals(HEIGHT_INCHES, u.getPerson().getHeightInches(), 0.01d);
+		
+		// Test update
+		u.getPerson().setHeightInches(1);
+		s.flush();
+		heightViaSql = (Double)s.createSQLQuery("select height_centimeters from t_user where t_user.username='steve'").uniqueResult();
+		assertEquals(2.54d, heightViaSql, 0.01d);
+		s.delete(u);
+		t.commit();
+		s.close();
+	}
+	
+
 	public void testNamedQuery() {
 		Session s = openSession();
 		Transaction t = s.beginTransaction();

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/component/basic/Person.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/component/basic/Person.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/component/basic/Person.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -13,6 +13,7 @@
 	private String currentAddress;
 	private String previousAddress;
 	private int yob;
+	private double heightInches;
 	Person() {}
 	public Person(String name, Date dob, String address) {
 		this.name = name;
@@ -60,4 +61,10 @@
 	public void setCurrentAddress(String currentAddress) {
 		this.currentAddress = currentAddress;
 	}
+	public double getHeightInches() {
+		return heightInches;
+	}
+	public void setHeightInches(double heightInches) {
+		this.heightInches = heightInches;
+	}	
 }

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/component/basic/User.hbm.xml
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/component/basic/User.hbm.xml	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/component/basic/User.hbm.xml	2009-10-22 20:02:10 UTC (rev 17821)
@@ -19,6 +19,12 @@
 			<property name="address"/>
 			<property name="previousAddress" insert="false"/>
 			<property name="yob" formula="year(dob)"/>
+			<property name="heightInches">
+				<column name="height_centimeters"
+					not-null="true" 
+					read="height_centimeters / 2.54" 
+					write="? * 2.54"/>
+			</property>
 			<property name="currentAddress"
 				column="address"
 				insert="false"

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/compositeelement/Child.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/compositeelement/Child.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/compositeelement/Child.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -9,6 +9,7 @@
 	private String bio;
 	private Parent parent;
 	private int bioLength;
+	private double heightInches;
 	Child() {}
 	public Child(String name) {
 		this.name = name;
@@ -43,6 +44,12 @@
 	public void setBio(String bio) {
 		this.bio = bio;
 	}
+	public double getHeightInches() {
+		return heightInches;
+	}
+	public void setHeightInches(double heightInches) {
+		this.heightInches = heightInches;
+	}	
 	public int hashCode() {
 		return name.hashCode();
 	}

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/compositeelement/CompositeElementTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/compositeelement/CompositeElementTest.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/compositeelement/CompositeElementTest.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -8,6 +8,7 @@
 import org.hibernate.Session;
 import org.hibernate.Transaction;
 import org.hibernate.cfg.Mappings;
+import org.hibernate.criterion.Restrictions;
 import org.hibernate.dialect.Dialect;
 import org.hibernate.dialect.function.SQLFunction;
 import org.hibernate.junit.functional.FunctionalTestCase;
@@ -85,6 +86,52 @@
 		t.commit();
 		s.close();
 	}
+	
+	public void testCustomColumnReadAndWrite() {
+		final double HEIGHT_INCHES = 49;
+		final double HEIGHT_CENTIMETERS = HEIGHT_INCHES * 2.54d;
+		Session s = openSession();
+		Transaction t = s.beginTransaction();
+		Child c = new Child( "Child One" );
+		c.setHeightInches(HEIGHT_INCHES);
+		Parent p = new Parent( "Parent" );
+		p.getChildren().add( c );
+		c.setParent( p );
+		s.save( p );
+		s.flush();
+		
+		// Test value conversion during insert		
+		Double heightViaSql = (Double)s.createSQLQuery("select height_centimeters from parentchild c where c.name='Child One'")
+			.uniqueResult();
+		assertEquals(HEIGHT_CENTIMETERS, heightViaSql, 0.01d);
+		
+		// Test projection		
+		Double heightViaHql = (Double)s.createQuery("select c.heightInches from Parent p join p.children c where p.name='Parent'")
+			.uniqueResult();
+		assertEquals(HEIGHT_INCHES, heightViaHql, 0.01d);
+		
+		// Test entity load via criteria
+		p = (Parent)s.createCriteria(Parent.class).add(Restrictions.eq("name", "Parent")).uniqueResult();
+		c = (Child)p.getChildren().iterator().next();
+		assertEquals(HEIGHT_INCHES, c.getHeightInches(), 0.01d);
+		
+		// Test predicate and entity load via HQL
+		p = (Parent)s.createQuery("from Parent p join p.children c where c.heightInches between ? and ?")
+			.setDouble(0, HEIGHT_INCHES - 0.01d)
+			.setDouble(1, HEIGHT_INCHES + 0.01d)
+			.uniqueResult();
+		c = (Child)p.getChildren().iterator().next();
+		assertEquals(HEIGHT_INCHES, c.getHeightInches(), 0.01d);
+		
+		// Test update
+		c.setHeightInches(1);
+		s.flush();
+		heightViaSql = (Double)s.createSQLQuery("select height_centimeters from parentchild c where c.name='Child One'").uniqueResult();
+		assertEquals(2.54d, heightViaSql, 0.01d);
+		s.delete( p );
+		t.commit();
+		s.close();
+	}
 
 }
 

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/compositeelement/Parent.hbm.xml
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/compositeelement/Parent.hbm.xml	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/compositeelement/Parent.hbm.xml	2009-10-22 20:02:10 UTC (rev 17821)
@@ -28,6 +28,12 @@
 				<property name="name" not-null="true"/>
 				<property name="bio"/>
 				<property name="bioLength" formula="length(bio)"/>
+				<property name="heightInches">
+					<column name="height_centimeters" 
+						not-null="true" 
+						read="height_centimeters / 2.54" 
+						write="? * 2.54"/>
+				</property>				
 			</composite-element>
 		</set>
 	</class>

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/cut/CompositeUserTypeTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/cut/CompositeUserTypeTest.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/cut/CompositeUserTypeTest.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -8,6 +8,7 @@
 import junit.framework.Test;
 
 import org.hibernate.Session;
+import org.hibernate.criterion.Restrictions;
 import org.hibernate.dialect.HSQLDialect;
 import org.hibernate.junit.functional.FunctionalTestCase;
 import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
@@ -57,6 +58,44 @@
 		t.commit();
 		s.close();
 	}
+	
+	public void testCustomColumnReadAndWrite() {
+		Session s = openSession();
+		org.hibernate.Transaction t = s.beginTransaction();
+		final BigDecimal AMOUNT = new BigDecimal(73000000d);
+		final BigDecimal AMOUNT_MILLIONS = AMOUNT.divide(new BigDecimal(1000000d));
+		MutualFund f = new MutualFund();
+		f.setHoldings( new MonetoryAmount( AMOUNT, Currency.getInstance("USD") ) );
+		s.persist(f);
+		s.flush();
+		
+		// Test value conversion during insert
+		BigDecimal amountViaSql = (BigDecimal)s.createSQLQuery("select amount_millions from MutualFund").uniqueResult();
+		assertEquals(AMOUNT_MILLIONS.doubleValue(), amountViaSql.doubleValue(), 0.01d);
+		
+		// Test projection
+		BigDecimal amountViaHql = (BigDecimal)s.createQuery("select f.holdings.amount from MutualFund f").uniqueResult();
+		assertEquals(AMOUNT.doubleValue(), amountViaHql.doubleValue(), 0.01d);
+		
+		// Test restriction and entity load via criteria
+		BigDecimal one = new BigDecimal(1);
+		f = (MutualFund)s.createCriteria(MutualFund.class)
+			.add(Restrictions.between("holdings.amount", AMOUNT.subtract(one), AMOUNT.add(one)))
+			.uniqueResult();
+		assertEquals(AMOUNT.doubleValue(), f.getHoldings().getAmount().doubleValue(), 0.01d);
+		
+		// Test predicate and entity load via HQL
+		f = (MutualFund)s.createQuery("from MutualFund f where f.holdings.amount between ? and ?")
+			.setBigDecimal(0, AMOUNT.subtract(one))
+			.setBigDecimal(1, AMOUNT.add(one))
+			.uniqueResult();
+		assertEquals(AMOUNT.doubleValue(), f.getHoldings().getAmount().doubleValue(), 0.01d);
+				
+		s.delete(f);
+		t.commit();
+		s.close();
+		
+	}
 
 }
 

Added: core/trunk/testsuite/src/test/java/org/hibernate/test/cut/MutualFund.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/cut/MutualFund.java	                        (rev 0)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/cut/MutualFund.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -0,0 +1,28 @@
+package org.hibernate.test.cut;
+
+/**
+ * @author Rob.Hasselbaum
+ *
+ */
+public class MutualFund {
+	
+	private Long id;
+	private MonetoryAmount holdings;
+	
+	public Long getId() {
+		return id;
+	}
+	
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public MonetoryAmount getHoldings() {
+		return holdings;
+	}
+
+	public void setHoldings(MonetoryAmount holdings) {
+		this.holdings = holdings;
+	}
+
+}

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/cut/Transaction.hbm.xml
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/cut/Transaction.hbm.xml	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/cut/Transaction.hbm.xml	2009-10-22 20:02:10 UTC (rev 17821)
@@ -21,5 +21,18 @@
 			<column name="currency" not-null="true"/>
 		</property>
 	</class>
+	
+    <class name="MutualFund" table="MutualFund">
+		<id name="id">
+			<generator class="native"/>
+		</id>
+		<property name="holdings" type="money">
+			<column name="amount_millions"
+				not-null="true"
+				read="amount_millions * 1000000.0"
+				write="? / 1000000.0"/>
+			<column name="currency" not-null="true"/>
+		</property>
+	</class>	
 
 </hibernate-mapping>
\ No newline at end of file

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/filter/DynamicFilterTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/filter/DynamicFilterTest.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/filter/DynamicFilterTest.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -11,21 +11,19 @@
 
 import junit.framework.Test;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.hibernate.Criteria;
 import org.hibernate.EntityMode;
 import org.hibernate.FetchMode;
 import org.hibernate.Hibernate;
 import org.hibernate.Session;
 import org.hibernate.Transaction;
-import org.hibernate.Criteria;
 import org.hibernate.cache.CacheKey;
 import org.hibernate.cache.entry.CollectionCacheEntry;
 import org.hibernate.cfg.Configuration;
 import org.hibernate.cfg.Environment;
-import org.hibernate.criterion.Restrictions;
 import org.hibernate.criterion.DetachedCriteria;
 import org.hibernate.criterion.Property;
+import org.hibernate.criterion.Restrictions;
 import org.hibernate.criterion.Subqueries;
 import org.hibernate.engine.SessionImplementor;
 import org.hibernate.impl.SessionFactoryImpl;
@@ -33,6 +31,8 @@
 import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
 import org.hibernate.persister.collection.CollectionPersister;
 import org.hibernate.transform.DistinctRootEntityResultTransformer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Implementation of DynamicFilterTest.
@@ -179,7 +179,25 @@
 		session.close();
 		testData.release();
 	}
+	
+	public void testFiltersWithCustomerReadAndWrite() {
+		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+		// Custom SQL read/write with filter
+		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+		log.info( "Starting HQL filter with custom SQL get/set tests" );
+		TestData testData = new TestData();
+		testData.prepare();
 
+		Session session = openSession();
+		session.enableFilter( "heavyProducts" ).setParameter("weightKilograms", 4d);
+		log.info( "HQL against Product..." );
+		List results = session.createQuery( "from Product").list();
+		assertEquals( 1, results.size() );
+
+		session.close();
+		testData.release();
+	}
+
 	public void testCriteriaQueryFilters() {
 		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 		// Criteria-query test
@@ -824,6 +842,7 @@
 			Product product1 = new Product();
 			product1.setName( "Acme Hair Gel" );
 			product1.setStockNumber( 123 );
+			product1.setWeightPounds( 0.25 );
 			product1.setEffectiveStartDate( lastMonth.getTime() );
 			product1.setEffectiveEndDate( nextMonth.getTime() );
 
@@ -848,6 +867,7 @@
 			Product product2 = new Product();
 			product2.setName( "Acme Super-Duper DTO Factory" );
 			product2.setStockNumber( 124 );
+			product1.setWeightPounds( 10.0 );
 			product2.setEffectiveStartDate( sixMonthsAgo.getTime() );
 			product2.setEffectiveEndDate( new Date() );
 

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/filter/Product.hbm.xml
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/filter/Product.hbm.xml	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/filter/Product.hbm.xml	2009-10-22 20:02:10 UTC (rev 17821)
@@ -12,6 +12,12 @@
 
     	<property name="name" type="string"/>
 	    <property name="stockNumber" column="STOCK_NUM" type="int"/>
+	    <property name="weightPounds">
+	    	<column name="weight_kg" 
+	    		not-null="true" 
+	    		write="0.453 * ?" 
+	    		read="weight_kg / 0.453"/>
+	    </property>
 
 	    <property name="effectiveStartDate" column="eff_start_dt" type="java.util.Date"/>
 	    <property name="effectiveEndDate" column="eff_end_dt" type="java.util.Date"/>
@@ -30,7 +36,8 @@
 	    </set>
 
 	    <filter name="effectiveDate" condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+	    <filter name="heavyProducts" condition=":weightKilograms &lt; weight_kg"/>
 
 	</class>
 
-</hibernate-mapping>
\ No newline at end of file
+</hibernate-mapping>

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/filter/Product.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/filter/Product.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/filter/Product.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -1,9 +1,9 @@
 // $Id: Product.java 6507 2005-04-25 16:57:32Z steveebersole $
 package org.hibernate.test.filter;
 
-import java.util.Set;
 import java.util.Date;
 import java.util.HashSet;
+import java.util.Set;
 
 /**
  * @author Steve Ebersole
@@ -14,6 +14,7 @@
 	private int stockNumber;  // int for ease of hashCode() impl
 	private Date effectiveStartDate;
 	private Date effectiveEndDate;
+	private double weightPounds;
 	private Set orderLineItems;
 	private Set categories;
 
@@ -73,6 +74,14 @@
 		this.effectiveEndDate = effectiveEndDate;
 	}
 
+	public double getWeightPounds() {
+		return weightPounds;
+	}
+
+	public void setWeightPounds(double weightPounds) {
+		this.weightPounds = weightPounds;
+	}
+	
 	public Set getCategories() {
 		return categories;
 	}

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/filter/defs.hbm.xml
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/filter/defs.hbm.xml	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/filter/defs.hbm.xml	2009-10-22 20:02:10 UTC (rev 17821)
@@ -20,6 +20,10 @@
 		<filter-param name="asOfDate" type="timestamp"/>
 	</filter-def>
 
+	<filter-def name="heavyProducts">
+		<filter-param name="weightKilograms" type="double"/>
+	</filter-def>
+
 	<filter-def name="seniorSalespersons">
 		<filter-param name="asOfDate" type="timestamp"/>
 	</filter-def>
@@ -31,4 +35,4 @@
     <filter-def name="unioned">
     </filter-def>
 
-</hibernate-mapping>
\ No newline at end of file
+</hibernate-mapping>

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -13,8 +13,6 @@
 import java.util.Map;
 
 import junit.framework.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import org.hibernate.Hibernate;
 import org.hibernate.HibernateException;
@@ -30,7 +28,6 @@
 import org.hibernate.dialect.HSQLDialect;
 import org.hibernate.dialect.MySQLDialect;
 import org.hibernate.dialect.Oracle8iDialect;
-
 import org.hibernate.dialect.PostgreSQLDialect;
 import org.hibernate.dialect.SQLServerDialect;
 import org.hibernate.dialect.Sybase11Dialect;
@@ -55,6 +52,8 @@
 import org.hibernate.type.ManyToOneType;
 import org.hibernate.type.Type;
 import org.hibernate.util.StringHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Tests the integration of the new AST parser into the loading of query results using
@@ -82,6 +81,7 @@
 				"hql/FooBarCopy.hbm.xml",
 				"hql/SimpleEntityWithAssociation.hbm.xml",
 				"hql/CrazyIdFieldNames.hbm.xml",
+				"hql/Image.hbm.xml",
 				"batchfetch/ProductLine.hbm.xml",
 				"cid/Customer.hbm.xml",
 				"cid/Order.hbm.xml",
@@ -1085,7 +1085,102 @@
 		t.commit();
 		s.close();
 	}
+	
+	public void testOrderedWithCustomColumnReadAndWrite() {
+		Session s = openSession();
+		Transaction t = s.beginTransaction();
+		SimpleEntityWithAssociation first = new SimpleEntityWithAssociation();
+		first.setNegatedNumber(1);
+		s.save(first);
+		SimpleEntityWithAssociation second = new SimpleEntityWithAssociation();
+		second.setNegatedNumber(2);
+		s.save(second);
+		s.flush();
 
+		// Check order via SQL. Numbers are negated in the DB, so second comes first.
+		List listViaSql = s.createSQLQuery("select id from simple_1 order by negated_num").list();
+		assertEquals(2, listViaSql.size());
+		assertEquals(second.getId().longValue(), ((Number)listViaSql.get(0)).longValue());
+		assertEquals(first.getId().longValue(), ((Number)listViaSql.get(1)).longValue());
+		
+		// Check order via HQL. Now first comes first b/c the read negates the DB negation.
+		List listViaHql = s.createQuery("from SimpleEntityWithAssociation order by negatedNumber").list();
+		assertEquals(2, listViaHql.size());
+		assertEquals(first.getId(), ((SimpleEntityWithAssociation)listViaHql.get(0)).getId());
+		assertEquals(second.getId(), ((SimpleEntityWithAssociation)listViaHql.get(1)).getId());
+		
+		s.delete(first);
+		s.delete(second);
+		t.commit();
+		s.close();
+		
+	}
+	
+	public void testHavingWithCustomColumnReadAndWrite() {
+		Session s = openSession();
+		Transaction t = s.beginTransaction();
+		SimpleEntityWithAssociation first = new SimpleEntityWithAssociation();
+		first.setNegatedNumber(5);
+		first.setName("simple");
+		s.save(first);
+		SimpleEntityWithAssociation second = new SimpleEntityWithAssociation();
+		second.setNegatedNumber(10);
+		second.setName("simple");
+		s.save(second);
+		SimpleEntityWithAssociation third = new SimpleEntityWithAssociation();
+		third.setNegatedNumber(20);
+		third.setName("complex");
+		s.save(third);
+		s.flush();
+
+		// Check order via HQL. Now first comes first b/c the read negates the DB negation.
+		Number r = (Number)s.createQuery("select sum(negatedNumber) from SimpleEntityWithAssociation " +
+				"group by name having sum(negatedNumber) < 20").uniqueResult();
+		assertEquals(r.intValue(), 15);
+		
+		s.delete(first);
+		s.delete(second);
+		s.delete(third);
+		t.commit();
+		s.close();
+		
+	}
+	
+	public void testLoadSnapshotWithCustomColumnReadAndWrite() {
+		// Exercises entity snapshot load when select-before-update is true.
+		Session s = openSession();
+		Transaction t = s.beginTransaction();
+		final double SIZE_IN_KB = 1536d;
+		final double SIZE_IN_MB = SIZE_IN_KB / 1024d;
+		Image image = new Image();
+		image.setName("picture.gif");
+		image.setSizeKb(SIZE_IN_KB);
+		s.persist(image);
+		s.flush();
+		
+		Double sizeViaSql = (Double)s.createSQLQuery("select size_mb from image").uniqueResult();
+		assertEquals(SIZE_IN_MB, sizeViaSql, 0.01d);
+		t.commit();
+		s.close();
+		
+		s = openSession();
+		t = s.beginTransaction();
+		final double NEW_SIZE_IN_KB = 2048d;
+		final double NEW_SIZE_IN_MB = NEW_SIZE_IN_KB / 1024d;
+		image.setSizeKb(NEW_SIZE_IN_KB);
+		s.update(image);
+		s.flush();
+
+		sizeViaSql = (Double)s.createSQLQuery("select size_mb from image").uniqueResult();
+		assertEquals(NEW_SIZE_IN_MB, sizeViaSql, 0.01d);		
+		
+		s.delete(image);
+		t.commit();
+		s.close();
+		
+	}
+		
+
 	private Human genSimpleHuman(String fName, String lName) {
 		Human h = new Human();
 		h.setName( new Name( fName, 'X', lName ) );
@@ -1188,13 +1283,13 @@
 		Transaction t = s.beginTransaction();
 		Human h = new Human();
 		h.setBodyWeight( (float) 74.0 );
-		h.setHeight(120.5);
+		h.setHeightInches(120.5);
 		h.setDescription("Me");
 		h.setName( new Name("Gavin", 'A', "King") );
 		h.setNickName("Oney");
 		s.persist(h);
 		Double sum = (Double) s.createQuery("select sum(h.bodyWeight) from Human h").uniqueResult();
-		Double avg = (Double) s.createQuery("select avg(h.height) from Human h").uniqueResult();
+		Double avg = (Double) s.createQuery("select avg(h.heightInches) from Human h").uniqueResult();	// uses custom read and write for column
 		assertEquals(sum.floatValue(), 74.0, 0.01);
 		assertEquals(avg.doubleValue(), 120.5, 0.01);
 		Long id = (Long) s.createQuery("select max(a.id) from Animal a").uniqueResult();
@@ -1208,7 +1303,7 @@
 		Transaction t = s.beginTransaction();
 		Human h = new Human();
 		h.setBodyWeight( (float) 74.0 );
-		h.setHeight(120.5);
+		h.setHeightInches(120.5);
 		h.setDescription("Me");
 		h.setName( new Name("Gavin", 'A', "King") );
 		h.setNickName("Oney");
@@ -1395,7 +1490,41 @@
 		txn.commit();
 		session.close();
 	}
+	
+	public void testFilterWithCustomColumnReadAndWrite() {
+		Session session = openSession();
+		Transaction txn = session.beginTransaction();
 
+		Human human = new Human();
+		human.setName( new Name( "Steve", 'L', "Ebersole" ) );
+		human.setHeightInches(73d);
+		session.save( human );
+
+		Human friend = new Human();
+		friend.setName( new Name( "John", 'Q', "Doe" ) );
+		friend.setHeightInches(50d);
+		session.save( friend );
+
+		human.setFriends( new ArrayList() );
+		friend.setFriends( new ArrayList() );
+		human.getFriends().add( friend );
+		friend.getFriends().add( human );
+
+		session.flush();
+
+		assertEquals( session.createFilter( human.getFriends(), "" ).list().size(), 1 );
+		assertEquals( session.createFilter( human.getFriends(), "where this.heightInches < ?" ).setDouble( 0, 51d ).list().size(), 1 );
+		assertEquals( session.createFilter( human.getFriends(), "where this.heightInches > ?" ).setDouble( 0, 51d ).list().size(), 0 );
+		assertEquals( session.createFilter( human.getFriends(), "where this.heightInches between 49 and 51" ).list().size(), 1 );
+		assertEquals( session.createFilter( human.getFriends(), "where this.heightInches not between 49 and 51" ).list().size(), 0 );
+
+		session.delete(human);
+		session.delete(friend);
+
+		txn.commit();
+		session.close();		
+	}
+
 	public void testSelectExpressions() {
 		createTestBaseData();
 		Session session = openSession();

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Animal.hbm.xml
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Animal.hbm.xml	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Animal.hbm.xml	2009-10-22 20:02:10 UTC (rev 17821)
@@ -47,8 +47,12 @@
 					<property name="last" column="name_last"/>
 				</component>
 				<property name="nickName"/>
-				<property name="height"/>
-
+				<property name="heightInches">
+					<column name="height_centimeters" 
+						not-null="true" 
+						read="height_centimeters / 2.54" 
+						write="? * 2.54"/>
+				</property>   
 				<property name="intValue"/>
 				<property name="floatValue"/>
 				<property name="bigDecimalValue"/>
@@ -147,4 +151,4 @@
 		</join>
 	</class>
 
-</hibernate-mapping>
\ No newline at end of file
+</hibernate-mapping>

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/hql/CriteriaClassicAggregationReturnTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/hql/CriteriaClassicAggregationReturnTest.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/hql/CriteriaClassicAggregationReturnTest.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -43,12 +43,12 @@
 		assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
 		assertEquals( "incorrect return type", Hibernate.INTEGER, translator.getReturnTypes()[0] );
 
-		translator = createNewQueryTranslator( "select count(h.height) from Human h", sfi() );
+		translator = createNewQueryTranslator( "select count(h.heightInches) from Human h", sfi() );
 		assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
 		assertEquals( "incorrect return type", Hibernate.INTEGER, translator.getReturnTypes()[0] );
 
 		// MAX, MIN return the type of the state-field to which they are applied.
-		translator = createNewQueryTranslator( "select max(h.height) from Human h", sfi() );
+		translator = createNewQueryTranslator( "select max(h.heightInches) from Human h", sfi() );
 		assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
 		assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
 
@@ -57,7 +57,7 @@
 		assertEquals( "incorrect return type", Hibernate.LONG, translator.getReturnTypes()[0] );
 
 		// AVG returns Float integrals, and otherwise the field type.
-		translator = createNewQueryTranslator( "select avg(h.height) from Human h", sfi() );
+		translator = createNewQueryTranslator( "select avg(h.heightInches) from Human h", sfi() );
 		assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
 		assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
 
@@ -78,7 +78,7 @@
 		assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
 		assertEquals( "incorrect return type", Hibernate.INTEGER, translator.getReturnTypes()[0] );
 
-		translator = createNewQueryTranslator( "select sum(h.height) from Human h", sfi() );
+		translator = createNewQueryTranslator( "select sum(h.heightInches) from Human h", sfi() );
 		assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
 		assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
 

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/hql/CriteriaHQLAlignmentTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/hql/CriteriaHQLAlignmentTest.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/hql/CriteriaHQLAlignmentTest.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -47,12 +47,12 @@
 		assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
 		assertEquals( "incorrect return type", Hibernate.LONG, translator.getReturnTypes()[0] );
 		
-		translator = createNewQueryTranslator( "select count(h.height) from Human h" );
+		translator = createNewQueryTranslator( "select count(h.heightInches) from Human h" );
 		assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
 		assertEquals( "incorrect return type", Hibernate.LONG, translator.getReturnTypes()[0] );
 				
 		// MAX, MIN return the type of the state-field to which they are applied. 
-		translator = createNewQueryTranslator( "select max(h.height) from Human h" );
+		translator = createNewQueryTranslator( "select max(h.heightInches) from Human h" );
 		assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
 		assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
 		
@@ -61,7 +61,7 @@
 		assertEquals( "incorrect return type", Hibernate.LONG, translator.getReturnTypes()[0] );
 		
 		// AVG returns Double.
-		translator = createNewQueryTranslator( "select avg(h.height) from Human h" );
+		translator = createNewQueryTranslator( "select avg(h.heightInches) from Human h" );
 		assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
 		assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
 		
@@ -83,7 +83,7 @@
 		assertEquals( "incorrect return type", Hibernate.LONG, translator.getReturnTypes()[0] );
 		
 		// SUM returns Double when applied to state-fields of floating point types; 
-		translator = createNewQueryTranslator( "select sum(h.height) from Human h" );
+		translator = createNewQueryTranslator( "select sum(h.heightInches) from Human h" );
 		assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
 		assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
 
@@ -123,18 +123,18 @@
 		// EJB3: COUNT returns Long
 		Long longValue = (Long) s.createCriteria( Human.class ).setProjection( Projections.rowCount()).uniqueResult();
 		assertEquals(longValue, new Long(1));
-		longValue = (Long) s.createCriteria( Human.class ).setProjection( Projections.count("height")).uniqueResult();
+		longValue = (Long) s.createCriteria( Human.class ).setProjection( Projections.count("heightInches")).uniqueResult();
 		assertEquals(longValue, new Long(1));
 		
 		 // MAX, MIN return the type of the state-field to which they are applied. 		
-		Double dblValue = (Double) s.createCriteria( Human.class ).setProjection( Projections.max( "height" )).uniqueResult();
+		Double dblValue = (Double) s.createCriteria( Human.class ).setProjection( Projections.max( "heightInches" )).uniqueResult();
 		assertNotNull(dblValue);
 		
 		longValue = (Long) s.createCriteria( Human.class ).setProjection( Projections.max( "id" )).uniqueResult();
 		assertNotNull(longValue);
 		
 		// AVG returns Double.
-		dblValue = (Double) s.createCriteria( Human.class ).setProjection( Projections.avg( "height" )).uniqueResult();
+		dblValue = (Double) s.createCriteria( Human.class ).setProjection( Projections.avg( "heightInches" )).uniqueResult();
 		assertNotNull(dblValue);
 		
 		dblValue = (Double) s.createCriteria( Human.class ).setProjection( Projections.avg( "id" )).uniqueResult();
@@ -151,7 +151,7 @@
 		assertNotNull(longValue);
 		
 		// SUM returns Double when applied to state-fields of floating point types; 
-		dblValue = (Double) s.createCriteria( Human.class ).setProjection( Projections.sum( "height" )).uniqueResult();
+		dblValue = (Double) s.createCriteria( Human.class ).setProjection( Projections.sum( "heightInches" )).uniqueResult();
 		assertNotNull(dblValue);
 		
 		dblValue = (Double) s.createCriteria( Human.class ).setProjection( Projections.sum( "floatValue" )).uniqueResult();

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Human.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Human.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Human.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -16,7 +16,7 @@
 	private Collection friends;
 	private Collection pets;
 	private Map family;
-	private double height;
+	private double heightInches;
 	
 	private BigInteger bigIntegerValue;
 	private BigDecimal bigDecimalValue;
@@ -58,11 +58,12 @@
 		this.nickName = nickName;
 	}
 	
-	public double getHeight() {
-		return height;
+	public double getHeightInches() {
+		return heightInches;
 	}
-	public void setHeight(double height) {
-		this.height = height;
+	
+	public void setHeightInches(double height) {
+		this.heightInches = height;
 	}
 
 	public Map getFamily() {

Added: core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Image.hbm.xml
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Image.hbm.xml	                        (rev 0)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Image.hbm.xml	2009-10-22 20:02:10 UTC (rev 17821)
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.hql">
+
+    <class name="Image" table="image" select-before-update="true" >
+        <id name="id" type="java.lang.Long" column="id">
+            <generator class="native"/>
+        </id>
+        <property name="name" type="java.lang.String" column="name"/>
+    	<property name="sizeKb" lazy="true">
+    		<column name="size_mb"
+    			read="size_mb * 1024.0"
+    			write="? / 1024.0"/>
+    	</property>
+    </class>
+
+</hibernate-mapping>

Added: core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Image.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Image.java	                        (rev 0)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/hql/Image.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -0,0 +1,49 @@
+package org.hibernate.test.hql;
+
+/**
+ * @author Rob.Hasselbaum
+ */
+public class Image {
+	
+	private Long id;
+	private String name;
+	private double sizeKb;
+	
+	/**
+	 * @return the id
+	 */
+	public Long getId() {
+		return id;
+	}
+	/**
+	 * @param id the id to set
+	 */
+	public void setId(Long id) {
+		this.id = id;
+	}
+	/**
+	 * @return the name
+	 */
+	public String getName() {
+		return name;
+	}
+	/**
+	 * @param name the name to set
+	 */
+	public void setName(String name) {
+		this.name = name;
+	}
+	/**
+	 * @return the size in kb
+	 */
+	public double getSizeKb() {
+		return sizeKb;
+	}
+	/**
+	 * @param sizeKb the size in kb to set
+	 */
+	public void setSizeKb(double sizeKb) {
+		this.sizeKb = sizeKb;
+	}
+
+}

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.hbm.xml
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.hbm.xml	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.hbm.xml	2009-10-22 20:02:10 UTC (rev 17821)
@@ -11,6 +11,11 @@
             <generator class="native"/>
         </id>
         <property name="name" column="NAME" type="string"/>
+        <property name="negatedNumber">
+			<column name="negated_num"
+				read="-negated_num"
+				write="0 - ?"/>        
+        </property>
         <set name="associatedEntities" cascade="all" inverse="true" lazy="true">
             <key column="SIMPLE_1_ID"/>
             <one-to-many class="SimpleAssociatedEntity"/>
@@ -29,4 +34,4 @@
         <many-to-one name="owner" class="SimpleEntityWithAssociation" column="SIMPLE_1_ID"/>
     </class>
 
-</hibernate-mapping>
\ No newline at end of file
+</hibernate-mapping>

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -1,7 +1,7 @@
 package org.hibernate.test.hql;
 
+import java.util.HashSet;
 import java.util.Set;
-import java.util.HashSet;
 
 /**
  * @author Steve Ebersole
@@ -9,6 +9,7 @@
 public class SimpleEntityWithAssociation {
 	private Long id;
 	private String name;
+	private Integer negatedNumber;
 	private Set associatedEntities = new HashSet();
 	private Set manyToManyAssociatedEntities = new HashSet();
 
@@ -34,7 +35,15 @@
 	public void setName(String name) {
 		this.name = name;
 	}
+	
+	public Integer getNegatedNumber() {
+		return negatedNumber;
+	}
 
+	public void setNegatedNumber(Integer negatedNumber) {
+		this.negatedNumber = negatedNumber;
+	}	
+
 	public Set getAssociatedEntities() {
 		return associatedEntities;
 	}

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/buildtime/InstrumentTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/buildtime/InstrumentTest.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/buildtime/InstrumentTest.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -5,18 +5,19 @@
 import junit.framework.TestSuite;
 
 import org.hibernate.intercept.FieldInterceptionHelper;
-import org.hibernate.test.instrument.domain.Document;
+import org.hibernate.junit.UnitTestCase;
+import org.hibernate.test.instrument.cases.Executable;
+import org.hibernate.test.instrument.cases.TestCustomColumnReadAndWrite;
 import org.hibernate.test.instrument.cases.TestDirtyCheckExecutable;
 import org.hibernate.test.instrument.cases.TestFetchAllExecutable;
+import org.hibernate.test.instrument.cases.TestInjectFieldInterceptorExecutable;
+import org.hibernate.test.instrument.cases.TestIsPropertyInitializedExecutable;
 import org.hibernate.test.instrument.cases.TestLazyExecutable;
 import org.hibernate.test.instrument.cases.TestLazyManyToOneExecutable;
-import org.hibernate.test.instrument.cases.TestInjectFieldInterceptorExecutable;
-import org.hibernate.test.instrument.cases.TestIsPropertyInitializedExecutable;
 import org.hibernate.test.instrument.cases.TestLazyPropertyCustomTypeExecutable;
 import org.hibernate.test.instrument.cases.TestManyToOneProxyExecutable;
 import org.hibernate.test.instrument.cases.TestSharedPKOneToOneExecutable;
-import org.hibernate.test.instrument.cases.Executable;
-import org.hibernate.junit.UnitTestCase;
+import org.hibernate.test.instrument.domain.Document;
 
 /**
  * @author Gavin King
@@ -67,6 +68,10 @@
 		execute( new TestSharedPKOneToOneExecutable() );
 	}
 
+	public void testCustomColumnReadAndWrite() throws Exception {
+		execute( new TestCustomColumnReadAndWrite() );
+	}	
+	
 	private void execute(Executable executable) throws Exception {
 		executable.prepare();
 		try {

Added: core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestCustomColumnReadAndWrite.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestCustomColumnReadAndWrite.java	                        (rev 0)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestCustomColumnReadAndWrite.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -0,0 +1,66 @@
+package org.hibernate.test.instrument.cases;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.test.instrument.domain.Document;
+import org.hibernate.test.instrument.domain.Folder;
+import org.hibernate.test.instrument.domain.Owner;
+
+/**
+ * @author Rob.Hasselbaum
+ */
+public class TestCustomColumnReadAndWrite extends AbstractExecutable {
+	public void execute() {
+		Session s = getFactory().openSession();
+		Transaction t = s.beginTransaction();
+		final double SIZE_IN_KB = 20480;
+		final double SIZE_IN_MB = SIZE_IN_KB / 1024d;
+		Owner o = new Owner();
+		Document doc = new Document();
+		Folder fol = new Folder();
+		o.setName("gavin");
+		doc.setName("Hibernate in Action");
+		doc.setSummary("blah");
+		doc.updateText("blah blah");	
+		fol.setName("books");
+		doc.setOwner(o);
+		doc.setFolder(fol);
+		doc.setSizeKb(SIZE_IN_KB);
+		fol.getDocuments().add(doc);
+		s.persist(o);
+		s.persist(fol);
+		t.commit();
+		s.close();
+
+		s = getFactory().openSession();
+		t = s.beginTransaction();
+		
+		// Check value conversion on insert
+		Double sizeViaSql = (Double)s.createSQLQuery("select size_mb from documents").uniqueResult();
+		assertEquals( SIZE_IN_MB, sizeViaSql, 0.01d );
+
+		// Test explicit fetch of all properties
+		doc = (Document) s.createQuery("from Document fetch all properties").uniqueResult();
+		assertTrue( Hibernate.isPropertyInitialized( doc, "sizeKb" ) );
+		assertEquals( SIZE_IN_KB, doc.getSizeKb() );
+		t.commit();
+		s.close();		
+
+		// Test lazy fetch with custom read
+		s = getFactory().openSession();
+		t = s.beginTransaction();
+		doc = (Document) s.get( Document.class, doc.getId() );
+		assertFalse( Hibernate.isPropertyInitialized( doc, "sizeKb" ) );
+		assertEquals( SIZE_IN_KB, doc.getSizeKb() );
+		s.delete(doc);
+		s.delete( doc.getOwner() );
+		s.delete( doc.getFolder() );
+		t.commit();
+		s.close();
+	}
+}

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/domain/Document.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/domain/Document.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/domain/Document.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -14,6 +14,7 @@
 	private String text;
 	private Owner owner;
 	private Folder folder;
+	private double sizeKb;
 	private Date lastTextModification = new Date();
 	/**
 	 * @return Returns the folder.
@@ -99,6 +100,18 @@
 	public void setUpperCaseName(String upperCaseName) {
 		this.upperCaseName = upperCaseName;
 	}
+	/**
+	 * @param sizeKb The size in KBs.
+	 */
+	public void setSizeKb(double sizeKb) {
+		this.sizeKb = sizeKb;
+	}
+	/**
+	 * @return The size in KBs.
+	 */
+	public double getSizeKb() {
+		return sizeKb;
+	}	
 	
 	public void updateText(String newText) {
 		if ( !newText.equals(text) ) {

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/domain/Documents.hbm.xml
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/domain/Documents.hbm.xml	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/domain/Documents.hbm.xml	2009-10-22 20:02:10 UTC (rev 17821)
@@ -53,6 +53,11 @@
     	<many-to-one name="owner" not-null="true" lazy="no-proxy" fetch="select"/>
     	<property name="text" not-null="true" length="2000" lazy="true"/>
     	<property name="lastTextModification" not-null="true" lazy="true" access="field"/>
+    	<property name="sizeKb" lazy="true">
+    		<column name="size_mb"
+    			read="size_mb * 1024.0"
+    			write="? / 1024.0"/>
+    	</property>
     </class>
 
     <class name="Entity" table="entity">

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/runtime/AbstractTransformingClassLoaderInstrumentTestCase.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/runtime/AbstractTransformingClassLoaderInstrumentTestCase.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/runtime/AbstractTransformingClassLoaderInstrumentTestCase.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -82,6 +82,10 @@
 		executeExecutable( "org.hibernate.test.instrument.cases.TestSharedPKOneToOneExecutable" );
 	}
 
+	public void testCustomColumnReadAndWrite() {
+		executeExecutable( "org.hibernate.test.instrument.cases.TestCustomColumnReadAndWrite" );
+	}	
+
 	// reflection code to ensure isolation into the created classloader ~~~~~~~
 
 	private static final Class[] SIG = new Class[] {};

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/runtime/CGLIBInstrumentationTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/runtime/CGLIBInstrumentationTest.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/runtime/CGLIBInstrumentationTest.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -1,10 +1,11 @@
 package org.hibernate.test.instrument.runtime;
 
-import org.hibernate.bytecode.BytecodeProvider;
-import org.hibernate.bytecode.cglib.BytecodeProviderImpl;
 import junit.framework.Test;
 import junit.framework.TestSuite;
 
+import org.hibernate.bytecode.BytecodeProvider;
+import org.hibernate.bytecode.cglib.BytecodeProviderImpl;
+
 /**
  * @author Steve Ebersole
  */
@@ -52,4 +53,9 @@
 	public void testSharedPKOneToOne() {
 		super.testSharedPKOneToOne();
 	}
+	
+	public void testCustomColumnReadAndWrite() {
+		super.testCustomColumnReadAndWrite();
+	}	
+
 }

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/runtime/JavassistInstrumentationTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/runtime/JavassistInstrumentationTest.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/instrument/runtime/JavassistInstrumentationTest.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -1,11 +1,12 @@
 //$Id: $
 package org.hibernate.test.instrument.runtime;
 
-import org.hibernate.bytecode.BytecodeProvider;
-import org.hibernate.bytecode.javassist.BytecodeProviderImpl;
 import junit.framework.Test;
 import junit.framework.TestSuite;
 
+import org.hibernate.bytecode.BytecodeProvider;
+import org.hibernate.bytecode.javassist.BytecodeProviderImpl;
+
 /**
  * @author Steve Ebersole
  */
@@ -53,4 +54,9 @@
 	public void testSharedPKOneToOne() {
 		super.testSharedPKOneToOne();
 	}
+
+	public void testCustomColumnReadAndWrite() {
+		super.testCustomColumnReadAndWrite();
+	}	
+	
 }

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/join/JoinTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/join/JoinTest.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/join/JoinTest.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -9,6 +9,7 @@
 import org.hibernate.Hibernate;
 import org.hibernate.Session;
 import org.hibernate.Transaction;
+import org.hibernate.criterion.Restrictions;
 import org.hibernate.junit.functional.FunctionalTestCase;
 import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
 
@@ -130,6 +131,82 @@
 		t.commit();
 		s.close();
 	}
+	
+	public void testCustomColumnReadAndWrite() {
+		Session s = openSession();
+		Transaction t = s.beginTransaction();
+		final double HEIGHT_INCHES = 73;
+		final double HEIGHT_CENTIMETERS = HEIGHT_INCHES * 2.54d;
+		Person p = new Person();
+		p.setName("Emmanuel");
+		p.setSex('M');
+		p.setHeightInches(HEIGHT_INCHES);
+		s.persist(p);
+		final double PASSWORD_EXPIRY_WEEKS = 4;
+		final double PASSWORD_EXPIRY_DAYS = PASSWORD_EXPIRY_WEEKS * 7d;
+		User u = new User();
+		u.setName("Steve");
+		u.setSex('M');
+		u.setPasswordExpiryDays(PASSWORD_EXPIRY_DAYS);
+		s.persist(u);
+		s.flush();
+		
+		// Test value conversion during insert
+		Double heightViaSql = (Double)s.createSQLQuery("select height_centimeters from person where name='Emmanuel'").uniqueResult();
+		assertEquals(HEIGHT_CENTIMETERS, heightViaSql, 0.01d);
+		Double expiryViaSql = (Double)s.createSQLQuery("select pwd_expiry_weeks from t_user where person_id=?")
+			.setLong(0, u.getId())
+			.uniqueResult();
+		assertEquals(PASSWORD_EXPIRY_WEEKS, expiryViaSql, 0.01d);
+		
+		// Test projection
+		Double heightViaHql = (Double)s.createQuery("select p.heightInches from Person p where p.name = 'Emmanuel'").uniqueResult();
+		assertEquals(HEIGHT_INCHES, heightViaHql, 0.01d);
+		Double expiryViaHql = (Double)s.createQuery("select u.passwordExpiryDays from User u where u.name = 'Steve'").uniqueResult();
+		assertEquals(PASSWORD_EXPIRY_DAYS, expiryViaHql, 0.01d);
+		
+		// Test restriction and entity load via criteria
+		p = (Person)s.createCriteria(Person.class)
+			.add(Restrictions.between("heightInches", HEIGHT_INCHES - 0.01d, HEIGHT_INCHES + 0.01d))
+			.uniqueResult();
+		assertEquals(HEIGHT_INCHES, p.getHeightInches(), 0.01d);
+		u = (User)s.createCriteria(User.class)
+			.add(Restrictions.between("passwordExpiryDays", PASSWORD_EXPIRY_DAYS - 0.01d, PASSWORD_EXPIRY_DAYS + 0.01d))
+			.uniqueResult();
+		assertEquals(PASSWORD_EXPIRY_DAYS, u.getPasswordExpiryDays(), 0.01d);
+		
+		// Test predicate and entity load via HQL
+		p = (Person)s.createQuery("from Person p where p.heightInches between ? and ?")
+			.setDouble(0, HEIGHT_INCHES - 0.01d)
+			.setDouble(1, HEIGHT_INCHES + 0.01d)
+			.uniqueResult();
+		assertEquals(HEIGHT_INCHES, p.getHeightInches(), 0.01d);
+		u = (User)s.createQuery("from User u where u.passwordExpiryDays between ? and ?")
+			.setDouble(0, PASSWORD_EXPIRY_DAYS - 0.01d)
+			.setDouble(1, PASSWORD_EXPIRY_DAYS + 0.01d)
+			.uniqueResult();
+		assertEquals(PASSWORD_EXPIRY_DAYS, u.getPasswordExpiryDays(), 0.01d);
+		
+		// Test update
+		p.setHeightInches(1);
+		u.setPasswordExpiryDays(7d);
+		s.flush();
+		heightViaSql = (Double)s.createSQLQuery("select height_centimeters from person where name='Emmanuel'").uniqueResult();
+		assertEquals(2.54d, heightViaSql, 0.01d);
+		expiryViaSql = (Double)s.createSQLQuery("select pwd_expiry_weeks from t_user where person_id=?")
+			.setLong(0, u.getId())
+			.uniqueResult();
+		assertEquals(1d, expiryViaSql, 0.01d);
+		
+		s.delete(p);
+		s.delete(u);
+		assertTrue( s.createQuery("from Person").list().isEmpty() );		
+		
+		t.commit();
+		s.close();
+		
+	}
+	
 
 }
 

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/join/Person.hbm.xml
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/join/Person.hbm.xml	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/join/Person.hbm.xml	2009-10-22 20:02:10 UTC (rev 17821)
@@ -36,6 +36,12 @@
 
         <property name="name" not-null="true" length="80"/>
         <property name="sex" not-null="true" update="false"/>
+		<property name="heightInches">
+			<column name="height_centimeters" 
+				not-null="true" 
+				read="height_centimeters / 2.54" 
+				write="? * 2.54"/>
+		</property>        
 
         <join table="address">
             <key column="address_id"/>
@@ -65,6 +71,11 @@
             <join table="t_user" fetch="select" optional="true">
                 <key column="person_id"/>
                 <property name="login" column="u_login"/>
+				<property name="passwordExpiryDays">
+					<column name="pwd_expiry_weeks" 
+						read="pwd_expiry_weeks * 7.0" 
+						write="? / 7.0"/>
+				</property>                
             </join>
             <join table="t_silly" fetch="select" optional="true">
                 <key column="person_id"/>

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/join/Person.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/join/Person.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/join/Person.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -11,6 +11,7 @@
 	private String address;
 	private String zip;
 	private String country;
+	private double heightInches;
 	private char sex;
 	
 	/**
@@ -78,6 +79,18 @@
 		this.zip = zip;
 	}
 	/**
+	 * @return the The height in inches.
+	 */
+	public double getHeightInches() {
+		return heightInches;
+	}
+	/**
+	 * @param heightInches The height in inches.
+	 */
+	public void setHeightInches(double heightInches) {
+		this.heightInches = heightInches;
+	}
+	/**
 	 * @param address The address to set.
 	 */
 	public void setAddress(String address) {

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/join/User.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/join/User.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/join/User.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -7,6 +7,7 @@
 public class User extends Person {
 	private String login;
 	private String silly;
+	private Double passwordExpiryDays;
 
 	/**
 	 * @return Returns the login.
@@ -20,4 +21,16 @@
 	public void setLogin(String login) {
 		this.login = login;
 	}
+	/**
+	 * @return The password expiry policy in days.
+	 */
+	public Double getPasswordExpiryDays() {
+		return passwordExpiryDays;
+	}
+	/**
+	 * @param passwordExpiryDays The password expiry policy in days. 
+	 */
+	public void setPasswordExpiryDays(Double passwordExpiryDays) {
+		this.passwordExpiryDays = passwordExpiryDays;
+	}	
 }

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Employee.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Employee.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Employee.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -9,6 +9,7 @@
 public class Employee extends Person {
 	private String title;
 	private BigDecimal salary;
+	private double passwordExpiryDays;
 	private Employee manager;
 	/**
 	 * @return Returns the title.
@@ -46,4 +47,16 @@
 	public void setSalary(BigDecimal salary) {
 		this.salary = salary;
 	}
+	/**
+	 * @return The password expiry policy in days.
+	 */
+	public double getPasswordExpiryDays() {
+		return passwordExpiryDays;
+	}
+	/**
+	 * @param passwordExpiryDays The password expiry policy in days. 
+	 */
+	public void setPasswordExpiryDays(double passwordExpiryDays) {
+		this.passwordExpiryDays = passwordExpiryDays;
+	}
 }

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/JoinedSubclassTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/JoinedSubclassTest.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/JoinedSubclassTest.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -179,6 +179,79 @@
 		t.commit();
 		s.close();
 	}
+	
+	public void testCustomColumnReadAndWrite() {
+		Session s = openSession();
+		Transaction t = s.beginTransaction();
+		final double HEIGHT_INCHES = 73;
+		final double HEIGHT_CENTIMETERS = HEIGHT_INCHES * 2.54d;
+		Person p = new Person();
+		p.setName("Emmanuel");
+		p.setSex('M');
+		p.setHeightInches(HEIGHT_INCHES);
+		s.persist(p);
+		final double PASSWORD_EXPIRY_WEEKS = 4;
+		final double PASSWORD_EXPIRY_DAYS = PASSWORD_EXPIRY_WEEKS * 7d;
+		Employee e = new Employee();
+		e.setName("Steve");
+		e.setSex('M');
+		e.setTitle("Mr");		
+		e.setPasswordExpiryDays(PASSWORD_EXPIRY_DAYS);
+		s.persist(e);
+		s.flush();
+		
+		// Test value conversion during insert
+		Double heightViaSql = (Double)s.createSQLQuery("select height_centimeters from JPerson where name='Emmanuel'").uniqueResult();
+		assertEquals(HEIGHT_CENTIMETERS, heightViaSql, 0.01d);
+		Double expiryViaSql = (Double)s.createSQLQuery("select pwd_expiry_weeks from JEmployee where person_id=?")
+			.setLong(0, e.getId())
+			.uniqueResult();
+		assertEquals(PASSWORD_EXPIRY_WEEKS, expiryViaSql, 0.01d);
+		
+		// Test projection
+		Double heightViaHql = (Double)s.createQuery("select p.heightInches from Person p where p.name = 'Emmanuel'").uniqueResult();
+		assertEquals(HEIGHT_INCHES, heightViaHql, 0.01d);
+		Double expiryViaHql = (Double)s.createQuery("select e.passwordExpiryDays from Employee e where e.name = 'Steve'").uniqueResult();
+		assertEquals(PASSWORD_EXPIRY_DAYS, expiryViaHql, 0.01d);
+		
+		// Test restriction and entity load via criteria
+		p = (Person)s.createCriteria(Person.class)
+			.add(Restrictions.between("heightInches", HEIGHT_INCHES - 0.01d, HEIGHT_INCHES + 0.01d))
+			.uniqueResult();
+		assertEquals(HEIGHT_INCHES, p.getHeightInches(), 0.01d);
+		e = (Employee)s.createCriteria(Employee.class)
+			.add(Restrictions.between("passwordExpiryDays", PASSWORD_EXPIRY_DAYS - 0.01d, PASSWORD_EXPIRY_DAYS + 0.01d))
+			.uniqueResult();
+		assertEquals(PASSWORD_EXPIRY_DAYS, e.getPasswordExpiryDays(), 0.01d);
+		
+		// Test predicate and entity load via HQL
+		p = (Person)s.createQuery("from Person p where p.heightInches between ? and ?")
+			.setDouble(0, HEIGHT_INCHES - 0.01d)
+			.setDouble(1, HEIGHT_INCHES + 0.01d)
+			.uniqueResult();
+		assertEquals(HEIGHT_INCHES, p.getHeightInches(), 0.01d);
+		e = (Employee)s.createQuery("from Employee e where e.passwordExpiryDays between ? and ?")
+			.setDouble(0, PASSWORD_EXPIRY_DAYS - 0.01d)
+			.setDouble(1, PASSWORD_EXPIRY_DAYS + 0.01d)
+			.uniqueResult();
+		assertEquals(PASSWORD_EXPIRY_DAYS, e.getPasswordExpiryDays(), 0.01d);
+		
+		// Test update
+		p.setHeightInches(1);
+		e.setPasswordExpiryDays(7);
+		s.flush();
+		heightViaSql = (Double)s.createSQLQuery("select height_centimeters from JPerson where name='Emmanuel'").uniqueResult();
+		assertEquals(2.54d, heightViaSql, 0.01d);
+		expiryViaSql = (Double)s.createSQLQuery("select pwd_expiry_weeks from JEmployee where person_id=?")
+			.setLong(0, e.getId())
+			.uniqueResult();
+		assertEquals(1d, expiryViaSql, 0.01d);
+		s.delete(p);
+		s.delete(e);	
+		t.commit();
+		s.close();
+		
+	}
 
 	public void testLockingJoinedSubclass() {
 		Session s = openSession();

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.hbm.xml
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.hbm.xml	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.hbm.xml	2009-10-22 20:02:10 UTC (rev 17821)
@@ -35,6 +35,12 @@
 		<property name="sex" 
 			not-null="true"
 			update="false"/>
+		<property name="heightInches">
+			<column name="height_centimeters" 
+				not-null="true" 
+				read="height_centimeters / 2.54" 
+				write="? * 2.54"/>
+		</property>
 		
 		<component name="address">
 			<property name="address"/>
@@ -49,6 +55,12 @@
 					length="20"/>
 				<property name="salary" 
 					length="0"/>
+				<property name="passwordExpiryDays">
+					<column name="pwd_expiry_weeks" 
+						not-null="true" 
+						read="pwd_expiry_weeks * 7.0" 
+						write="? / 7.0"/>
+				</property>					
 				<many-to-one name="manager"/>
 		</joined-subclass>
 		

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -10,6 +10,7 @@
 	private String name;
 	private char sex;
 	private int version;
+	private double heightInches;
 	private Address address = new Address();
 	/**
 	 * @return Returns the address.
@@ -68,6 +69,20 @@
 		this.name = identity;
 	}
 
+	/**
+	 * @return Returns the height in inches.
+	 */
+	public double getHeightInches() {
+		return heightInches;
+	}
+
+	/**
+	 * @param heightInches The height in inches to set.
+	 */
+	public void setHeightInches(double heightInches) {
+		this.heightInches = heightInches;
+	}
+
 	public int getVersion() {
 		return version;
 	}

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Alien.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Alien.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Alien.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -9,6 +9,7 @@
 	private String identity;
 	private String planet;
 	private String species;
+	private double heightInches;
 	
 	public void setIdentity(String identity) {
 		this.identity = identity;
@@ -28,6 +29,12 @@
 	public String getPlanet() {
 		return planet;
 	}
+	public double getHeightInches() {
+		return heightInches;
+	}
+	public void setHeightInches(double heightInches) {
+		this.heightInches = heightInches;
+	}
 	public void setId(Long id) {
 		this.id = id;
 	}

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Being.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Being.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Being.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -9,6 +9,7 @@
 	private String identity;
 	private String location;
 	private String species;
+	private double heightInches;	
 	
 	public void setLocation(String location) {
 		this.location = location;
@@ -28,4 +29,10 @@
 	public String getIdentity() {
 		return identity;
 	}
+	public double getHeightInches() {
+		return heightInches;
+	}
+	public void setHeightInches(double heightInches) {
+		this.heightInches = heightInches;
+	}
 }

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Beings.hbm.xml
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Beings.hbm.xml	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Beings.hbm.xml	2009-10-22 20:02:10 UTC (rev 17821)
@@ -36,6 +36,12 @@
 			not-null="true" 
 			update="false"/>
 		<property name="address"/>
+		<property name="heightInches">
+			<column name="height_centimeters" 
+				not-null="true" 
+				read="height_centimeters / 2.54" 
+				write="? * 2.54"/>
+		</property>		
 		
 	</class>
 	
@@ -53,16 +59,22 @@
 		<property name="species" 
 			not-null="true" 
 			update="false"/>
+		<property name="heightInches">
+			<column name="height_centimeters" 
+				not-null="true" 
+				read="height_centimeters / 2.54" 
+				write="? * 2.54"/>
+		</property>			
 			
 	</class>
 	
 	<class name="Being" mutable="false">
 	
 		<subselect>
-			select bid, name as ident, address as loc, 'human' as species 
+			select bid, name as ident, address as loc, 'human' as species, height_centimeters
 			from humans 
 			union 
-			select bid, ident, planet as loc, species 
+			select bid, ident, planet as loc, species, height_centimeters
 			from aliens
 		</subselect>
 		
@@ -77,6 +89,11 @@
 		<property name="identity" column="ident"/>
 		<property name="location" column="loc"/>
 		<property name="species"/>
+		<property name="heightInches">
+			<column name="height_centimeters" 
+				not-null="true" 
+				read="height_centimeters / 2.54"/>
+		</property>		
 		
 	</class>
 

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Human.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Human.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/Human.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -9,6 +9,7 @@
 	private String name;
 	private char sex;
 	private String address;
+	private double heightInches;
 	
 	public void setAddress(String address) {
 		this.address = address;
@@ -34,4 +35,10 @@
 	public Long getId() {
 		return id;
 	}
+	public double getHeightInches() {
+		return heightInches;
+	}
+	public void setHeightInches(double heightInches) {
+		this.heightInches = heightInches;
+	}
 }

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/SubselectTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/SubselectTest.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/subselect/SubselectTest.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -8,6 +8,7 @@
 
 import org.hibernate.Session;
 import org.hibernate.Transaction;
+import org.hibernate.criterion.Restrictions;
 import org.hibernate.junit.functional.FunctionalTestCase;
 import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
 
@@ -67,6 +68,57 @@
 		t.commit();
 		s.close();
 	}
+	
+	public void testCustomColumnReadAndWrite() {
+		Session s = openSession();
+		Transaction t = s.beginTransaction();
+		final double HUMAN_INCHES = 73;
+		final double ALIEN_INCHES = 931;
+		final double HUMAN_CENTIMETERS = HUMAN_INCHES * 2.54d;		
+		final double ALIEN_CENTIMETERS = ALIEN_INCHES * 2.54d;		
+		Human gavin = new Human();
+		gavin.setName( "gavin" );
+		gavin.setSex( 'M' );
+		gavin.setAddress( "Melbourne, Australia" );
+		gavin.setHeightInches( HUMAN_INCHES );
+		Alien x23y4 = new Alien();
+		x23y4.setIdentity( "x23y4$$hu%3" );
+		x23y4.setPlanet( "Mars" );
+		x23y4.setSpecies( "martian" );
+		x23y4.setHeightInches( ALIEN_INCHES );
+		s.save(gavin);
+		s.save(x23y4);
+		s.flush();
+		
+		// Test value conversion during insert
+		Double humanHeightViaSql = (Double)s.createSQLQuery("select height_centimeters from humans").uniqueResult();
+		assertEquals(HUMAN_CENTIMETERS, humanHeightViaSql, 0.01d);
+		Double alienHeightViaSql = (Double)s.createSQLQuery("select height_centimeters from aliens").uniqueResult();
+		assertEquals(ALIEN_CENTIMETERS, alienHeightViaSql, 0.01d);
+		s.clear();
+		
+		// Test projection
+		Double heightViaHql = (Double)s.createQuery("select heightInches from Being b where b.identity = 'gavin'").uniqueResult();
+		assertEquals(HUMAN_INCHES, heightViaHql, 0.01d);
+		
+		// Test restriction and entity load via criteria
+		Being b = (Being)s.createCriteria(Being.class)
+			.add(Restrictions.between("heightInches", HUMAN_INCHES - 0.01d, HUMAN_INCHES + 0.01d))
+			.uniqueResult();
+		assertEquals(HUMAN_INCHES, b.getHeightInches(), 0.01d);
+		
+		// Test predicate and entity load via HQL
+		b = (Being)s.createQuery("from Being b where b.heightInches between ? and ?")
+			.setDouble(0, ALIEN_INCHES - 0.01d)
+			.setDouble(1, ALIEN_INCHES + 0.01d)
+			.uniqueResult();
+		assertEquals(ALIEN_INCHES, b.getHeightInches(), 0.01d);
+                s.delete(gavin);
+                s.delete(x23y4);		
+		t.commit();
+		s.close();
+		
+	}
 
 }
 

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Employee.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Employee.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Employee.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -9,6 +9,7 @@
 public class Employee extends Person {
 	private String title;
 	private BigDecimal salary;
+	private double passwordExpiryDays;	
 	private Employee manager;
 	/**
 	 * @return Returns the title.
@@ -46,4 +47,16 @@
 	public void setSalary(BigDecimal salary) {
 		this.salary = salary;
 	}
+	/**
+	 * @return The password expiry policy in days.
+	 */
+	public double getPasswordExpiryDays() {
+		return passwordExpiryDays;
+	}
+	/**
+	 * @param passwordExpiryDays The password expiry policy in days. 
+	 */
+	public void setPasswordExpiryDays(double passwordExpiryDays) {
+		this.passwordExpiryDays = passwordExpiryDays;
+	}	
 }

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.hbm.xml
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.hbm.xml	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.hbm.xml	2009-10-22 20:02:10 UTC (rev 17821)
@@ -34,6 +34,12 @@
 		<property name="sex" 
 			not-null="true"
 			update="false"/>
+		<property name="heightInches">
+			<column name="height_centimeters" 
+				not-null="true" 
+				read="height_centimeters / 2.54" 
+				write="? * 2.54"/>
+		</property>			
 		
 		<component name="address">
 			<property name="address" index="AddressIndex"/>
@@ -47,6 +53,12 @@
 					length="20"/>
 				<property name="salary" 
 					length="0"/>
+				<property name="passwordExpiryDays">
+					<column name="pwd_expiry_weeks" 
+						not-null="true" 
+						read="pwd_expiry_weeks * 7.0" 
+						write="? / 7.0"/>
+				</property>					
 				<many-to-one name="manager"/>
 		</union-subclass>
 		

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -9,6 +9,7 @@
 	private long id;
 	private String name;
 	private char sex;
+	private double heightInches;
 	private Address address = new Address();
 	/**
 	 * @return Returns the address.
@@ -66,5 +67,16 @@
 	public void setName(String identity) {
 		this.name = identity;
 	}
-
+	/**
+	 * @return Returns the height in inches.
+	 */
+	public double getHeightInches() {
+		return heightInches;
+	}
+	/**
+	 * @param heightInches The height in inches to set.
+	 */
+	public void setHeightInches(double heightInches) {
+		this.heightInches = heightInches;
+	}
 }

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/UnionSubclassTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/UnionSubclassTest.java	2009-10-22 16:45:01 UTC (rev 17820)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/unionsubclass2/UnionSubclassTest.java	2009-10-22 20:02:10 UTC (rev 17821)
@@ -149,5 +149,79 @@
 		s.close();
 	}
 
+	public void testCustomColumnReadAndWrite() {
+		Session s = openSession();
+		Transaction t = s.beginTransaction();
+		final double HEIGHT_INCHES = 73;
+		final double HEIGHT_CENTIMETERS = HEIGHT_INCHES * 2.54d;
+		Person p = new Person();
+		p.setName("Emmanuel");
+		p.setSex('M');
+		p.setHeightInches(HEIGHT_INCHES);
+		s.persist(p);
+		final double PASSWORD_EXPIRY_WEEKS = 4;
+		final double PASSWORD_EXPIRY_DAYS = PASSWORD_EXPIRY_WEEKS * 7d;
+		Employee e = new Employee();
+		e.setName("Steve");
+		e.setSex('M');
+		e.setTitle("Mr");		
+		e.setPasswordExpiryDays(PASSWORD_EXPIRY_DAYS);
+		s.persist(e);
+		s.flush();
+		
+		// Test value conversion during insert
+		Double heightViaSql = (Double)s.createSQLQuery("select height_centimeters from UPerson where name='Emmanuel'").uniqueResult();
+		assertEquals(HEIGHT_CENTIMETERS, heightViaSql, 0.01d);
+		Double expiryViaSql = (Double)s.createSQLQuery("select pwd_expiry_weeks from UEmployee where person_id=?")
+			.setLong(0, e.getId())
+			.uniqueResult();
+		assertEquals(PASSWORD_EXPIRY_WEEKS, expiryViaSql, 0.01d);
+		
+		// Test projection
+		Double heightViaHql = (Double)s.createQuery("select p.heightInches from Person p where p.name = 'Emmanuel'").uniqueResult();
+		assertEquals(HEIGHT_INCHES, heightViaHql, 0.01d);
+		Double expiryViaHql = (Double)s.createQuery("select e.passwordExpiryDays from Employee e where e.name = 'Steve'").uniqueResult();
+		assertEquals(PASSWORD_EXPIRY_DAYS, expiryViaHql, 0.01d);
+		
+		// Test restriction and entity load via criteria
+		p = (Person)s.createCriteria(Person.class)
+			.add(Restrictions.between("heightInches", HEIGHT_INCHES - 0.01d, HEIGHT_INCHES + 0.01d))
+			.uniqueResult();
+		assertEquals(HEIGHT_INCHES, p.getHeightInches(), 0.01d);
+		e = (Employee)s.createCriteria(Employee.class)
+			.add(Restrictions.between("passwordExpiryDays", PASSWORD_EXPIRY_DAYS - 0.01d, PASSWORD_EXPIRY_DAYS + 0.01d))
+			.uniqueResult();
+		assertEquals(PASSWORD_EXPIRY_DAYS, e.getPasswordExpiryDays(), 0.01d);
+		
+		// Test predicate and entity load via HQL
+		p = (Person)s.createQuery("from Person p where p.heightInches between ? and ?")
+			.setDouble(0, HEIGHT_INCHES - 0.01d)
+			.setDouble(1, HEIGHT_INCHES + 0.01d)
+			.uniqueResult();
+		assertEquals(HEIGHT_INCHES, p.getHeightInches(), 0.01d);
+		e = (Employee)s.createQuery("from Employee e where e.passwordExpiryDays between ? and ?")
+			.setDouble(0, PASSWORD_EXPIRY_DAYS - 0.01d)
+			.setDouble(1, PASSWORD_EXPIRY_DAYS + 0.01d)
+			.uniqueResult();
+		assertEquals(PASSWORD_EXPIRY_DAYS, e.getPasswordExpiryDays(), 0.01d);
+		
+		// Test update
+		p.setHeightInches(1);
+		e.setPasswordExpiryDays(7);
+		s.flush();
+		heightViaSql = (Double)s.createSQLQuery("select height_centimeters from UPerson where name='Emmanuel'").uniqueResult();
+		assertEquals(2.54d, heightViaSql, 0.01d);
+		expiryViaSql = (Double)s.createSQLQuery("select pwd_expiry_weeks from UEmployee where person_id=?")
+			.setLong(0, e.getId())
+			.uniqueResult();
+		assertEquals(1d, expiryViaSql, 0.01d);
+		s.delete(p);
+		s.delete(e);
+		t.commit();
+		s.close();
+		
+	}
+	
+	
 }
 



More information about the hibernate-commits mailing list