Author: tomdz Date: Tue Sep 11 23:21:40 2007 New Revision: 574795 URL: http://svn.apache.org/viewvc?rev=574795&view=rev Log: Added support for reading the onUpdate and onDelete settings from a live database (for DDLUTILS-75)
Modified: db/ddlutils/trunk/src/java/database.dtd db/ddlutils/trunk/src/java/org/apache/ddlutils/model/CascadeActionEnum.java db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/MetaDataColumnDescriptor.java db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/SqlBuilder.java db/ddlutils/trunk/src/test/org/apache/ddlutils/io/RoundtripTestBase.java db/ddlutils/trunk/src/test/org/apache/ddlutils/io/TestConstraints.java Modified: db/ddlutils/trunk/src/java/database.dtd URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/database.dtd?rev=574795&r1=574794&r2=574795&view=diff ============================================================================== Binary files - no diff available. Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/model/CascadeActionEnum.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/model/CascadeActionEnum.java?rev=574795&r1=574794&r2=574795&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/model/CascadeActionEnum.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/model/CascadeActionEnum.java Tue Sep 11 23:21:40 2007 @@ -33,27 +33,34 @@ */ public class CascadeActionEnum extends ValuedEnum { - /** The integer value for the enum value for a cascading change. */ - public static final int VALUE_CASCADE = 1; - /** The integer value for the enum value for a set-null change. */ - public static final int VALUE_SETNULL = 2; - /** The integer value for the enum value for a restrict change. */ - public static final int VALUE_RESTRICT = 3; - /** The integer value for the enum value for no-change. */ - public static final int VALUE_NONE = 4; + /** The integer value for the enum value for a cascade action. */ + public static final int VALUE_CASCADE = 1; + /** The integer value for the enum value for a set-null action. */ + public static final int VALUE_SET_NULL = 2; + /** The integer value for the enum value for a set-null action. */ + public static final int VALUE_SET_DEFAULT = 3; + /** The integer value for the enum value for a restrict action. */ + public static final int VALUE_RESTRICT = 4; + /** The integer value for the enum value for no-action. */ + public static final int VALUE_NONE = 5; - /** The enum value for a cascade action which directs the database to change the value - of local column to the new value of the referenced column when it changes. */ - public static final CascadeActionEnum CASCADE = new CascadeActionEnum("cascade", VALUE_CASCADE); - /** The enum value for a cascade action which directs the database to set the local - column to null when the referenced column changes. */ - public static final CascadeActionEnum SETNULL = new CascadeActionEnum("setnull", VALUE_SETNULL); + /** The enum value for a cascade action which directs the database to apply the change to + the referenced table also to this table. E.g. if the referenced row is deleted, then + the local one will also be deleted when this value is used for the onDelete action. */ + public static final CascadeActionEnum CASCADE = new CascadeActionEnum("cascade", VALUE_CASCADE); + /** The enum value for a cascade action which directs the database to set the local columns + referenced by the foreign key to null when the referenced row changes/is deleted. */ + public static final CascadeActionEnum SET_NULL = new CascadeActionEnum("setnull", VALUE_SET_NULL); + /** The enum value for a cascade action which directs the database to set the local columns + referenced by the foreign key to the default value when the referenced row changes/is deleted. */ + public static final CascadeActionEnum SET_DEFAULT = new CascadeActionEnum("setdefault", VALUE_SET_DEFAULT); /** The enum value for a cascade action which directs the database to restrict the change - changes to the referenced column. The interpretation of this is database-dependent. */ - public static final CascadeActionEnum RESTRICT = new CascadeActionEnum("restrict", VALUE_RESTRICT); - /** The enum value for a cascade action which directs the database to take do nothing - to the local column when the value of the referenced column changes. */ - public static final CascadeActionEnum NONE = new CascadeActionEnum("none", VALUE_NONE); + changes to the referenced column. The interpretation of this is database-dependent, but it is + usually the same as [EMAIL PROTECTED] #NONE}. */ + public static final CascadeActionEnum RESTRICT = new CascadeActionEnum("restrict", VALUE_RESTRICT); + /** The enum value for the cascade action that directs the database to not change the local column + when the value of the referenced column changes, only check the foreign key constraint. */ + public static final CascadeActionEnum NONE = new CascadeActionEnum("none", VALUE_NONE); /** Version id for this class as relevant for serialization. */ private static final long serialVersionUID = -6378050861446415790L; Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java?rev=574795&r1=574794&r2=574795&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java Tue Sep 11 23:21:40 2007 @@ -42,6 +42,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.ddlutils.Platform; import org.apache.ddlutils.PlatformInfo; +import org.apache.ddlutils.model.CascadeActionEnum; import org.apache.ddlutils.model.Column; import org.apache.ddlutils.model.Database; import org.apache.ddlutils.model.ForeignKey; @@ -228,6 +229,8 @@ result.add(new MetaDataColumnDescriptor("FKTABLE_NAME", Types.VARCHAR)); result.add(new MetaDataColumnDescriptor("KEY_SEQ", Types.TINYINT, new Short((short)0))); result.add(new MetaDataColumnDescriptor("FK_NAME", Types.VARCHAR)); + result.add(new MetaDataColumnDescriptor("UPDATE_RULE", Types.TINYINT)); + result.add(new MetaDataColumnDescriptor("DELETE_RULE", Types.TINYINT)); result.add(new MetaDataColumnDescriptor("PKCOLUMN_NAME", Types.VARCHAR)); result.add(new MetaDataColumnDescriptor("FKCOLUMN_NAME", Types.VARCHAR)); @@ -779,10 +782,15 @@ column.setName((String)values.get("COLUMN_NAME")); column.setDefaultValue((String)values.get("COLUMN_DEF")); column.setTypeCode(((Integer)values.get("DATA_TYPE")).intValue()); - column.setPrecisionRadix(((Integer)values.get("NUM_PREC_RADIX")).intValue()); - String size = (String)values.get("COLUMN_SIZE"); - int scale = ((Integer)values.get("DECIMAL_DIGITS")).intValue(); + Integer precision = (Integer)values.get("NUM_PREC_RADIX"); + + if (precision != null) + { + column.setPrecisionRadix(precision.intValue()); + } + + String size = (String)values.get("COLUMN_SIZE"); if (size == null) { @@ -791,11 +799,14 @@ // we're setting the size after the precision and radix in case // the database prefers to return them in the size value column.setSize(size); - if (scale != 0) + + Integer scale = (Integer)values.get("DECIMAL_DIGITS"); + + if (scale != null) { // if there is a scale value, set it after the size (which probably did not contain // a scale specification) - column.setScale(scale); + column.setScale(scale.intValue()); } column.setRequired("NO".equalsIgnoreCase(((String)values.get("IS_NULLABLE")).trim())); column.setDescription((String)values.get("REMARKS")); @@ -895,6 +906,8 @@ { fk = new ForeignKey(fkName); fk.setForeignTableName((String)values.get("PKTABLE_NAME")); + fk.setOnUpdate(convertAction((Short)values.get("UPDATE_RULE"))); + fk.setOnDelete(convertAction((Short)values.get("DELETE_RULE"))); knownFks.put(fkName, fk); } @@ -909,6 +922,38 @@ fk.addReference(ref); } + /** + * Converts the JDBC action value (one of the <code>importKey</code> constants in the + * [EMAIL PROTECTED] DatabaseMetaData} class) to a [EMAIL PROTECTED] CascadeActionEnum}. + * + * @param jdbcActionValue The jdbc action value + * @return The enum value + */ + protected CascadeActionEnum convertAction(Short jdbcActionValue) + { + CascadeActionEnum action = CascadeActionEnum.NONE; + + if (jdbcActionValue != null) + { + switch (jdbcActionValue.shortValue()) + { + case DatabaseMetaData.importedKeyCascade: + action = CascadeActionEnum.CASCADE; + break; + case DatabaseMetaData.importedKeySetNull: + action = CascadeActionEnum.SET_NULL; + break; + case DatabaseMetaData.importedKeySetDefault: + action = CascadeActionEnum.SET_DEFAULT; + break; + case DatabaseMetaData.importedKeyRestrict: + action = CascadeActionEnum.RESTRICT; + break; + } + } + return action; + } + /** * Determines the indices for the indicated table. * Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/MetaDataColumnDescriptor.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/MetaDataColumnDescriptor.java?rev=574795&r1=574794&r2=574795&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/MetaDataColumnDescriptor.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/MetaDataColumnDescriptor.java Tue Sep 11 23:21:40 2007 @@ -115,17 +115,28 @@ } if (foundIdx > 0) { + Object result = null; + switch (_jdbcType) { case Types.BIT: - return new Boolean(resultSet.getBoolean(foundIdx)); + result = new Boolean(resultSet.getBoolean(foundIdx)); + break; case Types.INTEGER: - return new Integer(resultSet.getInt(foundIdx)); + result = new Integer(resultSet.getInt(foundIdx)); + break; case Types.TINYINT: - return new Short(resultSet.getShort(foundIdx)); + result = new Short(resultSet.getShort(foundIdx)); + break; default: - return resultSet.getString(foundIdx); + result = resultSet.getString(foundIdx); + break; } + if (resultSet.wasNull()) + { + result = null; + } + return result; } else { Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/SqlBuilder.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/SqlBuilder.java?rev=574795&r1=574794&r2=574795&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/SqlBuilder.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/SqlBuilder.java Tue Sep 11 23:21:40 2007 @@ -63,6 +63,7 @@ import org.apache.ddlutils.alteration.RemovePrimaryKeyChange; import org.apache.ddlutils.alteration.RemoveTableChange; import org.apache.ddlutils.alteration.TableChange; +import org.apache.ddlutils.model.CascadeActionEnum; import org.apache.ddlutils.model.Column; import org.apache.ddlutils.model.Database; import org.apache.ddlutils.model.ForeignKey; @@ -2466,11 +2467,11 @@ { for (int idx = 0; idx < table.getForeignKeyCount(); idx++) { - ForeignKey key = table.getForeignKey(idx); + ForeignKey foreignKey = table.getForeignKey(idx); - if (key.getForeignTableName() == null) + if (foreignKey.getForeignTableName() == null) { - _log.warn("Foreign key table is null for key " + key); + _log.warn("Foreign key table is null for key " + foreignKey); } else { @@ -2478,16 +2479,18 @@ if (getPlatformInfo().isEmbeddedForeignKeysNamed()) { print("CONSTRAINT "); - printIdentifier(getForeignKeyName(table, key)); + printIdentifier(getForeignKeyName(table, foreignKey)); print(" "); } print("FOREIGN KEY ("); - writeLocalReferences(key); + writeLocalReferences(foreignKey); print(") REFERENCES "); - printIdentifier(getTableName(database.findTable(key.getForeignTableName()))); + printIdentifier(getTableName(database.findTable(foreignKey.getForeignTableName()))); print(" ("); - writeForeignReferences(key); + writeForeignReferences(foreignKey); print(")"); + writeForeignKeyOnDeleteAction(table, foreignKey); + writeForeignKeyOnUpdateAction(table, foreignKey); } } } @@ -2495,29 +2498,31 @@ /** * Writes a single foreign key constraint using a alter table statement. * - * @param database The database model - * @param table The table - * @param key The foreign key + * @param database The database model + * @param table The table + * @param foreignKey The foreign key */ - protected void writeExternalForeignKeyCreateStmt(Database database, Table table, ForeignKey key) throws IOException + protected void writeExternalForeignKeyCreateStmt(Database database, Table table, ForeignKey foreignKey) throws IOException { - if (key.getForeignTableName() == null) + if (foreignKey.getForeignTableName() == null) { - _log.warn("Foreign key table is null for key " + key); + _log.warn("Foreign key table is null for key " + foreignKey); } else { writeTableAlterStmt(table); print("ADD CONSTRAINT "); - printIdentifier(getForeignKeyName(table, key)); + printIdentifier(getForeignKeyName(table, foreignKey)); print(" FOREIGN KEY ("); - writeLocalReferences(key); + writeLocalReferences(foreignKey); print(") REFERENCES "); - printIdentifier(getTableName(database.findTable(key.getForeignTableName()))); + printIdentifier(getTableName(database.findTable(foreignKey.getForeignTableName()))); print(" ("); - writeForeignReferences(key); + writeForeignReferences(foreignKey); print(")"); + writeForeignKeyOnDeleteAction(table, foreignKey); + writeForeignKeyOnUpdateAction(table, foreignKey); printEndOfStatement(); } } @@ -2553,6 +2558,70 @@ print(", "); } printIdentifier(key.getReference(idx).getForeignColumnName()); + } + } + + /** + * Writes the onDelete action for the given foreign key. + * + * @param table The table + * @param foreignKey The foreignkey + */ + private void writeForeignKeyOnDeleteAction(Table table, ForeignKey foreignKey) throws IOException + { + if (foreignKey.getOnDelete() != CascadeActionEnum.NONE) + { + print(" ON DELETE "); + switch (foreignKey.getOnDelete().getValue()) + { + case CascadeActionEnum.VALUE_CASCADE: + print("CASCADE"); + break; + case CascadeActionEnum.VALUE_SET_NULL: + print("SET NULL"); + break; + case CascadeActionEnum.VALUE_RESTRICT: + print("RESTRICT"); + break; + case CascadeActionEnum.VALUE_NONE: + print("NO ACTION"); + break; + default: + throw new ModelException("Unsupported cascade value '" + foreignKey.getOnDelete().getValue() + + "' for onDelete in foreign key in table " + table.getName()); + } + } + } + + /** + * Writes the onDelete action for the given foreign key. + * + * @param table The table + * @param foreignKey The foreignkey + */ + private void writeForeignKeyOnUpdateAction(Table table, ForeignKey foreignKey) throws IOException + { + if (foreignKey.getOnUpdate() != CascadeActionEnum.NONE) + { + print(" ON UPDATE "); + switch (foreignKey.getOnUpdate().getValue()) + { + case CascadeActionEnum.VALUE_CASCADE: + print("CASCADE"); + break; + case CascadeActionEnum.VALUE_SET_NULL: + print("SET NULL"); + break; + case CascadeActionEnum.VALUE_RESTRICT: + print("RESTRICT"); + break; + case CascadeActionEnum.VALUE_NONE: + print("NO ACTION"); + break; + default: + throw new ModelException("Unsupported cascade value '" + foreignKey.getOnUpdate().getValue() + + "' for onUpdate in foreign key in table " + table.getName()); + } } } Modified: db/ddlutils/trunk/src/test/org/apache/ddlutils/io/RoundtripTestBase.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/test/org/apache/ddlutils/io/RoundtripTestBase.java?rev=574795&r1=574794&r2=574795&view=diff ============================================================================== --- db/ddlutils/trunk/src/test/org/apache/ddlutils/io/RoundtripTestBase.java (original) +++ db/ddlutils/trunk/src/test/org/apache/ddlutils/io/RoundtripTestBase.java Tue Sep 11 23:21:40 2007 @@ -35,6 +35,7 @@ import org.apache.ddlutils.dynabean.SqlDynaBean; import org.apache.ddlutils.dynabean.SqlDynaClass; import org.apache.ddlutils.dynabean.SqlDynaProperty; +import org.apache.ddlutils.model.CascadeActionEnum; import org.apache.ddlutils.model.Column; import org.apache.ddlutils.model.Database; import org.apache.ddlutils.model.ForeignKey; @@ -150,6 +151,26 @@ getPlatform().insert(getModel(), bean); } + + /** + * Deletes the specified row from the table. + * + * @param tableName The name of the table (case insensitive) + * @param pkColumnValues The values for the pk columns in order of definition + */ + protected void deleteRow(String tableName, Object[] pkColumnValues) + { + Table table = getModel().findTable(tableName); + DynaBean bean = getModel().createDynaBeanFor(table); + Column[] pkColumns = table.getPrimaryKeyColumns(); + + for (int idx = 0; (idx < pkColumns.length) && (idx < pkColumnValues.length); idx++) + { + bean.set(pkColumns[idx].getName(), pkColumnValues[idx]); + } + getPlatform().delete(getModel(), bean); + } + /** * Returns a "SELECT * FROM [table name]" statement. It also takes * delimited identifier mode into account if enabled. @@ -541,6 +562,28 @@ assertEquals("Referenced table names do not match (ignoring case).", getPlatform().getSqlBuilder().shortenName(expected.getForeignTableName().toUpperCase(), getSqlBuilder().getMaxTableNameLength()), getPlatform().getSqlBuilder().shortenName(actual.getForeignTableName().toUpperCase(), getSqlBuilder().getMaxTableNameLength())); + } + if ((expected.getOnUpdate() == CascadeActionEnum.NONE) || (expected.getOnUpdate() == CascadeActionEnum.RESTRICT)) + { + assertTrue("Not the same onUpdate setting in foreign key "+actual.getName()+".", + (actual.getOnUpdate() == CascadeActionEnum.NONE) || (actual.getOnUpdate() == CascadeActionEnum.RESTRICT)); + } + else + { + assertEquals("Not the same onUpdate setting in foreign key "+actual.getName()+".", + expected.getOnUpdate(), + actual.getOnUpdate()); + } + if ((expected.getOnDelete() == CascadeActionEnum.NONE) || (expected.getOnDelete() == CascadeActionEnum.RESTRICT)) + { + assertTrue("Not the same onDelete setting in foreign key "+actual.getName()+".", + (actual.getOnDelete() == CascadeActionEnum.NONE) || (actual.getOnDelete() == CascadeActionEnum.RESTRICT)); + } + else + { + assertEquals("Not the same onDelete setting in foreign key "+actual.getName()+".", + expected.getOnDelete(), + actual.getOnDelete()); } assertEquals("Not the same number of references in foreign key "+actual.getName()+".", expected.getReferenceCount(), Modified: db/ddlutils/trunk/src/test/org/apache/ddlutils/io/TestConstraints.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/test/org/apache/ddlutils/io/TestConstraints.java?rev=574795&r1=574794&r2=574795&view=diff ============================================================================== --- db/ddlutils/trunk/src/test/org/apache/ddlutils/io/TestConstraints.java (original) +++ db/ddlutils/trunk/src/test/org/apache/ddlutils/io/TestConstraints.java Tue Sep 11 23:21:40 2007 @@ -19,6 +19,8 @@ * under the License. */ +import java.util.List; + import org.apache.commons.lang.StringUtils; import org.apache.ddlutils.model.Database; import org.apache.ddlutils.platform.sybase.SybasePlatform; @@ -438,5 +440,116 @@ "</database>"; performConstraintsTest(modelXml, true); + } + + /** + * Tests two tables with a foreign key with a restrict onDelete action. + */ + public void testForeignKeyWithOnDeleteRestrict() + { + final String modelXml = + "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+ + "<database name='roundtriptest'>\n"+ + " <table name='roundtrip_1'>\n"+ + " <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+ + " </table>\n"+ + " <table name='roundtrip_2'>\n"+ + " <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+ + " <column name='avalue' type='INTEGER' required='true'/>\n"+ + " <foreign-key foreignTable='roundtrip_1' onDelete='restrict'>\n"+ + " <reference local='avalue' foreign='pk'/>\n"+ + " </foreign-key>\n"+ + " </table>\n"+ + "</database>"; + + performConstraintsTest(modelXml, true); + } + + /** + * Tests two tables with a foreign key with a cascade onDelete action. + */ + public void testForeignKeyWithOnDeleteCascade() + { + final String modelXml = + "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+ + "<database name='roundtriptest'>\n"+ + " <table name='roundtrip_1'>\n"+ + " <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+ + " </table>\n"+ + " <table name='roundtrip_2'>\n"+ + " <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+ + " <column name='avalue' type='INTEGER' required='true'/>\n"+ + " <foreign-key foreignTable='roundtrip_1' onDelete='cascade'>\n"+ + " <reference local='avalue' foreign='pk'/>\n"+ + " </foreign-key>\n"+ + " </table>\n"+ + "</database>"; + + performConstraintsTest(modelXml, true); + + insertRow("roundtrip_1", new Object[] { new Integer(1) }); + insertRow("roundtrip_2", new Object[] { new Integer(5), new Integer(1) }); + + List beansTable1 = getRows("roundtrip_1"); + List beansTable2 = getRows("roundtrip_2"); + + assertEquals(1, beansTable1.size()); + assertEquals(1, beansTable2.size()); + assertEquals(new Integer(1), beansTable1.get(0), "pk"); + assertEquals(new Integer(5), beansTable2.get(0), "pk"); + assertEquals(new Integer(1), beansTable2.get(0), "avalue"); + + deleteRow("roundtrip_1", new Object[] { new Integer(1) }); + + beansTable1 = getRows("roundtrip_1"); + beansTable2 = getRows("roundtrip_2"); + + assertEquals(0, beansTable1.size()); + assertEquals(0, beansTable2.size()); + } + + /** + * Tests two tables with a foreign key with a cascade onDelete action. + */ + public void testForeignKeyWithOnDeleteSetNull() + { + final String modelXml = + "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+ + "<database name='roundtriptest'>\n"+ + " <table name='roundtrip_1'>\n"+ + " <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+ + " </table>\n"+ + " <table name='roundtrip_2'>\n"+ + " <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+ + " <column name='avalue' type='INTEGER' required='false'/>\n"+ + " <foreign-key foreignTable='roundtrip_1' onDelete='setnull'>\n"+ + " <reference local='avalue' foreign='pk'/>\n"+ + " </foreign-key>\n"+ + " </table>\n"+ + "</database>"; + + performConstraintsTest(modelXml, true); + + insertRow("roundtrip_1", new Object[] { new Integer(1) }); + insertRow("roundtrip_2", new Object[] { new Integer(5), new Integer(1) }); + + List beansTable1 = getRows("roundtrip_1"); + List beansTable2 = getRows("roundtrip_2"); + + assertEquals(1, beansTable1.size()); + assertEquals(1, beansTable2.size()); + assertEquals(new Integer(1), beansTable1.get(0), "pk"); + assertEquals(new Integer(5), beansTable2.get(0), "pk"); + assertEquals(new Integer(1), beansTable2.get(0), "avalue"); + + deleteRow("roundtrip_1", new Object[] { new Integer(1) }); + + beansTable1 = getRows("roundtrip_1"); + beansTable2 = getRows("roundtrip_2"); + + assertEquals(0, beansTable1.size()); + assertEquals(1, beansTable2.size()); + assertEquals(new Integer(5), beansTable2.get(0), "pk"); + assertEquals((Object)null, beansTable2.get(0), "avalue"); } }