polygene-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nic...@apache.org
Subject [01/33] polygene-java git commit: Introducing the beginning of an SQL entity store that is "enterprisey", laid out in a table per type, with manyassociations in mapping tables and so on.
Date Thu, 26 Oct 2017 07:34:31 GMT
Repository: polygene-java
Updated Branches:
  refs/heads/develop 0ef97e94d -> e2b886613


Introducing the beginning of an SQL entity store that is "enterprisey", laid out in a table per type, with manyassociations in mapping tables and so on.

Signed-off-by: niclas <niclas@hedhman.org>


Project: http://git-wip-us.apache.org/repos/asf/polygene-java/repo
Commit: http://git-wip-us.apache.org/repos/asf/polygene-java/commit/c3d8e013
Tree: http://git-wip-us.apache.org/repos/asf/polygene-java/tree/c3d8e013
Diff: http://git-wip-us.apache.org/repos/asf/polygene-java/diff/c3d8e013

Branch: refs/heads/develop
Commit: c3d8e013009eafd7f74344d4f08f66f0636e0a4c
Parents: 718fa57
Author: niclas <niclas@hedhman.org>
Authored: Fri May 12 14:18:16 2017 +0800
Committer: niclas <niclas@hedhman.org>
Committed: Fri May 12 14:18:16 2017 +0800

----------------------------------------------------------------------
 extensions/entitystore-jooq/build.gradle        |  44 +++
 extensions/entitystore-jooq/dev-status.xml      |  38 ++
 extensions/entitystore-jooq/src/docs/es-orm.txt |  58 +++
 .../entitystore/jooq/AssociationValue.java      |  25 ++
 .../polygene/entitystore/jooq/BaseEntity.java   |  16 +
 .../entitystore/jooq/JooqDslContext.java        |  44 +++
 .../jooq/JooqEntityStoreConfiguration.java      |  62 ++++
 .../entitystore/jooq/JooqEntityStoreMixin.java  | 198 +++++++++++
 .../jooq/JooqEntityStoreService.java            |  36 ++
 .../polygene/entitystore/jooq/SqlTable.java     | 356 +++++++++++++++++++
 .../polygene/entitystore/jooq/SqlType.java      | 148 ++++++++
 .../jooq/assembly/JooqEntityStoreAssembler.java |  74 ++++
 .../entitystore/jooq/JooqEntityStoreTest.java   |  71 ++++
 settings.gradle                                 |   1 +
 14 files changed, 1171 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c3d8e013/extensions/entitystore-jooq/build.gradle
----------------------------------------------------------------------
diff --git a/extensions/entitystore-jooq/build.gradle b/extensions/entitystore-jooq/build.gradle
new file mode 100644
index 0000000..32ceb70
--- /dev/null
+++ b/extensions/entitystore-jooq/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/c3d8e013/extensions/entitystore-jooq/dev-status.xml
----------------------------------------------------------------------
diff --git a/extensions/entitystore-jooq/dev-status.xml b/extensions/entitystore-jooq/dev-status.xml
new file mode 100644
index 0000000..0914c52
--- /dev/null
+++ b/extensions/entitystore-jooq/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>stable</codebase>
+
+    <!-- none, brief, good, complete -->
+    <documentation>good</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/c3d8e013/extensions/entitystore-jooq/src/docs/es-orm.txt
----------------------------------------------------------------------
diff --git a/extensions/entitystore-jooq/src/docs/es-orm.txt b/extensions/entitystore-jooq/src/docs/es-orm.txt
new file mode 100644
index 0000000..413eb7b
--- /dev/null
+++ b/extensions/entitystore-jooq/src/docs/es-orm.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-orm,ORM EntityStore]]
+= ORM EntityStore =
+
+[devstatus]
+--------------
+source=extensions/entitystore-orm/dev-status.xml
+--------------
+
+This entitystore is backed by a SQL server, and maps each 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-riak/src/test/java/org/apache/polygene/entitystore/orm/OrmEntityStoreTest.java
+tag=assembly
+----
+
+== Configuration ==
+
+Here are the available configuration properties:
+
+[snippet,java]
+----
+source=extensions/entitystore-riak/src/main/java/org/apache/polygene/entitystore/orm/OrmEntityStoreConfiguration.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/c3d8e013/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/AssociationValue.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/AssociationValue.java b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/AssociationValue.java
new file mode 100644
index 0000000..81331cf
--- /dev/null
+++ b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/AssociationValue.java
@@ -0,0 +1,25 @@
+/*
+ *  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;
+
+class AssociationValue
+{
+    String name;
+    String position;
+    String reference;
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c3d8e013/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/BaseEntity.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/BaseEntity.java b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/BaseEntity.java
new file mode 100644
index 0000000..07fb05c
--- /dev/null
+++ b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/BaseEntity.java
@@ -0,0 +1,16 @@
+package org.apache.polygene.entitystore.jooq;
+
+import java.time.Instant;
+import org.apache.polygene.api.entity.EntityDescriptor;
+import org.apache.polygene.api.identity.Identity;
+
+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/c3d8e013/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqDslContext.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqDslContext.java b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqDslContext.java
new file mode 100644
index 0000000..8737936
--- /dev/null
+++ b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqDslContext.java
@@ -0,0 +1,44 @@
+package org.apache.polygene.entitystore.jooq;
+
+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.apache.polygene.api.service.ServiceDescriptor;
+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 ServiceDescriptor serviceDescriptor )
+        {
+            Settings settings = serviceDescriptor.metaInfo( Settings.class );
+            SQLDialect sqlDialect = serviceDescriptor.metaInfo( SQLDialect.class );
+            Configuration configuration = new DefaultConfiguration()
+                .set( dataSource )
+                .set( sqlDialect )
+                .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/c3d8e013/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreConfiguration.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreConfiguration.java b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreConfiguration.java
new file mode 100644
index 0000000..e3a79f6
--- /dev/null
+++ b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreConfiguration.java
@@ -0,0 +1,62 @@
+/*
+ *  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.UseDefaults;
+import org.apache.polygene.api.property.Property;
+import org.apache.polygene.library.sql.common.SQLConfiguration;
+
+// START SNIPPET: config
+public interface JooqEntityStoreConfiguration 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();
+}
+// END SNIPPET: config

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c3d8e013/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreMixin.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreMixin.java b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreMixin.java
new file mode 100644
index 0000000..8974181
--- /dev/null
+++ b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreMixin.java
@@ -0,0 +1,198 @@
+/*
+ *  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.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.IdentityGenerator;
+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.structure.ModuleDescriptor;
+import org.apache.polygene.api.usecase.Usecase;
+import org.apache.polygene.spi.PolygeneSPI;
+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 JooqEntityStoreMixin
+    implements EntityStore, EntityStoreSPI
+{
+
+    @Structure
+    private PolygeneSPI spi;
+
+    @This
+    private SqlType sqlType;
+
+    @This
+    private SqlTable sqlTable;
+
+    @This
+    private JooqDslContext jooqDslContext;
+
+    @Service
+    private IdentityGenerator identityGenerator;
+
+    @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 );
+        }
+        AssociationStateDescriptor stateDescriptor = baseEntity.type.state();
+        Map<QualifiedName, Object> properties = new HashMap<>();
+        stateDescriptor.properties().forEach( prop ->
+                                              {
+                                                  QualifiedName qualifiedName = prop.qualifiedName();
+                                                  Object value = result.getValue( 0, qualifiedName.name() );
+                                                  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() );
+                                                    assocations.put( qualifiedName, parseEntityReference( value ) );
+                                                } );
+        Map<QualifiedName, List<EntityReference>> manyAssocs = new HashMap<>();
+        Map<QualifiedName, Map<String, EntityReference>> namedAssocs = new HashMap<>();
+        result.forEach( record ->
+                        {
+                            sqlTable.fetchAssociations( record, associationValue ->
+                            {
+                                // TODO: Perhaps introduce "preserveManyAssociationOrder" option which would have an additional column, separating 'ordinal position' and 'name position'
+                                if( associationValue.position == null )
+                                {
+                                    addManyAssociation( stateDescriptor, manyAssocs, associationValue );
+                                }
+                                else
+                                {
+                                    addNamedAssociation( stateDescriptor, namedAssocs, associationValue );
+                                }
+                            } );
+                        } );
+
+        return new DefaultEntityState( baseEntity.version,
+                                       baseEntity.modifedAt,
+                                       reference,
+                                       EntityStatus.LOADED,
+                                       baseEntity.type,
+                                       properties,
+                                       assocations,
+                                       manyAssocs,
+                                       namedAssocs );
+    }
+
+    private void addNamedAssociation( AssociationStateDescriptor stateDescriptor, Map<QualifiedName, Map<String, EntityReference>> namedAssocs, AssociationValue associationValue )
+    {
+        AssociationDescriptor descriptor = stateDescriptor.getNamedAssociationByName( associationValue.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 );
+        QualifiedName qualifiedName = descriptor.qualifiedName();
+        List<EntityReference> list = manyAssocs.computeIfAbsent( qualifiedName, k -> new ArrayList<>() );
+        list.add( parseEntityReference( associationValue.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 );
+    }
+
+    @Override
+    public EntityStoreUnitOfWork newUnitOfWork( ModuleDescriptor module, Usecase usecase, Instant currentTime )
+    {
+        return new DefaultEntityStoreUnitOfWork( module,
+                                                 this,
+                                                 identityGenerator.generate( JooqEntityStoreService.class ),
+                                                 usecase,
+                                                 currentTime
+        );
+    }
+
+    @Override
+    public Stream<EntityState> entityStates( ModuleDescriptor module )
+    {
+        return null;
+    }
+
+    private static class JooqStateCommitter
+        implements StateCommitter
+    {
+        public JooqStateCommitter( EntityStoreUnitOfWork unitOfWork, Iterable<EntityState> state )
+        {
+
+        }
+
+        @Override
+        public void commit()
+        {
+
+        }
+
+        @Override
+        public void cancel()
+        {
+
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c3d8e013/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreService.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreService.java b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreService.java
new file mode 100644
index 0000000..0259151
--- /dev/null
+++ b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreService.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.jooq;
+
+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( { JooqEntityStoreMixin.class } )
+public interface JooqEntityStoreService
+    extends EntityStore, EntityStateVersions, Configuration
+{
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c3d8e013/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/SqlTable.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/SqlTable.java b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/SqlTable.java
new file mode 100644
index 0000000..1cf0c1c
--- /dev/null
+++ b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/SqlTable.java
@@ -0,0 +1,356 @@
+/*
+ *  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.time.Instant;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+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.This;
+import org.apache.polygene.api.injection.scope.Uses;
+import org.apache.polygene.api.mixin.Mixins;
+import org.apache.polygene.api.property.Property;
+import org.apache.polygene.api.property.PropertyDescriptor;
+import org.apache.polygene.api.service.ServiceActivation;
+import org.apache.polygene.api.service.ServiceDescriptor;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.type.ValueType;
+import org.apache.polygene.api.unitofwork.NoSuchEntityTypeException;
+import org.apache.polygene.spi.entitystore.EntityNotFoundException;
+import org.jooq.Condition;
+import org.jooq.CreateTableAsStep;
+import org.jooq.Field;
+import org.jooq.Record;
+import org.jooq.Result;
+import org.jooq.SQLDialect;
+import org.jooq.Schema;
+import org.jooq.SelectJoinStep;
+import org.jooq.SelectQuery;
+import org.jooq.Table;
+import org.jooq.impl.DSL;
+
+@Mixins( SqlTable.Mixin.class )
+public interface SqlTable
+{
+    String IDENTITY_COLUMN_NAME = "identity";
+    String VALUEID_COLUMN_NAME = "value_id";
+    String VERSION_COLUMN_NAME = "version";
+    String APPLICATIONVERSION_COLUMN_NAME = "app_version";
+    String TYPE_COLUMN_NAME = "type";
+    String LASTMODIFIED_COLUMN_NAME = "modified_at";
+    String CREATED_COLUMN_NAME = "created_at";
+    String TABLENAME_COLUMN_NAME = "table_name";
+    String ASSOCIATIONS_COLUMN_NAME = "assocations";
+    String POSITION_COLUMN_NAME = "position";
+
+    String createNewTableName( Class<?> type );
+
+    Result<Record> createNewTable( Class<?> mixinType, EntityDescriptor descriptor );
+
+    boolean isProperty( Method method );
+
+    Table<Record> findTable( Class<?> type, EntityDescriptor descriptor );
+
+    Table<Record> createTable( Class<?> type, EntityDescriptor descriptor );
+
+    String findTableName( Class<?> type, EntityDescriptor descriptor );
+
+    List<Table<Record>> getTableJoins( EntityDescriptor entityDescriptor );
+
+    Result<Record> fetchTypeInfoFromTable( Class<?> entityType );
+
+    BaseEntity fetchBaseEntity( EntityReference reference, ModuleDescriptor module );
+
+    EntityDescriptor findEntityDescriptor( String typeName, ModuleDescriptor module );
+
+    Record createNewBaseEntity( EntityReference reference, EntityDescriptor descriptor );
+
+    SelectQuery<Record> createGetEntityQuery( EntityDescriptor entityDescriptor, EntityReference reference );
+
+    void fetchAssociations( Record record, Consumer<AssociationValue> consume );
+
+    class Mixin
+        implements SqlTable, ServiceActivation
+    {
+
+        @This
+        JooqDslContext dsl;
+
+        @This
+        private Configuration<JooqEntityStoreConfiguration> configuration;
+
+        @This
+        private SqlType sqlType;
+
+        @Uses
+        private ServiceDescriptor serviceDescriptor;
+
+        private Table<Record> typesTable;
+        private Table<Record> entitiesTable;
+        private Map<Class, Table<Record>> entityTables;
+        private Field<String> identityColumn;
+        private Field<String> valueIdentityColumn;
+        private Field<String> typeColumn;
+        private Field<String> versionColumn;
+        private Field<String> applicationVersionColumn;
+        private Field<Instant> modifiedColumn;
+        private Field<Instant> createdColumn;
+        private Field<String> tableNameColumn;
+        private Field<String> assocationsColumn;
+        private Field<String> positionColumn;
+
+        private Schema schema;
+
+        private SQLDialect dialect;
+
+        @Override
+        public void activateService()
+            throws Exception
+        {
+            configuration.refresh();
+            JooqEntityStoreConfiguration config = configuration.get();
+
+            // Prepare jooq DSL
+            dialect = serviceDescriptor.metaInfo( SQLDialect.class );
+
+            String schemaName = config.schemaName().get();
+            String typesTableName = config.typesTableName().get();
+            String entitiesTableName = config.entitiesTableName().get();
+            schema = DSL.schema( DSL.name( schemaName ) );
+            typesTable = DSL.table(
+                dialect.equals( SQLDialect.SQLITE )
+                ? DSL.name( typesTableName )
+                : DSL.name( schema.getName(), typesTableName )
+                                  );
+            entitiesTable = DSL.table(
+                dialect.equals( SQLDialect.SQLITE )
+                ? DSL.name( entitiesTableName )
+                : DSL.name( schema.getName(), entitiesTableName ) );
+
+            identityColumn = DSL.field( DSL.name( IDENTITY_COLUMN_NAME ), String.class );
+            valueIdentityColumn = DSL.field( DSL.name( VALUEID_COLUMN_NAME ), String.class );
+            versionColumn = DSL.field( DSL.name( VERSION_COLUMN_NAME ), String.class );
+            applicationVersionColumn = DSL.field( DSL.name( APPLICATIONVERSION_COLUMN_NAME ), String.class );
+            typeColumn = DSL.field( DSL.name( TYPE_COLUMN_NAME ), String.class );
+            modifiedColumn = DSL.field( DSL.name( LASTMODIFIED_COLUMN_NAME ), Instant.class );
+            createdColumn = DSL.field( DSL.name( CREATED_COLUMN_NAME ), Instant.class );
+            tableNameColumn = DSL.field( DSL.name( TABLENAME_COLUMN_NAME ), String.class );
+            assocationsColumn = DSL.field( DSL.name( ASSOCIATIONS_COLUMN_NAME ), String.class );
+            positionColumn = DSL.field( DSL.name( POSITION_COLUMN_NAME ), String.class );
+
+            // 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();
+                }
+            }
+        }
+
+        @Override
+        public BaseEntity fetchBaseEntity( EntityReference reference, ModuleDescriptor module )
+        {
+            BaseEntity result = new BaseEntity();
+
+            Result<Record> baseEntityResult = dsl
+                .select()
+                .from( entitiesTable )
+                .where( identityColumn.eq( reference.toURI() ) )
+                .fetch();
+
+            if( baseEntityResult.isEmpty() )
+            {
+                throw new EntityNotFoundException( reference );
+            }
+            Record baseEntity = baseEntityResult.get( 0 );
+            String typeName = baseEntity.field( typeColumn ).get( baseEntity );
+            result.type = findEntityDescriptor( typeName, module );
+            result.version = baseEntity.field( versionColumn ).get( baseEntity );
+            result.applicationVersion = baseEntity.field( applicationVersionColumn ).get( baseEntity );
+            result.identity = EntityReference.parseEntityReference( baseEntity.field( identityColumn ).get( baseEntity ) ).identity();
+            result.currentValueIdentity = EntityReference.parseEntityReference( baseEntity.field( valueIdentityColumn ).get( baseEntity ) ).identity();
+            result.modifedAt = baseEntity.field( modifiedColumn ).get( baseEntity );
+            result.createdAt = baseEntity.field( createdColumn ).get( baseEntity );
+            return result;
+        }
+
+        @Override
+        public void passivateService()
+            throws Exception
+        {
+            schema = null;
+        }
+
+        public Result<Record> fetchTypeInfoFromTable( Class<?> entityType )
+        {
+            return dsl
+                .select()
+                .from( typesTable )
+                .where( identityColumn.eq( entityType.getName() ) )
+                .fetch();
+        }
+
+        public List<Table<Record>> getTableJoins( EntityDescriptor entityDescriptor )
+        {
+            return entityDescriptor
+                .mixinTypes()
+                .map( ( Class<?> type ) -> findTable( type, entityDescriptor ) )
+                .collect( Collectors.toList() );
+        }
+
+        public Table<Record> findTable( Class<?> type, EntityDescriptor descriptor )
+        {
+            return entityTables.computeIfAbsent( type, t -> createTable( t, descriptor ) );
+        }
+
+        public Table<Record> createTable( Class<?> type, EntityDescriptor descriptor )
+        {
+            String tableName = findTableName( type, descriptor );
+            return null;
+        }
+
+        public String findTableName( Class<?> type, EntityDescriptor descriptor )
+        {
+            Result<Record> typeInfo = fetchTypeInfoFromTable( type );
+            if( typeInfo.isEmpty() )
+            {
+                typeInfo = createNewTable( type, descriptor );
+            }
+            return typeInfo.getValue( 0, tableNameColumn );
+        }
+
+        @Override
+        public String createNewTableName( Class<?> type )
+        {
+            return null;
+        }
+
+        public Result<Record> createNewTable( Class<?> mixinType, EntityDescriptor descriptor )
+        {
+            String tableName = createNewTableName( mixinType );
+            CreateTableAsStep<Record> table = dsl.createTable( tableName );
+            Arrays.stream( mixinType.getDeclaredMethods() )
+                  .filter( this::isProperty )
+                  .forEach( method ->
+                            {
+                                PropertyDescriptor propertyDescriptor = descriptor.state().findPropertyModelByName( method.getName() );
+                                ValueType valueType = propertyDescriptor.valueType();
+                                Class<?> propertyType = valueType.primaryType();
+                                String propertyName = method.getName();
+                                table.column( propertyName, sqlType.getSqlDataTypeFor( propertyType ) );
+                            } );
+
+            return fetchTypeInfoFromTable( mixinType );
+        }
+
+        public boolean isProperty( Method method )
+        {
+            return Property.class.isAssignableFrom( method.getReturnType() ) && method.getParameterCount() == 0;
+        }
+
+        public 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.name(), module.typeLookup() );
+            }
+        }
+
+        @Override
+        public Record createNewBaseEntity( EntityReference reference, EntityDescriptor descriptor )
+        {
+            return null;
+        }
+
+        /**
+         * 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 )
+        {
+            SelectJoinStep<Record> from = dsl.select().from( entitiesTable );
+            List<Table<Record>> joins = getTableJoins( entityDescriptor );
+            for( Table<Record> joinedTable : joins )
+            {
+                Field<String> joinedField = joinedTable.field( identityColumn );
+                Condition joinCondition = joinedField.eq( entitiesTable.field( valueIdentityColumn ) );
+                from = from.join( joinedTable ).on( joinCondition );
+            }
+            return from.where( identityColumn.eq( reference.identity().toString() ) ).getQuery();
+        }
+
+        @Override
+        public void fetchAssociations( Record record, Consumer<AssociationValue> consume )
+        {
+            AssociationValue value = new AssociationValue();
+            value.name = record.getValue( assocationsColumn );
+            value.position = record.getValue( positionColumn );
+            value.reference = record.getValue( this.assocationsColumn );
+            consume.accept( value );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c3d8e013/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
new file mode 100644
index 0000000..c872d62
--- /dev/null
+++ b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/SqlType.java
@@ -0,0 +1,148 @@
+/*
+ *  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.apache.polygene.api.mixin.Mixins;
+import org.jooq.DataType;
+import org.jooq.impl.SQLDataType;
+import org.jooq.types.Interval;
+
+@Mixins( SqlType.Mixin.class )
+public interface SqlType
+{
+    DataType<?> getSqlDataTypeFor( Class<?> propertyType );
+
+    class Mixin
+        implements SqlType
+    {
+        public DataType<?> getSqlDataTypeFor( Class<?> propertyType )
+        {
+            if( String.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.VARCHAR;
+            }
+            if( Integer.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.INTEGER;
+            }
+            if( Long.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.BIGINT;
+            }
+            if( Boolean.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.BOOLEAN;
+            }
+            if( Float.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.FLOAT;
+            }
+            if( Double.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.DOUBLE;
+            }
+            if( Instant.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.TIMESTAMPWITHTIMEZONE;
+            }
+            if( Interval.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.VARCHAR;
+            }
+            if( Period.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.VARCHAR;
+            }
+            if( LocalDate.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.LOCALDATE;
+            }
+            if( LocalTime.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.LOCALTIME;
+            }
+            if( LocalDateTime.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.LOCALDATETIME;
+            }
+            if( ZonedDateTime.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.OFFSETDATETIME;
+            }
+            if( OffsetDateTime.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.OFFSETDATETIME;
+            }
+            if( Character.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.CHAR( 1 );
+            }
+            if( Short.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.INTEGER;
+            }
+            if( Byte.class.isAssignableFrom( propertyType ) )
+            {
+                return SQLDataType.INTEGER;
+            }
+            if( propertyType.isPrimitive() )
+            {
+                if( propertyType.equals( Integer.TYPE ) )
+                {
+                    return SQLDataType.INTEGER;
+                }
+                if( propertyType.equals( Long.TYPE ) )
+                {
+                    return SQLDataType.BIGINT;
+                }
+                if( propertyType.equals( Boolean.TYPE ) )
+                {
+                    return SQLDataType.BOOLEAN;
+                }
+                if( propertyType.equals( Float.TYPE ) )
+                {
+                    return SQLDataType.FLOAT;
+                }
+                if( propertyType.equals( Double.TYPE ) )
+                {
+                    return SQLDataType.DOUBLE;
+                }
+                if( propertyType.equals( Character.TYPE ) )
+                {
+                    return SQLDataType.CHAR( 1 );
+                }
+                if( propertyType.equals( Short.TYPE ) )
+                {
+                    return SQLDataType.INTEGER;
+                }
+                if( propertyType.equals( Byte.TYPE ) )
+                {
+                    return SQLDataType.INTEGER;
+                }
+            }
+            return SQLDataType.VARCHAR;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c3d8e013/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
new file mode 100644
index 0000000..8a986a9
--- /dev/null
+++ b/extensions/entitystore-jooq/src/main/java/org/apache/polygene/entitystore/jooq/assembly/JooqEntityStoreAssembler.java
@@ -0,0 +1,74 @@
+/*
+ *  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.JooqEntityStoreConfiguration;
+import org.apache.polygene.entitystore.jooq.JooqEntityStoreService;
+import org.jooq.SQLDialect;
+import org.jooq.conf.RenderNameStyle;
+import org.jooq.conf.Settings;
+
+/**
+ * MySQL EntityStore assembly.
+ */
+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.services( JooqEntityStoreService.class )
+              .identifiedBy( identity )
+              .visibleIn( visibility() )
+              .setMetaInfo( getSQLDialect() )
+              .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/c3d8e013/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
new file mode 100644
index 0000000..8db2784
--- /dev/null
+++ b/extensions/entitystore-jooq/src/test/java/org/apache/polygene/entitystore/jooq/JooqEntityStoreTest.java
@@ -0,0 +1,71 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *
+ */
+package org.apache.polygene.entitystore.jooq;
+
+import java.util.HashMap;
+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.test.EntityTestAssembler;
+import org.apache.polygene.test.entity.AbstractEntityStoreTest;
+import org.apache.polygene.test.internal.DockerRule;
+import org.jooq.SQLDialect;
+import org.junit.ClassRule;
+
+public class JooqEntityStoreTest
+    extends AbstractEntityStoreTest
+{
+    @ClassRule
+    public static final DockerRule DOCKER = new DockerRule(
+        "mysql",
+        new HashMap<String, String>()
+        {{
+            put( "MYSQL_ROOT_PASSWORD", "" );
+            put( "MYSQL_ALLOW_EMPTY_PASSWORD", "yes" );
+            put( "MYSQL_DATABASE", "jdbc_test_db" );
+            put( "MYSQL_ROOT_HOST", "172.17.0.1" );
+        }},
+        30000L
+//        , "mysqld: ready for connections"   TODO: add this after next release of tdomzal/junit-docker-rule
+    );
+
+    @Override
+    // START SNIPPET: assembly
+    public void assemble( ModuleAssembly module )
+        throws AssemblyException
+    {
+        // END SNIPPET: assembly
+        super.assemble( module );
+        ModuleAssembly config = module.layer().module( "config" );
+        new EntityTestAssembler().visibleIn( Visibility.module ).assemble( config );
+
+        // START SNIPPET: assembly
+        new JooqEntityStoreAssembler()
+            .withConfig( config, Visibility.layer )
+            .identifiedBy( "jooq-entitystore" )
+            .assemble( module );
+        // END SNIPPET: assembly
+
+        config.forMixin( JooqEntityStoreConfiguration.class ).setMetaInfo( SQLDialect.MARIADB );
+        // START SNIPPET: assembly
+    }
+    // END SNIPPET: assembly
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c3d8e013/settings.gradle
----------------------------------------------------------------------
diff --git a/settings.gradle b/settings.gradle
index 0cee4a7..13b502c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -61,6 +61,7 @@ include 'core:api',
         'extensions:entitystore-hazelcast',
         'extensions:entitystore-jclouds',
         'extensions:entitystore-jdbm',
+        'extensions:entitystore-jooq',
         'extensions:entitystore-leveldb',
         'extensions:entitystore-memory',
         'extensions:entitystore-mongodb',


Mime
View raw message