http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/SqlType.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/SqlType.java
 
b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/SqlType.java
deleted file mode 100644
index 6b6dfdd..0000000
--- 
a/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/SqlType.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- *  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.polygene.entitystore.jooq;
-
-import java.time.Instant;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
-import java.time.OffsetDateTime;
-import java.time.Period;
-import java.time.ZonedDateTime;
-import org.jooq.DataType;
-import org.jooq.impl.SQLDataType;
-import org.jooq.types.Interval;
-
-class SqlType
-{
-    @SuppressWarnings( "unchecked" )
-    static <T> DataType<T> getSqlDataTypeFor( Class<?> propertyType )
-    {
-        if( String.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.VARCHAR;
-        }
-        if( Integer.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.INTEGER;
-        }
-        if( Long.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.BIGINT;
-        }
-        if( Boolean.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.BOOLEAN;
-        }
-        if( Float.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.FLOAT;
-        }
-        if( Double.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.DOUBLE;
-        }
-        if( Instant.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.TIMESTAMPWITHTIMEZONE;
-        }
-        if( Interval.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.VARCHAR;
-        }
-        if( Period.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.VARCHAR;
-        }
-        if( LocalDate.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.LOCALDATE;
-        }
-        if( LocalTime.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.LOCALTIME;
-        }
-        if( LocalDateTime.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.LOCALDATETIME;
-        }
-        if( ZonedDateTime.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.OFFSETDATETIME;
-        }
-        if( OffsetDateTime.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.OFFSETDATETIME;
-        }
-        if( Character.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.CHAR( 1 );
-        }
-        if( Short.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.INTEGER;
-        }
-        if( Byte.class.isAssignableFrom( propertyType ) )
-        {
-            return (DataType<T>) SQLDataType.INTEGER;
-        }
-        if( propertyType.isPrimitive() )
-        {
-            if( propertyType.equals( Integer.TYPE ) )
-            {
-                return (DataType<T>) SQLDataType.INTEGER;
-            }
-            if( propertyType.equals( Long.TYPE ) )
-            {
-                return (DataType<T>) SQLDataType.BIGINT;
-            }
-            if( propertyType.equals( Boolean.TYPE ) )
-            {
-                return (DataType<T>) SQLDataType.BOOLEAN;
-            }
-            if( propertyType.equals( Float.TYPE ) )
-            {
-                return (DataType<T>) SQLDataType.FLOAT;
-            }
-            if( propertyType.equals( Double.TYPE ) )
-            {
-                return (DataType<T>) SQLDataType.DOUBLE;
-            }
-            if( propertyType.equals( Character.TYPE ) )
-            {
-                return (DataType<T>) SQLDataType.CHAR( 1 );
-            }
-            if( propertyType.equals( Short.TYPE ) )
-            {
-                return (DataType<T>) SQLDataType.INTEGER;
-            }
-            if( propertyType.equals( Byte.TYPE ) )
-            {
-                return (DataType<T>) SQLDataType.INTEGER;
-            }
-        }
-        return (DataType<T>) SQLDataType.VARCHAR;
-    }
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/TableFields.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/TableFields.java
 
b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/TableFields.java
deleted file mode 100644
index db42413..0000000
--- 
a/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/TableFields.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- *  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.polygene.entitystore.jooq;
-
-import java.sql.Timestamp;
-import org.jooq.Field;
-
-import static org.apache.polygene.entitystore.jooq.TypesTable.makeField;
-
-public interface TableFields
-{
-    // Common in all tables
-    String IDENTITY_COLUMN_NAME = "_identity";
-    String CREATED_COLUMN_NAME = "_created_at";
-    String LASTMODIFIED_COLUMN_NAME = "_modified_at";
-
-    // Types Table
-    String TABLENAME_COLUMN_NAME = "_table_name";
-
-    // Entities Table
-    String VALUEID_COLUMN_NAME = "_value_id";
-    String TYPE_COLUMN_NAME = "_type";
-    String VERSION_COLUMN_NAME = "_version";
-    String APPLICATIONVERSION_COLUMN_NAME = "_app_version";
-
-    // Mixin Tables
-    String NAME_COLUMN_NAME = "_name";
-    String INDEX_COLUMN_NAME = "_index";    // either index in ManyAssociation 
or name in NamedAssociation
-    String REFERENCE_COLUMN_NAME = "_reference";
-    String ASSOCS_TABLE_POSTFIX = "_ASSOCS";
-
-
-    // Common Fields
-    Field<String> identityColumn = makeField( IDENTITY_COLUMN_NAME, 
String.class );
-    Field<Timestamp> createdColumn = makeField( CREATED_COLUMN_NAME, 
Timestamp.class );
-    Field<Timestamp> modifiedColumn = makeField( LASTMODIFIED_COLUMN_NAME, 
Timestamp.class );
-
-    // Types Table
-    Field<String> tableNameColumn = makeField( TABLENAME_COLUMN_NAME, 
String.class );
-
-    // Entities Table
-    Field<String> valueIdentityColumn = makeField( VALUEID_COLUMN_NAME, 
String.class );
-    Field<String> typeNameColumn = makeField( TYPE_COLUMN_NAME, String.class );
-    Field<String> versionColumn = makeField( VERSION_COLUMN_NAME, String.class 
);
-    Field<String> applicationVersionColumn = makeField( 
APPLICATIONVERSION_COLUMN_NAME, String.class );
-
-    // Mixin Tables
-
-    // The _ASSOCS table
-    Field<String> nameColumn = makeField( NAME_COLUMN_NAME, String.class );
-    Field<String> referenceColumn = makeField( REFERENCE_COLUMN_NAME, 
String.class );
-    Field<String> indexColumn = makeField( INDEX_COLUMN_NAME, String.class );
-
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/TypesTable.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/TypesTable.java
 
b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/TypesTable.java
deleted file mode 100644
index c816c95..0000000
--- 
a/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/TypesTable.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- *  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.polygene.entitystore.jooq;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Type;
-import java.sql.Timestamp;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import org.apache.polygene.api.association.AssociationDescriptor;
-import org.apache.polygene.api.common.QualifiedName;
-import org.apache.polygene.api.entity.EntityDescriptor;
-import org.apache.polygene.api.property.Property;
-import org.apache.polygene.api.property.PropertyDescriptor;
-import org.apache.polygene.api.util.Classes;
-import org.jooq.CreateTableColumnStep;
-import org.jooq.DataType;
-import org.jooq.Field;
-import org.jooq.Record;
-import org.jooq.Result;
-import org.jooq.SQLDialect;
-import org.jooq.Schema;
-import org.jooq.Table;
-import org.jooq.impl.DSL;
-
-public class TypesTable
-    implements TableFields
-{
-    private final Map<Class<?>, Table<Record>> mixinTablesCache = new 
ConcurrentHashMap<>();
-    private final Map<Class<?>, Table<Record>> mixinAssocsTablesCache = new 
ConcurrentHashMap<>();
-
-    private final Table<Record> typesTable;
-    private final SQLDialect dialect;
-    private final Schema schema;
-
-    private final JooqDslContext dsl;
-
-    TypesTable( JooqDslContext dsl, Schema schema,
-                SQLDialect dialect,
-                String typesTablesName
-              )
-    {
-        this.schema = schema;
-        this.dialect = dialect;
-        typesTable = tableOf( typesTablesName );
-        this.dsl = dsl;
-    }
-
-    static <T> Field<T> makeField( String columnName, Class<T> type )
-    {
-        return DSL.field( DSL.name( columnName ), type );
-    }
-
-    Table<Record> tableOf( String tableName )
-    {
-        return DSL.table(
-            dialect.equals( SQLDialect.SQLITE )
-            ? DSL.name( tableName )
-            : DSL.name( schema.getName(), tableName ) );
-    }
-
-    String tableNameOf( Class<?> mixinType )
-    {
-        Result<Record> typeInfo = fetchTypeInfoFromTable( mixinType );
-        if( typeInfo.isEmpty() )
-        {
-            return null;
-        }
-        return typeInfo.getValue( 0, tableNameColumn );
-    }
-
-    Table<Record> tableFor( Class<?> type, EntityDescriptor descriptor )
-    {
-        return mixinTablesCache.computeIfAbsent( type, t ->
-        {
-            String tableName = tableNameOf( t );
-            if( tableName == null )
-            {
-                Result<Record> newMixinTable = createNewMixinTable( type, 
descriptor );
-                return tableOf( newMixinTable.getValue( 0, tableNameColumn ) );
-            }
-            return tableOf( tableName );
-        } );
-    }
-
-    private Result<Record> fetchTypeInfoFromTable( Class<?> mixinTableName )
-    {
-        return dsl.select()
-                  .from( typesTable )
-                  .where( identityColumn.eq( mixinTableName.getName() ) )
-                  .fetch();
-    }
-
-    private Result<Record> createNewMixinTable( Class<?> mixinType, 
EntityDescriptor descriptor )
-    {
-        String mixinTypeName = mixinType.getName();
-        String tableName = createNewTableName( mixinType );
-        CreateTableColumnStep primaryTable = dsl.createTable( DSL.name( 
schema.getName(), tableName ) )
-                                                .column( identityColumn )
-                                                .column( createdColumn );
-        descriptor.state().properties().forEach(
-            property ->
-            {
-                QualifiedName qualifiedName = property.qualifiedName();
-                if( qualifiedName.type().replace( '-', '$' ).equals( 
mixinTypeName ) )
-                {
-                    primaryTable.column( fieldOf( property ) );
-                }
-            } );
-        descriptor.state().associations().forEach(
-            assoc ->
-            {
-                QualifiedName qualifiedName = assoc.qualifiedName();
-                if( qualifiedName.type().replace( '-', '$' ).equals( 
mixinTypeName ) )
-                {
-                    primaryTable.column( fieldOf( assoc ) );
-                }
-            } );
-        int result1 = primaryTable.execute();
-        int result3 = dsl.insertInto( typesTable )
-                         .set( identityColumn, mixinTypeName )
-                         .set( tableNameColumn, tableName )
-                         .set( createdColumn, new Timestamp( 
System.currentTimeMillis() ) )
-                         .set( modifiedColumn, new Timestamp( 
System.currentTimeMillis() ) )
-                         .execute();
-        return fetchTypeInfoFromTable( mixinType );
-    }
-
-    private String createNewTableName( Class<?> mixinType )
-    {
-        String typeName = mixinType.getSimpleName();
-        String postFix = "";
-        int counter = 0;
-        boolean found = false;
-        do
-        {
-            found = checkForTableNamed( typeName + postFix );
-            postFix = "_" + counter++;
-        } while( found );
-        return typeName;
-    }
-
-    private boolean checkForTableNamed( String tableName )
-    {
-        return dsl.select()
-                  .from( typesTable )
-                  .where( tableNameColumn.eq( tableName ) )
-                  .fetch().size() > 0;
-    }
-
-    private boolean isProperty( Method method )
-    {
-        return Property.class.isAssignableFrom( method.getReturnType() ) && 
method.getParameterCount() == 0;
-    }
-
-    Field<Object> fieldOf( PropertyDescriptor descriptor )
-    {
-        String propertyName = descriptor.qualifiedName().name();
-        return DSL.field( DSL.name( propertyName ), dataTypeOf( descriptor ) );
-    }
-
-    Field<String> fieldOf( AssociationDescriptor descriptor )
-    {
-        String propertyName = descriptor.qualifiedName().name();
-        return DSL.field( DSL.name( propertyName ), DSL.getDataType( 
String.class ) );
-    }
-
-    private <T> DataType<T> dataTypeOf( PropertyDescriptor property )
-    {
-        Type type = property.type();
-        Class<?> rawType = Classes.RAW_CLASS.apply( type );
-        return SqlType.getSqlDataTypeFor( rawType );
-    }
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/assembly/JooqEntityStoreAssembler.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/assembly/JooqEntityStoreAssembler.java
 
b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/assembly/JooqEntityStoreAssembler.java
deleted file mode 100644
index c251efe..0000000
--- 
a/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/assembly/JooqEntityStoreAssembler.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- *  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.polygene.entitystore.jooq.assembly;
-
-import org.apache.polygene.api.identity.Identity;
-import org.apache.polygene.api.identity.StringIdentity;
-import org.apache.polygene.bootstrap.Assembler;
-import org.apache.polygene.bootstrap.Assemblers;
-import org.apache.polygene.bootstrap.AssemblyException;
-import org.apache.polygene.bootstrap.ModuleAssembly;
-import org.apache.polygene.entitystore.jooq.JooqDslContext;
-import org.apache.polygene.entitystore.jooq.JooqEntityStoreConfiguration;
-import org.apache.polygene.entitystore.jooq.JooqEntityStoreService;
-import org.jooq.SQLDialect;
-import org.jooq.conf.RenderNameStyle;
-import org.jooq.conf.Settings;
-
-/**
- * JOOQ EntityStore assembly.
- */
-@SuppressWarnings( "WeakerAccess" )
-public class JooqEntityStoreAssembler extends 
Assemblers.VisibilityIdentityConfig<JooqEntityStoreAssembler>
-    implements Assembler
-{
-    public static final Identity DEFAULT_ENTITYSTORE_IDENTITY = new 
StringIdentity( "entitystore-jooq" );
-
-    @Override
-    public void assemble( ModuleAssembly module )
-    {
-        Settings settings = getSettings();
-        if( settings == null )
-        {
-            throw new AssemblyException( "Settings must not be null" );
-        }
-
-        String identity = ( hasIdentity() ? identity() : 
DEFAULT_ENTITYSTORE_IDENTITY ).toString();
-        module.transients( JooqDslContext.class );
-
-        module.services( JooqEntityStoreService.class )
-              .identifiedBy( identity )
-              .visibleIn( visibility() )
-              .instantiateOnStartup()
-              .setMetaInfo( settings );
-
-        if( hasConfig() )
-        {
-            configModule().entities( JooqEntityStoreConfiguration.class 
).visibleIn( configVisibility() );
-        }
-    }
-
-    protected Settings getSettings()
-    {
-        return new Settings().withRenderNameStyle( RenderNameStyle.QUOTED );
-    }
-
-    protected SQLDialect getSQLDialect()
-    {
-        return SQLDialect.DEFAULT;
-    }
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-jooq/src/test/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreTest.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-jooq/src/test/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreTest.java
 
b/extensions/entitystore-jooq/src/test/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreTest.java
deleted file mode 100644
index f36db0e..0000000
--- 
a/extensions/entitystore-jooq/src/test/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- *  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.polygene.entitystore.jooq;
-
-import org.apache.polygene.api.common.Visibility;
-import org.apache.polygene.bootstrap.AssemblyException;
-import org.apache.polygene.bootstrap.ModuleAssembly;
-import org.apache.polygene.entitystore.jooq.assembly.JooqEntityStoreAssembler;
-import org.apache.polygene.library.sql.assembly.DataSourceAssembler;
-import org.apache.polygene.library.sql.datasource.DataSourceConfiguration;
-import org.apache.polygene.library.sql.dbcp.DBCPDataSourceServiceAssembler;
-import org.apache.polygene.test.EntityTestAssembler;
-import org.apache.polygene.test.entity.AbstractEntityStoreTest;
-import org.jooq.SQLDialect;
-import org.junit.Rule;
-import org.junit.rules.TemporaryFolder;
-
-public class JooqEntityStoreTest extends AbstractEntityStoreTest
-{
-    @Rule
-    public final TemporaryFolder tmpDir = new TemporaryFolder();
-
-    @Override
-    // START SNIPPET: assembly
-    public void assemble( ModuleAssembly module )
-        throws AssemblyException
-    {
-        // END SNIPPET: assembly
-        super.assemble( module );
-        module.defaultServices();
-        ModuleAssembly config = module.layer().module( "config" );
-        new EntityTestAssembler().visibleIn( Visibility.module ).assemble( 
config );
-
-        // START SNIPPET: assembly
-        // Assemble a DataSource
-        new DataSourceAssembler()
-            .withDataSourceServiceIdentity( "datasource" )
-            .identifiedBy( "ds-mysql" )
-            .visibleIn( Visibility.module )
-            .assemble( module );
-
-        // Assemble the Apache DBCP based Service Importer
-        new DBCPDataSourceServiceAssembler()
-            .identifiedBy( "datasource" )
-            .visibleIn( Visibility.module )
-            .withConfig( config, Visibility.layer )
-            .assemble( module );
-
-        new JooqEntityStoreAssembler()
-            .withConfig( config, Visibility.layer )
-            .identifiedBy( "jooq-entitystore" )
-            .assemble( module );
-        // END SNIPPET: assembly
-
-        JooqEntityStoreConfiguration jooqDefaults = config.forMixin( 
JooqEntityStoreConfiguration.class )
-                                                          .setMetaInfo( 
SQLDialect.H2 )
-                                                          .declareDefaults();
-        jooqDefaults.entitiesTableName().set( "ENTITIES" );
-
-        DataSourceConfiguration dsDefaults = config.forMixin( 
DataSourceConfiguration.class ).declareDefaults();
-        dsDefaults.driver().set( org.h2.Driver.class.getName() );
-        dsDefaults.enabled().set( true );
-        dsDefaults.maxPoolSize().set( 3 );
-        dsDefaults.minPoolSize().set( 1 );
-        dsDefaults.username().set( "" );
-        dsDefaults.password().set( "" );
-        dsDefaults.url().set( "jdbc:h2:" + tmpDir.getRoot().getAbsolutePath() 
+ "/testdb;create=true" );
-        // START SNIPPET: assembly
-    }
-    // END SNIPPET: assembly
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-sql/build.gradle
----------------------------------------------------------------------
diff --git a/extensions/entitystore-sql/build.gradle 
b/extensions/entitystore-sql/build.gradle
new file mode 100644
index 0000000..32ceb70
--- /dev/null
+++ b/extensions/entitystore-sql/build.gradle
@@ -0,0 +1,44 @@
+/*
+ *  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.
+ *
+ *
+ */
+
+apply plugin: 'polygene-extension'
+
+description = "Apache Polygene™ ORM EntityStore Extension"
+
+jar { manifest { name = "Apache Polygene™ Extension - EntityStore - ORM" } }
+
+dependencies {
+  api polygene.core.bootstrap
+  api polygene.library( 'sql' )
+  api libraries.jooq
+
+  runtimeOnly polygene.core.runtime
+
+  testImplementation polygene.internals.testsupport
+  testImplementation polygene.library( 'sql-dbcp' )
+  testImplementation libraries.docker_junit
+
+  testRuntimeOnly libraries.logback
+  testRuntimeOnly libraries.derby
+  testRuntimeOnly libraries.h2
+  testRuntimeOnly libraries.mysql_connector
+  testRuntimeOnly libraries.postgres
+  testRuntimeOnly libraries.sqlite
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-sql/dev-status.xml
----------------------------------------------------------------------
diff --git a/extensions/entitystore-sql/dev-status.xml 
b/extensions/entitystore-sql/dev-status.xml
new file mode 100644
index 0000000..b6d4c31
--- /dev/null
+++ b/extensions/entitystore-sql/dev-status.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~  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.
+  ~
+  ~
+  -->
+<module xmlns="http://polygene.apache.org/schemas/2008/dev-status/1";
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+        
xsi:schemaLocation="http://polygene.apache.org/schemas/2008/dev-status/1
+        http://polygene.apache.org/schemas/2008/dev-status/1/dev-status.xsd";>
+  <status>
+        <!--none,early,beta,stable,mature-->
+        <codebase>early</codebase>
+
+        <!-- none, brief, good, complete -->
+        <documentation>brief</documentation>
+
+        <!-- none, some, good, complete -->
+        <unittests>good</unittests>
+    </status>
+    <licenses>
+        <license>ALv2</license>
+    </licenses>
+</module>

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-sql/src/docs/es-sql.txt
----------------------------------------------------------------------
diff --git a/extensions/entitystore-sql/src/docs/es-sql.txt 
b/extensions/entitystore-sql/src/docs/es-sql.txt
new file mode 100644
index 0000000..a36d463
--- /dev/null
+++ b/extensions/entitystore-sql/src/docs/es-sql.txt
@@ -0,0 +1,58 @@
+///////////////////////////////////////////////////////////////
+ * 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.
+///////////////////////////////////////////////////////////////
+
+[[extension-es-jooq,SQL EntityStore]]
+= ORM EntityStore =
+
+[devstatus]
+--------------
+source=extensions/entitystore-jooq/dev-status.xml
+--------------
+
+This entitystore is backed by a SQL server, and maps each mixin type of the 
Composite into separate tables. This is more
+enterprise-friendly, but comes at the cost of less performance compared to the 
<<extension-es-sql>>.
+
+This extension fully leverage the <<library-sql>> meaning that you must use it 
to assemble your DataSource and that you
+get <<library-circuitbreaker,Circuit Breaker>> and <<library-jmx, JMX>> 
integration for free.
+
+include::../../build/docs/buildinfo/artifact.txt[]
+
+== Assembly ==
+
+Assembly is done using the provided Assembler:
+
+[snippet,java]
+----
+source=extensions/entitystore-jooq/src/test/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreTest.java
+tag=assembly
+----
+
+== Configuration ==
+
+Here are the available configuration properties:
+
+[snippet,java]
+----
+source=extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreConfiguration.java
+tag=config
+----
+
+All authentication related properties are optional.
+By default no authentication is used.
+As soon as you provide a `username`, authentication is set up.

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/AssociationValue.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/AssociationValue.java
 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/AssociationValue.java
new file mode 100644
index 0000000..9fb6771
--- /dev/null
+++ 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/AssociationValue.java
@@ -0,0 +1,28 @@
+/*
+ *  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.polygene.entitystore.sql;
+
+import org.apache.polygene.api.common.QualifiedName;
+
+@SuppressWarnings( "WeakerAccess" )
+public class AssociationValue
+{
+    QualifiedName name;
+    String position;
+    String reference;
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/BaseEntity.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/BaseEntity.java
 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/BaseEntity.java
new file mode 100644
index 0000000..982366d
--- /dev/null
+++ 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/BaseEntity.java
@@ -0,0 +1,34 @@
+/*
+ *  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.polygene.entitystore.sql;
+
+import java.time.Instant;
+import org.apache.polygene.api.entity.EntityDescriptor;
+import org.apache.polygene.api.identity.Identity;
+
+@SuppressWarnings( "WeakerAccess" )
+public class BaseEntity
+{
+    EntityDescriptor type;
+    Identity identity;
+    String version;
+    String applicationVersion;
+    Instant modifedAt;
+    Instant createdAt;
+    Identity currentValueIdentity;
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/EntitiesTable.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/EntitiesTable.java
 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/EntitiesTable.java
new file mode 100644
index 0000000..d25e7fc
--- /dev/null
+++ 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/EntitiesTable.java
@@ -0,0 +1,363 @@
+/*
+ *  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.polygene.entitystore.sql;
+
+import java.lang.reflect.Method;
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.polygene.api.association.AssociationDescriptor;
+import org.apache.polygene.api.common.QualifiedName;
+import org.apache.polygene.api.composite.Composite;
+import org.apache.polygene.api.entity.EntityComposite;
+import org.apache.polygene.api.entity.EntityDescriptor;
+import org.apache.polygene.api.entity.EntityReference;
+import org.apache.polygene.api.identity.HasIdentity;
+import org.apache.polygene.api.identity.StringIdentity;
+import org.apache.polygene.api.property.PropertyDescriptor;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.type.EntityCompositeType;
+import org.apache.polygene.api.unitofwork.NoSuchEntityTypeException;
+import org.apache.polygene.api.util.Classes;
+import org.apache.polygene.spi.entitystore.EntityNotFoundException;
+import org.apache.polygene.spi.entitystore.EntityStoreUnitOfWork;
+import org.apache.polygene.spi.entitystore.helpers.DefaultEntityState;
+import org.jooq.Condition;
+import org.jooq.Field;
+import org.jooq.Record;
+import org.jooq.Result;
+import org.jooq.Schema;
+import org.jooq.SelectJoinStep;
+import org.jooq.SelectQuery;
+import org.jooq.Table;
+import org.jooq.impl.DSL;
+
+@SuppressWarnings( "WeakerAccess" )
+public class EntitiesTable
+    implements TableFields
+{
+    private static final Predicate<? super Class<?>> NOT_COMPOSITE = type -> 
!( type.equals( Composite.class ) || type.equals( EntityComposite.class ) );
+    private static final Predicate<? super Class<?>> NOT_HASIDENTITY = type -> 
!( type.equals( HasIdentity.class ) );
+    private Map<EntityCompositeType, Set<Class<?>>> mixinTypeCache = new 
ConcurrentHashMap<>();
+    private Map<Class<?>, MixinTable> mixinTablesCache = new 
ConcurrentHashMap<>();
+
+    private final Table<Record> entitiesTable;
+    private JooqDslContext dsl;
+    private final TypesTable types;
+    private final Schema schema;
+    private String applicationVersion;
+    private boolean replacementStrategy = false;  // Figure out later if we 
should support both and if so, how.
+
+    EntitiesTable( JooqDslContext dsl, Schema schema, TypesTable types, String 
applicationVersion, String entitiesTableName )
+    {
+        this.dsl = dsl;
+        this.types = types;
+        this.schema = schema;
+        this.applicationVersion = applicationVersion;
+        entitiesTable = types.tableOf( entitiesTableName );
+    }
+
+    public BaseEntity fetchEntity( EntityReference reference, ModuleDescriptor 
module )
+    {
+
+        Result<Record> baseEntityResult = dsl
+            .selectFrom( entitiesTable )
+            .where( identityColumn.eq( reference.identity().toString() ) )
+            .fetch();
+
+        if( baseEntityResult.isEmpty() )
+        {
+            throw new EntityNotFoundException( reference );
+        }
+        Record row = baseEntityResult.get( 0 );
+        return toBaseEntity( row, module );
+    }
+
+    protected BaseEntity toBaseEntity( Record row, ModuleDescriptor module )
+    {
+        BaseEntity result = new BaseEntity();
+        String typeName = row.field( typeNameColumn ).get( row );
+        result.type = findEntityDescriptor( typeName, module );
+        result.version = row.field( versionColumn ).get( row );
+        result.applicationVersion = row.field( applicationVersionColumn ).get( 
row );
+        result.identity = StringIdentity.identityOf( row.field( identityColumn 
).get( row ) );
+        result.currentValueIdentity = EntityReference.parseEntityReference( 
row.field( valueIdentityColumn ).get( row ) ).identity();
+        result.modifedAt = Instant.ofEpochMilli( row.field( modifiedColumn 
).get( row ).getTime() );
+        result.createdAt = Instant.ofEpochMilli( row.field( createdColumn 
).get( row ).getTime() );
+        return result;
+    }
+
+    public Stream<BaseEntity> fetchAll( EntityDescriptor type, 
ModuleDescriptor module )
+    {
+        Result<Record> baseEntityResult = dsl
+            .selectFrom( entitiesTable )
+            .fetch();
+        return baseEntityResult.stream().map( record -> toBaseEntity( record, 
module ) );
+    }
+
+    private EntityDescriptor findEntityDescriptor( String typeName, 
ModuleDescriptor module )
+    {
+        try
+        {
+            Class<?> type = getClass().getClassLoader().loadClass( typeName );
+            return module.typeLookup().lookupEntityModel( type );
+        }
+        catch( ClassNotFoundException e )
+        {
+            throw new NoSuchEntityTypeException( typeName, module);
+        }
+    }
+
+    void insertEntity( DefaultEntityState state, BaseEntity baseEntity )
+    {
+        EntityCompositeType compositeType = 
state.entityDescriptor().valueType();
+        Set<Class<?>> mixinTypes = mixinTypeCache.computeIfAbsent( 
compositeType, createMixinTypesSet( compositeType ) );
+        mixinTypes.forEach( type ->
+                            {
+                                MixinTable table = findMixinTable( type, 
state.entityDescriptor() );
+                                table.insertMixinState( state, 
baseEntity.currentValueIdentity.toString() );
+                            } );
+    }
+
+    void modifyEntity( DefaultEntityState state, BaseEntity baseEntity, 
EntityStoreUnitOfWork uow )
+    {
+        updateBaseEntity( baseEntity, uow );
+        if( replacementStrategy )
+        {
+            insertEntity( state, baseEntity );      // replacement strategy 
(more safe)
+        }
+        else
+        {
+            EntityCompositeType compositeType = 
state.entityDescriptor().valueType();
+            Set<Class<?>> mixinTypes = mixinTypeCache.computeIfAbsent( 
compositeType, createMixinTypesSet( compositeType ) );
+            mixinTypes.forEach( type ->
+                                {
+                                    MixinTable table = findMixinTable( type, 
state.entityDescriptor() );
+                                    table.modifyMixinState( state, 
baseEntity.currentValueIdentity.toString() );
+                                } );
+        }
+    }
+
+    private MixinTable findMixinTable( Class<?> type, EntityDescriptor 
entityDescriptor )
+    {
+        return mixinTablesCache.computeIfAbsent( type, t -> new MixinTable( 
dsl, schema, types, type, entityDescriptor ) );
+    }
+
+    private Set<Class<?>> mixinsOf( Stream<? extends AssociationDescriptor> 
stream )
+    {
+        return stream
+            .map( AssociationDescriptor::accessor )
+            .filter( Classes.instanceOf( Method.class ) )
+            .map( accessor -> (Method) accessor )
+            .map( Method::getDeclaringClass )
+            .filter( NOT_HASIDENTITY )
+            .filter( NOT_COMPOSITE )
+            .collect( Collectors.toSet() );
+    }
+
+    private Function<EntityCompositeType, Set<Class<?>>> createMixinTypesSet( 
EntityCompositeType compositeType )
+    {
+        return type ->
+        {
+            Set<Class<?>> mixins = compositeType
+                .properties()
+                .map( PropertyDescriptor::accessor )
+                .filter( Classes.instanceOf( Method.class ) )
+                .map( accessor -> (Method) accessor )
+                .map( Method::getDeclaringClass )
+                .filter( NOT_HASIDENTITY )
+                .filter( NOT_COMPOSITE )
+                .collect( Collectors.toSet() );
+            Set<Class<?>> mixinsWithAssociations = mixinsOf( 
compositeType.associations() );
+            Set<Class<?>> mixinsWithManyAssociations = mixinsOf( 
compositeType.manyAssociations() );
+            Set<Class<?>> mixinsWithNamedAssociations = mixinsOf( 
compositeType.namedAssociations() );
+            mixins.addAll( mixinsWithAssociations );
+            mixins.addAll( mixinsWithManyAssociations );
+            mixins.addAll( mixinsWithNamedAssociations );
+            return mixins;
+        };
+    }
+
+    void createNewBaseEntity( EntityReference reference, EntityDescriptor 
descriptor, EntityStoreUnitOfWork uow )
+    {
+        String valueIdentity = UUID.randomUUID().toString();
+        dsl.insertInto( entitiesTable )
+           .set( identityColumn, reference.identity().toString() )
+           .set( createdColumn, new Timestamp( 
uow.currentTime().toEpochMilli() ) )
+           .set( modifiedColumn, new Timestamp( 
uow.currentTime().toEpochMilli() ) )
+           .set( valueIdentityColumn, valueIdentity )
+           .set( typeNameColumn, descriptor.primaryType().getName() )
+           .set( versionColumn, "1" )
+           .set( applicationVersionColumn, applicationVersion )
+           .execute();
+    }
+
+    private void updateBaseEntity( BaseEntity entity, EntityStoreUnitOfWork 
uow )
+    {
+        entity.version = increment( entity.version );
+        if( replacementStrategy )
+        {
+            entity.currentValueIdentity = StringIdentity.identityOf( 
UUID.randomUUID().toString() );
+        }
+        dsl.update( entitiesTable )
+           .set( modifiedColumn, new Timestamp( 
uow.currentTime().toEpochMilli() ) )
+           .set( valueIdentityColumn, entity.currentValueIdentity.toString() )
+           .set( versionColumn, entity.version )
+           .set( applicationVersionColumn, applicationVersion )
+           .execute();
+    }
+
+    private String increment( String version )
+    {
+        long ver = Long.parseLong( version );
+        return Long.toString( ver + 1 );
+    }
+
+    /**
+     * Builds the SELECT statement for a given entity.
+     * <p>
+     * Example; If we have the following entity
+     * </p>
+     * <code><pre>
+     *     public interface LegalEntity
+     *     {
+     *         Property&lt;String&gt; registration();
+     *     }
+     * <p>
+     *     public interface Person extends LegalEntity
+     *     {
+     *         Property&lt;String&gt; name();
+     * <p>
+     *         &#64;Optional
+     *         Association&lt;Person&gt; spouse();
+     * <p>
+     *         ManyAssocation&lt;Person&gt; children();
+     *     }
+     * </pre></code>
+     * <p>
+     * and we do a simple;
+     * <code><pre>
+     *     Person p = uow.get( Person.class, "niclas" );
+     * </pre></code>
+     * <p>
+     * then the generated query will be
+     * </p>
+     * <code><pre>
+     *     SELECT * FROM ENTITIES
+     *     JOIN Person ON identity = ENTITIES.value_id
+     *     JOIN LegalEntity ON identity = ENTITIES.value_id
+     *     JOIN Person_Assoc ON identity = ENTITIES.value_id
+     *     WHERE ENTITIES.identity = '123'
+     * </pre></code>
+     *
+     * @param entityDescriptor The descriptor of the entity type to be built.
+     * @return The SELECT query that can be executed to retrieve the entity.
+     */
+    public SelectQuery<Record> createGetEntityQuery( EntityDescriptor 
entityDescriptor, EntityReference reference )
+    {
+        List<Table<Record>> joins = getMixinTables( entityDescriptor );
+        SelectJoinStep<Record> from = dsl.select().from( entitiesTable );
+        for( Table<Record> joinedTable : joins )
+        {
+            Condition joinCondition = valueIdentityColumn.eq( 
identityColumnOf( joinedTable ) );
+            from = from.leftJoin( joinedTable ).on( joinCondition );
+        }
+        return from.where( identityColumnOf( entitiesTable ).eq( 
reference.identity().toString() ) ).getQuery();
+    }
+
+    public void fetchAssociations( BaseEntity entity, EntityDescriptor 
entityDescriptor, Consumer<AssociationValue> consume )
+    {
+        List<Table<Record>> joins = getAssocationsTables( entityDescriptor );
+        SelectJoinStep<Record> from = dsl.select().from( entitiesTable );
+        for( Table<Record> joinedTable : joins )
+        {
+            Condition joinCondition = valueIdentityColumn.eq( 
identityColumnOf( joinedTable ) );
+            from = from.join( joinedTable ).on( joinCondition );
+        }
+        String reference = entity.identity.toString();
+        SelectQuery<Record> query = from.where( identityColumnOf( 
entitiesTable ).eq( reference ) ).getQuery();
+        Result<Record> result = query.fetch();
+        result.forEach( record ->
+                        {
+                            AssociationValue value = new AssociationValue();
+                            value.name = QualifiedName.fromClass( 
entityDescriptor.primaryType(), record.getValue( nameColumn ) );
+                            value.position = record.getValue( indexColumn );
+                            value.reference = record.getValue( referenceColumn 
);
+                            consume.accept( value );
+                        } );
+    }
+
+    private Field<String> identityColumnOf( Table<Record> joinedTable )
+    {
+        return DSL.field( DSL.name( joinedTable.getName(), 
identityColumn.getName() ), String.class );
+    }
+
+    public List<Table<Record>> getMixinTables( EntityDescriptor 
entityDescriptor )
+    {
+        return entityDescriptor
+            .mixinTypes()
+            .filter( NOT_COMPOSITE )
+            .filter( NOT_HASIDENTITY )
+            .map( ( Class<?> type ) -> types.tableFor( type, entityDescriptor 
) )
+            .collect( Collectors.toList() );
+    }
+
+    public List<Table<Record>> getAssocationsTables( EntityDescriptor 
entityDescriptor )
+    {
+        return entityDescriptor
+            .mixinTypes()
+            .filter( NOT_COMPOSITE )
+            .filter( NOT_HASIDENTITY )
+            .map( type -> findMixinTable( type, entityDescriptor ) )
+            .map( MixinTable::associationsTable )
+            .collect( Collectors.toList() );
+    }
+
+    public void removeEntity( EntityReference entityReference, 
EntityDescriptor descriptor )
+    {
+        ModuleDescriptor module = descriptor.module();
+        BaseEntity baseEntity = fetchEntity( entityReference, module );
+        if( replacementStrategy )
+        {
+            // TODO;  Mark deleted, I guess... not implemented
+        }
+        else
+        {
+            dsl.delete( entitiesTable )
+               .where(
+                   identityColumnOf( entitiesTable ).eq( 
entityReference.identity().toString() )
+                     )
+               .execute()
+            ;
+            String valueId = baseEntity.currentValueIdentity.toString();
+            List<Table<Record>> mixinTables = getMixinTables( descriptor );
+            List<Table<Record>> assocTables = getAssocationsTables( descriptor 
);
+            mixinTables.forEach( table -> dsl.delete( table ).where( 
identityColumnOf( table ).eq( valueId ) ).execute() );
+            assocTables.forEach( table -> dsl.delete( table ).where( 
identityColumnOf( table ).eq( valueId ) ).execute() );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/JooqDslContext.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/JooqDslContext.java
 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/JooqDslContext.java
new file mode 100644
index 0000000..d89c058
--- /dev/null
+++ 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/JooqDslContext.java
@@ -0,0 +1,58 @@
+/*
+ *  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.polygene.entitystore.sql;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import javax.sql.DataSource;
+import org.apache.polygene.api.injection.scope.Service;
+import org.apache.polygene.api.injection.scope.Uses;
+import org.apache.polygene.api.mixin.Mixins;
+import org.jooq.Configuration;
+import org.jooq.DSLContext;
+import org.jooq.SQLDialect;
+import org.jooq.conf.Settings;
+import org.jooq.impl.DSL;
+import org.jooq.impl.DefaultConfiguration;
+
+@Mixins( JooqDslContext.Mixin.class )
+public interface JooqDslContext extends DSLContext
+{
+
+    class Mixin
+        implements InvocationHandler
+    {
+        private DSLContext dsl;
+
+        public Mixin( @Service DataSource dataSource, @Uses Settings settings, 
@Uses SQLDialect dialect )
+        {
+            Configuration configuration = new DefaultConfiguration()
+                .set( dataSource )
+                .set( dialect )
+                .set( settings );
+            dsl = DSL.using( configuration );
+        }
+
+        @Override
+        public Object invoke( Object o, Method method, Object[] objects )
+            throws Throwable
+        {
+            return method.invoke( dsl, objects );       // delegate all
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/MixinTable.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/MixinTable.java
 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/MixinTable.java
new file mode 100644
index 0000000..80c26a2
--- /dev/null
+++ 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/MixinTable.java
@@ -0,0 +1,256 @@
+/*
+ *  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.polygene.entitystore.sql;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Method;
+import java.sql.Timestamp;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import org.apache.polygene.api.association.AssociationDescriptor;
+import org.apache.polygene.api.common.QualifiedName;
+import org.apache.polygene.api.entity.EntityDescriptor;
+import org.apache.polygene.api.entity.EntityReference;
+import org.apache.polygene.api.property.PropertyDescriptor;
+import org.apache.polygene.spi.entity.ManyAssociationState;
+import org.apache.polygene.spi.entity.NamedAssociationState;
+import org.apache.polygene.spi.entitystore.helpers.DefaultEntityState;
+import org.jooq.Field;
+import org.jooq.InsertSetMoreStep;
+import org.jooq.InsertSetStep;
+import org.jooq.Name;
+import org.jooq.Record;
+import org.jooq.Schema;
+import org.jooq.Table;
+import org.jooq.UpdateSetMoreStep;
+import org.jooq.impl.DSL;
+
+class MixinTable
+    implements TableFields
+{
+
+    private final Table<Record> mixinTable;
+    private final Table<Record> mixinAssocsTable;
+
+    private final JooqDslContext dsl;
+    private final Map<QualifiedName, Field<Object>> properties = new 
ConcurrentHashMap<>();
+    private final Map<QualifiedName, Field<String>> associations = new 
ConcurrentHashMap<>();
+    private final List<QualifiedName> manyAssociations = new 
CopyOnWriteArrayList<>();
+    private final List<QualifiedName> namedAssociations = new 
CopyOnWriteArrayList<>();
+
+    private TypesTable types;
+    private final Class<?> mixinType;
+
+    MixinTable( JooqDslContext dsl, Schema schema, TypesTable types, Class<?> 
mixinType,
+                EntityDescriptor descriptor )
+    {
+        this.dsl = dsl;
+        this.types = types;
+        this.mixinType = mixinType;
+        mixinTable = types.tableFor( mixinType, descriptor );
+        mixinAssocsTable = getAssocsTable( descriptor, schema );
+
+        descriptor.valueType().properties()
+                  .filter( this::isThisMixin )
+                  .forEach( propDescriptor ->
+                            {
+                                QualifiedName propertyName = 
propDescriptor.qualifiedName();
+                                Field<Object> propertyField = types.fieldOf( 
propDescriptor );
+                                properties.put( propertyName, propertyField );
+                            }
+                          );
+
+        descriptor.valueType().associations()
+                  .filter( this::isThisMixin )
+                  .forEach( assocDescriptor ->
+                            {
+                                QualifiedName assocName = 
assocDescriptor.qualifiedName();
+                                Field<String> assocField = types.fieldOf( 
assocDescriptor );
+                                associations.put( assocName, assocField );
+                            }
+                          );
+
+        descriptor.valueType().manyAssociations()
+                  .filter( this::isThisMixin )
+                  .forEach( assocDescriptor -> manyAssociations.add( 
assocDescriptor.qualifiedName() ) );
+
+        descriptor.valueType().namedAssociations()
+                  .filter( this::isThisMixin )
+                  .forEach( assocDescriptor -> namedAssociations.add( 
assocDescriptor.qualifiedName() ) );
+    }
+
+    void insertMixinState( DefaultEntityState state, String valueIdentity )
+    {
+        InsertSetMoreStep<Record> primaryTable =
+            dsl.insertInto( mixinTable )
+               .set( identityColumn, valueIdentity )
+               .set( createdColumn, new Timestamp( System.currentTimeMillis() 
) );
+
+        properties.forEach( ( propertyName, propertyField ) -> 
primaryTable.set( propertyField, state.propertyValueOf( propertyName ) ) );
+        associations.forEach( ( assocName, assocField ) ->
+                              {
+                                  EntityReference reference = 
state.associationValueOf( assocName );
+                                  String identity = null;
+                                  if( reference != null )
+                                  {
+                                      identity = 
reference.identity().toString();
+                                  }
+                                  primaryTable.set( assocField, identity );
+                              }
+                            );
+        int result = primaryTable.execute();
+
+        if( mixinAssocsTable != null )
+        {
+            insertManyAndNamedAssociations( state, valueIdentity );
+        }
+    }
+
+    private void insertManyAndNamedAssociations( DefaultEntityState state, 
String valueIdentity )
+    {
+        manyAssociations.forEach( assocName ->
+                                  {
+                                      InsertSetStep<Record> assocsTable = 
dsl.insertInto( mixinAssocsTable );
+                                      ManyAssociationState entityReferences = 
state.manyAssociationValueOf( assocName );
+                                      int endCount = entityReferences.count();
+                                      int counter = 0;
+                                      for( EntityReference ref : 
entityReferences )
+                                      {
+                                          InsertSetMoreStep<Record> set = 
assocsTable.set( identityColumn, valueIdentity )
+                                                                               
      .set( nameColumn, assocName.name() )
+                                                                               
      .set( indexColumn, "" + counter++ )
+                                                                               
      .set( referenceColumn, ref == null ? null : ref.identity().toString() );
+                                          if( ++counter < endCount )
+                                          {
+                                              set.newRecord();
+                                          }
+                                      }
+                                      InsertSetMoreStep<Record> assocs = 
assocsTable.set( Collections.emptyMap() );
+                                      assocs.execute();
+                                  } );
+
+        namedAssociations.forEach( assocName ->
+                                   {
+                                       InsertSetStep<Record> assocsTable = 
dsl.insertInto( mixinAssocsTable );
+                                       NamedAssociationState entityReferences 
= state.namedAssociationValueOf( assocName );
+                                       int count = entityReferences.count();
+                                       for( String name : entityReferences )
+                                       {
+                                           EntityReference ref = 
entityReferences.get( name );
+                                           InsertSetMoreStep<Record> set = 
assocsTable.set( identityColumn, valueIdentity )
+                                                                               
       .set( nameColumn, assocName.name() )
+                                                                               
       .set( indexColumn, name )
+                                                                               
       .set( referenceColumn, ref.identity().toString() );
+                                           if( --count > 0 )
+                                           {
+                                               set.newRecord();
+                                           }
+                                       }
+                                       InsertSetMoreStep<Record> assocs = 
assocsTable.set( Collections.emptyMap() );
+                                       assocs.execute();
+                                   } );
+    }
+
+    Table<Record> associationsTable()
+    {
+        return mixinAssocsTable;
+    }
+
+    private boolean isThisMixin( PropertyDescriptor descriptor )
+    {
+        Class<?> declaringClass = declaredIn( descriptor );
+        return mixinType.equals( declaringClass );
+    }
+
+    private boolean isThisMixin( AssociationDescriptor descriptor )
+    {
+        Class<?> declaringClass = declaredIn( descriptor );
+        return mixinType.equals( declaringClass );
+    }
+
+    private Class<?> declaredIn( PropertyDescriptor descriptor )
+    {
+        AccessibleObject accessor = descriptor.accessor();
+        if( accessor instanceof Method )
+        {
+            return ( (Method) accessor ).getDeclaringClass();
+        }
+        throw new UnsupportedOperationException( "Property declared as " + 
accessor.getClass() + " is not supported in this Entity Store yet." );
+    }
+
+    private Class<?> declaredIn( AssociationDescriptor descriptor )
+    {
+        AccessibleObject accessor = descriptor.accessor();
+        if( accessor instanceof Method )
+        {
+            return ( (Method) accessor ).getDeclaringClass();
+        }
+        throw new UnsupportedOperationException( "Property declared as " + 
accessor.getClass() + " is not supported in this Entity Store yet." );
+    }
+
+    void modifyMixinState( DefaultEntityState state, String valueId )
+    {
+        UpdateSetMoreStep<Record> primaryTable =
+            dsl.update( mixinTable )
+               .set( Collections.emptyMap() );  // empty map is a hack to get 
the right type returned from JOOQ.
+
+        properties.forEach( ( propertyName, propertyField ) -> 
primaryTable.set( propertyField, state.propertyValueOf( propertyName ) ) );
+        associations.forEach( ( assocName, assocField ) ->
+                              {
+                                  EntityReference reference = 
state.associationValueOf( assocName );
+                                  primaryTable.set( assocField,
+                                                    reference == null ? null : 
reference.identity().toString()
+                                                  );
+                              }
+                            );
+        int result = primaryTable.execute();
+
+        if( mixinAssocsTable != null )
+        {
+            // Need to remove existing records.
+            dsl.delete( mixinAssocsTable )
+               .where( identityColumn.eq( valueId ) )
+               .execute();
+            insertManyAndNamedAssociations( state, valueId );
+        }
+    }
+
+    private Table<Record> getAssocsTable( EntityDescriptor descriptor, Schema 
schema )
+    {
+        if( descriptor.state().manyAssociations().count() > 0
+            || descriptor.state().namedAssociations().count() > 0 )
+        {
+            Name tableName = DSL.name( schema.getName(), mixinTable.getName() 
+ ASSOCS_TABLE_POSTFIX );
+            Table<Record> table = DSL.table( tableName );
+            int result2 = dsl.createTableIfNotExists( table )
+                             .column( identityColumn )
+                             .column( nameColumn )
+                             .column( indexColumn )
+                             .column( referenceColumn )
+                             .execute();
+            return table;
+        }
+        else
+        {
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlEntityStoreConfiguration.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlEntityStoreConfiguration.java
 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlEntityStoreConfiguration.java
new file mode 100644
index 0000000..e93d7a3
--- /dev/null
+++ 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlEntityStoreConfiguration.java
@@ -0,0 +1,73 @@
+/*
+ *  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.polygene.entitystore.sql;
+
+import org.apache.polygene.api.common.UseDefaults;
+import org.apache.polygene.api.property.Property;
+import org.apache.polygene.library.sql.common.SQLConfiguration;
+
+// START SNIPPET: config
+public interface SqlEntityStoreConfiguration extends SQLConfiguration
+{
+    /**
+     * Name of the database schema to use.
+     * Ignored on SQL databases that don't support schemas.
+     */
+    @UseDefaults( "POLYGENE" )
+    @Override
+    Property<String> schemaName();
+
+    /**
+     * Name of the entities table.
+     * <p>
+     * This table contains the Identity and other metadata about each entity 
instance
+     * </p>
+     */
+    @UseDefaults( "ENTITIES" )
+    Property<String> entitiesTableName();
+
+    /**
+     * Name of the entity types table.
+     * <p>
+     * This table contains the metainfo about each type. Types are versioned 
according to
+     * application version, to support entity migration over time, and 
therefor there might
+     * be (but not necessarily) multiple tables for entity types that has 
evolved beyond
+     * what can be managed within a single table.
+     * </p>
+     */
+    @UseDefaults( "TYPES" )
+    Property<String> typesTableName();
+
+    /**
+     * Defines whether the database schema and table should be created if not 
already present.
+     */
+    @UseDefaults( "true" )
+    Property<Boolean> createIfMissing();
+
+    /**
+     * The SQL dialect that is being used.
+     * <p>
+     * Typically that is matching a supporting dialect in JOOQ.
+     * See {@link org.jooq.SQLDialect} for supported values.
+     * </p>
+     * @return The property with the dialect value.
+     */
+    @UseDefaults( "" )
+    Property<String> dialect();
+}
+// END SNIPPET: config

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlEntityStoreMixin.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlEntityStoreMixin.java
 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlEntityStoreMixin.java
new file mode 100644
index 0000000..5f17fd4
--- /dev/null
+++ 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlEntityStoreMixin.java
@@ -0,0 +1,367 @@
+/*
+ *  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.polygene.entitystore.sql;
+
+import java.sql.Timestamp;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.Period;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+import org.apache.polygene.api.association.AssociationDescriptor;
+import org.apache.polygene.api.association.AssociationStateDescriptor;
+import org.apache.polygene.api.common.QualifiedName;
+import org.apache.polygene.api.entity.EntityDescriptor;
+import org.apache.polygene.api.entity.EntityReference;
+import org.apache.polygene.api.identity.HasIdentity;
+import org.apache.polygene.api.identity.IdentityGenerator;
+import org.apache.polygene.api.injection.scope.Service;
+import org.apache.polygene.api.injection.scope.This;
+import org.apache.polygene.api.serialization.Serialization;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.type.ValueType;
+import org.apache.polygene.api.usecase.Usecase;
+import org.apache.polygene.spi.entity.EntityState;
+import org.apache.polygene.spi.entity.EntityStatus;
+import org.apache.polygene.spi.entitystore.DefaultEntityStoreUnitOfWork;
+import org.apache.polygene.spi.entitystore.EntityNotFoundException;
+import org.apache.polygene.spi.entitystore.EntityStore;
+import org.apache.polygene.spi.entitystore.EntityStoreSPI;
+import org.apache.polygene.spi.entitystore.EntityStoreUnitOfWork;
+import org.apache.polygene.spi.entitystore.StateCommitter;
+import org.apache.polygene.spi.entitystore.helpers.DefaultEntityState;
+import org.jooq.Record;
+import org.jooq.Result;
+import org.jooq.SelectQuery;
+
+import static 
org.apache.polygene.api.entity.EntityReference.parseEntityReference;
+
+public class SqlEntityStoreMixin
+    implements EntityStore, EntityStoreSPI
+{
+    @This
+    private SqlTable sqlTable;
+
+    @Service
+    private IdentityGenerator identityGenerator;
+
+    @Service
+    private Serialization serialization;
+
+    @Override
+    public EntityState newEntityState( EntityStoreUnitOfWork unitOfWork, 
EntityReference reference, EntityDescriptor entityDescriptor )
+    {
+        return new DefaultEntityState( unitOfWork.currentTime(), reference, 
entityDescriptor );
+    }
+
+    @Override
+    public EntityState entityStateOf( EntityStoreUnitOfWork unitOfWork, 
ModuleDescriptor module, EntityReference reference )
+    {
+        BaseEntity baseEntity = sqlTable.fetchBaseEntity( reference, module );
+        SelectQuery<Record> selectQuery = sqlTable.createGetEntityQuery( 
baseEntity.type, reference );
+        Result<Record> result = selectQuery.fetch();
+        if( result.isEmpty() )
+        {
+            throw new EntityNotFoundException( reference );
+        }
+        return toEntityState( result, baseEntity, reference, module );
+    }
+
+    protected EntityState toEntityState( Result<Record> result, BaseEntity 
baseEntity, EntityReference reference, ModuleDescriptor module )
+    {
+        AssociationStateDescriptor stateDescriptor = baseEntity.type.state();
+        Map<QualifiedName, Object> properties = new HashMap<>();
+        properties.put( HasIdentity.IDENTITY_STATE_NAME, baseEntity.identity );
+        stateDescriptor.properties()
+                       .filter( prop -> 
!HasIdentity.IDENTITY_STATE_NAME.equals( prop.qualifiedName() ) )
+                       .forEach( prop ->
+                                 {
+                                     QualifiedName qualifiedName = 
prop.qualifiedName();
+                                     Object value = result.getValue( 0, 
qualifiedName.name() );
+                                     value = amendValue( value, 
prop.valueType(), module );
+                                     properties.put( qualifiedName, value );
+                                 } );
+        Map<QualifiedName, EntityReference> assocations = new HashMap<>();
+        stateDescriptor.associations()
+                       .forEach( assoc ->
+                                 {
+                                     QualifiedName qualifiedName = 
assoc.qualifiedName();
+                                     String value = (String) result.getValue( 
0, qualifiedName.name() );
+                                     if( value != null )
+                                     {
+                                         assocations.put( qualifiedName, 
parseEntityReference( value ) );
+                                     }
+                                 } );
+        Map<QualifiedName, List<EntityReference>> manyAssocs = new HashMap<>();
+        Map<QualifiedName, Map<String, EntityReference>> namedAssocs = new 
HashMap<>();
+        sqlTable.fetchAssociations( baseEntity, baseEntity.type, 
associationValue ->
+        {
+            if( stateDescriptor.hasManyAssociation( associationValue.name ) )
+            {
+                addManyAssociation( stateDescriptor, manyAssocs, 
associationValue );
+            }
+            else if( stateDescriptor.hasNamedAssociation( 
associationValue.name ) )
+            {
+                addNamedAssociation( stateDescriptor, namedAssocs, 
associationValue );
+            }
+        } );
+
+        return new DefaultEntityState( baseEntity.version,
+                                       baseEntity.modifedAt,
+                                       reference,
+                                       EntityStatus.LOADED,
+                                       baseEntity.type,
+                                       properties,
+                                       assocations,
+                                       manyAssocs,
+                                       namedAssocs );
+    }
+
+    private Object amendValue( Object value, ValueType type, ModuleDescriptor 
module )
+    {
+        if( value == null )
+        {
+            return null;
+        }
+        if( value.getClass().isPrimitive() )
+        {
+            return value;
+        }
+        if( type.equals( ValueType.STRING )
+            || type.equals( ValueType.INTEGER )
+            || type.equals( ValueType.BOOLEAN )
+            || type.equals( ValueType.DOUBLE )
+            || type.equals( ValueType.IDENTITY )
+            || type.equals( ValueType.LONG )
+            || type.equals( ValueType.FLOAT )
+            || type.equals( ValueType.BYTE )
+            || type.equals( ValueType.CHARACTER )
+            || type.equals( ValueType.ENTITY_REFERENCE )
+            || type.equals( ValueType.SHORT )
+            || type.equals( ValueType.BIG_INTEGER )
+            || type.equals( ValueType.BIG_DECIMAL )
+            )
+        {
+            return value;
+        }
+        if( type.equals( ValueType.INSTANT ) )  // Instant type contains 
timezone (why?), and we promise to always return in UTC (or is that just bad 
testcases, and that we actually promise to return original instant timezone?).
+        {
+            if( value instanceof Instant )
+            {
+                return Instant.ofEpochMilli( ( (Instant) value 
).toEpochMilli() );
+            }
+            if( value instanceof OffsetDateTime )
+            {
+                return Instant.ofEpochMilli( ( (OffsetDateTime) value 
).toInstant().toEpochMilli() );
+            }
+            if( value instanceof ZonedDateTime )
+            {
+                return Instant.ofEpochMilli( ( (ZonedDateTime) value 
).toInstant().toEpochMilli() );
+            }
+        }
+        if( type.equals( ValueType.ZONED_DATE_TIME ) )
+        {
+            if( value instanceof ZonedDateTime )
+            {
+                return ( (ZonedDateTime) value ).withZoneSameInstant( 
ZoneOffset.UTC );
+            }
+            if( value instanceof OffsetDateTime )
+            {
+                return ( (OffsetDateTime) value 
).toZonedDateTime().withZoneSameInstant( ZoneOffset.UTC );
+            }
+        }
+        if( type.equals( ValueType.OFFSET_DATE_TIME ) )
+        {
+            if( value instanceof OffsetDateTime )
+            {
+                return ( (OffsetDateTime) value ).withOffsetSameInstant( 
ZoneOffset.UTC );
+            }
+            if( value instanceof ZonedDateTime )
+            {
+                return ( (ZonedDateTime) value 
).toOffsetDateTime().withOffsetSameInstant( ZoneOffset.UTC );
+            }
+        }
+        if( type.equals( ValueType.LOCAL_DATE_TIME ) )
+        {
+            if( value instanceof Timestamp )
+            {
+                return ( (Timestamp) value ).toLocalDateTime();
+            }
+        }
+        if( type.equals( ValueType.PERIOD ) )
+        {
+            if( value instanceof String )
+            {
+                return Period.parse( (String) value );
+            }
+        }
+        if( type.equals( ValueType.DURATION ) )
+        {
+            if( value instanceof String )
+            {
+                return Duration.parse( (String) value );
+            }
+        }
+        if( type.equals( ValueType.LOCAL_DATE ) )
+        {
+            if( value instanceof java.sql.Date )
+            {
+                return ( (java.sql.Date) value ).toLocalDate();
+            }
+        }
+        if( type.equals( ValueType.LOCAL_TIME ) )
+        {
+            if( value instanceof java.sql.Time )
+            {
+                return ( (java.sql.Time) value ).toLocalTime();
+            }
+        }
+        // otherwise, we deal with a serialized value.
+        return serialization.deserialize( module, type, (String) value );
+    }
+
+    private void addNamedAssociation( AssociationStateDescriptor 
stateDescriptor, Map<QualifiedName, Map<String, EntityReference>> namedAssocs, 
AssociationValue associationValue )
+    {
+        AssociationDescriptor descriptor = 
stateDescriptor.getNamedAssociationByName( associationValue.name.name() );
+        QualifiedName qualifiedName = descriptor.qualifiedName();
+        Map<String, EntityReference> map = namedAssocs.computeIfAbsent( 
qualifiedName, k -> new HashMap<>() );
+        map.put( associationValue.position, parseEntityReference( 
associationValue.reference ) );
+    }
+
+    private void addManyAssociation( AssociationStateDescriptor 
stateDescriptor, Map<QualifiedName, List<EntityReference>> manyAssocs, 
AssociationValue associationValue )
+    {
+        AssociationDescriptor descriptor = 
stateDescriptor.getManyAssociationByName( associationValue.name.name() );
+        QualifiedName qualifiedName = descriptor.qualifiedName();
+        List<EntityReference> list = manyAssocs.computeIfAbsent( 
qualifiedName, k -> new ArrayList<>() );
+        String reference = associationValue.reference;
+        list.add( reference == null ? null : parseEntityReference( reference ) 
);
+    }
+
+    @Override
+    public String versionOf( EntityStoreUnitOfWork unitOfWork, EntityReference 
reference )
+    {
+        BaseEntity baseEntity = sqlTable.fetchBaseEntity( reference, 
unitOfWork.module() );
+        return baseEntity.version;
+    }
+
+    @Override
+    public StateCommitter applyChanges( EntityStoreUnitOfWork unitOfWork, 
Iterable<EntityState> state )
+    {
+        return new JooqStateCommitter( unitOfWork, state, 
sqlTable.jooqDslContext() );
+    }
+
+    @Override
+    public EntityStoreUnitOfWork newUnitOfWork( ModuleDescriptor module, 
Usecase usecase, Instant currentTime )
+    {
+        return new DefaultEntityStoreUnitOfWork( module,
+                                                 this,
+                                                 identityGenerator.generate( 
SqlEntityStoreService.class ),
+                                                 usecase,
+                                                 currentTime
+        );
+    }
+
+    @Override
+    public Stream<EntityState> entityStates( ModuleDescriptor module )
+    {
+        Stream<? extends EntityDescriptor> entityTypes = 
module.entityComposites();
+        return entityTypes
+            .flatMap( type -> sqlTable.fetchAll( type, module ) )
+            .map( baseEntity ->
+                  {
+                      EntityReference reference = 
EntityReference.entityReferenceFor( baseEntity.identity );
+                      SelectQuery<Record> selectQuery = 
sqlTable.createGetEntityQuery( baseEntity.type, reference );
+                      Result<Record> result = selectQuery.fetch();
+                      return toEntityState( result, baseEntity, reference, 
module );
+                  } );
+    }
+
+    private class JooqStateCommitter
+        implements StateCommitter
+    {
+        private final EntityStoreUnitOfWork unitOfWork;
+        private final Iterable<EntityState> states;
+        private final JooqDslContext dslContext;
+        private final ModuleDescriptor module;
+
+        JooqStateCommitter( EntityStoreUnitOfWork unitOfWork, 
Iterable<EntityState> states, JooqDslContext dslContext )
+        {
+            this.unitOfWork = unitOfWork;
+            this.states = states;
+            this.dslContext = dslContext;
+            this.module = unitOfWork.module();
+        }
+
+        private void newState( DefaultEntityState state, EntityStoreUnitOfWork 
unitOfWork )
+        {
+            EntityReference ref = state.entityReference();
+            EntityDescriptor descriptor = state.entityDescriptor();
+            sqlTable.createNewBaseEntity( ref, descriptor, this.unitOfWork );
+            sqlTable.insertEntity( state, sqlTable.fetchBaseEntity( ref, 
module ), unitOfWork );
+        }
+
+        private void updateState( DefaultEntityState state, 
EntityStoreUnitOfWork unitOfWork )
+        {
+            EntityDescriptor descriptor = state.entityDescriptor();
+            BaseEntity baseEntity = sqlTable.fetchBaseEntity( 
state.entityReference(), descriptor.module() );
+            sqlTable.updateEntity( state, baseEntity, unitOfWork );
+        }
+
+        private void removeState( DefaultEntityState state )
+        {
+            EntityReference reference = state.entityReference();
+            EntityDescriptor descriptor = state.entityDescriptor();
+            sqlTable.removeEntity( reference, descriptor );
+        }
+
+        @Override
+        public void commit()
+        {
+            dslContext.transaction( configuration ->
+                                    {
+                                        for( EntityState es : this.states )
+                                        {
+                                            DefaultEntityState state = 
(DefaultEntityState) es;
+                                            if( state.status() == 
EntityStatus.NEW )
+                                            {
+                                                newState( state, unitOfWork );
+                                            }
+                                            if( state.status() == 
EntityStatus.UPDATED )
+                                            {
+                                                updateState( state, unitOfWork 
);
+                                            }
+                                            if( state.status() == 
EntityStatus.REMOVED )
+                                            {
+                                                removeState( state );
+                                            }
+                                        }
+                                    } );
+        }
+
+        @Override
+        public void cancel()
+        {
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlEntityStoreService.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlEntityStoreService.java
 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlEntityStoreService.java
new file mode 100644
index 0000000..cf401d6
--- /dev/null
+++ 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlEntityStoreService.java
@@ -0,0 +1,36 @@
+/*
+ *  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.polygene.entitystore.sql;
+
+import org.apache.polygene.api.concern.Concerns;
+import org.apache.polygene.api.configuration.Configuration;
+import org.apache.polygene.api.mixin.Mixins;
+import org.apache.polygene.spi.entitystore.ConcurrentModificationCheckConcern;
+import org.apache.polygene.spi.entitystore.EntityStateVersions;
+import org.apache.polygene.spi.entitystore.EntityStore;
+import org.apache.polygene.spi.entitystore.StateChangeNotificationConcern;
+
+/**
+ * SQL EntityStore service.
+ */
+@Concerns( { StateChangeNotificationConcern.class, 
ConcurrentModificationCheckConcern.class } )
+@Mixins( { SqlEntityStoreMixin.class } )
+public interface SqlEntityStoreService
+    extends EntityStore, EntityStateVersions, Configuration, SqlTable
+{
+}

Reply via email to