http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlTable.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlTable.java
 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlTable.java
new file mode 100644
index 0000000..6537782
--- /dev/null
+++ 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlTable.java
@@ -0,0 +1,271 @@
+/*
+ *  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.util.function.Consumer;
+import java.util.stream.Stream;
+import javax.sql.DataSource;
+import org.apache.polygene.api.PolygeneAPI;
+import org.apache.polygene.api.composite.TransientBuilderFactory;
+import org.apache.polygene.api.configuration.Configuration;
+import org.apache.polygene.api.entity.EntityDescriptor;
+import org.apache.polygene.api.entity.EntityReference;
+import org.apache.polygene.api.injection.scope.Service;
+import org.apache.polygene.api.injection.scope.Structure;
+import org.apache.polygene.api.injection.scope.This;
+import org.apache.polygene.api.injection.scope.Uses;
+import org.apache.polygene.api.mixin.Mixins;
+import org.apache.polygene.api.object.ObjectFactory;
+import org.apache.polygene.api.service.ServiceActivation;
+import org.apache.polygene.api.service.ServiceDescriptor;
+import org.apache.polygene.api.structure.Application;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.spi.entitystore.EntityStoreUnitOfWork;
+import org.apache.polygene.spi.entitystore.helpers.DefaultEntityState;
+import org.jooq.Record;
+import org.jooq.SQLDialect;
+import org.jooq.Schema;
+import org.jooq.SelectQuery;
+import org.jooq.conf.RenderNameStyle;
+import org.jooq.conf.Settings;
+import org.jooq.impl.DSL;
+
+/**
+ * This class handles all the Jooq interactions.
+ * <p>
+ * <p>
+ * <p>
+ * <h1>Tables</h1>
+ * <h2>Types Table</h2>
+ * <ul>
+ * <li>identity</li>
+ * <li>table_name</li>
+ * <li>created_at</li>
+ * <li>modified_at</li>
+ * </ul>
+ * <h2>Entities Table</h2>
+ * <ul>
+ * <li>identity</li>
+ * <li>app_version</li>
+ * <li>value_id</li>
+ * <li>version</li>
+ * <li>type</li>
+ * <li>modified_at</li>
+ * <li>created_at</li>
+ * </ul>
+ * <h2>Mixin Tables</h2>
+ * <p>
+ * Each Mixin is stored in its own table. Only the following column is always 
present;
+ * <ul>
+ * <li>identity - this is not entity identity but the UUID of the value_id in 
the Entities Table above.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Each Property of the Mixin (as defined by QualifiedName of the Property, 
will reside in its own column.
+ * All values in columns are (for now) serialized using a ValueSerialization 
service.
+ * </p>
+ * <p>
+ * Associations also has their own columns in the table, with the 
EntityReference.identity() stored in them.
+ * </p>
+ * <p>
+ * ManyAssociations and NamedAssociations are stored in a separate table, 
named &lt;mixintable&gt;_ASSOCS, see below.
+ * </p>
+ * <h2>Mixin_ASSOCS Table</h2>
+ * <ul>
+ * <li>identity - the value_id of the mixin value</li>
+ * <li>name - the name of the ManyAssociation or NamedAssociation</li>
+ * <li>position - for NamedAssociation this is the 'name' (i.e key) and for 
ManyAssociation this is the index into the list.</li>
+ * <li>reference - EntityReference.identity of that association</li>
+ * </ul>
+ */
+@Mixins( SqlTable.Mixin.class )
+public interface SqlTable extends ServiceActivation
+{
+    BaseEntity fetchBaseEntity( EntityReference reference, ModuleDescriptor 
module );
+
+    SelectQuery<Record> createGetEntityQuery( EntityDescriptor descriptor, 
EntityReference reference );
+
+    void fetchAssociations( BaseEntity entity, EntityDescriptor descriptor, 
Consumer<AssociationValue> consume );
+
+    void createNewBaseEntity( EntityReference ref, EntityDescriptor 
descriptor, EntityStoreUnitOfWork unitOfWork );
+
+    void insertEntity( DefaultEntityState state, BaseEntity baseEntity, 
EntityStoreUnitOfWork unitOfWork );
+
+    void updateEntity( DefaultEntityState state, BaseEntity baseEntity, 
EntityStoreUnitOfWork unitOfWork );
+
+    JooqDslContext jooqDslContext();
+
+    void removeEntity( EntityReference entityReference, EntityDescriptor 
descriptor );
+
+    Stream<BaseEntity> fetchAll( EntityDescriptor type, ModuleDescriptor 
module );
+
+    class Mixin
+        implements SqlTable, TableFields, ServiceActivation
+    {
+        @Structure
+        private Application application;
+
+        @Structure
+        private TransientBuilderFactory tbf;
+
+        @This
+        private Configuration<SqlEntityStoreConfiguration> configuration;
+
+        @Service
+        private DataSource datasource;
+
+        @Uses
+        private ServiceDescriptor serviceDescriptor;
+
+        private EntitiesTable entitiesTable;
+
+        private TypesTable types;
+        private JooqDslContext dsl;
+
+        @Override
+        public BaseEntity fetchBaseEntity( EntityReference reference, 
ModuleDescriptor module )
+        {
+            return entitiesTable.fetchEntity( reference, module );
+        }
+
+        @Override
+        public Stream<BaseEntity> fetchAll( EntityDescriptor type, 
ModuleDescriptor module )
+        {
+            return entitiesTable.fetchAll( type, module );
+        }
+
+        @Override
+        public SelectQuery<Record> createGetEntityQuery( EntityDescriptor 
descriptor, EntityReference reference )
+        {
+            return entitiesTable.createGetEntityQuery( descriptor, reference );
+        }
+
+        @Override
+        public void fetchAssociations( BaseEntity entity, EntityDescriptor 
descriptor, Consumer<AssociationValue> consume )
+        {
+            entitiesTable.fetchAssociations( entity, descriptor, consume );
+        }
+
+        @Override
+        public void createNewBaseEntity( EntityReference ref, EntityDescriptor 
descriptor, EntityStoreUnitOfWork unitOfWork )
+        {
+            entitiesTable.createNewBaseEntity( ref, descriptor, unitOfWork );
+        }
+
+        @Override
+        public void insertEntity( DefaultEntityState state, BaseEntity 
baseEntity, EntityStoreUnitOfWork unitOfWork )
+        {
+            entitiesTable.insertEntity( state, baseEntity );
+        }
+
+        @Override
+        public void updateEntity( DefaultEntityState state, BaseEntity 
baseEntity, EntityStoreUnitOfWork unitOfWork )
+        {
+            entitiesTable.modifyEntity( state, baseEntity, unitOfWork );
+        }
+
+        @Override
+        public JooqDslContext jooqDslContext()
+        {
+            return dsl;
+        }
+
+        @Override
+        public void removeEntity( EntityReference reference, EntityDescriptor 
descriptor )
+        {
+            entitiesTable.removeEntity( reference, descriptor );
+        }
+
+        @Override
+        public void activateService()
+            throws Exception
+        {
+            SqlEntityStoreConfiguration config = this.configuration.get();
+            SQLDialect dialect = getSqlDialect( config );
+
+            Settings settings = serviceDescriptor
+                .metaInfo( Settings.class )
+                .withRenderNameStyle( RenderNameStyle.QUOTED );
+            dsl = tbf.newTransient( JooqDslContext.class, settings, dialect );
+
+            String schemaName = config.schemaName().get();
+            String typesTableName = config.typesTableName().get();
+            String entitiesTableName = config.entitiesTableName().get();
+            Schema schema = DSL.schema( DSL.name( schemaName ) );
+            types = new TypesTable( dsl, schema, dialect, typesTableName );
+            entitiesTable = new EntitiesTable( dsl, schema, types, 
application.version(), entitiesTableName );
+
+            // Eventually create schema
+            if( config.createIfMissing().get() )
+            {
+                if( !dialect.equals( SQLDialect.SQLITE )
+                    && dsl.meta().getSchemas().stream().noneMatch( s -> 
schema.getName().equalsIgnoreCase( s.getName() ) ) )
+                {
+                    dsl.createSchema( schema ).execute();
+                }
+
+                dsl.createTableIfNotExists( DSL.name( schemaName, 
typesTableName ) )
+                   .column( identityColumn )
+                   .column( tableNameColumn )
+                   .column( createdColumn )
+                   .column( modifiedColumn )
+                   .execute();
+
+                dsl.createTableIfNotExists( DSL.name( schemaName, 
entitiesTableName ) )
+                   .column( identityColumn )
+                   .column( applicationVersionColumn )
+                   .column( valueIdentityColumn )
+                   .column( versionColumn )
+                   .column( typeNameColumn )
+                   .column( modifiedColumn )
+                   .column( createdColumn )
+                   .execute();
+            }
+            datasource.getConnection().commit();
+        }
+
+        @Override
+        public void passivateService()
+            throws Exception
+        {
+
+        }
+
+        private SQLDialect getSqlDialect( SqlEntityStoreConfiguration config )
+        {
+            SQLDialect dialect = null;
+            String dialectString = config.dialect().get();
+            if( dialectString.length() == 0 )
+            {
+                dialect = SQLDialect.DEFAULT;
+            }
+            else
+            {
+                try
+                {
+                    dialect = SQLDialect.valueOf( dialectString );
+                }
+                catch( IllegalArgumentException e )
+                {
+                    throw new IllegalArgumentException( "Invalid SQLDialect: 
'" + dialectString + "'" );
+                }
+            }
+            return dialect;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/6055b8f7/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlType.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlType.java
 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlType.java
new file mode 100644
index 0000000..21de44d
--- /dev/null
+++ 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/SqlType.java
@@ -0,0 +1,156 @@
+/*
+ *  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.math.BigDecimal;
+import java.math.BigInteger;
+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.DefaultDataType;
+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.REAL;
+        }
+        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( Byte.class.isAssignableFrom( propertyType ) )
+        {
+            return (DataType<T>) SQLDataType.INTEGER;
+        }
+        if( BigDecimal.class.isAssignableFrom( propertyType ) )
+        {
+            return (DataType<T>) SQLDataType.DECIMAL;
+        }
+        if( BigInteger.class.isAssignableFrom( propertyType ) )
+        {
+            return (DataType<T>) SQLDataType.DECIMAL(50, 0);
+        }
+        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.REAL;
+            }
+            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-sql/src/main/java/org/apache/polygene/entitystore/sql/TableFields.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/TableFields.java
 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/TableFields.java
new file mode 100644
index 0000000..cfa9d99
--- /dev/null
+++ 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/TableFields.java
@@ -0,0 +1,69 @@
+/*
+ *  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 org.jooq.Field;
+
+import static org.apache.polygene.entitystore.sql.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-sql/src/main/java/org/apache/polygene/entitystore/sql/TypesTable.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/TypesTable.java
 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/TypesTable.java
new file mode 100644
index 0000000..d04bc95
--- /dev/null
+++ 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/TypesTable.java
@@ -0,0 +1,189 @@
+/*
+ *  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.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-sql/src/main/java/org/apache/polygene/entitystore/sql/assembly/SqlEntityStoreAssembler.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/assembly/SqlEntityStoreAssembler.java
 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/assembly/SqlEntityStoreAssembler.java
new file mode 100644
index 0000000..b1f660f
--- /dev/null
+++ 
b/extensions/entitystore-sql/src/main/java/org/apache/polygene/entitystore/sql/assembly/SqlEntityStoreAssembler.java
@@ -0,0 +1,77 @@
+/*
+ *  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.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.sql.JooqDslContext;
+import org.apache.polygene.entitystore.sql.SqlEntityStoreConfiguration;
+import org.apache.polygene.entitystore.sql.SqlEntityStoreService;
+import org.jooq.SQLDialect;
+import org.jooq.conf.RenderNameStyle;
+import org.jooq.conf.Settings;
+
+/**
+ * JOOQ EntityStore assembly.
+ */
+@SuppressWarnings( "WeakerAccess" )
+public class SqlEntityStoreAssembler extends 
Assemblers.VisibilityIdentityConfig<SqlEntityStoreAssembler>
+    implements Assembler
+{
+    public static final Identity DEFAULT_ENTITYSTORE_IDENTITY = 
StringIdentity.identityOf( "entitystore-sql" );
+
+    @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( SqlEntityStoreService.class )
+              .identifiedBy( identity )
+              .visibleIn( visibility() )
+              .instantiateOnStartup()
+              .setMetaInfo( settings );
+
+        if( hasConfig() )
+        {
+            configModule().entities( SqlEntityStoreConfiguration.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-sql/src/test/java/org/apache/polygene/entitystore/sql/SqlEntityStoreTest.java
----------------------------------------------------------------------
diff --git 
a/extensions/entitystore-sql/src/test/java/org/apache/polygene/entitystore/sql/SqlEntityStoreTest.java
 
b/extensions/entitystore-sql/src/test/java/org/apache/polygene/entitystore/sql/SqlEntityStoreTest.java
new file mode 100644
index 0000000..d8f0d59
--- /dev/null
+++ 
b/extensions/entitystore-sql/src/test/java/org/apache/polygene/entitystore/sql/SqlEntityStoreTest.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.polygene.entitystore.sql;
+
+import org.apache.polygene.api.common.Visibility;
+import org.apache.polygene.bootstrap.AssemblyException;
+import org.apache.polygene.bootstrap.ModuleAssembly;
+import org.apache.polygene.entitystore.sql.assembly.SqlEntityStoreAssembler;
+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 SqlEntityStoreTest 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 SqlEntityStoreAssembler()
+            .withConfig( config, Visibility.layer )
+            .identifiedBy( "sql-entitystore" )
+            .assemble( module );
+        // END SNIPPET: assembly
+
+        SqlEntityStoreConfiguration jooqDefaults = config.forMixin( 
SqlEntityStoreConfiguration.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/settings.gradle
----------------------------------------------------------------------
diff --git a/settings.gradle b/settings.gradle
index 78570dc..3a4067c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -61,13 +61,13 @@ include 'core:api',
         'extensions:entitystore-hazelcast',
         'extensions:entitystore-jclouds',
         'extensions:entitystore-jdbm',
-        'extensions:entitystore-jooq',
         'extensions:entitystore-leveldb',
         'extensions:entitystore-memory',
         'extensions:entitystore-mongodb',
         'extensions:entitystore-preferences',
         'extensions:entitystore-redis',
         'extensions:entitystore-riak',
+        'extensions:entitystore-sql',
         'extensions:entitystore-sqlkv',
         'extensions:indexing-elasticsearch',
         'extensions:indexing-rdf',

Reply via email to