You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2012/12/06 12:09:56 UTC

[26/51] [partial] ISIS-188: moving components into correct directories.

http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/SqlObjectStoreException.java
----------------------------------------------------------------------
diff --git a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/SqlObjectStoreException.java b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/SqlObjectStoreException.java
new file mode 100644
index 0000000..03c8fab
--- /dev/null
+++ b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/SqlObjectStoreException.java
@@ -0,0 +1,42 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.runtimes.dflt.objectstores.sql;
+
+import org.apache.isis.runtimes.dflt.runtime.persistence.ObjectPersistenceException;
+
+public class SqlObjectStoreException extends ObjectPersistenceException {
+    private static final long serialVersionUID = 1L;
+
+    public SqlObjectStoreException() {
+        super();
+    }
+
+    public SqlObjectStoreException(final String s) {
+        super(s);
+    }
+
+    public SqlObjectStoreException(final String s, final Throwable cause) {
+        super(s, cause);
+    }
+
+    public SqlObjectStoreException(final Throwable cause) {
+        super(cause);
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/SqlPersistorInstaller.java
----------------------------------------------------------------------
diff --git a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/SqlPersistorInstaller.java b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/SqlPersistorInstaller.java
new file mode 100644
index 0000000..6a544a8
--- /dev/null
+++ b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/SqlPersistorInstaller.java
@@ -0,0 +1,88 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.runtimes.dflt.objectstores.sql;
+
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapterFactory;
+import org.apache.isis.runtimes.dflt.objectstores.sql.auto.AutoMapperFactory;
+import org.apache.isis.runtimes.dflt.objectstores.sql.jdbc.JdbcConnectorFactory;
+import org.apache.isis.runtimes.dflt.objectstores.sql.jdbc.installer.JdbcFieldMappingFactoryInstaller;
+import org.apache.isis.runtimes.dflt.runtime.installerregistry.installerapi.PersistenceMechanismInstallerAbstract;
+import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.ObjectStoreSpi;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
+import org.apache.isis.runtimes.dflt.runtime.system.persistence.AdapterManagerSpi;
+import org.apache.isis.runtimes.dflt.runtime.system.persistence.IdentifierGenerator;
+
+public class SqlPersistorInstaller extends PersistenceMechanismInstallerAbstract {
+
+    public static final String NAME = "sql";
+    
+    private SqlObjectStore objectStore;
+    private DatabaseConnectorPool connectionPool;
+
+    public SqlPersistorInstaller() {
+        super(NAME);
+    }
+
+    @Override
+    protected ObjectStoreSpi createObjectStore(final IsisConfiguration configuration, final ObjectAdapterFactory objectFactory, final AdapterManagerSpi adapterManager) {
+
+        if (objectStore == null) {
+            final FieldMappingLookup fieldMappingLookup = new FieldMappingLookup();
+            final JdbcFieldMappingFactoryInstaller installer = new JdbcFieldMappingFactoryInstaller();
+
+            Defaults.initialise(SqlObjectStore.BASE_NAME, IsisContext.getConfiguration());
+
+            installer.load(fieldMappingLookup);
+            // fieldMappingLookup.setValueMappingFactory(new
+            // JdbcFieldMappingFactoryInstaller());
+
+            final ObjectMappingLookup objectMappingLookup = new ObjectMappingLookup();
+            objectMappingLookup.setValueMappingLookup(fieldMappingLookup);
+            objectMappingLookup.setObjectMappingFactory(new AutoMapperFactory());
+            objectMappingLookup.setConnectionPool(connectionPool);
+
+            final SqlObjectStore objectStore = new SqlObjectStore();
+            objectStore.setMapperLookup(objectMappingLookup);
+            objectStore.setConnectionPool(connectionPool);
+            this.objectStore = objectStore;
+        }
+        return objectStore;
+    }
+
+    public SqlObjectStore getObjectStore() {
+        return objectStore;
+    }
+
+    @Override
+    public IdentifierGenerator createIdentifierGenerator(final IsisConfiguration configuration) {
+        final DatabaseConnectorFactory connectorFactory = new JdbcConnectorFactory();
+        connectionPool = new DatabaseConnectorPool(connectorFactory, 1);
+
+        return new SqlIdentifierGenerator(connectionPool);
+    }
+
+    /*
+     * 
+     * 
+     * @Override protected AdapterManagerExtended createAdapterManager(final
+     * IsisConfiguration configuration) { return new XmlAdapterManager(); }
+     */
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/StoredProcedure.java
----------------------------------------------------------------------
diff --git a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/StoredProcedure.java b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/StoredProcedure.java
new file mode 100644
index 0000000..c18c3f3
--- /dev/null
+++ b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/StoredProcedure.java
@@ -0,0 +1,26 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.runtimes.dflt.objectstores.sql;
+
+public interface StoredProcedure {
+
+    boolean getBoolean(int i);
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/TitleMapping.java
----------------------------------------------------------------------
diff --git a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/TitleMapping.java b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/TitleMapping.java
new file mode 100644
index 0000000..1ff9a7f
--- /dev/null
+++ b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/TitleMapping.java
@@ -0,0 +1,71 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.runtimes.dflt.objectstores.sql;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+
+public class TitleMapping {
+    private final String column = Sql.identifier("NO_title");
+
+    protected String getColumn() {
+        return column;
+    }
+
+    public void appendWhereClause(final StringBuffer sql, final String title) {
+        appendAssignment(sql, title);
+    }
+
+    private void appendAssignment(final StringBuffer sql, final String title) {
+        sql.append(column);
+        sql.append(" = ");
+        appendTitle(sql, title);
+    }
+
+    public void appendColumnDefinitions(final StringBuffer sql) {
+        sql.append(column);
+        sql.append(" ");
+        sql.append("varchar(200)");
+    }
+
+    public void appendColumnNames(final StringBuffer sql) {
+        sql.append(column);
+    }
+
+    public void appendInsertValues(final DatabaseConnector connector, final StringBuffer sql, final ObjectAdapter object) {
+        if (object == null) {
+            sql.append("NULL");
+        } else {
+            connector.addToQueryValues(object.titleString().toLowerCase());
+        }
+        sql.append("?");
+    }
+
+    private void appendTitle(final StringBuffer sql, final String title) {
+        final String titleString = title.toLowerCase();
+        sql.append(Sql.escapeAndQuoteValue(titleString));
+    }
+
+    public void appendUpdateAssignment(final DatabaseConnector connector, final StringBuffer sql, final ObjectAdapter object) {
+        sql.append(column);
+        sql.append(" = ");
+        appendInsertValues(connector, sql, object);
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/VersionMapping.java
----------------------------------------------------------------------
diff --git a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/VersionMapping.java b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/VersionMapping.java
new file mode 100644
index 0000000..b6d4ca2
--- /dev/null
+++ b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/VersionMapping.java
@@ -0,0 +1,106 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.runtimes.dflt.objectstores.sql;
+
+import java.sql.Timestamp;
+import java.util.Date;
+
+import org.apache.isis.core.metamodel.adapter.version.SerialNumberVersion;
+import org.apache.isis.core.metamodel.adapter.version.Version;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
+
+public class VersionMapping {
+    private String lastActivityDateColumn;
+    private String lastActivityUserColumn;
+    private String versionColumn;
+
+    public void init() {
+        lastActivityDateColumn = Sql.identifier("MODIFIED_ON");
+        lastActivityUserColumn = Sql.identifier("MODIFIED_BY");
+        versionColumn = Sql.identifier("VERSION");
+    }
+
+    public String insertColumns() {
+        return versionColumn + ", " + lastActivityUserColumn + ", " + lastActivityDateColumn;
+    }
+
+    public String insertValues(final DatabaseConnector connector, final Version version) {
+        connector.addToQueryValues(version.getSequence());
+        IsisContext.getSession().getAuthenticationSession().getUserName();
+        String user = IsisContext.getSession().getAuthenticationSession().getUserName();// version.getUser();
+        if (user == "") {
+            user = "unknown";
+        }
+        connector.addToQueryValues(user);
+        connector.addToQueryValues(new Timestamp(new Date().getTime()));
+        return "?,?,?";
+    }
+
+    public String whereClause(final DatabaseConnector connector, final Version version) {
+        connector.addToQueryValues(version.getSequence());
+        return versionColumn + " = ?";
+    }
+
+    public String updateAssigment(final DatabaseConnector connector, final long nextSequence) {
+        connector.addToQueryValues(nextSequence);
+        return versionColumn + " = ?";
+    }
+
+    public String appendColumnNames() {
+        final StringBuffer sql = new StringBuffer();
+        sql.append(versionColumn);
+        sql.append(",");
+        sql.append(lastActivityUserColumn);
+        sql.append(",");
+        sql.append(lastActivityDateColumn);
+        return sql.toString();
+    }
+
+    public String appendColumnDefinitions() {
+        final StringBuffer sql = new StringBuffer();
+
+        sql.append(versionColumn);
+        sql.append(" bigint");
+
+        sql.append(",");
+        sql.append(lastActivityUserColumn);
+        sql.append(" varchar(32)");
+
+        sql.append(",");
+        sql.append(lastActivityDateColumn);
+        sql.append(" " + Defaults.TYPE_TIMESTAMP());
+
+        return sql.toString();
+    }
+
+    public Object appendUpdateValues(final DatabaseConnector connector, final long versionSequence) {
+        connector.addToQueryValues(versionSequence);
+        return versionColumn + "= ?";
+    }
+
+    public Version getLock(final Results rs) {
+        final long number = rs.getLong(versionColumn);
+        final String user = rs.getString(lastActivityUserColumn);
+        final Date time = rs.getJavaDateTime(lastActivityDateColumn, Defaults.getCalendar());
+        final Version version = SerialNumberVersion.create(number, user, time);
+        return version;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AbstractAutoMapper.java
----------------------------------------------------------------------
diff --git a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AbstractAutoMapper.java b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AbstractAutoMapper.java
new file mode 100644
index 0000000..567626f
--- /dev/null
+++ b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AbstractAutoMapper.java
@@ -0,0 +1,365 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.runtimes.dflt.objectstores.sql.auto;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import com.google.common.collect.Maps;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.commons.exceptions.NotYetImplementedException;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
+import org.apache.isis.core.metamodel.adapter.oid.Oid;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.runtimes.dflt.objectstores.sql.AbstractMapper;
+import org.apache.isis.runtimes.dflt.objectstores.sql.CollectionMapper;
+import org.apache.isis.runtimes.dflt.objectstores.sql.DatabaseConnector;
+import org.apache.isis.runtimes.dflt.objectstores.sql.Defaults;
+import org.apache.isis.runtimes.dflt.objectstores.sql.FieldMappingLookup;
+import org.apache.isis.runtimes.dflt.objectstores.sql.ObjectMappingLookup;
+import org.apache.isis.runtimes.dflt.objectstores.sql.Sql;
+import org.apache.isis.runtimes.dflt.objectstores.sql.SqlObjectStoreException;
+import org.apache.isis.runtimes.dflt.objectstores.sql.mapping.FieldMapping;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
+import org.apache.isis.runtimes.dflt.runtime.system.persistence.AdapterManagerSpi;
+import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceSession;
+
+public abstract class AbstractAutoMapper extends AbstractMapper {
+	
+    private static final Logger LOG = Logger.getLogger(AbstractAutoMapper.class);
+
+    protected final Map<ObjectAssociation, FieldMapping> fieldMappingByField = Maps.newLinkedHashMap();
+    
+    protected CollectionMapper collectionMappers[];
+    protected String collectionMapperFields[];
+    protected boolean dbCreatesId;
+
+    protected ObjectSpecification specification;
+    protected String table;
+
+    final String className;
+    final String parameterBase;
+    final FieldMappingLookup lookup;
+    final ObjectMappingLookup objectMappingLookup;
+
+    protected AbstractAutoMapper(final String className, final String parameterBase, final FieldMappingLookup lookup, final ObjectMappingLookup objectMappingLookup) {
+    	this.specification = specificationFor(className);
+        this.className = className;
+        this.parameterBase = parameterBase;
+        this.lookup = lookup;
+        this.objectMappingLookup = objectMappingLookup;
+    }
+
+    protected AbstractAutoMapper(final FieldMappingLookup lookup, final AbstractAutoMapper abstractAutoMapper, final String className) {
+
+        this.specification = getSpecificationLoader().loadSpecification(className);
+        this.className = className;
+
+        this.parameterBase = null;
+        this.lookup = null;
+        this.objectMappingLookup = null;
+    }
+
+	private static ObjectSpecification specificationFor(final String className) {
+		ObjectSpecification specification = IsisContext.getSpecificationLoader().loadSpecification(className);
+        List<OneToOneAssociation> properties = specification.getProperties();
+		if (isNullOrEmpty(properties) && !specification.isAbstract()) {
+            throw new SqlObjectStoreException(specification.getFullIdentifier() + " has no fields: " + specification);
+        }
+		return specification;
+	}
+    
+
+    protected void setUpFieldMappers() {
+        setUpFieldMappers(lookup, objectMappingLookup, className, parameterBase);
+    }
+
+    private void setUpFieldMappers(final FieldMappingLookup lookup, final ObjectMappingLookup objectMappingLookup, final String className, final String parameterBase) {
+        final IsisConfiguration configParameters = getConfiguration();
+        table = configParameters.getString(parameterBase + ".table." + className);
+        if (table == null) {
+            final String name = getTableNameFromSpecification(specification);
+            table = name;
+        } else {
+            table = Sql.tableIdentifier(table);
+        }
+
+        dbCreatesId = configParameters.getBoolean(parameterBase + "db-ids", false);
+        if (configParameters.getBoolean(parameterBase + "all-fields", true)) {
+            setupFullMapping(lookup, objectMappingLookup, className, configParameters, parameterBase);
+        } else {
+            // setupSpecifiedMapping(specification, configParameters,
+            // parameterBase);
+        }
+
+        LOG.info("table mapping: " + table + " (" + columnList(fieldMappingByField) + ")");
+    }
+
+    protected String getTableNameFromSpecification(final ObjectSpecification objectSpecification) {
+        return Sql.tableIdentifier(Sql.sqlName(Defaults.getTablePrefix() + objectSpecification.getShortIdentifier()));
+    }
+
+    protected List<ObjectAssociation> fields = new ArrayList<ObjectAssociation>();
+
+    protected void getExtraFields(final List<ObjectAssociation> fields) {
+    }
+
+    private void setupFullMapping(final FieldMappingLookup lookup, final ObjectMappingLookup objectMapperLookup, final String className, final IsisConfiguration configParameters, final String parameterBase) {
+
+        fields.addAll(specification.getAssociations());
+
+        int simpleFieldCount = 0;
+        int collectionFieldCount = 0;
+        for (int i = 0; i < fields.size(); i++) {
+            if (fields.get(i).isNotPersisted()) {
+                continue;
+            } else if (fields.get(i).isOneToManyAssociation()) {
+                collectionFieldCount++;
+            } else {
+                simpleFieldCount++;
+            }
+        }
+
+        final ObjectAssociation[] oneToOneProperties = new ObjectAssociation[simpleFieldCount];
+        final ObjectAssociation[] oneToManyProperties = new ObjectAssociation[collectionFieldCount];
+        collectionMappers = new CollectionMapper[collectionFieldCount];
+        collectionMapperFields = new String[collectionFieldCount];
+        final IsisConfiguration subset = getConfiguration().createSubset(parameterBase + ".mapper.");
+
+        for (int i = 0, simpleFieldNo = 0, collectionFieldNo = 0; i < fields.size(); i++) {
+            final ObjectAssociation field = fields.get(i);
+            if (field.isNotPersisted()) {
+                continue;
+            } else if (field.isOneToManyAssociation()) {
+                oneToManyProperties[collectionFieldNo] = field;
+
+                // TODO: Replace "new ForeignKeyCollectionMapper" with a factory
+                // method(?) to allow a different
+                // default CollectionMapper
+
+                // TODO: I think the default order should be changed - and I
+                // think I (KAM) have dropped support for the
+                // original "association-table" implementation. This means the
+                // current checks are misleading.
+                final String type = subset.getString(field.getId());
+                if (type == null || type.equals("association-table")) {
+                    // collectionMappers[collectionFieldNo] = new
+                    // AutoCollectionMapper(specification,
+                    // oneToManyProperties[collectionFieldNo], lookup);
+                    // collectionMappers[collectionFieldNo] = new
+                    // ForeignKeyCollectionMapper(oneToManyProperties[collectionFieldNo],
+                    // parameterBase, lookup,
+                    // objectMapperLookup);
+
+                    CollectionMapper collectionMapper = null;
+
+                    // Trying to detect recursion, here.
+                    // Let ForeignKeyInChildCollectionMapper find itself when a
+                    // field is a collection of the current
+                    // field type.
+                    if (this instanceof ForeignKeyInChildCollectionMapper) {
+                        final ForeignKeyInChildCollectionMapper mc = (ForeignKeyInChildCollectionMapper) this;
+
+                        if (mc.priorField == field) {
+                            collectionMapper = mc;
+                        }
+                    }
+
+                    if (collectionMapper == null) {
+                        // TODO: Polymorphism - is it sufficient for the
+                        // collectionMapper to handle the subclasses?
+                        final ObjectSpecification fieldSpecification = field.getSpecification();
+                        if (fieldSpecification.hasSubclasses() || fieldSpecification.isAbstract()) {
+                            // PolymorphicForeignKeyInChildCollectionBaseMapper
+                            // Or PolymorphicForeignKeyInChildCollectionMapper
+                            collectionMapper = new PolymorphicForeignKeyInChildCollectionBaseMapper(oneToManyProperties[collectionFieldNo], parameterBase, lookup, objectMapperLookup, this, field);
+                        } else {
+                            final ForeignKeyInChildCollectionMapper mapper = new ForeignKeyInChildCollectionMapper(oneToManyProperties[collectionFieldNo], parameterBase, lookup, objectMapperLookup, this, field);
+                            mapper.setUpFieldMappers();
+                            collectionMapper = mapper;
+                        }
+                    }
+
+                    collectionMappers[collectionFieldNo] = collectionMapper;
+                    collectionMapperFields[collectionFieldNo] = field.getId();
+
+                } else if (type.equals("fk-table")) {
+                    final String property = parameterBase + field.getId() + ".element-type";
+                    final String elementType = configParameters.getString(property);
+                    if (elementType == null) {
+                        throw new SqlObjectStoreException("Expected property " + property);
+                    }
+                    /*
+                     * collectionMappers[collectionFieldNo] = new
+                     * ForeignKeyCollectionMapper(elementType,
+                     * oneToManyProperties[collectionFieldNo], parameterBase,
+                     * lookup, objectMapperLookup);
+                     */
+                } else {
+                    // TODO use other mappers where necessary
+                    throw new NotYetImplementedException("for " + type);
+                }
+
+                collectionFieldNo++;
+            } else if (field.isOneToOneAssociation()) {
+                oneToOneProperties[simpleFieldNo] = field;
+                simpleFieldNo++;
+            } else {
+                oneToOneProperties[simpleFieldNo] = field;
+                simpleFieldNo++;
+            }
+        }
+
+        
+        for (final ObjectAssociation field : oneToOneProperties) {
+        	if(fieldMappingByField.containsKey(field)) {
+        		continue;
+        	}
+        	final FieldMapping mapping = lookup.createMapping(specification, field);
+            fieldMappingByField.put(field, mapping);
+        }
+    }
+
+    protected String columnList(final Map<ObjectAssociation, FieldMapping> fieldMappingByField) {
+    	return columnList(fieldMappingByField.values());
+    }
+
+    /*
+     * private void setupSpecifiedMapping( final ObjectSpecification
+     * specification, final IsisConfiguration configParameters, final String
+     * parameterBase) { IsisConfiguration columnMappings =
+     * IsisContext.getConfiguration().createSubset(parameterBase + "column");
+     * int columnsSize = columnMappings.size(); // columnNames = new
+     * String[columnsSize]; oneToOneProperties = new
+     * ObjectAssociation[columnsSize];
+     * 
+     * int i = 0; for (Enumeration names = columnMappings.propertyNames();
+     * names.hasMoreElements(); i++) { String columnName = (String)
+     * names.nextElement(); String fieldName =
+     * columnMappings.getString(columnName); oneToOneProperties[i] =
+     * specification.getAssociation(fieldName); // columnNames[i] = columnName;
+     * }
+     * 
+     * IsisConfiguration collectionMappings =
+     * IsisContext.getConfiguration().createSubset( parameterBase +
+     * "collection"); int collectionsSize = collectionMappings.size();
+     * collectionMappers = new AutoCollectionMapper[collectionsSize];
+     * oneToManyProperties = new ObjectAssociation[collectionsSize];
+     * 
+     * int j = 0; for (Enumeration names = collectionMappings.propertyNames();
+     * names.hasMoreElements(); j++) { String propertyName = (String)
+     * names.nextElement(); String collectionName =
+     * collectionMappings.getString(propertyName); String type =
+     * collectionMappings.getString(collectionName);
+     * 
+     * oneToManyProperties[j] = specification.getAssociation(collectionName); if
+     * (type.equals("auto")) { collectionMappers[j] = new
+     * AutoCollectionMapper(this, specification, oneToManyProperties[j],
+     * getLookup()); } else { // TODO use other mappers where necessary // new
+     * ReversedAutoAssociationMapper(specification, collectionName,
+     * parameterBase);
+     * 
+     * throw new NotYetImplementedException(); } } }
+     */
+    protected String columnList(final Collection<FieldMapping> fieldMappings) {
+        final StringBuffer sql = new StringBuffer();
+        for (final FieldMapping mapping : fieldMappings) {
+            if (sql.length() > 0) {
+                sql.append(",");
+            }
+            mapping.appendColumnNames(sql);
+        }
+        return sql.toString();
+    }
+
+    protected ObjectAdapter getAdapter(final ObjectSpecification spec, final Oid oid) {
+        final ObjectAdapter adapter = getAdapterManager().getAdapterFor(oid);
+        if (adapter != null) {
+            return adapter;
+        }
+        
+        // REVIEW: where the oid is a TypedOid, the following two lines could be replaced by getPersistenceSession().recreatePersistentAdapter(oid)
+        // is preferable, since then reuses the PojoRecreator impl defined within SqlPersistorInstaller
+        final Object recreatedPojo = spec.createObject();
+        return getPersistenceSession().mapRecreatedPojo(oid, recreatedPojo);
+    }
+
+    protected FieldMapping fieldMappingFor(final ObjectAssociation field) {
+        return fieldMappingByField.get(field);
+    }
+
+    @Override
+    public boolean needsTables(final DatabaseConnector connection) {
+        for (int i = 0; collectionMappers != null && i < collectionMappers.length; i++) {
+            if (collectionMappers[i].needsTables(connection)) {
+                return true;
+            }
+        }
+        return !connection.hasTable(table);
+    }
+
+    protected String values(final DatabaseConnector connector, final ObjectAdapter object) {
+        final StringBuffer sql = new StringBuffer();
+        for (final FieldMapping mapping : fieldMappingByField.values()) {
+            mapping.appendInsertValues(connector, sql, object);
+            sql.append(",");
+        }
+        return sql.toString();
+    }
+
+    
+    private static boolean isNullOrEmpty(List<?> list) {
+    	return list == null || list.size() == 0;
+    }
+
+
+
+    @Override
+    public String toString() {
+        return "AbstractAutoMapper [table=" + table + ",noColumns=" + fieldMappingByField.size() + ",specification=" + specification.getFullIdentifier() + "]";
+    }
+
+	protected SpecificationLoaderSpi getSpecificationLoader() {
+		return IsisContext.getSpecificationLoader();
+	}
+
+	protected IsisConfiguration getConfiguration() {
+		return IsisContext.getConfiguration();
+	}
+
+    protected PersistenceSession getPersistenceSession() {
+        return IsisContext.getPersistenceSession();
+    }
+
+    protected AdapterManager getAdapterManager() {
+        return getPersistenceSession().getAdapterManager();
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AutoCollectionMapper.java
----------------------------------------------------------------------
diff --git a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AutoCollectionMapper.java b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AutoCollectionMapper.java
new file mode 100644
index 0000000..a8f7df9
--- /dev/null
+++ b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AutoCollectionMapper.java
@@ -0,0 +1,171 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.runtimes.dflt.objectstores.sql.auto;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.commons.debug.DebugBuilder;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.ResolveState;
+import org.apache.isis.core.metamodel.adapter.oid.Oid;
+import org.apache.isis.core.metamodel.adapter.oid.RootOid;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
+import org.apache.isis.core.metamodel.facets.typeof.TypeOfFacet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.runtimes.dflt.objectstores.sql.AbstractMapper;
+import org.apache.isis.runtimes.dflt.objectstores.sql.CollectionMapper;
+import org.apache.isis.runtimes.dflt.objectstores.sql.DatabaseConnector;
+import org.apache.isis.runtimes.dflt.objectstores.sql.FieldMappingLookup;
+import org.apache.isis.runtimes.dflt.objectstores.sql.IdMapping;
+import org.apache.isis.runtimes.dflt.objectstores.sql.Results;
+import org.apache.isis.runtimes.dflt.objectstores.sql.Sql;
+import org.apache.isis.runtimes.dflt.objectstores.sql.jdbc.JdbcObjectReferenceMapping;
+import org.apache.isis.runtimes.dflt.objectstores.sql.mapping.ObjectReferenceMapping;
+import org.apache.isis.runtimes.dflt.runtime.persistence.PersistorUtil;
+
+public class AutoCollectionMapper extends AbstractMapper implements CollectionMapper {
+    private static final Logger LOG = Logger.getLogger(AutoCollectionMapper.class);
+    private String tableName;
+    private final ObjectAssociation field;
+    private final ObjectReferenceMapping elementMapping;
+    private final IdMapping idMapping;
+
+    public AutoCollectionMapper(final ObjectSpecification specification, final ObjectAssociation field, final FieldMappingLookup lookup) {
+        this.field = field;
+
+        final ObjectSpecification spec = field.getFacet(TypeOfFacet.class).valueSpec();
+        idMapping = lookup.createIdMapping();
+        elementMapping = lookup.createMapping(spec);
+
+        final String className = specification.getShortIdentifier();
+        final String columnName = field.getId();
+        tableName = Sql.sqlName(className) + "_" + asSqlName(columnName);
+        tableName = Sql.identifier(tableName);
+    }
+
+    @Override
+    public void createTables(final DatabaseConnector connector) {
+        if (!connector.hasTable(tableName)) {
+            final StringBuffer sql = new StringBuffer();
+            sql.append("create table ");
+            sql.append(tableName);
+            sql.append(" (");
+
+            idMapping.appendColumnDefinitions(sql);
+            sql.append(", ");
+            elementMapping.appendColumnDefinitions(sql);
+
+            sql.append(")");
+
+            connector.update(sql.toString());
+        }
+    }
+
+    @Override
+    public void loadInternalCollection(final DatabaseConnector connector, final ObjectAdapter parentAdapter) {
+        final ObjectAdapter collectionAdapter = field.get(parentAdapter);
+        if (!collectionAdapter.canTransitionToResolving()) {
+            return;
+        } 
+        
+        if(LOG.isDebugEnabled()) {
+            LOG.debug("loading internal collection " + field);
+        }
+        
+        try {
+            PersistorUtil.startResolving(collectionAdapter);
+            
+            final StringBuffer sql = new StringBuffer();
+            sql.append("select ");
+            idMapping.appendColumnNames(sql);
+            sql.append(", ");
+            elementMapping.appendColumnNames(sql);
+            sql.append(" from ");
+            sql.append(tableName);
+            
+            final Results rs = connector.select(sql.toString());
+            final List<ObjectAdapter> list = new ArrayList<ObjectAdapter>();
+            while (rs.next()) {
+                final ObjectAdapter element = ((JdbcObjectReferenceMapping) elementMapping).initializeField(rs);
+                if(LOG.isDebugEnabled()) {
+                    LOG.debug("  element  " + element.getOid());
+                }
+                list.add(element);
+            }
+            final CollectionFacet collectionFacet = collectionAdapter.getSpecification().getFacet(CollectionFacet.class);
+            collectionFacet.init(collectionAdapter, list.toArray(new ObjectAdapter[list.size()]));
+            rs.close();
+        } finally {
+            PersistorUtil.toEndState(collectionAdapter);
+        }
+    }
+
+    @Override
+    public boolean needsTables(final DatabaseConnector connector) {
+        return !connector.hasTable(tableName);
+    }
+
+    @Override
+    public void saveInternalCollection(final DatabaseConnector connector, final ObjectAdapter parent) {
+        final ObjectAdapter collection = field.get(parent);
+        LOG.debug("saving internal collection " + collection);
+
+        StringBuffer sql = new StringBuffer();
+        sql.append("delete from ");
+        sql.append(tableName);
+        sql.append(" where ");
+        final RootOid oid = (RootOid) parent.getOid();
+        idMapping.appendWhereClause(connector, sql, oid);
+        connector.update(sql.toString());
+
+        sql = new StringBuffer();
+        sql.append("insert into ");
+        sql.append(tableName);
+        sql.append(" (");
+        idMapping.appendColumnNames(sql);
+        sql.append(", ");
+        elementMapping.appendColumnNames(sql);
+        sql.append(" ) values (");
+        idMapping.appendInsertValues(connector, sql, parent);
+        sql.append(", ");
+
+        final CollectionFacet collectionFacet = collection.getSpecification().getFacet(CollectionFacet.class);
+        for (final ObjectAdapter element : collectionFacet.iterable(collection)) {
+            final StringBuffer values = new StringBuffer();
+            elementMapping.appendInsertValues(connector, values, element);
+            connector.update(sql.toString() + values + ")");
+        }
+    }
+
+    @Override
+    public void debugData(final DebugBuilder debug) {
+        debug.appendln(field.getName(), "collection");
+        debug.indent();
+        debug.appendln("Table", tableName);
+        debug.appendln("ID mapping", idMapping);
+        debug.appendln("Element mapping", elementMapping);
+        debug.unindent();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AutoMapper.java
----------------------------------------------------------------------
diff --git a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AutoMapper.java b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AutoMapper.java
new file mode 100644
index 0000000..9c8dda6
--- /dev/null
+++ b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AutoMapper.java
@@ -0,0 +1,510 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.runtimes.dflt.objectstores.sql.auto;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Vector;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.applib.Identifier;
+import org.apache.isis.core.commons.debug.DebugBuilder;
+import org.apache.isis.core.commons.debug.DebuggableWithTitle;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.oid.Oid;
+import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller;
+import org.apache.isis.core.metamodel.adapter.oid.RootOid;
+import org.apache.isis.core.metamodel.adapter.oid.TypedOid;
+import org.apache.isis.core.metamodel.adapter.util.InvokeUtils;
+import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
+import org.apache.isis.core.metamodel.adapter.version.Version;
+import org.apache.isis.core.metamodel.facets.notpersisted.NotPersistedFacet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.ObjectSpecificationException;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.runtimes.dflt.objectstores.sql.CollectionMapper;
+import org.apache.isis.runtimes.dflt.objectstores.sql.DatabaseConnector;
+import org.apache.isis.runtimes.dflt.objectstores.sql.Defaults;
+import org.apache.isis.runtimes.dflt.objectstores.sql.FieldMappingLookup;
+import org.apache.isis.runtimes.dflt.objectstores.sql.IdMapping;
+import org.apache.isis.runtimes.dflt.objectstores.sql.ObjectMapping;
+import org.apache.isis.runtimes.dflt.objectstores.sql.ObjectMappingLookup;
+import org.apache.isis.runtimes.dflt.objectstores.sql.Results;
+import org.apache.isis.runtimes.dflt.objectstores.sql.Sql;
+import org.apache.isis.runtimes.dflt.objectstores.sql.SqlObjectStoreException;
+import org.apache.isis.runtimes.dflt.objectstores.sql.TitleMapping;
+import org.apache.isis.runtimes.dflt.objectstores.sql.VersionMapping;
+import org.apache.isis.runtimes.dflt.objectstores.sql.mapping.FieldMapping;
+import org.apache.isis.runtimes.dflt.runtime.persistence.ObjectNotFoundException;
+import org.apache.isis.runtimes.dflt.runtime.persistence.PersistorUtil;
+import org.apache.isis.runtimes.dflt.runtime.persistence.query.PersistenceQueryFindByPattern;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
+
+public class AutoMapper extends AbstractAutoMapper implements ObjectMapping, DebuggableWithTitle {
+
+    private static final Logger LOG = Logger.getLogger(AutoMapper.class);
+    private final IdMapping idMapping;
+    private final VersionMapping versionMapping;
+    private final TitleMapping titleMapping;
+    private final boolean useVersioning;
+
+    public AutoMapper(final String className, final String parameterBase, final FieldMappingLookup lookup,
+        final ObjectMappingLookup objectMapperLookup) {
+        super(className, parameterBase, lookup, objectMapperLookup);
+        idMapping = lookup.createIdMapping();
+        versionMapping = lookup.createVersionMapping();
+        titleMapping = lookup.createTitleMapping();
+
+        useVersioning = Defaults.useVersioning(specification.getShortIdentifier());
+
+        setUpFieldMappers();
+    }
+
+    protected VersionMapping getVersionMapping() {
+        return versionMapping;
+    }
+
+    protected IdMapping getIdMapping() {
+        return idMapping;
+    }
+
+    @Override
+    public void createTables(final DatabaseConnector connection) {
+        if (!connection.hasTable(table)) {
+            final StringBuffer sql = new StringBuffer();
+            sql.append("create table ");
+            sql.append(table);
+            sql.append(" (");
+            idMapping.appendCreateColumnDefinitions(sql);
+            sql.append(", ");
+            for (final FieldMapping mapping : fieldMappingByField.values()) {
+                mapping.appendColumnDefinitions(sql);
+                sql.append(",");
+            }
+            titleMapping.appendColumnDefinitions(sql);
+            sql.append(", ");
+            sql.append(versionMapping.appendColumnDefinitions());
+            sql.append(")");
+            connection.update(sql.toString());
+        }
+        for (int i = 0; collectionMappers != null && i < collectionMappers.length; i++) {
+            if (collectionMappers[i].needsTables(connection)) {
+                collectionMappers[i].createTables(connection);
+            }
+        }
+    }
+
+    @Override
+    public void createObject(final DatabaseConnector connector, final ObjectAdapter object) {
+        final int versionSequence = 1;
+        final Version version = createVersion(versionSequence);
+
+        final StringBuffer sql = new StringBuffer();
+        sql.append("insert into " + table + " (");
+        idMapping.appendColumnNames(sql);
+        sql.append(", ");
+        final String columnList = columnList(fieldMappingByField);
+        if (columnList.length() > 0) {
+            sql.append(columnList);
+            sql.append(", ");
+        }
+        titleMapping.appendColumnNames(sql);
+        sql.append(", ");
+        sql.append(versionMapping.insertColumns());
+        sql.append(") values (");
+        idMapping.appendInsertValues(connector, sql, object);
+        sql.append(", ");
+        sql.append(values(connector, object));
+        titleMapping.appendInsertValues(connector, sql, object);
+        sql.append(", ");
+        sql.append(versionMapping.insertValues(connector, version));
+        sql.append(") ");
+
+        connector.insert(sql.toString());
+        object.setVersion(version);
+
+        for (final CollectionMapper collectionMapper : collectionMappers) {
+            collectionMapper.saveInternalCollection(connector, object);
+        }
+    }
+
+    @Override
+    public void destroyObject(final DatabaseConnector connector, final ObjectAdapter adapter) {
+        final StringBuffer sql = new StringBuffer();
+        sql.append("delete from " + table + " WHERE ");
+        final RootOid oid = (RootOid) adapter.getOid();
+        idMapping.appendWhereClause(connector, sql, oid);
+        sql.append(" AND ");
+        sql.append(versionMapping.whereClause(connector, adapter.getVersion()));
+        final int updateCount = connector.update(sql.toString());
+        if (updateCount == 0) {
+            LOG.info("concurrency conflict object " + this + "; no deletion performed");
+            throw new ConcurrencyException("", adapter.getOid());
+        }
+    }
+
+    @Override
+    public Vector<ObjectAdapter> getInstances(final DatabaseConnector connector, final ObjectSpecification spec) {
+        final StringBuffer sql = createSelectStatement();
+        final Vector<ObjectAdapter> instances = new Vector<ObjectAdapter>();
+        loadInstancesToVector(connector, spec, completeSelectStatement(sql), instances);
+        return instances;
+    }
+
+    @Override
+    public Vector<ObjectAdapter> getInstances(final DatabaseConnector connector, final ObjectSpecification spec,
+        final PersistenceQueryFindByPattern query) {
+        final Vector<ObjectAdapter> instances = new Vector<ObjectAdapter>();
+
+        final StringBuffer sql = createSelectStatement();
+        int initialLength = 0;
+
+        int foundFields = 0;
+        final ObjectAdapter pattern = query.getPattern();
+
+        // for all fields in the query.getPattern, build a SQL select clause for
+        // this spec.
+        final Object o = pattern.getObject();
+        final ObjectSpecification patternSpec = pattern.getSpecification();
+        final List<ObjectAssociation> patternAssociations = patternSpec.getAssociations();
+        for (final ObjectAssociation patternAssoc : patternAssociations) {
+            final Method method;
+            final Identifier identifier = patternAssoc.getIdentifier();
+            final String memberName = identifier.getMemberName();
+            final String methodName = memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
+
+            try {
+                if (true) {
+                    final ObjectAdapter field = patternAssoc.get(pattern);
+                    if (field != null) {
+                        final String id = patternAssoc.getId();
+                        try {
+                            final ObjectAssociation oa = spec.getAssociation(id);
+                            final NotPersistedFacet fc = oa.getFacet(NotPersistedFacet.class);
+                            if (fc != null) {
+                                continue;
+                            }
+                        } catch (final ObjectSpecificationException e) {
+                            // this is OK
+                        }
+
+                        if (foundFields == 0) {
+                            sql.append(" WHERE ");
+                            initialLength = sql.length();
+                        }
+
+                        if (sql.length() > initialLength) {
+                            sql.append(" AND ");
+                        }
+
+                        final FieldMapping fieldMapping = fieldMappingFor(patternAssoc);
+                        if (fieldMapping != null) {
+                            fieldMapping.appendWhereClause(connector, sql, pattern);
+                        } else {
+                            // Have to use getXXX method if the fieldMapping is
+                            // null..
+                            final ObjectSpecification specification = patternAssoc.getSpecification();
+
+                            method = o.getClass().getMethod("get" + methodName, (Class<?>[]) null);
+                            final Object res = InvokeUtils.invoke(method, o);
+
+                            if (specification.isValue()) {
+                                // If the property (memberName) is a value type,
+                                // use the value.
+                                final String fieldName = Sql.sqlFieldName(identifier.getMemberName());
+                                sql.append(fieldName + "=?");
+                                connector.addToQueryValues(res);
+                            } else {
+                                throw new SqlObjectStoreException("Unhandled combination!");
+                            }
+                        }
+                        foundFields++;
+                    }
+                }
+            } catch (final SecurityException e) {
+                LOG.debug(e.getMessage());
+            } catch (final NoSuchMethodException e) {
+                LOG.info("Unable to invode method: get" + methodName + " in getInstances");
+                LOG.debug(e.getMessage());
+            }
+        }
+        // if (foundFields > 0) {
+        loadInstancesToVector(connector, spec, completeSelectStatement(sql), instances);
+        // }
+        return instances;
+    }
+
+    @Override
+    public Vector<ObjectAdapter> getInstances(final DatabaseConnector connector, final ObjectSpecification spec,
+        final String title) {
+        final Vector<ObjectAdapter> instances = new Vector<ObjectAdapter>();
+
+        final StringBuffer sql = createSelectStatement();
+        sql.append(" WHERE ");
+        titleMapping.appendWhereClause(sql, title);
+        loadInstancesToVector(connector, spec, completeSelectStatement(sql), instances);
+        return instances;
+    }
+
+    @Override
+    public ObjectAdapter getObject(final DatabaseConnector connector, final TypedOid typedOid) {
+        final StringBuffer sql = createSelectStatement();
+        sql.append(" WHERE ");
+        idMapping.appendWhereClause(connector, sql, (RootOid) typedOid);
+        final Results rs = connector.select(completeSelectStatement(sql));
+        final ObjectSpecification objectSpec = getSpecificationLoader().lookupBySpecId(typedOid.getObjectSpecId());
+        if (rs.next()) {
+            return loadMappedObject(connector, objectSpec, rs);
+        } else {
+            throw new ObjectNotFoundException("No object with with " + typedOid + " in table " + table);
+        }
+    }
+
+    @Override
+    public boolean hasInstances(final DatabaseConnector connector, final ObjectSpecification cls) {
+        final String statement = "select count(*) from " + table;
+        final int instances = connector.count(statement);
+        return instances > 0;
+    }
+
+    private StringBuffer createSelectStatement() {
+        final StringBuffer sql = new StringBuffer();
+        sql.append("select ");
+        idMapping.appendColumnNames(sql);
+        sql.append(", ");
+        final String columnList = columnList(fieldMappingByField);
+        if (columnList.length() > 0) {
+            sql.append(columnList);
+            sql.append(", ");
+        }
+        sql.append(versionMapping.insertColumns());
+        sql.append(" from " + table);
+        return sql;
+    } /*
+       * if (whereClause != null) { sql.append(" WHERE "); sql.append(whereClause); } else if (whereClause != null) {
+       * sql.append(" WHERE "); idMapping.appendWhereClause(sql, oid); }
+       */
+
+    private String completeSelectStatement(final StringBuffer sql) {
+        sql.append(" order by ");
+        idMapping.appendColumnNames(sql);
+        return sql.toString();
+    }
+
+    protected void loadFields(final ObjectAdapter adapter, final Results rs) {
+        PersistorUtil.startResolving(adapter);
+        try {
+            for (final FieldMapping mapping : fieldMappingByField.values()) {
+                mapping.initializeField(adapter, rs);
+            }
+            /*
+             * for (int i = 0; i < oneToManyProperties.length; i++) { /* Need to set up collection to be a ghost before
+             * we access as below
+             */
+            // CollectionAdapter collection = (CollectionAdapter)
+            /*
+             * oneToManyProperties[i].get(object); }
+             */
+            adapter.setVersion(versionMapping.getLock(rs));
+        } finally {
+            PersistorUtil.toEndState(adapter);
+        }
+    }
+
+    // KAM
+    private void loadCollections(final DatabaseConnector connector, final ObjectAdapter instance) {
+
+        for (final CollectionMapper mapper : collectionMappers) {
+            mapper.loadInternalCollection(connector, instance);
+        }
+    }
+
+    private void loadInstancesToVector(final DatabaseConnector connector, final ObjectSpecification cls,
+        final String selectStatment, final Vector<ObjectAdapter> instances) {
+        LOG.debug("loading instances from SQL " + table);
+
+        try {
+            final Results rs = connector.select(selectStatment);
+            final int maxInstances = Defaults.getMaxInstances();
+            for (int count = 0; rs.next() && count < maxInstances; count++) {
+                final ObjectAdapter instance = loadMappedObject(connector, cls, rs);
+                LOG.debug("  instance  " + instance);
+                instances.addElement(instance);
+            }
+            rs.close();
+        } catch (final SqlObjectStoreException e) {
+            // Invalid SELECT means no object found.. don't worry about it,
+            // here.
+        }
+    }
+
+    private ObjectAdapter loadMappedObject(final DatabaseConnector connector, final ObjectSpecification cls,
+        final Results rs) {
+        final Oid oid = idMapping.recreateOid(rs, specification);
+        final ObjectAdapter adapter = getAdapter(cls, oid);
+
+        if (adapter.canTransitionToResolving()) {
+            loadFields(adapter, rs);
+            loadCollections(connector, adapter); // KAM
+        }
+        return adapter;
+    }
+
+    @Override
+    public void resolve(final DatabaseConnector connector, final ObjectAdapter object) {
+        LOG.debug("loading data from SQL " + table + " for " + object);
+        final StringBuffer sql = new StringBuffer();
+        sql.append("select ");
+        sql.append(columnList(fieldMappingByField));
+        sql.append(",");
+        sql.append(versionMapping.appendColumnNames());
+        sql.append(" from " + table + " WHERE ");
+        final RootOid oid = (RootOid) object.getOid();
+        idMapping.appendWhereClause(connector, sql, oid);
+
+        final Results rs = connector.select(sql.toString());
+        if (rs.next()) {
+            loadFields(object, rs);
+            rs.close();
+
+            for (final CollectionMapper collectionMapper : collectionMappers) {
+                collectionMapper.loadInternalCollection(connector, object);
+            }
+        } else {
+            rs.close();
+            throw new SqlObjectStoreException("Unable to load data from " + table + " with id "
+                + object.getOid().enString(getOidMarshaller()));
+        }
+    }
+
+    @Override
+    public void resolveCollection(final DatabaseConnector connector, final ObjectAdapter object,
+        final ObjectAssociation field) {
+        if (collectionMappers.length > 0) {
+            final DatabaseConnector secondConnector = connector.getConnectionPool().acquire();
+            for (final CollectionMapper collectionMapper : collectionMappers) {
+                collectionMapper.loadInternalCollection(secondConnector, object);
+            }
+            connector.getConnectionPool().release(secondConnector);
+        }
+    }
+
+    @Override
+    public void startup(final DatabaseConnector connector, final ObjectMappingLookup objectMapperLookup) {
+        if (needsTables(connector)) {
+            createTables(connector);
+        }
+    }
+
+    @Override
+    public void save(final DatabaseConnector connector, final ObjectAdapter adapter) {
+        final Version version = adapter.getVersion();
+        final long nextSequence;
+        if (useVersioning) {
+            nextSequence = version.getSequence() + 1;
+        } else {
+            nextSequence = version.getSequence();
+        }
+
+        final StringBuffer sql = new StringBuffer();
+        sql.append("UPDATE " + table + " SET ");
+        for (final FieldMapping mapping : fieldMappingByField.values()) {
+            mapping.appendUpdateValues(connector, sql, adapter);
+            sql.append(", ");
+        }
+        sql.append(versionMapping.updateAssigment(connector, nextSequence));
+        sql.append(", ");
+        titleMapping.appendUpdateAssignment(connector, sql, adapter);
+        sql.append(" WHERE ");
+        final RootOid oid = (RootOid) adapter.getOid();
+        idMapping.appendWhereClause(connector, sql, oid);
+        if (useVersioning) {
+            sql.append(" AND ");
+            sql.append(versionMapping.whereClause(connector, adapter.getVersion()));
+        }
+
+        final int updateCount = connector.update(sql.toString());
+        if (updateCount == 0) {
+            LOG.info("concurrency conflict object " + this + "; no update performed");
+            throw new ConcurrencyException("", adapter.getOid());
+        } else {
+            adapter.setVersion(createVersion(nextSequence));
+        }
+
+        // TODO update collections - change only when needed rather than
+        // reinserting from scratch
+        for (final CollectionMapper collectionMapper : collectionMappers) {
+            collectionMapper.saveInternalCollection(connector, adapter);
+        }
+    }
+
+    @Override
+    public boolean saveCollection(final DatabaseConnector connection, final ObjectAdapter parent, final String fieldName) {
+        int i = 0;
+        for (final String collectionFieldName : collectionMapperFields) {
+            if (collectionFieldName.equals(fieldName)) {
+                final CollectionMapper fieldMapper = collectionMappers[i];
+                fieldMapper.saveInternalCollection(connection, parent);
+                return true;
+            }
+            i++;
+        }
+        return false;
+    }
+
+    // //////////////////////////////////////////////////////////////
+    // debugging, toString
+    // //////////////////////////////////////////////////////////////
+
+    @Override
+    public void debugData(final DebugBuilder debug) {
+        debug.appendln("ID mapping", idMapping);
+        debug.appendln("ID mapping", versionMapping);
+        debug.appendln("ID mapping", titleMapping);
+        for (final FieldMapping mapping : fieldMappingByField.values()) {
+            mapping.debugData(debug);
+        }
+        for (final CollectionMapper collectionMapper : collectionMappers) {
+            collectionMapper.debugData(debug);
+        }
+
+    }
+
+    @Override
+    public String debugTitle() {
+        return toString();
+    }
+
+    @Override
+    public String toString() {
+        return "AutoMapper [table=" + table + ",id=" + idMapping + ",noColumns=" + fieldMappingByField.size()
+            + ",specification=" + specification.getFullIdentifier() + "]";
+    }
+
+    // //////////////////////////////////////////////////////////////
+    // dependencies (from context)
+    // //////////////////////////////////////////////////////////////
+
+    protected OidMarshaller getOidMarshaller() {
+        return IsisContext.getOidMarshaller();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AutoMapperFactory.java
----------------------------------------------------------------------
diff --git a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AutoMapperFactory.java b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AutoMapperFactory.java
new file mode 100644
index 0000000..2f506b4
--- /dev/null
+++ b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AutoMapperFactory.java
@@ -0,0 +1,32 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.runtimes.dflt.objectstores.sql.auto;
+
+import org.apache.isis.runtimes.dflt.objectstores.sql.FieldMappingLookup;
+import org.apache.isis.runtimes.dflt.objectstores.sql.ObjectMapping;
+import org.apache.isis.runtimes.dflt.objectstores.sql.ObjectMappingFactory;
+import org.apache.isis.runtimes.dflt.objectstores.sql.ObjectMappingLookup;
+
+public class AutoMapperFactory implements ObjectMappingFactory {
+    @Override
+    public ObjectMapping createMapper(final String className, final String propertiesBase, final FieldMappingLookup lookup, final ObjectMappingLookup objectMapperLookup) {
+        return new AutoMapper(className, propertiesBase, lookup, objectMapperLookup);
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyCollectionMapper.java
----------------------------------------------------------------------
diff --git a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyCollectionMapper.java b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyCollectionMapper.java
new file mode 100644
index 0000000..6561038
--- /dev/null
+++ b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyCollectionMapper.java
@@ -0,0 +1,379 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.runtimes.dflt.objectstores.sql.auto;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.commons.debug.DebugBuilder;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.oid.Oid;
+import org.apache.isis.core.metamodel.adapter.oid.RootOid;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.runtimes.dflt.objectstores.sql.CollectionMapper;
+import org.apache.isis.runtimes.dflt.objectstores.sql.DatabaseConnector;
+import org.apache.isis.runtimes.dflt.objectstores.sql.FieldMappingLookup;
+import org.apache.isis.runtimes.dflt.objectstores.sql.IdMapping;
+import org.apache.isis.runtimes.dflt.objectstores.sql.IdMappingAbstract;
+import org.apache.isis.runtimes.dflt.objectstores.sql.ObjectMapping;
+import org.apache.isis.runtimes.dflt.objectstores.sql.ObjectMappingLookup;
+import org.apache.isis.runtimes.dflt.objectstores.sql.Results;
+import org.apache.isis.runtimes.dflt.objectstores.sql.Sql;
+import org.apache.isis.runtimes.dflt.objectstores.sql.VersionMapping;
+import org.apache.isis.runtimes.dflt.objectstores.sql.mapping.FieldMapping;
+import org.apache.isis.runtimes.dflt.objectstores.sql.mapping.ObjectReferenceMapping;
+import org.apache.isis.runtimes.dflt.runtime.persistence.PersistorUtil;
+
+/**
+ * Stores 1-to-many collections by creating a foreign-key column in the table
+ * for the incoming objectAssociation class. This assumes this the class is only
+ * ever in 1 collection parent.
+ * 
+ * @version $Rev$ $Date$
+ */
+public class ForeignKeyCollectionMapper extends AbstractAutoMapper implements CollectionMapper {
+    private static final Logger LOG = Logger.getLogger(ForeignKeyCollectionMapper.class);
+    private final ObjectAssociation field;
+    private final IdMapping idMapping;
+    private final VersionMapping versionMapping;
+    private final ObjectReferenceMapping foreignKeyMapping;
+    private String foreignKeyName;
+    private String columnName;
+    private final ObjectMappingLookup objectMapperLookup2;
+
+    private ObjectMapping originalMapping = null;
+
+    public ForeignKeyCollectionMapper(final ObjectAssociation objectAssociation, final String parameterBase, final FieldMappingLookup lookup, final ObjectMappingLookup objectMapperLookup) {
+        super(objectAssociation.getSpecification().getFullIdentifier(), parameterBase, lookup, objectMapperLookup);
+
+        this.field = objectAssociation;
+
+        objectMapperLookup2 = objectMapperLookup;
+
+        idMapping = lookup.createIdMapping();
+        versionMapping = lookup.createVersionMapping();
+
+        setColumnName(determineColumnName(objectAssociation));
+        foreignKeyName = Sql.sqlName("fk_" + getColumnName());
+
+        foreignKeyName = Sql.identifier(foreignKeyName);
+        foreignKeyMapping = lookup.createMapping(columnName, specification);
+    }
+
+    protected ForeignKeyCollectionMapper(final FieldMappingLookup lookup, final AbstractAutoMapper abstractAutoMapper, final ObjectAssociation field) {
+        super(lookup, abstractAutoMapper, field.getSpecification().getFullIdentifier());
+
+        this.field = field;
+        objectMapperLookup2 = null;
+
+        idMapping = lookup.createIdMapping();
+        versionMapping = lookup.createVersionMapping();
+
+        setColumnName(determineColumnName(field));
+        foreignKeyName = Sql.sqlName("fk_" + getColumnName());
+
+        foreignKeyName = Sql.identifier(foreignKeyName);
+        foreignKeyMapping = lookup.createMapping(columnName, specification);
+    }
+
+    protected String determineColumnName(final ObjectAssociation objectAssociation) {
+        return objectAssociation.getSpecification().getShortIdentifier();
+    }
+
+    public String getColumnName() {
+        return columnName;
+    }
+
+    public void setColumnName(final String columnName) {
+        this.columnName = columnName;
+    }
+
+    protected VersionMapping getVersionMapping() {
+        return versionMapping;
+    }
+
+    protected ObjectReferenceMapping getForeignKeyMapping() {
+        return foreignKeyMapping;
+    }
+
+    protected String getForeignKeyName() {
+        return foreignKeyName;
+    }
+
+    @Override
+    public void startup(final DatabaseConnector connector) {
+        if (originalMapping == null) {
+            originalMapping = objectMappingLookup.getMapping(specification, null);
+        }
+        originalMapping.startup(connector, objectMapperLookup2);
+        super.startup(connector);
+    }
+
+    @Override
+    public boolean needsTables(final DatabaseConnector connection) {
+        return !connection.hasColumn(table, foreignKeyName);
+    }
+
+    @Override
+    public void createTables(final DatabaseConnector connection) {
+        if (connection.hasTable(table)) {
+            final StringBuffer sql = new StringBuffer();
+            sql.append("alter table ");
+            sql.append(table);
+            sql.append(" add ");
+            appendColumnDefinitions(sql);
+            connection.update(sql.toString());
+        } else {
+            final StringBuffer sql = new StringBuffer();
+            sql.append("create table ");
+            sql.append(table);
+            sql.append(" (");
+            idMapping.appendCreateColumnDefinitions(sql);
+            sql.append(", ");
+
+            appendColumnDefinitions(sql);
+
+            // for (final FieldMapping mapping : fieldMappings) {
+            // mapping.appendColumnDefinitions(sql);
+            // sql.append(",");
+            // }
+            // sql.append(versionMapping.appendColumnDefinitions());
+            sql.append(")");
+            connection.update(sql.toString());
+        }
+    }
+
+    public IdMappingAbstract getIdMapping() {
+        return idMapping;
+    }
+
+    protected void appendCollectionUpdateColumnsToNull(final StringBuffer sql) {
+        sql.append(foreignKeyName + "=NULL ");
+    }
+
+    protected void appendCollectionWhereValues(final DatabaseConnector connector, final ObjectAdapter parent, final StringBuffer sql) {
+        foreignKeyMapping.appendUpdateValues(connector, sql, parent);
+    }
+
+    protected void appendCollectionUpdateValues(final DatabaseConnector connector, final ObjectAdapter parent, final StringBuffer sql) {
+        appendCollectionWhereValues(connector, parent, sql);
+    }
+
+    protected void appendColumnDefinitions(final StringBuffer sql) {
+        foreignKeyMapping.appendColumnDefinitions(sql);
+    }
+
+    @Override
+    public void loadInternalCollection(final DatabaseConnector connector, final ObjectAdapter parentAdapter) {
+
+        final ObjectAdapter collectionAdapter = field.get(parentAdapter);
+        if (!collectionAdapter.canTransitionToResolving()) {
+            return;
+        } 
+        
+        if(LOG.isDebugEnabled()) {
+            LOG.debug("loading internal collection " + field);
+        }
+        final List<ObjectAdapter> list = new ArrayList<ObjectAdapter>();
+        try {
+            PersistorUtil.startResolving(collectionAdapter);
+            
+            loadCollectionIntoList(connector, parentAdapter, table, specification, getIdMapping(), fieldMappingByField, versionMapping, list);
+            
+            final CollectionFacet collectionFacet = collectionAdapter.getSpecification().getFacet(CollectionFacet.class);
+            collectionFacet.init(collectionAdapter, list.toArray(new ObjectAdapter[list.size()]));
+            
+        } finally {
+            PersistorUtil.toEndState(collectionAdapter);
+        }
+
+        // TODO: Need to finalise this behaviour. At the moment, all
+        // collections will get infinitely resolved. I
+        // don't think this is desirable.
+        for (final ObjectAdapter field : list) {
+            // final ObjectMapping mapping =
+            // objectMappingLookup.getMapping(field, connector);
+            if (field.getSpecification().isOfType(parentAdapter.getSpecification())) {
+                loadInternalCollection(connector, field);
+            }
+        }
+    }
+
+    protected void loadCollectionIntoList(final DatabaseConnector connector, final ObjectAdapter parent, final String table, final ObjectSpecification specification, final IdMappingAbstract idMappingAbstract, final Map<ObjectAssociation, FieldMapping> fieldMappingByField, final VersionMapping versionMapping,
+            final List<ObjectAdapter> list) {
+
+        final StringBuffer sql = new StringBuffer();
+        sql.append("select ");
+        idMappingAbstract.appendColumnNames(sql);
+
+        sql.append(", ");
+        final String columnList = columnList(fieldMappingByField);
+        if (columnList.length() > 0) {
+            sql.append(columnList);
+            sql.append(", ");
+        }
+        sql.append(versionMapping.appendColumnNames());
+        sql.append(" from ");
+        sql.append(table);
+        sql.append(" where ");
+        appendCollectionWhereValues(connector, parent, sql);
+
+        final Results rs = connector.select(sql.toString());
+        while (rs.next()) {
+            final Oid oid = idMappingAbstract.recreateOid(rs, specification);
+            final ObjectAdapter element = getAdapter(specification, oid);
+            loadFields(element, rs, fieldMappingByField);
+            LOG.debug("  element  " + element.getOid());
+            list.add(element);
+        }
+        rs.close();
+    }
+
+    protected void loadFields(final ObjectAdapter adapter, final Results rs, final Map<ObjectAssociation, FieldMapping> fieldMappingByField) {
+        if (!adapter.canTransitionToResolving()) {
+            return;
+        }
+
+        try {
+            PersistorUtil.startResolving(adapter);
+            for (final FieldMapping mapping : fieldMappingByField.values()) {
+                mapping.initializeField(adapter, rs);
+            }
+            adapter.setVersion(versionMapping.getLock(rs));
+        } finally {
+            PersistorUtil.toEndState(adapter);
+        }
+    }
+
+    /**
+     * Override this in the Polymorphic case to return just the elements that
+     * are appropriate for the subclass currently being handled.
+     * 
+     * @param collection
+     * @return those elements that ought to be used.
+     */
+    protected Iterator<ObjectAdapter> getElementsForCollectionAsIterator(final ObjectAdapter collection) {
+        final CollectionFacet collectionFacet = collection.getSpecification().getFacet(CollectionFacet.class);
+        final Iterable<ObjectAdapter> elements = collectionFacet.iterable(collection);
+        return elements.iterator();
+    }
+
+    @Override
+    public void saveInternalCollection(final DatabaseConnector connector, final ObjectAdapter parent) {
+        final ObjectAdapter collection = field.get(parent);
+        if(LOG.isDebugEnabled()) {
+            LOG.debug("Saving internal collection " + collection);
+        }
+
+        final Iterator<ObjectAdapter> elements = getElementsForCollectionAsIterator(collection);
+
+        // TODO What is needed to allow a collection update (add/remove) to mark
+        // the collection as dirty?
+        // checkIfDirty(collection);
+
+        if (elements.hasNext() == false) {
+            return;
+        }
+
+        clearCollectionParent(connector, parent);
+
+        resetCollectionParent(connector, parent, elements);
+    }
+
+    protected void clearCollectionParent(final DatabaseConnector connector, final ObjectAdapter parent) {
+        // Delete collection parent
+        final StringBuffer sql = new StringBuffer();
+        sql.append("update ");
+        sql.append(table);
+        sql.append(" set ");
+        appendCollectionUpdateColumnsToNull(sql);
+        sql.append(" where ");
+        appendCollectionWhereValues(connector, parent, sql);
+        connector.update(sql.toString());
+    }
+
+    protected void resetCollectionParent(final DatabaseConnector connector, final ObjectAdapter parent, final Iterator<ObjectAdapter> elements) {
+        // Reinstall collection parent
+        final StringBuffer update = new StringBuffer();
+        update.append("update ");
+        update.append(table);
+        update.append(" set ");
+        appendCollectionUpdateValues(connector, parent, update);
+        update.append(" where ");
+
+        idMapping.appendColumnNames(update);
+
+        update.append(" IN (");
+
+        int count = 0;
+        for (final Iterator<ObjectAdapter> iterator = elements; iterator.hasNext();) {
+            final ObjectAdapter element = iterator.next();
+
+            if (count++ > 0) {
+                update.append(",");
+            }
+            final RootOid elementOid = (RootOid) element.getOid();
+            idMapping.appendObjectId(connector, update, elementOid);
+        }
+        update.append(")");
+        if (count > 0) {
+            connector.insert(update.toString());
+        }
+    }
+
+    protected void checkIfDirty(final ObjectAdapter collection) {
+        // Test: is dirty?
+        final ObjectSpecification collectionSpecification = collection.getSpecification();
+        if (collectionSpecification.isDirty(collection)) {
+            LOG.debug(collection.getOid() + " is dirty");
+        } else {
+            LOG.debug(collection.getOid() + " is clean");
+        }
+
+        final CollectionFacet collectionFacetD = collection.getSpecification().getFacet(CollectionFacet.class);
+        for (final ObjectAdapter element : collectionFacetD.iterable(collection)) {
+            if (collectionSpecification.isDirty(element)) {
+                LOG.debug(element.getOid() + " is dirty");
+            } else {
+                LOG.debug(element.getOid() + " is clean");
+            }
+        }
+    }
+
+    @Override
+    public void debugData(final DebugBuilder debug) {
+        debug.appendln(field.getName(), "collection");
+        debug.indent();
+        debug.appendln("Foreign key name", foreignKeyName);
+        debug.appendln("Foreign key mapping", foreignKeyMapping);
+        debug.appendln("ID mapping", idMapping);
+        debug.appendln("Version mapping", versionMapping);
+        debug.appendln("Original mapping", originalMapping);
+        debug.unindent();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyInChildCollectionMapper.java
----------------------------------------------------------------------
diff --git a/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyInChildCollectionMapper.java b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyInChildCollectionMapper.java
new file mode 100644
index 0000000..e333d7b
--- /dev/null
+++ b/framework/objectstore/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyInChildCollectionMapper.java
@@ -0,0 +1,88 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+/**
+ * 
+ */
+package org.apache.isis.runtimes.dflt.objectstores.sql.auto;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.metamodel.facets.FacetedMethod;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.metamodel.specloader.specimpl.OneToManyAssociationImpl;
+import org.apache.isis.runtimes.dflt.objectstores.sql.FieldMappingLookup;
+import org.apache.isis.runtimes.dflt.objectstores.sql.ObjectMappingLookup;
+
+/**
+ * Used to map 1-to-many collections by creating, in the child table, 1 column
+ * per parent collection. The column is named by combining the final part of the
+ * parent class name and the collection variable name.
+ * 
+ * @author Kevin
+ */
+public class ForeignKeyInChildCollectionMapper extends ForeignKeyCollectionMapper {
+    private static final Logger LOG = Logger.getLogger(ForeignKeyCollectionMapper.class);
+
+    protected final ObjectAssociation priorField; // prevents recursion
+    protected final List<ObjectAssociation> priorFields;
+
+    public ForeignKeyInChildCollectionMapper(final ObjectAssociation objectAssociation, final String parameterBase, final FieldMappingLookup lookup, final ObjectMappingLookup objectMapperLookup, final AbstractAutoMapper abstractAutoMapper, final ObjectAssociation field) {
+        super(objectAssociation, parameterBase, lookup, objectMapperLookup);
+
+        priorFields = abstractAutoMapper.fields;
+        priorField = field;
+
+        setUpFieldMappers();
+    }
+
+    protected ForeignKeyInChildCollectionMapper(final FieldMappingLookup lookup, final AbstractAutoMapper abstractAutoMapper, final ObjectAssociation field) {
+        super(lookup, abstractAutoMapper, field);
+        priorFields = null;
+        priorField = null;
+    }
+
+    @Override
+    protected void getExtraFields(final List<ObjectAssociation> existingFields) {
+        if (priorFields != null) {
+            for (final ObjectAssociation priorField1 : priorFields) {
+                if (existingFields.contains(priorField1) == false) {
+                    existingFields.add(priorField1);
+                } else {
+                    LOG.debug("Skipping prior field: " + priorField1.getName());
+                }
+            }
+        }
+    }
+
+    @Override
+    protected String determineColumnName(final ObjectAssociation objectAssociation) {
+        if (objectAssociation instanceof OneToManyAssociationImpl) {
+            final OneToManyAssociationImpl fkAssoc = (OneToManyAssociationImpl) objectAssociation;
+            final FacetedMethod peer = fkAssoc.getFacetedMethod();
+            final String fullClassName = peer.getIdentifier().getClassName();
+            final int lastPos = fullClassName.lastIndexOf('.');
+            return fullClassName.substring(lastPos + 1) + "_" + fkAssoc.getId();
+        } else {
+            return objectAssociation.getSpecification().getShortIdentifier();
+        }
+    }
+}