You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by mm...@apache.org on 2022/05/06 10:39:40 UTC

[ignite-extensions] branch master updated: IGNITE-16908 Move ignite-hibernate modules to the Ignite Extension (#137)

This is an automated email from the ASF dual-hosted git repository.

mmuzaf pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite-extensions.git


The following commit(s) were added to refs/heads/master by this push:
     new 12cc59d  IGNITE-16908 Move ignite-hibernate modules to the Ignite Extension (#137)
12cc59d is described below

commit 12cc59de9c3848c0fa2b9b88face558f3cf10ca7
Author: Maxim Muzafarov <ma...@gmail.com>
AuthorDate: Fri May 6 13:39:36 2022 +0300

    IGNITE-16908 Move ignite-hibernate modules to the Ignite Extension (#137)
---
 modules/hibernate-ext/examples/README.md           |   34 +
 modules/hibernate-ext/examples/README.txt          |   27 +
 .../examples/config/example-default.xml            |   76 +
 .../examples/config/example-ignite.xml             |   31 +
 .../examples/config/hibernate/README.txt           |    8 +
 .../hibernate/example-hibernate-L2-cache.xml       |   64 +
 .../core/src/test/config/examples.properties       |   25 +
 .../modules/core/src/test/config/log4j-test.xml    |   97 +
 .../modules/core/src/test/config/tests.properties  |   19 +
 .../hibernate-ext/examples/pom-standalone-lgpl.xml |  120 ++
 modules/hibernate-ext/examples/pom-standalone.xml  |  123 ++
 modules/hibernate-ext/examples/pom.xml             |  199 ++
 .../apache/ignite/examples/ExampleNodeStartup.java |   36 +
 .../org/apache/ignite/examples/ExamplesUtils.java  |  132 ++
 .../hibernate/HibernateL2CacheExample.java         |  307 +++
 .../ignite/examples/datagrid/hibernate/Post.java   |  130 ++
 .../ignite/examples/datagrid/hibernate/User.java   |  154 ++
 .../examples/datagrid/hibernate/package-info.java  |   22 +
 .../store/hibernate/CacheHibernatePersonStore.java |  122 ++
 .../hibernate/CacheHibernateStoreExample.java      |  163 ++
 .../datagrid/store/hibernate/Person.hbm.xml        |   34 +
 .../datagrid/store/hibernate/hibernate.cfg.xml     |   44 +
 .../datagrid/store/hibernate/package-info.java     |   22 +
 .../org/apache/ignite/examples/model/Address.java  |   72 +
 .../org/apache/ignite/examples/model/Employee.java |   93 +
 .../apache/ignite/examples/model/EmployeeKey.java  |   93 +
 .../apache/ignite/examples/model/Organization.java |  132 ++
 .../ignite/examples/model/OrganizationType.java    |   32 +
 .../org/apache/ignite/examples/model/Person.java   |  148 ++
 .../apache/ignite/examples/model/package-info.java |   23 +
 .../org/apache/ignite/examples/package-info.java   |   23 +
 .../ignite/examples/util/DbH2ServerStartup.java    |   98 +
 .../apache/ignite/examples/util/package-info.java  |   22 +
 .../HibernateL2CacheExampleMultiNodeSelfTest.java  |   34 +
 .../examples/HibernateL2CacheExampleSelfTest.java  |   33 +
 .../IgniteLgplExamplesSelfTestSuite.java           |   45 +
 modules/hibernate-ext/hibernate/README.txt         |   48 +
 .../hibernate/assembly/hibernate-ext.xml           |   35 +
 .../hibernate/licenses/apache-2.0.txt              |  202 ++
 .../modules/core/src/test/config/log4j-test.xml    |   97 +
 .../modules/core/src/test/config/tests.properties  |  153 ++
 modules/hibernate-ext/hibernate/pom.xml            |  191 ++
 .../hibernate/HibernateAccessStrategyAdapter.java  |  352 ++++
 .../hibernate/HibernateAccessStrategyFactory.java  |  268 +++
 .../cache/hibernate/HibernateCacheProxy.java       |  770 ++++++++
 .../hibernate/HibernateExceptionConverter.java     |   29 +
 .../cache/hibernate/HibernateKeyTransformer.java   |   29 +
 .../cache/hibernate/HibernateKeyWrapper.java       |  109 ++
 .../HibernateNonStrictAccessStrategy.java          |  236 +++
 .../hibernate/HibernateReadOnlyAccessStrategy.java |  109 ++
 .../HibernateReadWriteAccessStrategy.java          |  357 ++++
 .../ignite/cache/hibernate/HibernateRegion.java    |   74 +
 .../cache/hibernate/HibernateRegionFactory.java    |  153 ++
 .../HibernateTransactionalAccessStrategy.java      |  155 ++
 .../hibernate/IgniteCachedDomainDataAccess.java    |  132 ++
 .../hibernate/IgniteCollectionDataAccess.java      |   75 +
 .../cache/hibernate/IgniteDomainDataRegion.java    |  190 ++
 .../cache/hibernate/IgniteEntityDataAccess.java    |  100 +
 .../cache/hibernate/IgniteGeneralDataRegion.java   |   89 +
 .../cache/hibernate/IgniteNaturalIdDataAccess.java |   98 +
 .../cache/hibernate/IgniteQueryResultsRegion.java  |   70 +
 .../cache/hibernate/IgniteTimestampsRegion.java    |   40 +
 .../ignite/cache/hibernate/package-info.java       |   25 +
 .../store/hibernate/CacheHibernateBlobStore.java   |  545 ++++++
 .../hibernate/CacheHibernateBlobStoreEntry.hbm.xml |   31 +
 .../hibernate/CacheHibernateBlobStoreEntry.java    |   89 +
 .../hibernate/CacheHibernateBlobStoreFactory.java  |  235 +++
 .../CacheHibernateStoreSessionListener.java        |  226 +++
 .../ignite/cache/store/hibernate/package-info.java |   23 +
 .../hibernate/src/test/config/factory-cache.xml    |   59 +
 .../hibernate/src/test/config/factory-cache1.xml   |   61 +
 .../test/config/factory-incorrect-store-cache.xml  |   60 +
 .../HibernateL2CacheConfigurationSelfTest.java     |  413 ++++
 .../hibernate/HibernateL2CacheMultiJvmTest.java    |  435 +++++
 .../cache/hibernate/HibernateL2CacheSelfTest.java  | 1987 ++++++++++++++++++++
 .../HibernateL2CacheStrategySelfTest.java          |  572 ++++++
 .../HibernateL2CacheTransactionalSelfTest.java     |  155 ++
 ...bernateL2CacheTransactionalUseSyncSelfTest.java |   31 +
 .../CacheHibernateBlobStoreNodeRestartTest.java    |   47 +
 .../hibernate/CacheHibernateBlobStoreSelfTest.java |  115 ++
 .../CacheHibernateStoreFactorySelfTest.java        |  332 ++++
 ...CacheHibernateStoreSessionListenerSelfTest.java |  242 +++
 .../ignite/cache/store/hibernate/hibernate.cfg.xml |   41 +
 .../ignite/cache/store/hibernate/package-info.java |   23 +
 .../testsuites/IgniteHibernate5TestSuite.java      |   50 +
 .../ignite/cache/store/hibernate/hibernate.cfg.xml |   41 +
 modules/hibernate-ext/pom.xml                      |   67 +
 pom.xml                                            |    1 +
 88 files changed, 12904 insertions(+)

diff --git a/modules/hibernate-ext/examples/README.md b/modules/hibernate-ext/examples/README.md
new file mode 100644
index 0000000..8d8a26b
--- /dev/null
+++ b/modules/hibernate-ext/examples/README.md
@@ -0,0 +1,34 @@
+# Apache Ignite Examples
+
+This module contains examples of how to run [Apache Ignite](ignite.apache.org) and [Apache Ignite](ignite.apache.org) with 3rd party components.
+
+Instructions on how to start examples can be found in [README.txt](README.txt).
+
+## Running examples on JDK 9/10/11
+
+Ignite uses proprietary SDK APIs that are not available by default. See also [How to run Ignite on JDK 9,10 and 11](https://ignite.apache.org/docs/latest/setup#running-ignite-with-java-11-or-later)
+
+To set up local IDE to easier access to examples, it is possible to add following options as default for all applications
+
+``--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED
+   --add-exports=java.base/sun.nio.ch=ALL-UNNAMED
+   --add-exports=java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED
+   --add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED
+   --add-exports=java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED
+   --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED
+   --illegal-access=permit``
+
+For example, for IntelliJ IDEA it is possible to use Application Templates.
+
+Use 'Run' -> 'Edit Configuration' menu.
+
+<img src="https://docs.google.com/drawings/d/e/2PACX-1vQFgjhrPsLPUmic8CA_s1YpjVwA2vQITxNsLrAKOecZxIQEZSb1Ps2XKh0QEn8z9vtYiUofnGek_cag/pub?w=960&h=720"/>
+
+## Contributing to Examples
+
+*Notice* When updating classpath of examples and in case any modifications required in [pom.xml](pom.xml)
+please make sure that corresponding changes were applied to
+ * [pom-standalone.xml](pom-standalone.xml),
+ * [pom-standalone-lgpl.xml](pom-standalone-lgpl.xml).
+ 
+ These pom files are finalized during release and placed to the `examples` folder with these examples code.
diff --git a/modules/hibernate-ext/examples/README.txt b/modules/hibernate-ext/examples/README.txt
new file mode 100644
index 0000000..8e3bce0
--- /dev/null
+++ b/modules/hibernate-ext/examples/README.txt
@@ -0,0 +1,27 @@
+Apache Ignite Examples
+======================
+
+Examples are shipped as a separate Maven project, so to start running you simply need
+to import provided `pom.xml` file into your favourite IDE.
+
+
+Starting Remote Nodes
+=====================
+
+Remote nodes for examples should always be started with special configuration file which enables P2P
+class loading: `examples/config/example-ignite.xml`. To run a remote node in IDE use `ExampleNodeStartup` class.
+
+
+LGPL
+=====
+It is required some lgpl-based libs, for example: ignite-hibernate or ignite-schedule.
+In case these libs can not be found by this maven project please download Apache Ignite sources
+at https://ignite.apache.org/download.cgi#sources.
+
+There is a way to gain required libs from sources:
+
+Run "./mvnw clean install -DskipTests " at Apache Ignite sources.
+This case will install lgpl-based libs to local maven repository.
+
+Required libs will also appear at /target/release-package/libs/optional sub-folders.
+Found libs should be copied to global or project's classpath.
diff --git a/modules/hibernate-ext/examples/config/example-default.xml b/modules/hibernate-ext/examples/config/example-default.xml
new file mode 100644
index 0000000..e6c359d
--- /dev/null
+++ b/modules/hibernate-ext/examples/config/example-default.xml
@@ -0,0 +1,76 @@
+<?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.
+-->
+
+<!--
+    Ignite configuration with all defaults and enabled p2p deployment and enabled events.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <bean abstract="true" id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
+        <!-- Set to true to enable distributed class loading for examples, default is false. -->
+        <property name="peerClassLoadingEnabled" value="true"/>
+
+        <!-- Enable task execution events for examples. -->
+        <property name="includeEventTypes">
+            <list>
+                <!--Task execution events-->
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_STARTED"/>
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_FINISHED"/>
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_FAILED"/>
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_TIMEDOUT"/>
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_SESSION_ATTR_SET"/>
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_REDUCED"/>
+
+                <!--Cache events-->
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_PUT"/>
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_READ"/>
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_REMOVED"/>
+            </list>
+        </property>
+
+        <!-- Explicitly configure TCP discovery SPI to provide list of initial nodes. -->
+        <property name="discoverySpi">
+            <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+                <property name="ipFinder">
+                    <!--
+                        Ignite provides several options for automatic discovery that can be used
+                        instead os static IP based discovery. For information on all options refer
+                        to our documentation: http://apacheignite.readme.io/docs/cluster-config
+                    -->
+                    <!-- Uncomment static IP finder to enable static-based discovery of initial nodes. -->
+                    <!--<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">-->
+                    <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder">
+                        <property name="addresses">
+                            <list>
+                                <!-- In distributed environment, replace with actual host IP address. -->
+                                <value>127.0.0.1:47500..47509</value>
+                            </list>
+                        </property>
+                    </bean>
+                </property>
+            </bean>
+        </property>
+    </bean>
+</beans>
diff --git a/modules/hibernate-ext/examples/config/example-ignite.xml b/modules/hibernate-ext/examples/config/example-ignite.xml
new file mode 100644
index 0000000..a3e7e22
--- /dev/null
+++ b/modules/hibernate-ext/examples/config/example-ignite.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<!--
+    Ignite configuration with all defaults and enabled p2p deployment and enabled events.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd">
+    <!-- Imports default Ignite configuration -->
+    <import resource="example-default.xml"/>
+
+    <bean parent="ignite.cfg"/>
+</beans>
diff --git a/modules/hibernate-ext/examples/config/hibernate/README.txt b/modules/hibernate-ext/examples/config/hibernate/README.txt
new file mode 100644
index 0000000..5b7ab29
--- /dev/null
+++ b/modules/hibernate-ext/examples/config/hibernate/README.txt
@@ -0,0 +1,8 @@
+Hibernate L2 Cache Configuration Example
+----------------------------------------
+
+This folder contains example-hibernate-L2-cache.xml file that demonstrates
+how to configure Hibernate to use Apache Ignite cache as an L2 cache provider.
+
+This file is also used in Hibernate example located in org.apache.ignite.examples.datagrid.hibernate
+package.
diff --git a/modules/hibernate-ext/examples/config/hibernate/example-hibernate-L2-cache.xml b/modules/hibernate-ext/examples/config/hibernate/example-hibernate-L2-cache.xml
new file mode 100644
index 0000000..a2f7e89
--- /dev/null
+++ b/modules/hibernate-ext/examples/config/hibernate/example-hibernate-L2-cache.xml
@@ -0,0 +1,64 @@
+<?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.
+  -->
+
+
+<!DOCTYPE hibernate-configuration PUBLIC
+    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
+
+<!--
+    Configuration file for HibernateL2CacheExample.
+-->
+
+<hibernate-configuration>
+    <session-factory>
+        <!-- Database connection settings -->
+        <property name="connection.url">jdbc:h2:mem:example;DB_CLOSE_DELAY=-1</property>
+
+        <!-- Drop and re-create the database schema on startup. -->
+        <property name="hbm2ddl.auto">create</property>
+
+        <!-- Enable L2 cache. -->
+        <property name="cache.use_second_level_cache">true</property>
+
+        <!-- Enable query cache. -->
+        <property name="cache.use_query_cache">true</property>
+
+        <!-- Generate L2 cache statistics. -->
+        <property name="generate_statistics">true</property>
+
+        <!-- Specify Ignite as L2 cache provider. -->
+        <property name="cache.region.factory_class">org.apache.ignite.cache.hibernate.HibernateRegionFactory</property>
+
+        <!-- Specify connection release mode. -->
+        <property name="connection.release_mode">on_close</property>
+
+        <!-- Set default L2 cache access type. -->
+        <property name="org.apache.ignite.hibernate.default_access_type">READ_ONLY</property>
+
+        <!-- Specify the entity classes for mapping. -->
+        <mapping class="org.apache.ignite.examples.datagrid.hibernate.User"/>
+        <mapping class="org.apache.ignite.examples.datagrid.hibernate.Post"/>
+
+        <!-- Per-class L2 cache settings. -->
+        <class-cache class="org.apache.ignite.examples.datagrid.hibernate.User" usage="read-only"/>
+        <class-cache class="org.apache.ignite.examples.datagrid.hibernate.Post" usage="read-only"/>
+        <collection-cache collection="org.apache.ignite.examples.datagrid.hibernate.User.posts" usage="read-only"/>
+    </session-factory>
+</hibernate-configuration>
diff --git a/modules/hibernate-ext/examples/modules/core/src/test/config/examples.properties b/modules/hibernate-ext/examples/modules/core/src/test/config/examples.properties
new file mode 100644
index 0000000..b1b8bc1
--- /dev/null
+++ b/modules/hibernate-ext/examples/modules/core/src/test/config/examples.properties
@@ -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.
+#
+
+ScalarCacheAffinityExample1=examples/config/example-ignite.xml
+ScalarCacheAffinityExample2=examples/config/example-ignite.xml
+ScalarCacheAffinitySimpleExample=examples/config/example-ignite.xml
+ScalarCacheExample=examples/config/example-ignite.xml
+ScalarCacheQueryExample=examples/config/example-ignite.xml
+ScalarCountGraphTrianglesExample=examples/config/example-ignite.xml
+ScalarPopularNumbersRealTimeExample=examples/config/example-ignite.xml
+DataRegionEexample=examples/config/example-data-regions.xml
\ No newline at end of file
diff --git a/modules/hibernate-ext/examples/modules/core/src/test/config/log4j-test.xml b/modules/hibernate-ext/examples/modules/core/src/test/config/log4j-test.xml
new file mode 100755
index 0000000..b78fa9c
--- /dev/null
+++ b/modules/hibernate-ext/examples/modules/core/src/test/config/log4j-test.xml
@@ -0,0 +1,97 @@
+<?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.
+-->
+
+<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN"
+    "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
+<!--
+    Log4j configuration.
+-->
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+    <!--
+        Logs System.out messages to console.
+    -->
+    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+        <!-- Log to STDOUT. -->
+        <param name="Target" value="System.out"/>
+
+        <!-- Log from DEBUG and higher. -->
+        <param name="Threshold" value="DEBUG"/>
+
+        <!-- The default pattern: Date Priority [Category] Message\n -->
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="[%d{ISO8601}][%-5p][%t][%c{1}] %m%n"/>
+        </layout>
+
+        <!-- Do not log beyond INFO level. -->
+        <filter class="org.apache.log4j.varia.LevelRangeFilter">
+            <param name="levelMin" value="DEBUG"/>
+            <param name="levelMax" value="INFO"/>
+        </filter>
+    </appender>
+
+    <!--
+        Logs all System.err messages to console.
+    -->
+    <appender name="CONSOLE_ERR" class="org.apache.log4j.ConsoleAppender">
+        <!-- Log to STDERR. -->
+        <param name="Target" value="System.err"/>
+
+        <!-- Log from WARN and higher. -->
+        <param name="Threshold" value="WARN"/>
+
+        <!-- The default pattern: Date Priority [Category] Message\n -->
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="[%d{ISO8601}][%-5p][%t][%c{1}] %m%n"/>
+        </layout>
+    </appender>
+
+    <!--
+        Logs all output to specified file.
+    -->
+    <appender name="FILE" class="org.apache.log4j.RollingFileAppender">
+        <param name="Threshold" value="DEBUG"/>
+        <param name="File" value="${IGNITE_HOME}/work/log/ignite.log"/>
+        <param name="Append" value="true"/>
+        <param name="MaxFileSize" value="10MB"/>
+        <param name="MaxBackupIndex" value="10"/>
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="[%d{ISO8601}][%-5p][%t][%c{1}] %m%n"/>
+        </layout>
+    </appender>
+
+    <!-- Disable all open source debugging. -->
+    <category name="org">
+        <level value="INFO"/>
+    </category>
+
+    <category name="org.eclipse.jetty">
+        <level value="INFO"/>
+    </category>
+
+    <!-- Default settings. -->
+    <root>
+        <!-- Print at info by default. -->
+        <level value="INFO"/>
+
+        <!-- Append to file and console. -->
+        <appender-ref ref="FILE"/>
+        <appender-ref ref="CONSOLE"/>
+        <appender-ref ref="CONSOLE_ERR"/>
+    </root>
+</log4j:configuration>
diff --git a/modules/hibernate-ext/examples/modules/core/src/test/config/tests.properties b/modules/hibernate-ext/examples/modules/core/src/test/config/tests.properties
new file mode 100644
index 0000000..7bdcbed
--- /dev/null
+++ b/modules/hibernate-ext/examples/modules/core/src/test/config/tests.properties
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+
+# Local address to bind to.
+local.ip=127.0.0.1
diff --git a/modules/hibernate-ext/examples/pom-standalone-lgpl.xml b/modules/hibernate-ext/examples/pom-standalone-lgpl.xml
new file mode 100644
index 0000000..89b7ecf
--- /dev/null
+++ b/modules/hibernate-ext/examples/pom-standalone-lgpl.xml
@@ -0,0 +1,120 @@
+<?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.
+-->
+
+<!--
+    POM file.
+-->
+<project
+    xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <properties>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+
+        <lgpl.folder>src/main/java</lgpl.folder>
+    </properties>
+
+    <groupId>org.apache.ignite</groupId>
+    <artifactId>ignite-hibernate-examples-ext</artifactId>
+    <version>to_be_replaced_by_ignite_version</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.cache</groupId>
+            <artifactId>cache-api</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-core</artifactId>
+            <version>to_be_replaced_by_ignite_version</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-spring</artifactId>
+            <version>to_be_replaced_by_ignite_version</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-log4j</artifactId>
+            <version>to_be_replaced_by_ignite_version</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-indexing</artifactId>
+            <version>to_be_replaced_by_ignite_version</version>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-cli</groupId>
+            <artifactId>commons-cli</artifactId>
+            <version>1.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-hibernate-ext</artifactId>
+            <version>5.1.0</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/java</directory>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </resource>
+            <resource>
+                <directory>config</directory>
+            </resource>
+        </resources>
+
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>add-sources</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>${lgpl.folder}</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/modules/hibernate-ext/examples/pom-standalone.xml b/modules/hibernate-ext/examples/pom-standalone.xml
new file mode 100644
index 0000000..1758fbb
--- /dev/null
+++ b/modules/hibernate-ext/examples/pom-standalone.xml
@@ -0,0 +1,123 @@
+<?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.
+-->
+
+<project
+    xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <properties>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+
+        <lgpl.folder>src/main/java</lgpl.folder>
+    </properties>
+
+    <groupId>org.apache.ignite</groupId>
+    <artifactId>ignite-hibernate-examples-ext</artifactId>
+    <version>to_be_replaced_by_ignite_version</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.cache</groupId>
+            <artifactId>cache-api</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-core</artifactId>
+            <version>to_be_replaced_by_ignite_version</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-spring</artifactId>
+            <version>to_be_replaced_by_ignite_version</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-log4j</artifactId>
+            <version>to_be_replaced_by_ignite_version</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-indexing</artifactId>
+            <version>to_be_replaced_by_ignite_version</version>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-cli</groupId>
+            <artifactId>commons-cli</artifactId>
+            <version>1.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-kubernetes</artifactId>
+            <version>to_be_replaced_by_ignite_version</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-hibernate-ext</artifactId>
+            <version>5.1.0</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/java</directory>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </resource>
+            <resource>
+                <directory>config</directory>
+            </resource>
+        </resources>
+
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>add-sources</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>${lgpl.folder}</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/modules/hibernate-ext/examples/pom.xml b/modules/hibernate-ext/examples/pom.xml
new file mode 100644
index 0000000..299eb4b
--- /dev/null
+++ b/modules/hibernate-ext/examples/pom.xml
@@ -0,0 +1,199 @@
+<?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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.ignite</groupId>
+        <artifactId>ignite-hibernate-parent-ext</artifactId>
+        <version>5.3.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>ignite-hibernate-examples-ext</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.cache</groupId>
+            <artifactId>cache-api</artifactId>
+            <version>${javax.cache.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-spring</artifactId>
+            <!--Remove exclusion while upgrading ignite-spring version to 5.0-->
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-core</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-beans</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!--Remove spring-core and spring-beans dependencies while upgrading ignite-spring version to 5.0-->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-core</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-beans</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-log4j</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-indexing</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-calcite</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-cli</groupId>
+            <artifactId>commons-cli</artifactId>
+            <version>1.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-core</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.javassist</groupId>
+            <artifactId>javassist</artifactId>
+            <version>${javassist.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-hibernate-ext</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <properties>
+        <lgpl.folder>src/main/java</lgpl.folder>
+        <lgpl.test.folder>src/test/java</lgpl.test.folder>
+    </properties>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>${lgpl.folder}</directory>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </resource>
+            <resource>
+                <directory>config</directory>
+            </resource>
+        </resources>
+
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>add-sources</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>${lgpl.folder}</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+
+                    <execution>
+                        <id>add-tests</id>
+                        <phase>generate-test-sources</phase>
+                        <goals>
+                            <goal>add-test-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>${lgpl.test.folder}</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+
+                <configuration>
+                    <additionalDependencies>
+                        <dependency>
+                            <groupId>com.fasterxml.jackson.core</groupId>
+                            <artifactId>jackson-core</artifactId>
+                            <version>${jackson.version}</version>
+                        </dependency>
+
+                        <dependency>
+                            <groupId>com.fasterxml.jackson.core</groupId>
+                            <artifactId>jackson-databind</artifactId>
+                            <version>${jackson.version}</version>
+                        </dependency>
+
+                        <dependency>
+                            <groupId>com.fasterxml.jackson.core</groupId>
+                            <artifactId>jackson-annotations</artifactId>
+                            <version>${jackson.version}</version>
+                        </dependency>
+                    </additionalDependencies>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/ExampleNodeStartup.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/ExampleNodeStartup.java
new file mode 100644
index 0000000..dd8a72b
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/ExampleNodeStartup.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.ignite.examples;
+
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.Ignition;
+
+/**
+ * Starts up an empty node with example compute configuration.
+ */
+public class ExampleNodeStartup {
+    /**
+     * Start up an empty node with example compute configuration.
+     *
+     * @param args Command line arguments, none required.
+     * @throws IgniteException If failed.
+     */
+    public static void main(String[] args) throws IgniteException {
+        Ignition.start("examples/config/example-ignite.xml");
+    }
+}
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/ExamplesUtils.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/ExamplesUtils.java
new file mode 100644
index 0000000..af71876
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/ExamplesUtils.java
@@ -0,0 +1,132 @@
+/*
+ * 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.ignite.examples;
+
+import java.net.URL;
+import java.util.List;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.cluster.ClusterGroup;
+
+/**
+ *
+ */
+public class ExamplesUtils {
+    /** */
+    private static final ClassLoader CLS_LDR = ExamplesUtils.class.getClassLoader();
+
+    /**
+     * Exits with code {@code -1} if maximum memory is below 90% of minimally allowed threshold.
+     *
+     * @param min Minimum memory threshold.
+     */
+    public static void checkMinMemory(long min) {
+        long maxMem = Runtime.getRuntime().maxMemory();
+
+        if (maxMem < .85 * min) {
+            System.err.println("Heap limit is too low (" + (maxMem / (1024 * 1024)) +
+                "MB), please increase heap size at least up to " + (min / (1024 * 1024)) + "MB.");
+
+            System.exit(-1);
+        }
+    }
+
+    /**
+     * Returns URL resolved by class loader for classes in examples project.
+     *
+     * @param path Path to the resource.
+     * @return Resolved URL.
+     */
+    public static URL url(String path) {
+        URL url = CLS_LDR.getResource(path);
+
+        if (url == null)
+            throw new RuntimeException("Failed to resolve resource URL by path: " + path);
+
+        return url;
+    }
+
+    /**
+     * Checks minimum topology size for running a certain example.
+     *
+     * @param grp Cluster to check size for.
+     * @param size Minimum number of nodes required to run a certain example.
+     * @return {@code True} if check passed, {@code false} otherwise.
+     */
+    public static boolean checkMinTopologySize(ClusterGroup grp, int size) {
+        int prjSize = grp.nodes().size();
+
+        if (prjSize < size) {
+            System.err.println(">>> Please start at least " + size + " cluster nodes to run example.");
+
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks if cluster has server nodes.
+     *
+     * @param ignite Ignite instance.
+     * @return {@code True} if cluster has server nodes, {@code false} otherwise.
+     */
+    public static boolean hasServerNodes(Ignite ignite) {
+        if (ignite.cluster().forServers().nodes().isEmpty()) {
+            System.err.println("Server nodes not found (start data nodes with ExampleNodeStartup class)");
+
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Convenience method for printing query results.
+     *
+     * @param res Query results.
+     */
+    public static void printQueryResults(List<?> res) {
+        if (res == null || res.isEmpty())
+            System.out.println("Query result set is empty.");
+        else {
+            for (Object row : res) {
+                if (row instanceof List) {
+                    System.out.print("(");
+
+                    List<?> l = (List)row;
+
+                    for (int i = 0; i < l.size(); i++) {
+                        Object o = l.get(i);
+
+                        if (o instanceof Double || o instanceof Float)
+                            System.out.printf("%.2f", o);
+                        else
+                            System.out.print(l.get(i));
+
+                        if (i + 1 != l.size())
+                            System.out.print(',');
+                    }
+
+                    System.out.println(')');
+                }
+                else
+                    System.out.println("  " + row);
+            }
+        }
+    }
+}
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/hibernate/HibernateL2CacheExample.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/hibernate/HibernateL2CacheExample.java
new file mode 100644
index 0000000..9667446
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/hibernate/HibernateL2CacheExample.java
@@ -0,0 +1,307 @@
+/*
+ * 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.ignite.examples.datagrid.hibernate;
+
+import java.net.URL;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.examples.ExampleNodeStartup;
+import org.apache.ignite.examples.ExamplesUtils;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.boot.MetadataSources;
+import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
+import org.hibernate.cache.spi.access.AccessType;
+import org.hibernate.stat.SecondLevelCacheStatistics;
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ * This example demonstrates the use of Ignite In-Memory Data Ignite cluster as a Hibernate
+ * Second-Level cache provider.
+ * <p>
+ * The Hibernate Second-Level cache (or "L2 cache" shortly) lets you significantly
+ * reduce the number of requests to the underlying SQL database. Because database
+ * access is known to be an expansive operation, using L2 cache may improve
+ * performance dramatically.
+ * <p>
+ * This example defines 2 entity classes: {@link User} and {@link Post}, with
+ * 1 <-> N relation, and marks them with appropriate annotations for Hibernate
+ * object-relational mapping to SQL tables of an underlying H2 in-memory database.
+ * The example launches node in the same JVM and registers it in
+ * Hibernate configuration as an L2 cache implementation. It then stores and
+ * queries instances of the entity classes to and from the database, having
+ * Hibernate SQL output, L2 cache statistics output, and Ignite cache metrics
+ * output enabled.
+ * <p>
+ * When running example, it's easy to notice that when an object is first
+ * put into a database, the L2 cache is not used and it's contents is empty.
+ * However, when an object is first read from the database, it is immediately
+ * stored in L2 cache (which is Ignite In-Memory Data Ignite cluster in fact), which can
+ * be seen in stats output. Further requests of the same object only read the data
+ * from L2 cache and do not hit the database.
+ * <p>
+ * In this example, the Hibernate query cache is also enabled. Query cache lets you
+ * avoid hitting the database in case of repetitive queries with the same parameter
+ * values. You may notice that when the example runs the same query repeatedly in
+ * loop, only the first query hits the database and the successive requests take the
+ * data from L2 cache.
+ * <p>
+ * Note: this example uses {@link AccessType#READ_ONLY} L2 cache access type, but you
+ * can experiment with other access types by modifying the Hibernate configuration file
+ * {@code IGNITE_HOME/examples/config/hibernate/example-hibernate-L2-cache.xml}, used by the example.
+ * <p>
+ * Remote nodes should always be started with special configuration file which
+ * enables P2P class loading: {@code 'ignite.{sh|bat} examples/config/example-ignite.xml'}.
+ * <p>
+ * Alternatively you can run {@link ExampleNodeStartup} in another JVM which will
+ * start node with {@code examples/config/example-ignite.xml} configuration.
+ */
+public class HibernateL2CacheExample {
+    /** JDBC URL for backing database (an H2 in-memory database is used). */
+    private static final String JDBC_URL = "jdbc:h2:mem:example;DB_CLOSE_DELAY=-1";
+
+    /** Path to hibernate configuration file (will be resolved from application {@code CLASSPATH}). */
+    private static final String HIBERNATE_CFG = "hibernate/example-hibernate-L2-cache.xml";
+
+    /** Entity names for stats output. */
+    private static final List<String> ENTITY_NAMES =
+        Arrays.asList(User.class.getName(), Post.class.getName(), User.class.getName() + ".posts");
+
+    /** Caches' names. */
+    private static final String USER_CACHE_NAME = "org.apache.ignite.examples.datagrid.hibernate.User";
+
+    /** */
+    private static final String USER_POSTS_CACHE_NAME = "org.apache.ignite.examples.datagrid.hibernate.User.posts";
+
+    /** */
+    private static final String POST_CACHE_NAME = "org.apache.ignite.examples.datagrid.hibernate.Post";
+
+    /** */
+    static final String MODULE_PATH = System.getProperty("user.dir");
+
+    /**
+     * Executes example.
+     *
+     * @param args Command line arguments, none required.
+     * @throws IgniteException If example execution failed.
+     */
+    public static void main(String[] args) throws IgniteException {
+        // Start the node, run the example, and stop the node when finished.
+        try (Ignite ignite = Ignition.start(MODULE_PATH + "/config/example-ignite.xml")) {
+            // We use a single session factory, but create a dedicated session
+            // for each transaction or query. This way we ensure that L1 cache
+            // is not used (L1 cache has per-session scope only).
+            System.out.println();
+            System.out.println(">>> Hibernate L2 cache example started.");
+
+            // Auto-close cache at the end of the example.
+            try (
+                // Create all required caches.
+                IgniteCache c1 = createCache(timestampsCacheName(), ATOMIC);
+                IgniteCache c2 = createCache(queryResultsCacheName(), ATOMIC);
+                IgniteCache c3 = createCache(USER_CACHE_NAME, TRANSACTIONAL);
+                IgniteCache c4 = createCache(USER_POSTS_CACHE_NAME, TRANSACTIONAL);
+                IgniteCache c5 = createCache(POST_CACHE_NAME, TRANSACTIONAL)
+            ) {
+                URL hibernateCfg = ExamplesUtils.url(HIBERNATE_CFG);
+
+                try (SessionFactory sesFactory = createHibernateSessionFactory(hibernateCfg)) {
+                    System.out.println();
+                    System.out.println(">>> Creating objects.");
+
+                    final long userId;
+
+                    Session ses = sesFactory.openSession();
+
+                    try {
+                        Transaction tx = ses.beginTransaction();
+
+                        User user = new User("jedi", "Luke", "Skywalker");
+
+                        user.getPosts().add(new Post(user, "Let the Force be with you."));
+
+                        ses.save(user);
+
+                        tx.commit();
+
+                        // Create a user object, store it in DB, and save the database-generated
+                        // object ID. You may try adding more objects in a similar way.
+                        userId = user.getId();
+                    }
+                    finally {
+                        ses.close();
+                    }
+
+                    // Output L2 cache and Ignite cache stats. You may notice that
+                    // at this point the object is not yet stored in L2 cache, because
+                    // the read was not yet performed.
+                    printStats(sesFactory);
+
+                    System.out.println();
+                    System.out.println(">>> Querying object by ID.");
+
+                    // Query user by ID several times. First time we get an L2 cache
+                    // miss, and the data is queried from DB, but it is then stored
+                    // in cache and successive queries hit the cache and return
+                    // immediately, no SQL query is made.
+                    for (int i = 0; i < 3; i++) {
+                        ses = sesFactory.openSession();
+
+                        try {
+                            Transaction tx = ses.beginTransaction();
+
+                            User user = (User)ses.get(User.class, userId);
+
+                            System.out.println("User: " + user);
+
+                            for (Post post : user.getPosts())
+                                System.out.println("\tPost: " + post);
+
+                            tx.commit();
+                        }
+                        finally {
+                            ses.close();
+                        }
+                    }
+
+                    // Output the stats. We should see 1 miss and 2 hits for
+                    // User and Collection object (stored separately in L2 cache).
+                    // The Post is loaded with the collection, so it won't imply
+                    // a miss.
+                    printStats(sesFactory);
+                }
+            }
+            finally {
+                // Distributed cache could be removed from cluster only by #destroyCache() call.
+                ignite.destroyCache(timestampsCacheName());
+                ignite.destroyCache(queryResultsCacheName());
+                ignite.destroyCache(USER_CACHE_NAME);
+                ignite.destroyCache(USER_POSTS_CACHE_NAME);
+                ignite.destroyCache(POST_CACHE_NAME);
+            }
+        }
+    }
+
+    /**
+     * Creates cache.
+     *
+     * @param name Cache name.
+     * @param atomicityMode Atomicity mode.
+     * @return Cache configuration.
+     */
+    private static IgniteCache createCache(String name, CacheAtomicityMode atomicityMode) {
+        CacheConfiguration ccfg = new CacheConfiguration(name);
+
+        ccfg.setAtomicityMode(atomicityMode);
+        ccfg.setWriteSynchronizationMode(FULL_SYNC);
+
+        return Ignition.ignite().getOrCreateCache(ccfg);
+    }
+
+    /**
+     * Creates a new Hibernate {@link SessionFactory} using a programmatic
+     * configuration.
+     *
+     * @param hibernateCfg Hibernate configuration file.
+     * @return New Hibernate {@link SessionFactory}.
+     */
+    private static SessionFactory createHibernateSessionFactory(URL hibernateCfg) {
+        StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder();
+
+        builder.applySetting("hibernate.connection.url", JDBC_URL);
+        builder.applySetting("hibernate.show_sql", true);
+
+        builder.configure(hibernateCfg);
+
+        return new MetadataSources(builder.build()).buildMetadata().buildSessionFactory();
+    }
+
+    /**
+     * Prints Hibernate L2 cache statistics to standard output.
+     *
+     * @param sesFactory Hibernate {@link SessionFactory}, for which to print
+     *                   statistics.
+     */
+    private static void printStats(SessionFactory sesFactory) {
+        System.out.println("=== Hibernate L2 cache statistics ===");
+
+        for (String entityName : ENTITY_NAMES) {
+            System.out.println("\tEntity: " + entityName);
+
+            SecondLevelCacheStatistics stats =
+                sesFactory.getStatistics().getSecondLevelCacheStatistics(entityName);
+
+            System.out.println("\t\tPuts: " + stats.getPutCount());
+            System.out.println("\t\tHits: " + stats.getHitCount());
+            System.out.println("\t\tMisses: " + stats.getMissCount());
+        }
+
+        System.out.println("=====================================");
+    }
+
+    /**
+     * Returns the name of the timestamps cache to a specific version of apache-hibernate.
+     *
+     * @return Name of the update timestamps cache.
+     */
+    private static String timestampsCacheName() {
+        return isIgniteHibernate51orBelowEnabled() ?
+            // Represents the name of timestamps region specific to hibernate 5.1 {@see HibernateTimestampsRegion}.
+            "org.hibernate.cache.spi.UpdateTimestampsCache" :
+            // Represents the name of timestamps region specific to hibernate 5.3 {@see IgniteTimestampsRegion}.
+            "default-update-timestamps-region";
+    }
+
+    /**
+     * Returns the name of the query results cache to a specific version of apache-hibernate.
+     *
+     * @return Name of the update timestamps cache.
+     */
+    private static String queryResultsCacheName() {
+        return isIgniteHibernate51orBelowEnabled() ?
+            // Represents the name of query results region specific to hibernate 5.1 {@see HibernateQueryResultsRegion}.
+            "org.hibernate.cache.internal.StandardQueryCache" :
+            // Represents the name of query results region specific to hibernate 5.3 {@see IgniteQueryResultsRegion}.
+            "default-query-results-region";
+    }
+
+    /**
+     * Returns {@code true} if ignite-hibernate 5.1 is enabled.
+     *
+     * @return {@code true} if ignite-hibernate 5.1 is enabled.
+     */
+    private static boolean isIgniteHibernate51orBelowEnabled() {
+        try {
+            Class.forName("org.apache.ignite.cache.hibernate.HibernateTimestampsRegion");
+
+            return true;
+        }
+        catch (ClassNotFoundException ignore) {
+            return false;
+        }
+    }
+}
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/hibernate/Post.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/hibernate/Post.java
new file mode 100644
index 0000000..1ee4ec1
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/hibernate/Post.java
@@ -0,0 +1,130 @@
+/*
+ * 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.ignite.examples.datagrid.hibernate;
+
+import java.util.Date;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+
+/**
+ * An entity class representing a post, that a
+ * {@link User} has made on some public service.
+ */
+@Entity
+class Post {
+    /** ID. */
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private long id;
+
+    /** Author. */
+    @ManyToOne
+    private User author;
+
+    /** Text. */
+    private String text;
+
+    /** Created timestamp. */
+    private Date created;
+
+    /**
+     * Default constructor (required by Hibernate).
+     */
+    Post() {
+        // No-op.
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param author Author.
+     * @param text Text.
+     */
+    Post(User author, String text) {
+        this.author = author;
+        this.text = text;
+        created = new Date();
+    }
+
+    /**
+     * @return ID.
+     */
+    public long getId() {
+        return id;
+    }
+
+    /**
+     * @param id New ID.
+     */
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    /**
+     * @return Author.
+     */
+    public User getAuthor() {
+        return author;
+    }
+
+    /**
+     * @param author New author.
+     */
+    public void setAuthor(User author) {
+        this.author = author;
+    }
+
+    /**
+     * @return Text.
+     */
+    public String getText() {
+        return text;
+    }
+
+    /**
+     * @param text New text.
+     */
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    /**
+     * @return Created timestamp.
+     */
+    public Date getCreated() {
+        return (Date)created.clone();
+    }
+
+    /**
+     * @param created New created timestamp.
+     */
+    public void setCreated(Date created) {
+        this.created = (Date)created.clone();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "Post [id=" + id +
+            ", text=" + text +
+            ", created=" + created +
+            ']';
+    }
+}
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/hibernate/User.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/hibernate/User.java
new file mode 100644
index 0000000..df16924
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/hibernate/User.java
@@ -0,0 +1,154 @@
+/*
+ * 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.ignite.examples.datagrid.hibernate;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import org.hibernate.annotations.NaturalId;
+
+/**
+ * A user entity class. Represents a user of some public service,
+ * having a number of personal information fields as well as a
+ * number of posts written.
+ */
+@Entity
+class User {
+    /** ID. */
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private long id;
+
+    /** Login. */
+    @NaturalId
+    private String login;
+
+    /** First name. */
+    private String firstName;
+
+    /** Last name. */
+    private String lastName;
+
+    /** Posts. */
+    @OneToMany(mappedBy = "author", cascade = CascadeType.ALL)
+    private Set<Post> posts = new HashSet<>();
+
+    /**
+     * Default constructor (required by Hibernate).
+     */
+    User() {
+        // No-op.
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param login Login.
+     * @param firstName First name.
+     * @param lastName Last name.
+     */
+    User(String login, String firstName, String lastName) {
+        this.login = login;
+        this.firstName = firstName;
+        this.lastName = lastName;
+    }
+
+    /**
+     * @return ID.
+     */
+    public long getId() {
+        return id;
+    }
+
+    /**
+     * @param id New ID.
+     */
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    /**
+     * @return Login.
+     */
+    public String getLogin() {
+        return login;
+    }
+
+    /**
+     * @param login New login.
+     */
+    public void setLogin(String login) {
+        this.login = login;
+    }
+
+    /**
+     * @return First name.
+     */
+    public String getFirstName() {
+        return firstName;
+    }
+
+    /**
+     * @param firstName New first name.
+     */
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    /**
+     * @return Last name.
+     */
+    public String getLastName() {
+        return lastName;
+    }
+
+    /**
+     * @param lastName New last name.
+     */
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+
+    /**
+     * @return Posts.
+     */
+    public Set<Post> getPosts() {
+        return posts;
+    }
+
+    /**
+     * @param posts New posts.
+     */
+    public void setPosts(Set<Post> posts) {
+        this.posts = posts;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "User [id=" + id +
+            ", login=" + login +
+            ", firstName=" + firstName +
+            ", lastName=" + lastName +
+            ']';
+    }
+}
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/hibernate/package-info.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/hibernate/package-info.java
new file mode 100644
index 0000000..7bddaaf
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/hibernate/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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 description. -->
+ * Hibernate example.
+ */
+package org.apache.ignite.examples.datagrid.hibernate;
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/store/hibernate/CacheHibernatePersonStore.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/store/hibernate/CacheHibernatePersonStore.java
new file mode 100644
index 0000000..ade5d18
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/store/hibernate/CacheHibernatePersonStore.java
@@ -0,0 +1,122 @@
+/*
+ * 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.ignite.examples.datagrid.store.hibernate;
+
+import java.util.List;
+import java.util.UUID;
+import javax.cache.integration.CacheLoaderException;
+import javax.cache.integration.CacheWriterException;
+import org.apache.ignite.cache.store.CacheStore;
+import org.apache.ignite.cache.store.CacheStoreAdapter;
+import org.apache.ignite.cache.store.CacheStoreSession;
+import org.apache.ignite.examples.model.Person;
+import org.apache.ignite.lang.IgniteBiInClosure;
+import org.apache.ignite.resources.CacheStoreSessionResource;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+
+/**
+ * Example of {@link CacheStore} implementation that uses Hibernate
+ * and deals with maps {@link UUID} to {@link Person}.
+ */
+public class CacheHibernatePersonStore extends CacheStoreAdapter<Long, Person> {
+    /** Auto-injected store session. */
+    @CacheStoreSessionResource
+    private CacheStoreSession ses;
+
+    /** {@inheritDoc} */
+    @Override public Person load(Long key) {
+        System.out.println(">>> Store load [key=" + key + ']');
+
+        Session hibSes = ses.attachment();
+
+        try {
+            return (Person)hibSes.get(Person.class, key);
+        }
+        catch (HibernateException e) {
+            throw new CacheLoaderException("Failed to load value from cache store [key=" + key + ']', e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void write(javax.cache.Cache.Entry<? extends Long, ? extends Person> entry) {
+        Long key = entry.getKey();
+        Person val = entry.getValue();
+
+        System.out.println(">>> Store write [key=" + key + ", val=" + val + ']');
+
+        Session hibSes = ses.attachment();
+
+        try {
+            hibSes.saveOrUpdate(val);
+        }
+        catch (HibernateException e) {
+            throw new CacheWriterException("Failed to put value to cache store [key=" + key + ", val" + val + "]", e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings({"JpaQueryApiInspection"})
+    @Override public void delete(Object key) {
+        System.out.println(">>> Store delete [key=" + key + ']');
+
+        Session hibSes = ses.attachment();
+
+        try {
+            hibSes.createQuery("delete " + Person.class.getSimpleName() + " where key = :key").
+                setParameter("key", key).
+                executeUpdate();
+        }
+        catch (HibernateException e) {
+            throw new CacheWriterException("Failed to remove value from cache store [key=" + key + ']', e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void loadCache(IgniteBiInClosure<Long, Person> clo, Object... args) {
+        if (args == null || args.length == 0 || args[0] == null)
+            throw new CacheLoaderException("Expected entry count parameter is not provided.");
+
+        final int entryCnt = (Integer)args[0];
+
+        Session hibSes = ses.attachment();
+
+        try {
+            int cnt = 0;
+
+            List list = hibSes.createCriteria(Person.class).
+                setMaxResults(entryCnt).
+                list();
+
+            if (list != null) {
+                for (Object obj : list) {
+                    Person person = (Person)obj;
+
+                    clo.apply(person.id, person);
+
+                    cnt++;
+                }
+            }
+
+            System.out.println(">>> Loaded " + cnt + " values into cache.");
+        }
+        catch (HibernateException e) {
+            throw new CacheLoaderException("Failed to load values from cache store.", e);
+        }
+    }
+}
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/store/hibernate/CacheHibernateStoreExample.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/store/hibernate/CacheHibernateStoreExample.java
new file mode 100644
index 0000000..f7a03a7
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/store/hibernate/CacheHibernateStoreExample.java
@@ -0,0 +1,163 @@
+/*
+ * 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.ignite.examples.datagrid.store.hibernate;
+
+import java.util.UUID;
+import javax.cache.configuration.Factory;
+import javax.cache.configuration.FactoryBuilder;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.cache.store.CacheStoreSessionListener;
+import org.apache.ignite.cache.store.hibernate.CacheHibernateStoreSessionListener;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.examples.ExampleNodeStartup;
+import org.apache.ignite.examples.ExamplesUtils;
+import org.apache.ignite.examples.model.Person;
+import org.apache.ignite.examples.util.DbH2ServerStartup;
+import org.apache.ignite.transactions.Transaction;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+
+/**
+ * Demonstrates usage of cache with underlying persistent store configured.
+ * <p>
+ * This example uses {@link CacheHibernatePersonStore} as a persistent store.
+ * <p>
+ * To start the example, you should:
+ * <ul>
+ *     <li>Start H2 database TCP server using {@link DbH2ServerStartup}.</li>
+ *     <li>Start a few nodes using {@link ExampleNodeStartup}.</li>
+ *     <li>Start example using {@link CacheHibernateStoreExample}.</li>
+ * </ul>
+ * <p>
+ * Remote nodes can be started with {@link ExampleNodeStartup} in another JVM which will
+ * start node with {@code examples/config/example-ignite.xml} configuration.
+ */
+public class CacheHibernateStoreExample {
+    /** Hibernate configuration resource path. */
+    private static final String HIBERNATE_CFG =
+        "/org/apache/ignite/examples/datagrid/store/hibernate/hibernate.cfg.xml";
+
+    /** Cache name. */
+    private static final String CACHE_NAME = CacheHibernateStoreExample.class.getSimpleName();
+
+    /** Heap size required to run this example. */
+    public static final int MIN_MEMORY = 1024 * 1024 * 1024;
+
+    /** Number of entries to load. */
+    private static final int ENTRY_COUNT = 100_000;
+
+    /** Global person ID to use across entire example. */
+    private static final Long id = Math.abs(UUID.randomUUID().getLeastSignificantBits());
+
+    /**
+     * Executes example.
+     *
+     * @param args Command line arguments, none required.
+     * @throws IgniteException If example execution failed.
+     */
+    public static void main(String[] args) throws IgniteException {
+        ExamplesUtils.checkMinMemory(MIN_MEMORY);
+
+        // To start ignite with desired configuration uncomment the appropriate line.
+        try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) {
+            System.out.println();
+            System.out.println(">>> Cache store example started.");
+
+            CacheConfiguration<Long, Person> cacheCfg = new CacheConfiguration<>(CACHE_NAME);
+
+            // Set atomicity as transaction, since we are showing transactions in example.
+            cacheCfg.setAtomicityMode(TRANSACTIONAL);
+
+            // Configure Hibernate store.
+            cacheCfg.setCacheStoreFactory(FactoryBuilder.factoryOf(CacheHibernatePersonStore.class));
+
+            // Configure Hibernate session listener.
+            cacheCfg.setCacheStoreSessionListenerFactories(new Factory<CacheStoreSessionListener>() {
+                @Override public CacheStoreSessionListener create() {
+                    CacheHibernateStoreSessionListener lsnr = new CacheHibernateStoreSessionListener();
+
+                    lsnr.setHibernateConfigurationPath(HIBERNATE_CFG);
+
+                    return lsnr;
+                }
+            });
+
+            cacheCfg.setReadThrough(true);
+            cacheCfg.setWriteThrough(true);
+
+            // Auto-close cache at the end of the example.
+            try (IgniteCache<Long, Person> cache = ignite.getOrCreateCache(cacheCfg)) {
+                // Make initial cache loading from persistent store. This is a
+                // distributed operation and will call CacheStore.loadCache(...)
+                // method on all nodes in topology.
+                loadCache(cache);
+
+                // Start transaction and execute several cache operations with
+                // read/write-through to persistent store.
+                executeTransaction(cache);
+            }
+            finally {
+                // Distributed cache could be removed from cluster only by #destroyCache() call.
+                ignite.destroyCache(CACHE_NAME);
+            }
+        }
+    }
+
+    /**
+     * Makes initial cache loading.
+     *
+     * @param cache Cache to load.
+     */
+    private static void loadCache(IgniteCache<Long, Person> cache) {
+        long start = System.currentTimeMillis();
+
+        // Start loading cache from persistent store on all caching nodes.
+        cache.loadCache(null, ENTRY_COUNT);
+
+        long end = System.currentTimeMillis();
+
+        System.out.println(">>> Loaded " + cache.size() + " keys with backups in " + (end - start) + "ms.");
+    }
+
+    /**
+     * Executes transaction with read/write-through to persistent store.
+     *
+     * @param cache Cache to execute transaction on.
+     */
+    private static void executeTransaction(IgniteCache<Long, Person> cache) {
+        try (Transaction tx = Ignition.ignite().transactions().txStart()) {
+            Person val = cache.get(id);
+
+            System.out.println("Read value: " + val);
+
+            val = cache.getAndPut(id, new Person(id, "Isaac", "Newton"));
+
+            System.out.println("Overwrote old value: " + val);
+
+            val = cache.get(id);
+
+            System.out.println("Read value: " + val);
+
+            tx.commit();
+        }
+
+        System.out.println("Read value after commit: " + cache.get(id));
+    }
+}
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/store/hibernate/Person.hbm.xml b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/store/hibernate/Person.hbm.xml
new file mode 100644
index 0000000..0728a03
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/store/hibernate/Person.hbm.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+
+
+<!DOCTYPE hibernate-mapping PUBLIC
+        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping default-access="field">
+    <class name="org.apache.ignite.examples.model.Person" table="PERSON">
+        <!-- ID. -->
+        <id name="id"/>
+
+        <!-- We only map data we are interested in. -->
+        <property name="firstName" column="first_name"/>
+        <property name="lastName" column="last_name"/>
+    </class>
+</hibernate-mapping>
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/store/hibernate/hibernate.cfg.xml b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/store/hibernate/hibernate.cfg.xml
new file mode 100644
index 0000000..7bf70af
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/store/hibernate/hibernate.cfg.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+
+<!DOCTYPE hibernate-configuration PUBLIC
+        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
+
+<!--
+    Hibernate configuration.
+-->
+<hibernate-configuration>
+    <session-factory>
+        <!-- Database connection URL. -->
+        <property name="connection.url">jdbc:h2:tcp://localhost/mem:ExampleDb</property>
+
+        <!-- Database connection username. -->
+        <property name="connection.username">sa</property>
+
+        <!-- Only validate the database schema on startup in production mode. -->
+        <property name="hbm2ddl.auto">update</property>
+
+        <!-- Do not output SQL. -->
+        <property name="show_sql">false</property>
+
+        <!-- Mappings. -->
+        <mapping resource="org/apache/ignite/examples/datagrid/store/hibernate/Person.hbm.xml"/>
+    </session-factory>
+</hibernate-configuration>
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/store/hibernate/package-info.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/store/hibernate/package-info.java
new file mode 100644
index 0000000..7210b49
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/datagrid/store/hibernate/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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 description. -->
+ * Contains Hibernate-based cache store implementation.
+ */
+package org.apache.ignite.examples.datagrid.store.hibernate;
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/Address.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/Address.java
new file mode 100644
index 0000000..ba0701b
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/Address.java
@@ -0,0 +1,72 @@
+/*
+ * 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.ignite.examples.model;
+
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.binary.BinaryReader;
+import org.apache.ignite.binary.BinaryWriter;
+import org.apache.ignite.binary.Binarylizable;
+
+/**
+ * Employee address.
+ * <p>
+ * This class implements {@link Binarylizable} only for example purposes,
+ * in order to show how to customize serialization and deserialization of
+ * binary objects.
+ */
+public class Address implements Binarylizable {
+    /** Street. */
+    private String street;
+
+    /** ZIP code. */
+    private int zip;
+
+    /**
+     * Required for binary deserialization.
+     */
+    public Address() {
+        // No-op.
+    }
+
+    /**
+     * @param street Street.
+     * @param zip ZIP code.
+     */
+    public Address(String street, int zip) {
+        this.street = street;
+        this.zip = zip;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeBinary(BinaryWriter writer) throws BinaryObjectException {
+        writer.writeString("street", street);
+        writer.writeInt("zip", zip);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void readBinary(BinaryReader reader) throws BinaryObjectException {
+        street = reader.readString("street");
+        zip = reader.readInt("zip");
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "Address [street=" + street +
+            ", zip=" + zip + ']';
+    }
+}
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/Employee.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/Employee.java
new file mode 100644
index 0000000..a59ffce
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/Employee.java
@@ -0,0 +1,93 @@
+/*
+ * 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.ignite.examples.model;
+
+import java.util.Collection;
+
+/**
+ * This class represents employee object.
+ */
+public class Employee {
+    /** Name. */
+    private String name;
+
+    /** Salary. */
+    private long salary;
+
+    /** Address. */
+    private Address addr;
+
+    /** Departments. */
+    private Collection<String> departments;
+
+    /**
+     * Required for binary deserialization.
+     */
+    public Employee() {
+        // No-op.
+    }
+
+    /**
+     * @param name Name.
+     * @param salary Salary.
+     * @param addr Address.
+     * @param departments Departments.
+     */
+    public Employee(String name, long salary, Address addr, Collection<String> departments) {
+        this.name = name;
+        this.salary = salary;
+        this.addr = addr;
+        this.departments = departments;
+    }
+
+    /**
+     * @return Name.
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * @return Salary.
+     */
+    public long salary() {
+        return salary;
+    }
+
+    /**
+     * @return Address.
+     */
+    public Address address() {
+        return addr;
+    }
+
+    /**
+     * @return Departments.
+     */
+    public Collection<String> departments() {
+        return departments;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "Employee [name=" + name +
+            ", salary=" + salary +
+            ", address=" + addr +
+            ", departments=" + departments + ']';
+    }
+}
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/EmployeeKey.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/EmployeeKey.java
new file mode 100644
index 0000000..55e7967
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/EmployeeKey.java
@@ -0,0 +1,93 @@
+/*
+ * 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.ignite.examples.model;
+
+import org.apache.ignite.cache.affinity.AffinityKeyMapped;
+
+/**
+ * This class represents key for employee object.
+ * <p>
+ * Used in query example to collocate employees
+ * with their organizations.
+ */
+public class EmployeeKey {
+    /** ID. */
+    private int id;
+
+    /** Organization ID. */
+    @AffinityKeyMapped
+    private int organizationId;
+
+    /**
+     * Required for binary deserialization.
+     */
+    public EmployeeKey() {
+        // No-op.
+    }
+
+    /**
+     * @param id ID.
+     * @param organizationId Organization ID.
+     */
+    public EmployeeKey(int id, int organizationId) {
+        this.id = id;
+        this.organizationId = organizationId;
+    }
+
+    /**
+     * @return ID.
+     */
+    public int id() {
+        return id;
+    }
+
+    /**
+     * @return Organization ID.
+     */
+    public int organizationId() {
+        return organizationId;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        EmployeeKey key = (EmployeeKey)o;
+
+        return id == key.id && organizationId == key.organizationId;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        int res = id;
+
+        res = 31 * res + organizationId;
+
+        return res;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "EmployeeKey [id=" + id +
+            ", organizationId=" + organizationId + ']';
+    }
+}
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/Organization.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/Organization.java
new file mode 100644
index 0000000..fc90c07
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/Organization.java
@@ -0,0 +1,132 @@
+/*
+ * 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.ignite.examples.model;
+
+import java.sql.Timestamp;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+
+/**
+ * This class represents organization object.
+ */
+public class Organization {
+    /** */
+    private static final AtomicLong ID_GEN = new AtomicLong();
+
+    /** Organization ID (indexed). */
+    @QuerySqlField(index = true)
+    private Long id;
+
+    /** Organization name (indexed). */
+    @QuerySqlField(index = true)
+    private String name;
+
+    /** Address. */
+    private Address addr;
+
+    /** Type. */
+    private OrganizationType type;
+
+    /** Last update time. */
+    private Timestamp lastUpdated;
+
+    /**
+     * Required for binary deserialization.
+     */
+    public Organization() {
+        // No-op.
+    }
+
+    /**
+     * @param name Organization name.
+     */
+    public Organization(String name) {
+        id = ID_GEN.incrementAndGet();
+
+        this.name = name;
+    }
+
+    /**
+     * @param id Organization ID.
+     * @param name Organization name.
+     */
+    public Organization(long id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    /**
+     * @param name Name.
+     * @param addr Address.
+     * @param type Type.
+     * @param lastUpdated Last update time.
+     */
+    public Organization(String name, Address addr, OrganizationType type, Timestamp lastUpdated) {
+        id = ID_GEN.incrementAndGet();
+
+        this.name = name;
+        this.addr = addr;
+        this.type = type;
+
+        this.lastUpdated = lastUpdated;
+    }
+
+    /**
+     * @return Organization ID.
+     */
+    public Long id() {
+        return id;
+    }
+
+    /**
+     * @return Name.
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * @return Address.
+     */
+    public Address address() {
+        return addr;
+    }
+
+    /**
+     * @return Type.
+     */
+    public OrganizationType type() {
+        return type;
+    }
+
+    /**
+     * @return Last update time.
+     */
+    public Timestamp lastUpdated() {
+        return lastUpdated;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "Organization [id=" + id +
+            ", name=" + name +
+            ", address=" + addr +
+            ", type=" + type +
+            ", lastUpdated=" + lastUpdated + ']';
+    }
+}
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/OrganizationType.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/OrganizationType.java
new file mode 100644
index 0000000..8b22600
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/OrganizationType.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.examples.model;
+
+/**
+ * Organization type enum.
+ */
+public enum OrganizationType {
+    /** Non-profit organization. */
+    NON_PROFIT,
+
+    /** Private organization. */
+    PRIVATE,
+
+    /** Government organization. */
+    GOVERNMENT
+}
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/Person.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/Person.java
new file mode 100644
index 0000000..29a2d9d
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/Person.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.ignite.examples.model;
+
+import java.io.Serializable;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ignite.cache.affinity.AffinityKey;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.cache.query.annotations.QueryTextField;
+
+/**
+ * Person class.
+ */
+public class Person implements Serializable {
+    /** */
+    private static final AtomicLong ID_GEN = new AtomicLong();
+
+    /** Name of index by two fields (orgId, salary). */
+    public static final String ORG_SALARY_IDX = "ORG_SALARY_IDX";
+
+    /** Person ID (indexed). */
+    @QuerySqlField(index = true)
+    public Long id;
+
+    /** Organization ID (indexed). */
+    @QuerySqlField(index = true, orderedGroups = @QuerySqlField.Group(name = ORG_SALARY_IDX, order = 0))
+    public Long orgId;
+
+    /** First name (not-indexed). */
+    @QuerySqlField
+    public String firstName;
+
+    /** Last name (not indexed). */
+    @QuerySqlField
+    public String lastName;
+
+    /** Resume text (create LUCENE-based TEXT index for this field). */
+    @QueryTextField
+    public String resume;
+
+    /** Salary (indexed). */
+    @QuerySqlField(index = true, orderedGroups = @QuerySqlField.Group(name = ORG_SALARY_IDX, order = 1))
+    public double salary;
+
+    /** Custom cache key to guarantee that person is always colocated with its organization. */
+    private transient AffinityKey<Long> key;
+
+    /**
+     * Default constructor.
+     */
+    public Person() {
+        // No-op.
+    }
+
+    /**
+     * Constructs person record.
+     *
+     * @param org       Organization.
+     * @param firstName First name.
+     * @param lastName  Last name.
+     * @param salary    Salary.
+     * @param resume    Resume text.
+     */
+    public Person(Organization org, String firstName, String lastName, double salary, String resume) {
+        // Generate unique ID for this person.
+        id = ID_GEN.incrementAndGet();
+
+        orgId = org.id();
+
+        this.firstName = firstName;
+        this.lastName = lastName;
+        this.salary = salary;
+        this.resume = resume;
+    }
+
+    /**
+     * Constructs person record.
+     *
+     * @param id Person ID.
+     * @param orgId Organization ID.
+     * @param firstName First name.
+     * @param lastName Last name.
+     * @param salary    Salary.
+     * @param resume    Resume text.
+     */
+    public Person(Long id, Long orgId, String firstName, String lastName, double salary, String resume) {
+        this.id = id;
+        this.orgId = orgId;
+        this.firstName = firstName;
+        this.lastName = lastName;
+        this.salary = salary;
+        this.resume = resume;
+    }
+
+    /**
+     * Constructs person record.
+     *
+     * @param id Person ID.
+     * @param firstName First name.
+     * @param lastName Last name.
+     */
+    public Person(Long id, String firstName, String lastName) {
+        this.id = id;
+
+        this.firstName = firstName;
+        this.lastName = lastName;
+    }
+
+    /**
+     * Gets cache affinity key. Since in some examples person needs to be collocated with organization, we create
+     * custom affinity key to guarantee this collocation.
+     *
+     * @return Custom affinity key to guarantee that person is always collocated with organization.
+     */
+    public AffinityKey<Long> key() {
+        if (key == null)
+            key = new AffinityKey<>(id, orgId);
+
+        return key;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override public String toString() {
+        return "Person [id=" + id +
+                ", orgId=" + orgId +
+                ", lastName=" + lastName +
+                ", firstName=" + firstName +
+                ", salary=" + salary +
+                ", resume=" + resume + ']';
+    }
+}
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/package-info.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/package-info.java
new file mode 100644
index 0000000..d6e1c4c
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/model/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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 description. -->
+ * Model classes for Apache Ignite examples.
+ */
+
+package org.apache.ignite.examples.model;
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/package-info.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/package-info.java
new file mode 100644
index 0000000..843fd68
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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 description. -->
+ * Basic examples for ignite functionality.
+ */
+
+package org.apache.ignite.examples;
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/util/DbH2ServerStartup.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/util/DbH2ServerStartup.java
new file mode 100644
index 0000000..f3da07d
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/util/DbH2ServerStartup.java
@@ -0,0 +1,98 @@
+/*
+ * 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.ignite.examples.util;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.sql.SQLException;
+import org.apache.ignite.IgniteException;
+import org.h2.jdbcx.JdbcConnectionPool;
+import org.h2.tools.RunScript;
+import org.h2.tools.Server;
+
+/**
+ * Start H2 database TCP server in order to access sample in-memory database from other processes.
+ */
+public class DbH2ServerStartup {
+    /** Create table script. */
+    private static final String CREATE_PERSON_TABLE =
+        "create table if not exists PERSON(id bigint not null, first_name varchar(50), last_name varchar(50), PRIMARY KEY(id));";
+
+    /** Sample data script. */
+    private static final String POPULATE_PERSON_TABLE =
+        "delete from PERSON;\n" +
+        "insert into PERSON(id, first_name, last_name) values(1, 'Johannes', 'Kepler');\n" +
+        "insert into PERSON(id, first_name, last_name) values(2, 'Galileo', 'Galilei');\n" +
+        "insert into PERSON(id, first_name, last_name) values(3, 'Henry', 'More');\n" +
+        "insert into PERSON(id, first_name, last_name) values(4, 'Polish', 'Brethren');\n" +
+        "insert into PERSON(id, first_name, last_name) values(5, 'Robert', 'Boyle');\n" +
+        "insert into PERSON(id, first_name, last_name) values(6, 'Wilhelm', 'Leibniz');";
+
+    /**
+     * Populate sample database.
+     *
+     * @throws SQLException if
+     */
+    public static void populateDatabase() throws SQLException {
+        // Try to connect to database TCP server.
+        JdbcConnectionPool dataSrc = JdbcConnectionPool.create("jdbc:h2:tcp://localhost/mem:ExampleDb", "sa", "");
+
+        // Create Person table in database.
+        RunScript.execute(dataSrc.getConnection(), new StringReader(CREATE_PERSON_TABLE));
+
+        // Populates Person table with sample data in database.
+        RunScript.execute(dataSrc.getConnection(), new StringReader(POPULATE_PERSON_TABLE));
+    }
+
+    /**
+     * Start H2 database TCP server.
+     *
+     * @param args Command line arguments, none required.
+     * @throws IgniteException If start H2 database TCP server failed.
+     */
+    public static void main(String[] args) throws IgniteException {
+        try {
+            // Start H2 database TCP server in order to access sample in-memory database from other processes.
+            Server.createTcpServer("-tcpDaemon").start();
+
+            populateDatabase();
+
+            // Try to connect to database TCP server.
+            JdbcConnectionPool dataSrc = JdbcConnectionPool.create("jdbc:h2:tcp://localhost/mem:ExampleDb", "sa", "");
+
+            // Create Person table in database.
+            RunScript.execute(dataSrc.getConnection(), new StringReader(CREATE_PERSON_TABLE));
+
+            // Populates Person table with sample data in database.
+            RunScript.execute(dataSrc.getConnection(), new StringReader(POPULATE_PERSON_TABLE));
+        }
+        catch (SQLException e) {
+            throw new IgniteException("Failed to start database TCP server", e);
+        }
+
+        try {
+            do {
+                System.out.println("Type 'q' and press 'Enter' to stop H2 TCP server...");
+            }
+            while ('q' != System.in.read());
+        }
+        catch (IOException ignored) {
+            // No-op.
+        }
+    }
+}
diff --git a/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/util/package-info.java b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/util/package-info.java
new file mode 100644
index 0000000..1d87d02
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/main/java/org/apache/ignite/examples/util/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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 description. -->
+ * Contains utility classes for examples.
+ */
+package org.apache.ignite.examples.util;
diff --git a/modules/hibernate-ext/examples/src/test/java/org/apache/ignite/examples/HibernateL2CacheExampleMultiNodeSelfTest.java b/modules/hibernate-ext/examples/src/test/java/org/apache/ignite/examples/HibernateL2CacheExampleMultiNodeSelfTest.java
new file mode 100644
index 0000000..e727820
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/test/java/org/apache/ignite/examples/HibernateL2CacheExampleMultiNodeSelfTest.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.examples;
+
+import org.apache.ignite.examples.datagrid.hibernate.HibernateL2CacheExample;
+
+/**
+ * Multi-node test for {@link HibernateL2CacheExample}.
+ */
+public class HibernateL2CacheExampleMultiNodeSelfTest extends HibernateL2CacheExampleSelfTest {
+    /** */
+    static final String MODULE_PATH = System.getProperty("user.dir");
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        for (int i = 0; i < RMT_NODES_CNT; i++)
+            startGrid("node-" + i, MODULE_PATH + "/config/example-ignite.xml");
+    }
+}
diff --git a/modules/hibernate-ext/examples/src/test/java/org/apache/ignite/examples/HibernateL2CacheExampleSelfTest.java b/modules/hibernate-ext/examples/src/test/java/org/apache/ignite/examples/HibernateL2CacheExampleSelfTest.java
new file mode 100644
index 0000000..93e24b0
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/test/java/org/apache/ignite/examples/HibernateL2CacheExampleSelfTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.ignite.examples;
+
+import org.apache.ignite.examples.datagrid.hibernate.HibernateL2CacheExample;
+import org.apache.ignite.testframework.junits.common.GridAbstractExamplesTest;
+import org.junit.Test;
+
+/**
+ * Tests the {@link HibernateL2CacheExample}.
+ */
+public class HibernateL2CacheExampleSelfTest extends GridAbstractExamplesTest {
+    /** */
+    @Test
+    public void testHibernateL2CacheExample() {
+        HibernateL2CacheExample.main(EMPTY_ARGS);
+    }
+}
diff --git a/modules/hibernate-ext/examples/src/test/java/org/apache/ignite/testsuites/IgniteLgplExamplesSelfTestSuite.java b/modules/hibernate-ext/examples/src/test/java/org/apache/ignite/testsuites/IgniteLgplExamplesSelfTestSuite.java
new file mode 100644
index 0000000..f12494b
--- /dev/null
+++ b/modules/hibernate-ext/examples/src/test/java/org/apache/ignite/testsuites/IgniteLgplExamplesSelfTestSuite.java
@@ -0,0 +1,45 @@
+/*
+ *  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.ignite.testsuites;
+
+import org.apache.ignite.examples.HibernateL2CacheExampleMultiNodeSelfTest;
+import org.apache.ignite.examples.HibernateL2CacheExampleSelfTest;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import static org.apache.ignite.IgniteSystemProperties.IGNITE_OVERRIDE_MCAST_GRP;
+
+/**
+ * Examples test suite. <p> Contains only Spring ignite examples tests.
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+    HibernateL2CacheExampleSelfTest.class,
+
+    // Multi-node.
+    HibernateL2CacheExampleMultiNodeSelfTest.class
+})
+public class IgniteLgplExamplesSelfTestSuite {
+    /** */
+    @BeforeClass
+    public static void init() {
+        System.setProperty(IGNITE_OVERRIDE_MCAST_GRP,
+            GridTestUtils.getNextMulticastGroup(IgniteLgplExamplesSelfTestSuite.class));
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/README.txt b/modules/hibernate-ext/hibernate/README.txt
new file mode 100644
index 0000000..a38a38f
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/README.txt
@@ -0,0 +1,48 @@
+Apache Ignite Hibernate Module
+------------------------------
+
+Apache Ignite Hibernate module provides Hibernate second-level cache (L2 cache) implementation based
+on Apache Ignite In-Memory Data Grid.
+
+To enable Hibernate module when starting a standalone node, move 'optional/ignite-hibernate' folder to
+'libs' folder before running 'ignite.{sh|bat}' script. The content of the module folder will
+be added to classpath in this case.
+
+Importing Hibernate Module In Maven Project
+-------------------------------------------
+
+If you are using Maven to manage dependencies of your project, you can add Hibernate module
+dependency like this (replace '${ignite.version}' with actual Ignite version you are
+interested in):
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+                        http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    ...
+    <dependencies>
+        ...
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-hibernate-ext</artifactId>
+            <version>${ignite.version}</version>
+        </dependency>
+        ...
+    </dependencies>
+    ...
+</project>
+
+
+LGPL dependencies
+-----------------
+
+Ignite includes the following optional LGPL dependencies:
+ - Hibernate L2 Cache Integration, http://hibernate.org/orm/
+ - JTS Topology Suite for Geospatial indexing, http://tsusiatsoftware.net/jts/main.html
+ - cron4j for cron-based task scheduling, http://www.sauronsoftware.it/projects/cron4j
+
+Apache binary releases cannot include LGPL dependencies. If you would like include
+optional LGPL dependencies into your release, you should download the source release
+from Ignite website and do the build with the following maven command:
+
+./mvnw clean package -DskipTests
diff --git a/modules/hibernate-ext/hibernate/assembly/hibernate-ext.xml b/modules/hibernate-ext/hibernate/assembly/hibernate-ext.xml
new file mode 100644
index 0000000..a63d3f3
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/assembly/hibernate-ext.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3
+          http://maven.apache.org/xsd/assembly-1.1.3.xsd">
+    <id>bin</id>
+
+    <includeBaseDirectory>false</includeBaseDirectory>
+
+    <formats>
+        <format>zip</format>
+    </formats>
+
+    <componentDescriptors>
+        <componentDescriptor>../../../assembly/bin-component-shared.xml</componentDescriptor>
+    </componentDescriptors>
+</assembly>
diff --git a/modules/hibernate-ext/hibernate/licenses/apache-2.0.txt b/modules/hibernate-ext/hibernate/licenses/apache-2.0.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/licenses/apache-2.0.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
diff --git a/modules/hibernate-ext/hibernate/modules/core/src/test/config/log4j-test.xml b/modules/hibernate-ext/hibernate/modules/core/src/test/config/log4j-test.xml
new file mode 100755
index 0000000..b78fa9c
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/modules/core/src/test/config/log4j-test.xml
@@ -0,0 +1,97 @@
+<?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.
+-->
+
+<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN"
+    "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
+<!--
+    Log4j configuration.
+-->
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+    <!--
+        Logs System.out messages to console.
+    -->
+    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+        <!-- Log to STDOUT. -->
+        <param name="Target" value="System.out"/>
+
+        <!-- Log from DEBUG and higher. -->
+        <param name="Threshold" value="DEBUG"/>
+
+        <!-- The default pattern: Date Priority [Category] Message\n -->
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="[%d{ISO8601}][%-5p][%t][%c{1}] %m%n"/>
+        </layout>
+
+        <!-- Do not log beyond INFO level. -->
+        <filter class="org.apache.log4j.varia.LevelRangeFilter">
+            <param name="levelMin" value="DEBUG"/>
+            <param name="levelMax" value="INFO"/>
+        </filter>
+    </appender>
+
+    <!--
+        Logs all System.err messages to console.
+    -->
+    <appender name="CONSOLE_ERR" class="org.apache.log4j.ConsoleAppender">
+        <!-- Log to STDERR. -->
+        <param name="Target" value="System.err"/>
+
+        <!-- Log from WARN and higher. -->
+        <param name="Threshold" value="WARN"/>
+
+        <!-- The default pattern: Date Priority [Category] Message\n -->
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="[%d{ISO8601}][%-5p][%t][%c{1}] %m%n"/>
+        </layout>
+    </appender>
+
+    <!--
+        Logs all output to specified file.
+    -->
+    <appender name="FILE" class="org.apache.log4j.RollingFileAppender">
+        <param name="Threshold" value="DEBUG"/>
+        <param name="File" value="${IGNITE_HOME}/work/log/ignite.log"/>
+        <param name="Append" value="true"/>
+        <param name="MaxFileSize" value="10MB"/>
+        <param name="MaxBackupIndex" value="10"/>
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="[%d{ISO8601}][%-5p][%t][%c{1}] %m%n"/>
+        </layout>
+    </appender>
+
+    <!-- Disable all open source debugging. -->
+    <category name="org">
+        <level value="INFO"/>
+    </category>
+
+    <category name="org.eclipse.jetty">
+        <level value="INFO"/>
+    </category>
+
+    <!-- Default settings. -->
+    <root>
+        <!-- Print at info by default. -->
+        <level value="INFO"/>
+
+        <!-- Append to file and console. -->
+        <appender-ref ref="FILE"/>
+        <appender-ref ref="CONSOLE"/>
+        <appender-ref ref="CONSOLE_ERR"/>
+    </root>
+</log4j:configuration>
diff --git a/modules/hibernate-ext/hibernate/modules/core/src/test/config/tests.properties b/modules/hibernate-ext/hibernate/modules/core/src/test/config/tests.properties
new file mode 100644
index 0000000..18b3606
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/modules/core/src/test/config/tests.properties
@@ -0,0 +1,153 @@
+#
+# 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.
+#
+
+# Local address to bind to.
+local.ip=127.0.0.1
+
+# TCP communication port
+comm.tcp.port=30010
+
+# JBoss JNDI
+# JBoss context factory for JNDI connection establishing.
+jboss.jndi.context.factory=org.jnp.interfaces.NamingContextFactory
+# JBoss specific parameter for JNDI connection establishing.
+jboss.jndi.pkg.prefixes=org.jboss.naming:org.jnp.interfaces
+# URL of JBoss server for the 1st node.
+jboss.jndi.node1.provider.url=jnp://localhost:1199
+# URL of JBoss server for the 2nd node.
+jboss.jndi.node2.provider.url=jnp://localhost:1299
+# JBoss Discovery test max wait time.
+jboss.disco.test.wait=180000
+
+# Deployment configuration paths.
+# You will either need to override deploy.uri.dir or supply CLASSES_URI as system property.
+#
+# Path to keystore with private and public keys.
+deploy.uri.secure.keystore=@{IGNITE_HOME}/modules/tests/config/securedeploy/keystore
+# Temporary dir where deployment unit stored before deploy.
+deploy.uri.tmpdir=${java.io.tmpdir}/gg
+# Deployment dir for file scanner test with different types of GAR's.
+deploy.uri.file2.path=${java.io.tmpdir}/gg/verification/
+# URI string.
+deploy.uri.file2=file://freq=200@localhost/${java.io.tmpdir}/gg/verification/
+# File scanner URI for local file deployment.
+deploy.uri.file=file://localhost/@{IGNITE_HOME}/modules/extdata/uri/target/file/
+# FTP scanner URI for FTP deployment.
+deploy.uri.ftp=ftp://ftptest:iddqd@94.72.60.102:21/test/deployment
+# Classes scanner URI for classes deployment. Must be overridden for every user.
+deploy.uri.cls=${CLASSES_URI}
+# Http scanner URI for HTTP deployment.
+deploy.uri.http=http://fake.uri
+# Http scanner URI for secure SSL HTTPs deployment.
+deploy.uri.https=https://fake.uri
+# Directory with descriptors to construct GAR files.
+deploy.gar.descriptor.dir=modules/urideploy/src/test/java/org/apache/ignite/spi/deployment/uri/META-INF
+
+# Directory with a number of descriptors for the Ant gar task.
+ant.gar.descriptor.dir=modules/extdata/p2p/META-INF
+# Temporary directory for the Ant task resulting GAR file.
+ant.gar.tmpdir=${java.io.tmpdir}/gg
+# The same as p2p.uri.cls but without protocol
+ant.gar.srcdir=@{IGNITE_HOME}/modules/extdata/uri/target/classes/
+
+# Paths to use in URI deployment SPI tests
+urideployment.jar.uri=modules/extdata/uri/target/deploy/uri.jar
+urideployment.path.tmp=modules/extdata/uri/target/deploy_tmp/
+
+# GAR paths to use in URI deployment SPI tests
+ant.urideployment.gar.uri=file://freq=5000@localhost/EXTDATA/uri/target/deploy
+ant.urideployment.gar.file=modules/extdata/uri/target/deploy/uri.gar
+ant.urideployment.gar.libs-file=modules/extdata/uri/target/deploy2/uri-libs.gar
+ant.urideployment.gar.classes-file=modules/extdata/uri/target/deploy2/uri-classes.gar
+ant.urideployment.gar.path=modules/extdata/uri/target/deploy/
+
+# Classpath directory for GridP2PUserVersionChangeSelfTest
+ant.userversion.class.dir=@{IGNITE_HOME}/modules/tests/java/
+
+# Multicast discovery self test.
+discovery.mbeanserver.selftest.baseport=50000
+
+# TCP communication self test.
+comm.mbeanserver.selftest.baseport=50100
+
+# Kernel tests.
+grid.comm.selftest.sender.timeout=1000
+grid.comm.selftest.timeout=10000
+
+#P2P tests
+#Overwrite this property. It should point to P2P module compilation directory.
+p2p.uri.cls=file://localhost/@{IGNITE_HOME}/modules/extdata/p2p/target/classes/
+p2p.uri.cls.second=file://localhost/@{IGNITE_HOME}/modules/extdata/uri/target/classes/
+
+# AOP tests.
+# Connector port for RMI.
+connector.rmi.port=7657
+# Connector port for XFire Web Service.
+connector.ws.port=9090
+
+# Load test duration in minutes.
+load.test.duration=500
+load.test.threadnum=50
+load.test.nodenum=5
+
+# Loaders tests
+loader.self.test.config=modules/core/src/test/config/loaders/grid-cfg.xml
+loader.self.multipletest.config=modules/core/src/test/config/loaders/grid-cfg-2-grids.xml
+loader.self.test.jboss.config=modules/core/src/test/config/loaders/grid-cfg.xml
+
+# WebSphere jmx properties
+websphere.jmx.connector.host=localhost
+websphere.jmx.connector.port=8880
+websphere.jmx.connector.security=false
+websphere.jmx.username=
+websphere.jmx.pwd=
+
+# GlassFish jmx properties for GlassFish Loader
+glassfish.jmx.rmi.connector.port=8686
+glassfish.jmx.username=admin
+glassfish.jmx.password=adminadmin
+
+# Tomcat jmx properties for Servlet Loader
+tomcat.jmx.rmi.connector.port=1097
+
+# Marshaller for tests
+#marshaller.class=org.apache.ignite.marshaller.jdk.GridJdkMarshaller
+
+# EC2 configuration for tests
+#amazon.access.key=
+#amazon.secret.key=
+
+# SSH config.
+ssh.username=uname
+ssh.password=passwd
+
+# SSL tests keystore.
+ssl.keystore.path=@{IGNITE_HOME}/modules/clients/src/test/keystore/server.jks
+ssl.keystore.password=123456
+
+# node01 signed with trust-one, node02 and node03 by trust-two, node02old is expired
+# trust-both contains both CAs
+ssl.keystore.node01.path=@{IGNITE_HOME}/modules/clients/src/test/keystore/ca/node01.jks
+ssl.keystore.node02.path=@{IGNITE_HOME}/modules/clients/src/test/keystore/ca/node02.jks
+ssl.keystore.node03.path=@{IGNITE_HOME}/modules/clients/src/test/keystore/ca/node03.jks
+ssl.keystore.trustone.path=@{IGNITE_HOME}/modules/clients/src/test/keystore/ca/trust-one.jks
+ssl.keystore.trusttwo.path=@{IGNITE_HOME}/modules/clients/src/test/keystore/ca/trust-two.jks
+ssl.keystore.trustboth.path=@{IGNITE_HOME}/modules/clients/src/test/keystore/ca/trust-both.jks
+ssl.keystore.node02old.path=@{IGNITE_HOME}/modules/clients/src/test/keystore/ca/node02old.jks
+
+# Hadoop home directory.
+hadoop.home=@{HADOOP_HOME}
diff --git a/modules/hibernate-ext/hibernate/pom.xml b/modules/hibernate-ext/hibernate/pom.xml
new file mode 100644
index 0000000..f51ad27
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/pom.xml
@@ -0,0 +1,191 @@
+<?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.
+-->
+
+<!--
+    POM file.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.ignite</groupId>
+        <artifactId>ignite-hibernate-parent-ext</artifactId>
+        <version>5.3.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>ignite-hibernate-ext</artifactId>
+    <version>${project.parent.version}</version>
+
+    <url>https://ignite.apache.org</url>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-core</artifactId>
+            <version>${hibernate.core.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-jta</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.ow2.jotm</groupId>
+            <artifactId>jotm-core</artifactId>
+            <version>${jotm.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-dbcp</groupId>
+            <artifactId>commons-dbcp</artifactId>
+            <version>${commons.dbcp.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <version>${h2.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.resource</groupId>
+            <artifactId>connector-api</artifactId>
+            <version>1.5</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-core</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-spring</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-log4j</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-beans</artifactId>
+            <version>${spring.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+            <version>${spring.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.thoughtworks.xstream</groupId>
+            <artifactId>xstream</artifactId>
+            <version>1.4.8</version>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- JDK9+ -->
+
+        <dependency>
+            <groupId>org.jboss.spec.javax.rmi</groupId>
+            <artifactId>jboss-rmi-api_1.0_spec</artifactId>
+            <version>${jboss.rmi.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.xml.bind</groupId>
+            <artifactId>jaxb-api</artifactId>
+            <version>${jaxb.api.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>lgpl</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-deploy-plugin</artifactId>
+                        <configuration>
+                            <skip>false</skip>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <build>
+        <testResources>
+            <testResource>
+                <directory>src/main/java</directory>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </testResource>
+            <testResource>
+                <directory>src/test/java</directory>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </testResource>
+        </testResources>
+
+        <plugins>
+            <!-- Generate the OSGi MANIFEST.MF for this bundle. -->
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateAccessStrategyAdapter.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateAccessStrategyAdapter.java
new file mode 100644
index 0000000..44edf8e
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateAccessStrategyAdapter.java
@@ -0,0 +1,352 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.IgniteKernal;
+import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteCallable;
+import org.apache.ignite.resources.IgniteInstanceResource;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Common interface used to implement Hibernate L2 cache access strategies.
+ * <p>
+ * The expected sequences of steps related to various CRUD operations executed by Hibernate are:
+ * <p>
+ * Insert:
+ * <ul>
+ *     <li>Start DB transaction.</li>
+ *     <li>Execute database insert.</li>
+ *     <li>Call {@link HibernateAccessStrategyAdapter#insert}.</li>
+ *     <li>Commit DB transaction.</li>
+ *     <li>Call {@link HibernateAccessStrategyAdapter#afterInsert}.</li>
+ * </ul>
+ * In case if some step fails and DB transaction is rolled back then
+ * {@link HibernateAccessStrategyAdapter#afterInsert} is not called.
+ * <p>
+ * Update:
+ * <ul>
+ *     <li>Start DB transaction.</li>
+ *     <li>Call {@link HibernateAccessStrategyAdapter#lock}.</li>
+ *     <li>Execute database update.</li>
+ *     <li>Call {@link HibernateAccessStrategyAdapter#update}.</li>
+ *     <li>Commit DB transaction.</li>
+ *     <li>Call {@link HibernateAccessStrategyAdapter#afterUpdate}.</li>
+ * </ul>
+ * In case if {@link HibernateAccessStrategyAdapter#lock} was called, but some other step fails and DB
+ * transaction is rolled back then {@link HibernateAccessStrategyAdapter#unlock} is called for all locked keys.
+ * <p>
+ * Delete:
+ * <ul>
+ *     <li>Start DB transaction.</li>
+ *     <li>Call {@link HibernateAccessStrategyAdapter#lock} for removing key.</li>
+ *     <li>Execute database delete.</li>
+ *     <li>Call {@link HibernateAccessStrategyAdapter#remove}.</li>
+ *     <li>Commit DB transaction.</li>
+ *     <li>Call {@link HibernateAccessStrategyAdapter#unlock}.</li>
+ * </ul>
+ * In case if {@link HibernateAccessStrategyAdapter#lock} was called, but some other step fails and DB
+ * transaction is rolled back then {@link HibernateAccessStrategyAdapter#unlock} is called for all locked keys.
+ * <p>
+ * In case if custom SQL update query is executed Hibernate clears entire cache region,
+ * for this case operations sequence is:
+ * <ul>
+ *     <li>Start DB transaction.</li>
+ *     <li>Call {@link HibernateAccessStrategyAdapter#lockRegion}.</li>
+ *     <li>Execute database query.</li>
+ *     <li>Call {@link HibernateAccessStrategyAdapter#removeAll}.</li>
+ *     <li>Commit DB transaction.</li>
+ *     <li>Call {@link HibernateAccessStrategyAdapter#unlockRegion}.</li>
+ * </ul>
+ */
+public abstract class HibernateAccessStrategyAdapter {
+    /** */
+    protected final HibernateCacheProxy cache;
+
+    /** */
+    private final HibernateExceptionConverter eConverter;
+
+    /** Grid. */
+    protected final Ignite ignite;
+
+    /** */
+    protected final IgniteLogger log;
+
+    /**
+     * @param ignite Node.
+     * @param cache Cache.
+     * @param eConverter Exception converter.
+     */
+    protected HibernateAccessStrategyAdapter(
+        Ignite ignite,
+        HibernateCacheProxy cache,
+        HibernateExceptionConverter eConverter) {
+        this.cache = cache;
+        this.ignite = ignite;
+        this.eConverter = eConverter;
+
+        log = ignite.log().getLogger(getClass());
+    }
+
+    /**
+     * @param e Exception.
+     * @return Runtime exception to be thrown.
+     */
+    final RuntimeException convertException(Exception e) {
+        return eConverter.convert(e);
+    }
+
+    /**
+     * @param key Key.
+     * @return Cached value.
+     */
+    @Nullable public Object get(Object key) {
+        try {
+            Object val = cache.get(key);
+
+            if (log.isDebugEnabled())
+                log.debug("Get [cache=" + cache.name() + ", key=" + key + ", val=" + val + ']');
+
+            return val;
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+    }
+
+    /**
+     * @param key Key.
+     * @param val Value.
+     * @param minimalPutOverride MinimalPut flag
+     */
+    public void putFromLoad(Object key, Object val, boolean minimalPutOverride) {
+        putFromLoad(key, val);
+    }
+
+    /**
+     * Puts in cache value loaded from the database.
+     *
+     * @param key Key.
+     * @param val Value.
+     */
+    public void putFromLoad(Object key, Object val) {
+        if (log.isDebugEnabled())
+            log.debug("Put from load [cache=" + cache.name() + ", key=" + key + ", val=" + val + ']');
+
+        try {
+            cache.put(key, val);
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+    }
+
+    /**
+     * Called during database transaction execution before Hibernate attempts to update or remove given key.
+     *
+     * @param key Key.
+     */
+    public abstract void lock(Object key);
+
+    /**
+     * Called after Hibernate failed to update or successfully removed given key.
+     *
+     * @param key Key.
+     */
+    public abstract void unlock(Object key);
+
+    /**
+     * Called after Hibernate updated object in the database but before transaction completed.
+     *
+     * @param key Key.
+     * @param val Value.
+     * @return {@code True} if operation updated cache.
+     */
+    public abstract boolean update(Object key, Object val);
+
+    /**
+     * Called after Hibernate updated object in the database and transaction successfully completed.
+     *
+     * @param key Key.
+     * @param val Value.
+     * @return {@code True} if operation updated cache.
+     */
+    public abstract boolean afterUpdate(Object key, Object val);
+
+    /**
+     * Called after Hibernate inserted object in the database but before transaction completed.
+     *
+     * @param key Key.
+     * @param val Value.
+     * @return {@code True} if operation updated cache.
+     */
+    public abstract boolean insert(Object key, Object val);
+
+    /**
+     * Called after Hibernate inserted object in the database and transaction successfully completed.
+     *
+     * @param key Key.
+     * @param val Value.
+     * @return {@code True} if operation updated cache.
+     */
+    public abstract boolean afterInsert(Object key, Object val);
+
+    /**
+     * Called after Hibernate removed object from database but before transaction completed.
+     *
+     * @param key Key,
+     */
+    public abstract void remove(Object key);
+
+    /**
+     * Called to remove object from cache without regard to transaction.
+     *
+     * @param key Key.
+     */
+    public void evict(Object key) {
+        evict(ignite, cache, key);
+    }
+
+    /**
+     * Called to remove all data from cache without regard to transaction.
+     */
+    public void evictAll() {
+        if (log.isDebugEnabled())
+            log.debug("Evict all [cache=" + cache.name() + ']');
+
+        try {
+            evictAll(cache);
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+    }
+
+    /**
+     * Called during database transaction execution to clear entire cache region after
+     * Hibernate executed database update, but before transaction completed.
+     */
+    public final void removeAll() {
+        evictAll();
+    }
+
+    /**
+     * Called during database transaction execution before Hibernate executed
+     * update operation which should invalidate entire cache region.
+     */
+    public void lockRegion() {
+        // No-op.
+    }
+
+    /**
+     * Called after transaction clearing entire cache region completed.
+     */
+    public void unlockRegion() {
+        // No-op.
+    }
+
+    /**
+     * Called to remove object from cache without regard to transaction.
+     *
+     * @param ignite Grid.
+     * @param cache Cache.
+     * @param key Key.
+     */
+    public static void evict(Ignite ignite, HibernateCacheProxy cache, Object key) {
+        key = cache.keyTransformer().transform(key);
+
+        ignite.compute(ignite.cluster()).call(new ClearKeyCallable(key, cache.name()));
+    }
+
+    /**
+     * Called to remove all data from cache without regard to transaction.
+     *
+     * @param cache Cache.
+     * @throws IgniteCheckedException If failed.
+     */
+    public static void evictAll(IgniteInternalCache<Object, Object> cache) throws IgniteCheckedException {
+        cache.clear();
+    }
+
+    /**
+     * Callable invalidates given key.
+     */
+    private static class ClearKeyCallable implements IgniteCallable<Void>, Externalizable {
+        /** */
+        private static final long serialVersionUID = 0L;
+
+        /** */
+        @IgniteInstanceResource
+        private Ignite ignite;
+
+        /** */
+        private Object key;
+
+        /** */
+        private String cacheName;
+
+        /**
+         * Empty constructor required by {@link Externalizable}.
+         */
+        public ClearKeyCallable() {
+            // No-op.
+        }
+
+        /**
+         * @param key Key to clear.
+         * @param cacheName Cache name.
+         */
+        private ClearKeyCallable(Object key, String cacheName) {
+            this.key = key;
+            this.cacheName = cacheName;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Void call() throws IgniteCheckedException {
+            IgniteInternalCache<Object, Object> cache = ((IgniteKernal)ignite).getCache(cacheName);
+
+            assert cache != null;
+
+            cache.clearLocally(key);
+
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void writeExternal(ObjectOutput out) throws IOException {
+            out.writeObject(key);
+
+            U.writeString(out, cacheName);
+        }
+
+        /** {@inheritDoc} */
+        @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+            key = in.readObject();
+
+            cacheName = U.readString(in);
+        }
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateAccessStrategyFactory.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateAccessStrategyFactory.java
new file mode 100644
index 0000000..480876d
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateAccessStrategyFactory.java
@@ -0,0 +1,268 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.configuration.TransactionConfiguration;
+import org.apache.ignite.internal.IgniteKernal;
+import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
+import org.apache.ignite.internal.util.typedef.G;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+
+/**
+ * Access strategy factory.
+ */
+public class HibernateAccessStrategyFactory {
+    /** */
+    private final HibernateKeyTransformer keyTransformer;
+
+    /** */
+    private final HibernateExceptionConverter eConverter;
+
+    /**
+     * Hibernate L2 cache grid name property name.
+     *
+     * @deprecated Use {@link #IGNITE_INSTANCE_NAME_PROPERTY}.
+     *      If {@link #IGNITE_INSTANCE_NAME_PROPERTY} is specified it takes precedence.
+     */
+    @Deprecated
+    public static final String GRID_NAME_PROPERTY = "org.apache.ignite.hibernate.grid_name";
+
+    /** Hibernate L2 cache Ignite instance name property name. */
+    public static final String IGNITE_INSTANCE_NAME_PROPERTY = "org.apache.ignite.hibernate.ignite_instance_name";
+
+    /** Property prefix used to specify region name to cache name mapping. */
+    public static final String REGION_CACHE_PROPERTY = "org.apache.ignite.hibernate.region_cache.";
+
+    /** */
+    public static final String DFLT_ACCESS_TYPE_PROPERTY = "org.apache.ignite.hibernate.default_access_type";
+
+    /** */
+    public static final String GRID_CONFIG_PROPERTY = "org.apache.ignite.hibernate.grid_config";
+
+    /** Disable atomicity check when caches are created lazily. */
+    public static final String VERIFY_ATOMICITY = "org.apache.ignite.hibernate.verify_atomicity";
+
+    /** When set, all cache names in ignite will be fetched using the specified prefix. */
+    public static final String CACHE_PREFIX = "org.apache.ignite.hibernate.cache_prefix";
+
+    /** Grid providing caches. */
+    private Ignite ignite;
+
+    /** Region name to cache name (without prefix) mapping. */
+    private final Map<String, String> regionCaches = new HashMap<>();
+
+    /** */
+    private final ThreadLocal threadLoc = new ThreadLocal();
+
+    /** */
+    private final ConcurrentHashMap<String, ThreadLocal> threadLocMap = new ConcurrentHashMap<>();
+
+    /** */
+    private String cachePrefix;
+
+    /** */
+    private boolean verifyAtomicity = true;
+
+    /**
+     * @param keyTransformer Key transformer.
+     * @param eConverter Exception converter.
+     */
+    HibernateAccessStrategyFactory(HibernateKeyTransformer keyTransformer, HibernateExceptionConverter eConverter) {
+        this.keyTransformer = keyTransformer;
+        this.eConverter = eConverter;
+    }
+
+    /**
+     * @param cfgValues {@link Map} of config values.
+     */
+    public void start(Map<Object, Object> cfgValues) {
+        cachePrefix = cfgValues.getOrDefault(CACHE_PREFIX, "").toString();
+
+        verifyAtomicity = Boolean.valueOf(cfgValues.getOrDefault(VERIFY_ATOMICITY, verifyAtomicity).toString());
+
+        Object gridCfg = cfgValues.get(GRID_CONFIG_PROPERTY);
+
+        Object igniteInstanceName = cfgValues.get(IGNITE_INSTANCE_NAME_PROPERTY);
+
+        if (gridCfg != null) {
+            try {
+                ignite = G.start(gridCfg.toString());
+            }
+            catch (IgniteException e) {
+                throw eConverter.convert(e);
+            }
+        }
+        else
+            ignite = Ignition.ignite(igniteInstanceName == null ? null : igniteInstanceName.toString());
+
+        for (Map.Entry entry : cfgValues.entrySet()) {
+            String key = entry.getKey().toString();
+
+            if (key.startsWith(REGION_CACHE_PROPERTY)) {
+                String regionName = key.substring(REGION_CACHE_PROPERTY.length());
+
+                String cacheName = entry.getValue().toString();
+
+                if (((IgniteKernal)ignite).getCache(cachePrefix + cacheName) == null) {
+                    throw new IllegalArgumentException("Cache '" + cacheName + "' specified for region '" + regionName + "' " +
+                        "is not configured.");
+                }
+
+                regionCaches.put(regionName, cacheName);
+            }
+        }
+
+        IgniteLogger log = ignite.log().getLogger(getClass());
+
+        if (log.isDebugEnabled())
+            log.debug("HibernateRegionFactory started [igniteInstanceName=" + igniteInstanceName + ']');
+    }
+
+    /**
+     * @return Ignite node.
+     */
+    public Ignite node() {
+        return ignite;
+    }
+
+    /**
+     * @param regionName L2 cache region name.
+     * @return Cache for given region.
+     */
+    HibernateCacheProxy regionCache(String regionName) {
+        String cacheName = regionCaches.get(regionName);
+
+        if (cacheName == null)
+            cacheName = regionName;
+
+        cacheName = cachePrefix + cacheName;
+
+        Supplier<IgniteInternalCache<Object, Object>> lazyCache = new LazyCacheSupplier(cacheName, regionName);
+
+        return new HibernateCacheProxy(cacheName, lazyCache, keyTransformer);
+    }
+
+    /** */
+    private class LazyCacheSupplier implements Supplier<IgniteInternalCache<Object, Object>> {
+        /** */
+        private final AtomicReference<IgniteInternalCache<Object, Object>> reference = new AtomicReference<>();
+
+        /** */
+        private final String cacheName;
+
+        /** */
+        private final String regionName;
+
+        /** */
+        private LazyCacheSupplier(String cacheName, String regionName) {
+            this.cacheName = cacheName;
+            this.regionName = regionName;
+        }
+
+        /** {@inheritDoc} */
+        @Override public IgniteInternalCache<Object, Object> get() {
+            IgniteInternalCache<Object, Object> cache = reference.get();
+
+            if (cache == null) {
+                cache = ((IgniteKernal)ignite).getCache(cacheName);
+
+                if (cache == null)
+                    throw new IllegalArgumentException("Cache '" + cacheName + "' for region '" + regionName + "' is not configured.");
+
+                reference.compareAndSet(null, cache);
+            }
+
+            return cache;
+        }
+    }
+
+    /**
+     * @param cache Cache.
+     * @return Access strategy implementation.
+     */
+    HibernateAccessStrategyAdapter createReadOnlyStrategy(HibernateCacheProxy cache) {
+        return new HibernateReadOnlyAccessStrategy(ignite, cache, eConverter);
+    }
+
+    /**
+     * @param cache Cache.
+     * @return Access strategy implementation.
+     */
+    HibernateAccessStrategyAdapter createNonStrictReadWriteStrategy(HibernateCacheProxy cache) {
+        ThreadLocal threadLoc = threadLocMap.get(cache.name());
+
+        if (threadLoc == null) {
+            ThreadLocal old = threadLocMap.putIfAbsent(cache.name(), threadLoc = new ThreadLocal());
+
+            if (old != null)
+                threadLoc = old;
+        }
+
+        return new HibernateNonStrictAccessStrategy(ignite, cache, threadLoc, eConverter);
+    }
+
+    /**
+     * @param cache Cache.
+     * @return Access strategy implementation.
+     */
+    HibernateAccessStrategyAdapter createReadWriteStrategy(HibernateCacheProxy cache) {
+        if (verifyAtomicity) {
+            if (cache.configuration().getAtomicityMode() != TRANSACTIONAL) {
+                throw new IllegalArgumentException("Hibernate READ-WRITE access strategy must have Ignite cache with " +
+                    "'TRANSACTIONAL' atomicity mode: " + cache.name());
+            }
+        }
+
+        return new HibernateReadWriteAccessStrategy(ignite, cache, threadLoc, eConverter);
+    }
+
+    /**
+     * @param cache Cache.
+     * @return Access strategy implementation.
+     */
+    HibernateAccessStrategyAdapter createTransactionalStrategy(HibernateCacheProxy cache) {
+        if (verifyAtomicity) {
+            if (cache.configuration().getAtomicityMode() != TRANSACTIONAL) {
+                throw new IllegalArgumentException("Hibernate TRANSACTIONAL access strategy must have Ignite cache with " +
+                    "'TRANSACTIONAL' atomicity mode: " + cache.name());
+            }
+
+            TransactionConfiguration txCfg = ignite.configuration().getTransactionConfiguration();
+
+            if (txCfg == null ||
+                (txCfg.getTxManagerFactory() == null
+                    && txCfg.getTxManagerLookupClassName() == null
+                    && cache.configuration().getTransactionManagerLookupClassName() == null)) {
+                throw new IllegalArgumentException("Hibernate TRANSACTIONAL access strategy must have Ignite with " +
+                    "Factory<TransactionManager> configured (see IgniteConfiguration." +
+                    "getTransactionConfiguration().setTxManagerFactory()): " + cache.name());
+            }
+        }
+
+        return new HibernateTransactionalAccessStrategy(ignite, cache, eConverter);
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateCacheProxy.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateCacheProxy.java
new file mode 100644
index 0000000..15b7fc4
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateCacheProxy.java
@@ -0,0 +1,770 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+import javax.cache.Cache;
+import javax.cache.expiry.ExpiryPolicy;
+import javax.cache.processor.EntryProcessor;
+import javax.cache.processor.EntryProcessorResult;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cache.CacheEntry;
+import org.apache.ignite.cache.CacheMetrics;
+import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.cache.affinity.Affinity;
+import org.apache.ignite.cluster.ClusterGroup;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.apache.ignite.lang.IgniteBiPredicate;
+import org.apache.ignite.mxbean.CacheMetricsMXBean;
+import org.apache.ignite.transactions.Transaction;
+import org.apache.ignite.transactions.TransactionConcurrency;
+import org.apache.ignite.transactions.TransactionIsolation;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Hibernate cache proxy used to substitute hibernate keys with ignite keys.
+ */
+public class HibernateCacheProxy implements IgniteInternalCache<Object, Object> {
+    /** Delegate is lazily loaded which allows for creation of caches after the SPI is bootstrapped */
+    private final Supplier<IgniteInternalCache<Object, Object>> delegate;
+
+    /** Transformer. */
+    private final HibernateKeyTransformer keyTransformer;
+
+    /** */
+    private String cacheName;
+
+    /**
+     * @param cacheName Cache name. Should match delegate.get().name(). Needed for lazy loading.
+     * @param delegate Delegate.
+     * @param keyTransformer Key keyTransformer.
+     */
+    HibernateCacheProxy(
+        String cacheName,
+        Supplier<IgniteInternalCache<Object, Object>> delegate,
+        HibernateKeyTransformer keyTransformer
+    ) {
+        assert cacheName != null;
+        assert delegate != null;
+        assert keyTransformer != null;
+
+        this.cacheName = cacheName;
+        this.delegate = delegate;
+        this.keyTransformer = keyTransformer;
+    }
+
+    /**
+     * @return HibernateKeyTransformer
+     */
+    public HibernateKeyTransformer keyTransformer() {
+        return keyTransformer;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String name() {
+        return cacheName;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean skipStore() {
+        return delegate.get().skipStore();
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalCache setSkipStore(boolean skipStore) {
+        return delegate.get().setSkipStore(skipStore);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isEmpty() {
+        return delegate.get().isEmpty();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean containsKey(Object key) {
+        return delegate.get().containsKey(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Boolean> containsKeyAsync(Object key) {
+        return delegate.get().containsKeyAsync(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean containsKeys(Collection keys) {
+        return delegate.get().containsKey(transform(keys));
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Boolean> containsKeysAsync(Collection keys) {
+        return delegate.get().containsKeysAsync(transform(keys));
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Object localPeek(
+        Object key,
+        CachePeekMode[] peekModes
+    ) throws IgniteCheckedException {
+        return delegate.get().localPeek(keyTransformer.transform(key), peekModes);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Iterable<Cache.Entry<Object, Object>> localEntries(
+        CachePeekMode[] peekModes
+    ) throws IgniteCheckedException {
+        return delegate.get().localEntries(peekModes);
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Object get(Object key) throws IgniteCheckedException {
+        return delegate.get().get(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public CacheEntry getEntry(Object key) throws IgniteCheckedException {
+        return delegate.get().getEntry(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture getAsync(Object key) {
+        return delegate.get().getAsync(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<CacheEntry<Object, Object>> getEntryAsync(Object key) {
+        return delegate.get().getEntryAsync(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public Map getAll(@Nullable Collection keys) throws IgniteCheckedException {
+        return delegate.get().getAll(transform(keys));
+    }
+
+    /** {@inheritDoc} */
+    @Override public Collection<CacheEntry<Object, Object>> getEntries(
+        @Nullable Collection keys) throws IgniteCheckedException {
+        return delegate.get().getEntries(transform(keys));
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Map<Object, Object>> getAllAsync(@Nullable Collection keys) {
+        return delegate.get().getAllAsync(transform(keys));
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Collection<CacheEntry<Object, Object>>> getEntriesAsync(
+        @Nullable Collection keys
+    ) {
+        return delegate.get().getEntriesAsync(transform(keys));
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Object getAndPut(Object key, Object val) throws IgniteCheckedException {
+        return delegate.get().getAndPut(keyTransformer.transform(key), val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture getAndPutAsync(Object key, Object val) {
+        return delegate.get().getAndPutAsync(keyTransformer.transform(key), val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean put(Object key, Object val) throws IgniteCheckedException {
+        return delegate.get().put(keyTransformer.transform(key), val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Boolean> putAsync(Object key, Object val) {
+        return delegate.get().putAsync(keyTransformer.transform(key), val);
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Object getAndPutIfAbsent(Object key, Object val) throws IgniteCheckedException {
+        return delegate.get().getAndPutIfAbsent(keyTransformer.transform(key), val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture getAndPutIfAbsentAsync(Object key, Object val) {
+        return delegate.get().getAndPutIfAbsentAsync(keyTransformer.transform(key), val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean putIfAbsent(Object key, Object val) throws IgniteCheckedException {
+        return delegate.get().putIfAbsent(keyTransformer.transform(key), val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Boolean> putIfAbsentAsync(Object key, Object val) {
+        return delegate.get().putIfAbsentAsync(keyTransformer.transform(key), val);
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Object getAndReplace(Object key, Object val) throws IgniteCheckedException {
+        return delegate.get().getAndReplace(keyTransformer.transform(key), val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture getAndReplaceAsync(Object key, Object val) {
+        return delegate.get().getAndReplaceAsync(keyTransformer.transform(key), val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean replace(Object key, Object val) throws IgniteCheckedException {
+        return delegate.get().replace(keyTransformer.transform(key), val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Boolean> replaceAsync(Object key, Object val) {
+        return delegate.get().replaceAsync(keyTransformer.transform(key), val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean replace(Object key, Object oldVal, Object newVal) throws IgniteCheckedException {
+        return delegate.get().replace(keyTransformer.transform(key), oldVal, newVal);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Boolean> replaceAsync(Object key, Object oldVal, Object newVal) {
+        return delegate.get().replaceAsync(keyTransformer.transform(key), oldVal, newVal);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void putAll(@Nullable Map m) throws IgniteCheckedException {
+        delegate.get().putAll(transform(m));
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<?> putAllAsync(@Nullable Map m) {
+        return delegate.get().putAllAsync(transform(m));
+    }
+
+    /** {@inheritDoc} */
+    @Override public Set keySet() {
+        return delegate.get().keySet();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Set<Cache.Entry<Object, Object>> entrySet() {
+        return delegate.get().entrySet();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Transaction txStart(
+        TransactionConcurrency concurrency,
+        TransactionIsolation isolation
+    ) {
+        return delegate.get().txStart(concurrency, isolation);
+    }
+
+    /** {@inheritDoc} */
+    @Override public GridNearTxLocal txStartEx(
+        TransactionConcurrency concurrency,
+        TransactionIsolation isolation
+    ) {
+        return delegate.get().txStartEx(concurrency, isolation);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Transaction txStart(
+        TransactionConcurrency concurrency,
+        TransactionIsolation isolation,
+        long timeout,
+        int txSize
+    ) {
+        return delegate.get().txStart(concurrency, isolation, timeout, txSize);
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public GridNearTxLocal tx() {
+        return delegate.get().tx();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean evict(Object key) {
+        return delegate.get().evict(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public void evictAll(@Nullable Collection keys) {
+        delegate.get().evictAll(transform(keys));
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clearLocally(boolean srv, boolean near, boolean readers) {
+        delegate.get().clearLocally(srv, near, readers);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean clearLocally(Object key) {
+        return delegate.get().clearLocally(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clearLocallyAll(Set keys, boolean srv, boolean near, boolean readers) {
+        delegate.get().clearLocallyAll((Set<?>)transform(keys), srv, near, readers);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clear(Object key) throws IgniteCheckedException {
+        delegate.get().clear(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clearAll(Set keys) throws IgniteCheckedException {
+        delegate.get().clearAll((Set<?>)transform(keys));
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clear() throws IgniteCheckedException {
+        delegate.get().clear();
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<?> clearAsync() {
+        return delegate.get().clearAsync();
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<?> clearAsync(Object key) {
+        return delegate.get().clearAsync(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<?> clearAllAsync(Set keys) {
+        return delegate.get().clearAllAsync((Set<?>)transform(keys));
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Object getAndRemove(Object key) throws IgniteCheckedException {
+        return delegate.get().getAndRemove(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture getAndRemoveAsync(Object key) {
+        return delegate.get().getAndRemoveAsync(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean remove(Object key) throws IgniteCheckedException {
+        return delegate.get().remove(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Boolean> removeAsync(Object key) {
+        return delegate.get().removeAsync(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean remove(Object key, Object val) throws IgniteCheckedException {
+        return delegate.get().remove(keyTransformer.transform(key), val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Boolean> removeAsync(Object key, Object val) {
+        return delegate.get().removeAsync(keyTransformer.transform(key), val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void removeAll(@Nullable Collection keys) throws IgniteCheckedException {
+        delegate.get().removeAll(transform(keys));
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<?> removeAllAsync(@Nullable Collection keys) {
+        return delegate.get().removeAllAsync(transform(keys));
+    }
+
+    /** {@inheritDoc} */
+    @Override public void removeAll() throws IgniteCheckedException {
+        delegate.get().removeAll();
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<?> removeAllAsync() {
+        return delegate.get().removeAllAsync();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean lock(Object key, long timeout) throws IgniteCheckedException {
+        return delegate.get().lock(keyTransformer.transform(key), timeout);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Boolean> lockAsync(Object key, long timeout) {
+        return delegate.get().lockAsync(keyTransformer.transform(key), timeout);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean lockAll(@Nullable Collection keys, long timeout) throws IgniteCheckedException {
+        return delegate.get().lockAll(transform(keys), timeout);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Boolean> lockAllAsync(@Nullable Collection keys, long timeout) {
+        return delegate.get().lockAllAsync(transform(keys), timeout);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void unlock(Object key) throws IgniteCheckedException {
+        delegate.get().unlock(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public void unlockAll(@Nullable Collection keys) throws IgniteCheckedException {
+        delegate.get().unlockAll(transform(keys));
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isLocked(Object key) {
+        return delegate.get().isLocked(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isLockedByThread(Object key) {
+        return delegate.get().isLockedByThread(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public int size() {
+        return delegate.get().size();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long sizeLong() {
+        return delegate.get().sizeLong();
+    }
+
+    /** {@inheritDoc} */
+    @Override public int localSize(CachePeekMode[] peekModes) throws IgniteCheckedException {
+        return delegate.get().localSize(peekModes);
+    }
+
+    /** {@inheritDoc} */
+    @Override public long localSizeLong(CachePeekMode[] peekModes) throws IgniteCheckedException {
+        return delegate.get().localSizeLong(peekModes);
+    }
+
+    /** {@inheritDoc} */
+    @Override public long localSizeLong(int partition, CachePeekMode[] peekModes) throws IgniteCheckedException {
+        return delegate.get().localSizeLong(partition, peekModes);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int size(CachePeekMode[] peekModes) throws IgniteCheckedException {
+        return delegate.get().size(peekModes);
+    }
+
+    /** {@inheritDoc} */
+    @Override public long sizeLong(CachePeekMode[] peekModes) throws IgniteCheckedException {
+        return delegate.get().sizeLong(peekModes);
+    }
+
+    /** {@inheritDoc} */
+    @Override public long sizeLong(int partition, CachePeekMode[] peekModes) throws IgniteCheckedException {
+        return delegate.get().sizeLong(partition, peekModes);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Integer> sizeAsync(CachePeekMode[] peekModes) {
+        return delegate.get().sizeAsync(peekModes);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Long> sizeLongAsync(CachePeekMode[] peekModes) {
+        return delegate.get().sizeLongAsync(peekModes);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Long> sizeLongAsync(int partition, CachePeekMode[] peekModes) {
+        return delegate.get().sizeLongAsync(partition, peekModes);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int nearSize() {
+        return delegate.get().nearSize();
+    }
+
+    /** {@inheritDoc} */
+    @Override public int primarySize() {
+        return delegate.get().primarySize();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long primarySizeLong() {
+        return delegate.get().primarySizeLong();
+    }
+
+    /** {@inheritDoc} */
+    @Override public CacheConfiguration configuration() {
+        return delegate.get().configuration();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Affinity affinity() {
+        return delegate.get().affinity();
+    }
+
+    /** {@inheritDoc} */
+    @Override public CacheMetrics clusterMetrics() {
+        return delegate.get().clusterMetrics();
+    }
+
+    /** {@inheritDoc} */
+    @Override public CacheMetrics clusterMetrics(ClusterGroup grp) {
+        return delegate.get().clusterMetrics(grp);
+    }
+
+    /** {@inheritDoc} */
+    @Override public CacheMetrics localMetrics() {
+        return delegate.get().localMetrics();
+    }
+
+    /** {@inheritDoc} */
+    @Override public CacheMetricsMXBean clusterMxBean() {
+        return delegate.get().clusterMxBean();
+    }
+
+    /** {@inheritDoc} */
+    @Override public CacheMetricsMXBean localMxBean() {
+        return delegate.get().localMxBean();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long offHeapEntriesCount() {
+        return delegate.get().offHeapEntriesCount();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long offHeapAllocatedSize() {
+        return delegate.get().offHeapAllocatedSize();
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<?> rebalance() {
+        return delegate.get().rebalance();
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Object getForcePrimary(Object key) throws IgniteCheckedException {
+        return delegate.get().getForcePrimary(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture getForcePrimaryAsync(Object key) {
+        return delegate.get().getForcePrimaryAsync(keyTransformer.transform(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public Map getAllOutTx(Set keys) throws IgniteCheckedException {
+        return delegate.get().getAllOutTx((Set<?>)transform(keys));
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Map<Object, Object>> getAllOutTxAsync(Set keys) {
+        return delegate.get().getAllOutTxAsync((Set<?>)transform(keys));
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public ExpiryPolicy expiry() {
+        return delegate.get().expiry();
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalCache withExpiryPolicy(ExpiryPolicy plc) {
+        return delegate.get().withExpiryPolicy(plc);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalCache withNoRetries() {
+        return delegate.get().withNoRetries();
+    }
+
+    /** {@inheritDoc} */
+    @Override public <K1, V1> IgniteInternalCache<K1, V1> withAllowAtomicOpsInTx() {
+        return delegate.get().withAllowAtomicOpsInTx();
+    }
+
+    /** {@inheritDoc} */
+    @Override public GridCacheContext context() {
+        return delegate.get().context();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void localLoadCache(
+        @Nullable IgniteBiPredicate p,
+        @Nullable Object... args
+    ) throws IgniteCheckedException {
+        delegate.get().localLoadCache(p, args);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<?> localLoadCacheAsync(
+        @Nullable IgniteBiPredicate p,
+        @Nullable Object... args
+    ) {
+        return delegate.get().localLoadCacheAsync(p, args);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Collection<Integer> lostPartitions() {
+        return delegate.get().lostPartitions();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void preloadPartition(int part) throws IgniteCheckedException {
+        delegate.get().preloadPartition(part);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<?> preloadPartitionAsync(int part) throws IgniteCheckedException {
+        return delegate.get().preloadPartitionAsync(part);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean localPreloadPartition(int part) throws IgniteCheckedException {
+        return delegate.get().localPreloadPartition(part);
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public EntryProcessorResult invoke(
+        @Nullable AffinityTopologyVersion topVer,
+        Object key,
+        EntryProcessor entryProcessor,
+        Object... args
+    ) throws IgniteCheckedException {
+        return delegate.get().invoke(topVer, key, entryProcessor, args);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Map> invokeAllAsync(Map map, Object... args) {
+        return delegate.get().invokeAllAsync(map, args);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Map invokeAll(Map map, Object... args) throws IgniteCheckedException {
+        return delegate.get().invokeAll(map, args);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<Map> invokeAllAsync(Set keys, EntryProcessor entryProcessor, Object... args) {
+        return delegate.get().invokeAllAsync((Set<?>)transform(keys), entryProcessor, args);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Map invokeAll(Set keys, EntryProcessor entryProcessor, Object... args) throws IgniteCheckedException {
+        return delegate.get().invokeAll((Set<?>)transform(keys), entryProcessor, args);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<EntryProcessorResult> invokeAsync(
+        Object key,
+        EntryProcessor entryProcessor,
+        Object... args
+    ) {
+        return delegate.get().invokeAsync(keyTransformer.transform(key), entryProcessor, args);
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public EntryProcessorResult invoke(
+        Object key,
+        EntryProcessor entryProcessor,
+        Object... args
+    ) throws IgniteCheckedException {
+        return delegate.get().invoke(keyTransformer.transform(key), entryProcessor, args);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Iterator<Cache.Entry<Object, Object>> scanIterator(
+        boolean keepBinary,
+        @Nullable IgniteBiPredicate p
+    ) throws IgniteCheckedException {
+        return delegate.get().scanIterator(keepBinary, p);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<?> removeAllConflictAsync(Map drMap) throws IgniteCheckedException {
+        return delegate.get().removeAllConflictAsync(drMap);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void removeAllConflict(Map drMap) throws IgniteCheckedException {
+        delegate.get().removeAllConflictAsync(drMap);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<?> putAllConflictAsync(Map drMap) throws IgniteCheckedException {
+        return delegate.get().putAllConflictAsync(drMap);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void putAllConflict(Map drMap) throws IgniteCheckedException {
+        delegate.get().putAllConflict(drMap);
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalCache keepBinary() {
+        return delegate.get().keepBinary();
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteInternalCache cache() {
+        return delegate.get().cache();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Iterator iterator() {
+        return delegate.get().iterator();
+    }
+
+    /**
+     * @param keys Keys.
+     */
+    private Collection<Object> transform(Collection<Object> keys) {
+        Collection<Object> res = new LinkedList<>();
+
+        for (Object o : keys)
+            res.add(keyTransformer.transform(o));
+
+        return res;
+    }
+
+    /**
+     * @param map Map.
+     */
+    private Map<Object, Object> transform(Map<Object, Object> map) {
+        Map<Object, Object> res = new HashMap<>();
+
+        Set<Map.Entry<Object, Object>> ents = map.entrySet();
+
+        for (Map.Entry<Object, Object> e : ents)
+            res.put(keyTransformer.transform(e.getKey()), e.getValue());
+
+        return res;
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateExceptionConverter.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateExceptionConverter.java
new file mode 100644
index 0000000..110bec5
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateExceptionConverter.java
@@ -0,0 +1,29 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+/**
+ * Converts Ignite errors into Hibernate runtime exceptions.
+ */
+public interface HibernateExceptionConverter {
+    /**
+     * @param e Exception.
+     * @return Converted exception.
+     */
+    public RuntimeException convert(Exception e);
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateKeyTransformer.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateKeyTransformer.java
new file mode 100644
index 0000000..97fc0e9
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateKeyTransformer.java
@@ -0,0 +1,29 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+/**
+ * An interface for transforming hibernate keys to Ignite keys.
+ */
+public interface HibernateKeyTransformer {
+    /**
+     * @param key Hibernate key.
+     * @return Transformed key.
+     */
+    public Object transform(Object key);
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateKeyWrapper.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateKeyWrapper.java
new file mode 100644
index 0000000..16e5c46
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateKeyWrapper.java
@@ -0,0 +1,109 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import java.io.Serializable;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.hibernate.cache.internal.DefaultCacheKeysFactory;
+import org.hibernate.engine.spi.SessionFactoryImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * Hibernate cache key wrapper.
+ */
+public class HibernateKeyWrapper implements Serializable {
+    /** Key. */
+    private final Object key;
+
+    /** Entry. */
+    private final String entry;
+
+    /** */
+    private final String tenantId;
+
+    /**
+     * @param key Key.
+     * @param entry Entry.
+     * @param tenantId Tenant ID.
+     */
+    HibernateKeyWrapper(Object key, String entry, String tenantId) {
+        this.key = key;
+        this.entry = entry;
+        this.tenantId = tenantId;
+    }
+
+    /**
+     * @return ID.
+     */
+    Object id() {
+        return key;
+    }
+
+    /**
+     * @param id ID.
+     * @param persister Persister.
+     * @param tenantIdentifier Tenant ID.
+     * @return Cache key.
+     * @see DefaultCacheKeysFactory#staticCreateCollectionKey(Object, CollectionPersister, SessionFactoryImplementor, String)
+     */
+    static Object staticCreateCollectionKey(Object id,
+        CollectionPersister persister,
+        String tenantIdentifier) {
+        return new HibernateKeyWrapper(id, persister.getRole(), tenantIdentifier);
+    }
+
+    /**
+     * @param id ID.
+     * @param persister Persister.
+     * @param tenantIdentifier Tenant ID.
+     * @return Cache key.
+     * @see DefaultCacheKeysFactory#staticCreateEntityKey(Object, EntityPersister, SessionFactoryImplementor, String)
+     */
+    public static Object staticCreateEntityKey(Object id, EntityPersister persister, String tenantIdentifier) {
+        return new HibernateKeyWrapper(id, persister.getRootEntityName(), tenantIdentifier);
+    }
+
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        HibernateKeyWrapper that = (HibernateKeyWrapper)o;
+
+        return (key != null ? key.equals(that.key) : that.key == null) &&
+            (entry != null ? entry.equals(that.entry) : that.entry == null) &&
+            (tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        int res = key != null ? key.hashCode() : 0;
+        res = 31 * res + (entry != null ? entry.hashCode() : 0);
+        res = 31 * res + (tenantId != null ? tenantId.hashCode() : 0);
+        return res;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(HibernateKeyWrapper.class, this);
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateNonStrictAccessStrategy.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateNonStrictAccessStrategy.java
new file mode 100644
index 0000000..b7d27fe
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateNonStrictAccessStrategy.java
@@ -0,0 +1,236 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.util.GridLeanSet;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * Implementation of NONSTRICT_READ_WRITE cache access strategy.
+ * <p>
+ * Configuration of L2 cache and per-entity cache access strategy can be set in the
+ * Hibernate configuration file:
+ * <pre name="code" class="xml">
+ * &lt;hibernate-configuration&gt;
+ *     &lt;!-- Enable L2 cache. --&gt;
+ *     &lt;property name="cache.use_second_level_cache"&gt;true&lt;/property&gt;
+ *
+ *     &lt;!-- Use Ignite as L2 cache provider. --&gt;
+ *     &lt;property name="cache.region.factory_class"&gt;org.apache.ignite.cache.hibernate.HibernateRegionFactory&lt;/property&gt;
+ *
+ *     &lt;!-- Specify entity. --&gt;
+ *     &lt;mapping class="com.example.Entity"/&gt;
+ *
+ *     &lt;!-- Enable L2 cache with nonstrict-read-write access strategy for entity. --&gt;
+ *     &lt;class-cache class="com.example.Entity" usage="nonstrict-read-write"/&gt;
+ * &lt;/hibernate-configuration&gt;
+ * </pre>
+ * Also cache access strategy can be set using annotations:
+ * <pre name="code" class="java">
+ * &#064;javax.persistence.Entity
+ * &#064;javax.persistence.Cacheable
+ * &#064;org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+ * public class Entity { ... }
+ * </pre>
+ */
+public class HibernateNonStrictAccessStrategy extends HibernateAccessStrategyAdapter {
+    /** */
+    private final ThreadLocal<WriteContext> writeCtx;
+
+    /**
+     * @param ignite Grid.
+     * @param cache Cache.
+     * @param writeCtx Thread local instance used to track updates done during one Hibernate transaction.
+     * @param eConverter Exception converter.
+     */
+    HibernateNonStrictAccessStrategy(Ignite ignite,
+        HibernateCacheProxy cache,
+        ThreadLocal writeCtx,
+        HibernateExceptionConverter eConverter) {
+        super(ignite, cache, eConverter);
+
+        this.writeCtx = (ThreadLocal<WriteContext>)writeCtx;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void lock(Object key) {
+        WriteContext ctx = writeCtx.get();
+
+        if (ctx == null)
+            writeCtx.set(ctx = new WriteContext());
+
+        ctx.locked(key);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void unlock(Object key) {
+        try {
+            WriteContext ctx = writeCtx.get();
+
+            if (ctx != null && ctx.unlocked(key)) {
+                writeCtx.remove();
+
+                ctx.updateCache(cache);
+            }
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean update(Object key, Object val) {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean afterUpdate(Object key, Object val) {
+        WriteContext ctx = writeCtx.get();
+
+        if (log.isDebugEnabled())
+            log.debug("Put after update [cache=" + cache.name() + ", key=" + key + ", val=" + val + ']');
+
+        if (ctx != null) {
+            ctx.updated(key, val);
+
+            unlock(key);
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean insert(Object key, Object val) {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean afterInsert(Object key, Object val) {
+        if (log.isDebugEnabled())
+            log.debug("Put after insert [cache=" + cache.name() + ", key=" + key + ", val=" + val + ']');
+
+        try {
+            cache.put(key, val);
+
+            return true;
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void remove(Object key) {
+        WriteContext ctx = writeCtx.get();
+
+        if (ctx != null)
+            ctx.removed(key);
+    }
+
+    /**
+     * Information about updates done during single database transaction.
+     */
+    @SuppressWarnings("TypeMayBeWeakened")
+    private static class WriteContext {
+        /** */
+        @GridToStringInclude
+        private Map<Object, Object> updates;
+
+        /** */
+        @GridToStringInclude
+        private Set<Object> rmvs;
+
+        /** */
+        @GridToStringInclude
+        private Set<Object> locked = new GridLeanSet<>();
+
+        /**
+         * Marks key as locked.
+         *
+         * @param key Key.
+         */
+        void locked(Object key) {
+            locked.add(key);
+        }
+
+        /**
+         * Marks key as unlocked.
+         *
+         * @param key Key.
+         * @return {@code True} if last locked key was unlocked.
+         */
+        boolean unlocked(Object key) {
+            locked.remove(key);
+
+            return locked.isEmpty();
+        }
+
+        /**
+         * Marks key as updated.
+         *
+         * @param key Key.
+         * @param val Value.
+         */
+        void updated(Object key, Object val) {
+            if (updates == null)
+                updates = new LinkedHashMap<>();
+
+            updates.put(key, val);
+        }
+
+        /**
+         * Marks key as removed.
+         *
+         * @param key Key.
+         */
+        void removed(Object key) {
+            if (rmvs == null)
+                rmvs = new GridLeanSet<>();
+
+            rmvs.add(key);
+        }
+
+        /**
+         * Updates cache.
+         *
+         * @param cache Cache.
+         * @throws IgniteCheckedException If failed.
+         */
+        void updateCache(HibernateCacheProxy cache) throws IgniteCheckedException {
+            if (!F.isEmpty(rmvs))
+                cache.removeAll(rmvs);
+
+            if (!F.isEmpty(updates))
+                cache.putAll(updates);
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(WriteContext.class, this);
+        }
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateReadOnlyAccessStrategy.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateReadOnlyAccessStrategy.java
new file mode 100644
index 0000000..07570ba
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateReadOnlyAccessStrategy.java
@@ -0,0 +1,109 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+
+/**
+ * Implementation of READ_ONLY cache access strategy.
+ * <p>
+ * Configuration of L2 cache and per-entity cache access strategy can be set in the
+ * Hibernate configuration file:
+ * <pre name="code" class="xml">
+ * &lt;hibernate-configuration&gt;
+ *     &lt;!-- Enable L2 cache. --&gt;
+ *     &lt;property name="cache.use_second_level_cache"&gt;true&lt;/property&gt;
+ *
+ *     &lt;!-- Use Ignite as L2 cache provider. --&gt;
+ *     &lt;property name="cache.region.factory_class"&gt;org.apache.ignite.cache.hibernate.HibernateRegionFactory&lt;/property&gt;
+ *
+ *     &lt;!-- Specify entity. --&gt;
+ *     &lt;mapping class="com.example.Entity"/&gt;
+ *
+ *     &lt;!-- Enable L2 cache with read-only access strategy for entity. --&gt;
+ *     &lt;class-cache class="com.example.Entity" usage="read-only"/&gt;
+ * &lt;/hibernate-configuration&gt;
+ * </pre>
+ * Also cache access strategy can be set using annotations:
+ * <pre name="code" class="java">
+ * &#064;javax.persistence.Entity
+ * &#064;javax.persistence.Cacheable
+ * &#064;org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
+ * public class Entity { ... }
+ * </pre>
+ *
+ */
+public class HibernateReadOnlyAccessStrategy extends HibernateAccessStrategyAdapter {
+    /**
+     * @param ignite Node.
+     * @param cache Cache.
+     * @param eConverter Exception converter.
+     */
+    public HibernateReadOnlyAccessStrategy(
+        Ignite ignite,
+        HibernateCacheProxy cache,
+        HibernateExceptionConverter eConverter) {
+        super(ignite, cache, eConverter);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean insert(Object key, Object val) {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean afterInsert(Object key, Object val) {
+        if (log.isDebugEnabled())
+            log.debug("Put [cache=" + cache.name() + ", key=" + key + ']');
+
+        try {
+            cache.put(key, val);
+
+            return true;
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void lock(Object key) {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public void unlock(Object key) {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public void remove(Object key) {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean update(Object key, Object val) {
+        throw new UnsupportedOperationException("Updates are not supported for read-only access strategy.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean afterUpdate(Object key, Object val) {
+        throw new UnsupportedOperationException("Updates are not supported for read-only access strategy.");
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateReadWriteAccessStrategy.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateReadWriteAccessStrategy.java
new file mode 100644
index 0000000..cc02810
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateReadWriteAccessStrategy.java
@@ -0,0 +1,357 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import java.util.Set;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.apache.ignite.internal.util.GridLeanSet;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+
+/**
+ * Implementation of READ_WRITE cache access strategy.
+ * <p>
+ * Configuration of L2 cache and per-entity cache access strategy can be set in the
+ * Hibernate configuration file:
+ * <pre name="code" class="xml">
+ * &lt;hibernate-configuration&gt;
+ *     &lt;!-- Enable L2 cache. --&gt;
+ *     &lt;property name="cache.use_second_level_cache"&gt;true&lt;/property&gt;
+ *
+ *     &lt;!-- Use Ignite as L2 cache provider. --&gt;
+ *     &lt;property name="cache.region.factory_class"&gt;org.apache.ignite.cache.hibernate.HibernateRegionFactory&lt;/property&gt;
+ *
+ *     &lt;!-- Specify entity. --&gt;
+ *     &lt;mapping class="com.example.Entity"/&gt;
+ *
+ *     &lt;!-- Enable L2 cache with read-write access strategy for entity. --&gt;
+ *     &lt;class-cache class="com.example.Entity" usage="read-write"/&gt;
+ * &lt;/hibernate-configuration&gt;
+ * </pre>
+ * Also cache access strategy can be set using annotations:
+ * <pre name="code" class="java">
+ * &#064;javax.persistence.Entity
+ * &#064;javax.persistence.Cacheable
+ * &#064;org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+ * public class Entity { ... }
+ * </pre>
+ */
+public class HibernateReadWriteAccessStrategy extends HibernateAccessStrategyAdapter {
+    /** */
+    private final ThreadLocal<TxContext> txCtx;
+
+    /**
+     * @param ignite Grid.
+     * @param cache Cache.
+     * @param txCtx Thread local instance used to track updates done during one Hibernate transaction.
+     * @param eConverter Exception converter.
+     */
+    protected HibernateReadWriteAccessStrategy(
+        Ignite ignite,
+        HibernateCacheProxy cache,
+        ThreadLocal txCtx,
+        HibernateExceptionConverter eConverter) {
+        super(ignite, cache, eConverter);
+
+        this.txCtx = (ThreadLocal<TxContext>)txCtx;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object get(Object key) {
+        boolean success = false;
+
+        Object val = null;
+
+        try {
+            val = cache.get(key);
+
+            success = true;
+
+            return val;
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+        finally {
+            if (!success)
+                rollbackCurrentTx();
+
+            if (log.isDebugEnabled()) {
+                log.debug("Get [cache=" + cache.name() + ", key=" + key + ", val=" + val +
+                    ", success=" + success + ']');
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void putFromLoad(Object key, Object val) {
+        boolean success = false;
+
+        try {
+            cache.put(key, val);
+
+            success = true;
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+        finally {
+            if (!success)
+                rollbackCurrentTx();
+
+            if (log.isDebugEnabled()) {
+                log.debug("Put from load [cache=" + cache.name() + ", key=" + key + ", val=" + val +
+                    ", success=" + success + ']');
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void lock(Object key) {
+        boolean success = false;
+
+        try {
+            TxContext ctx = txCtx.get();
+
+            if (ctx == null)
+                txCtx.set(ctx = new TxContext());
+
+            lockKey(key);
+
+            ctx.locked(key);
+
+            success = true;
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+        finally {
+            if (!success)
+                rollbackCurrentTx();
+
+            if (log.isDebugEnabled())
+                log.debug("Lock [cache=" + cache.name() + ", key=" + key + ", success=" + success + ']');
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void unlock(Object key) {
+        boolean success = false;
+
+        try {
+            TxContext ctx = txCtx.get();
+
+            if (ctx != null)
+                unlock(ctx, key);
+
+            success = true;
+        }
+        catch (Exception e) {
+            throw convertException(e);
+        }
+        finally {
+            if (!success)
+                rollbackCurrentTx();
+
+            if (log.isDebugEnabled())
+                log.debug("Unlock [cache=" + cache.name() + ", key=" + key + ", success=" + success + ']');
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean update(Object key, Object val) {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean afterUpdate(Object key, Object val) {
+        boolean success = false;
+        boolean res = false;
+
+        try {
+            TxContext ctx = txCtx.get();
+
+            if (ctx != null) {
+                cache.put(key, val);
+
+                unlock(ctx, key);
+
+                res = true;
+            }
+
+            success = true;
+
+            return res;
+        }
+        catch (Exception e) {
+            throw convertException(e);
+        }
+        finally {
+            if (!success)
+                rollbackCurrentTx();
+
+            if (log.isDebugEnabled()) {
+                log.debug("Put after update [cache=" + cache.name() + ", key=" + key + ", val=" + val +
+                    ", success=" + success + ']');
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean insert(Object key, Object val) {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean afterInsert(Object key, Object val) {
+        boolean success = false;
+
+        try {
+            cache.put(key, val);
+
+            success = true;
+
+            return true;
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+        finally {
+            if (!success)
+                rollbackCurrentTx();
+
+            if (log.isDebugEnabled()) {
+                log.debug("Put after insert [cache=" + cache.name() + ", key=" + key + ", val=" + val +
+                    ", success=" + success + ']');
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void remove(Object key) {
+        boolean success = false;
+
+        try {
+            TxContext ctx = txCtx.get();
+
+            if (ctx != null)
+                cache.remove(key);
+
+            success = true;
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+        finally {
+            if (!success)
+                rollbackCurrentTx();
+
+            if (log.isDebugEnabled())
+                log.debug("Remove [cache=" + cache.name() + ", key=" + key + ", success=" + success + ']');
+        }
+    }
+
+    /**
+     *
+     * @param ctx Transaction context.
+     * @param key Key.
+     */
+    private void unlock(TxContext ctx, Object key) {
+        if (ctx.unlocked(key)) { // Finish transaction if last key is unlocked.
+            txCtx.remove();
+
+            GridNearTxLocal tx = cache.tx();
+
+            assert tx != null;
+
+            try {
+                tx.proxy().commit();
+            }
+            finally {
+                tx.proxy().close();
+            }
+
+            assert cache.tx() == null;
+        }
+    }
+
+    /**
+     * Roll backs current transaction.
+     */
+    private void rollbackCurrentTx() {
+        try {
+            TxContext ctx = txCtx.get();
+
+            if (ctx != null) {
+                txCtx.remove();
+
+                GridNearTxLocal tx = cache.tx();
+
+                if (tx != null)
+                    tx.proxy().rollback();
+            }
+        }
+        catch (IgniteException e) {
+            log.error("Failed to rollback cache transaction.", e);
+        }
+    }
+
+    /**
+     * @param key Key.
+     * @throws IgniteCheckedException If failed.
+     */
+    private void lockKey(Object key) throws IgniteCheckedException {
+        if (cache.tx() == null)
+            cache.txStart(PESSIMISTIC, REPEATABLE_READ);
+
+        cache.get(key); // Acquire distributed lock.
+    }
+
+    /**
+     * Information about updates done during single database transaction.
+     */
+    @SuppressWarnings("TypeMayBeWeakened")
+    private static class TxContext {
+        /** */
+        private Set<Object> locked = new GridLeanSet<>();
+
+        /**
+         * Marks key as locked.
+         *
+         * @param key Key.
+         */
+        void locked(Object key) {
+            locked.add(key);
+        }
+
+        /**
+         * Marks key as unlocked.
+         *
+         * @param key Key.
+         * @return {@code True} if last locked key was unlocked.
+         */
+        boolean unlocked(Object key) {
+            locked.remove(key);
+
+            return locked.isEmpty();
+        }
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateRegion.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateRegion.java
new file mode 100644
index 0000000..184196e
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateRegion.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.ignite.cache.hibernate;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.spi.ExtendedStatisticsSupport;
+import org.hibernate.cache.spi.Region;
+import org.hibernate.cache.spi.RegionFactory;
+import org.hibernate.cache.spi.support.AbstractRegion;
+
+/**
+ * Implementation of {@link Region}. This interface defines base contract for all L2 cache regions.
+ */
+public abstract class HibernateRegion extends AbstractRegion implements ExtendedStatisticsSupport {
+    /** Cache instance. */
+    protected final HibernateCacheProxy cache;
+
+    /** Grid instance. */
+    protected Ignite ignite;
+
+    /** */
+    protected HibernateRegion(RegionFactory factory, String name, Ignite ignite, HibernateCacheProxy cache) {
+        super(name, factory);
+
+        this.ignite = ignite;
+        this.cache = cache;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clear() {
+        try {
+            cache.clear();
+        } catch (IgniteCheckedException e) {
+            throw new CacheException("Problem clearing cache [name=" + cache.name() + "]", e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void destroy() throws CacheException {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getElementCountInMemory() {
+        return cache.offHeapEntriesCount();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getElementCountOnDisk() {
+        return cache.sizeLong();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getSizeInMemory() {
+        return cache.offHeapAllocatedSize();
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateRegionFactory.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateRegionFactory.java
new file mode 100644
index 0000000..5d6edf4
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateRegionFactory.java
@@ -0,0 +1,153 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import java.time.Instant;
+import java.util.Map;
+import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
+import org.hibernate.boot.spi.SessionFactoryOptions;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.cfg.spi.DomainDataRegionBuildingContext;
+import org.hibernate.cache.cfg.spi.DomainDataRegionConfig;
+import org.hibernate.cache.spi.DomainDataRegion;
+import org.hibernate.cache.spi.QueryResultsRegion;
+import org.hibernate.cache.spi.RegionFactory;
+import org.hibernate.cache.spi.TimestampsRegion;
+import org.hibernate.cache.spi.access.AccessType;
+import org.hibernate.cache.spi.support.RegionNameQualifier;
+import org.hibernate.engine.spi.SessionFactoryImplementor;
+import static org.apache.ignite.cache.hibernate.HibernateAccessStrategyFactory.DFLT_ACCESS_TYPE_PROPERTY;
+import static org.hibernate.cache.spi.access.AccessType.NONSTRICT_READ_WRITE;
+
+/**
+ * Hibernate L2 cache region factory.
+ * <p>
+ * Following Hibernate settings should be specified to enable second level cache and to use this
+ * region factory for caching:
+ * <pre name="code" class="brush: xml; gutter: false;">
+ * hibernate.cache.use_second_level_cache=true
+ * hibernate.cache.region.factory_class=org.apache.ignite.cache.hibernate.HibernateRegionFactory
+ * </pre>
+ * Note that before region factory is started you need to start properly configured Ignite node in the same JVM.
+ * For example to start Ignite node one of loader provided in {@code org.apache.ignite.grid.startup} package can be used.
+ * <p>
+ * Name of Ignite instance to be used for region factory must be specified as following Hibernate property:
+ * <pre name="code" class="brush: xml; gutter: false;">
+ * org.apache.ignite.hibernate.ignite_instance_name=&lt;Ignite instance name&gt;
+ * </pre>
+ * Each Hibernate cache region must be associated with some {@link IgniteInternalCache}, by default it is assumed that
+ * for each cache region there is a {@link IgniteInternalCache} with the same name. Also it is possible to define
+ * region to cache mapping using properties with prefix {@code org.apache.ignite.hibernate.region_cache}.
+ * For example if for region with name "region1" cache with name "cache1" should be used then following
+ * Hibernate property should be specified:
+ * <pre name="code" class="brush: xml; gutter: false;">
+ * org.apache.ignite.hibernate.region_cache.region1=cache1
+ * </pre>
+ */
+public class HibernateRegionFactory implements RegionFactory {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** */
+    static final HibernateExceptionConverter EXCEPTION_CONVERTER = new HibernateExceptionConverter() {
+        @Override public RuntimeException convert(Exception e) {
+            return new CacheException(e);
+        }
+    };
+
+    /** Default region access type. */
+    private AccessType dfltAccessType;
+
+    /** Key transformer. */
+    private final HibernateKeyTransformer hibernate4transformer = new HibernateKeyTransformer() {
+        @Override public Object transform(Object key) {
+            return key;
+        }
+    };
+
+    /** */
+    private final HibernateAccessStrategyFactory accessStgyFactory =
+        new HibernateAccessStrategyFactory(hibernate4transformer, EXCEPTION_CONVERTER);
+
+    /** */
+    private SessionFactoryOptions options;
+
+    /** {@inheritDoc} */
+    @Override public void start(SessionFactoryOptions options, Map cfgValues) throws CacheException {
+        this.options = options;
+
+        String accessType = cfgValues.getOrDefault(
+            DFLT_ACCESS_TYPE_PROPERTY, NONSTRICT_READ_WRITE.name()).toString();
+
+        dfltAccessType = AccessType.valueOf(accessType);
+
+        accessStgyFactory.start(cfgValues);
+    }
+
+    /**
+     * @return Access strategy factory.
+     */
+    HibernateAccessStrategyFactory accessStrategyFactory() {
+        return accessStgyFactory;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void stop() {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isMinimalPutsEnabledByDefault() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public AccessType getDefaultAccessType() {
+        return dfltAccessType;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long nextTimestamp() {
+        return Instant.now().toEpochMilli();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String qualify(String regionName) {
+        return RegionNameQualifier.INSTANCE.qualify(regionName, options);
+    }
+
+    /** {@inheritDoc} */
+    @Override public DomainDataRegion buildDomainDataRegion(
+        DomainDataRegionConfig regionCfg,
+        DomainDataRegionBuildingContext buildingCtx) {
+        return new IgniteDomainDataRegion(regionCfg, this, null, buildingCtx,
+            accessStgyFactory);
+    }
+
+    /** {@inheritDoc} */
+    @Override public QueryResultsRegion buildQueryResultsRegion(String regionName, SessionFactoryImplementor sessFactory) {
+        return new IgniteQueryResultsRegion(this, regionName, accessStgyFactory.node(),
+            accessStgyFactory.regionCache(regionName));
+    }
+
+    /** {@inheritDoc} */
+    @Override public TimestampsRegion buildTimestampsRegion(String regionName, SessionFactoryImplementor sessFactory) {
+        return new IgniteTimestampsRegion(this, regionName, accessStgyFactory.node(),
+            accessStgyFactory.regionCache(regionName));
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateTransactionalAccessStrategy.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateTransactionalAccessStrategy.java
new file mode 100644
index 0000000..259c6a8
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/HibernateTransactionalAccessStrategy.java
@@ -0,0 +1,155 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Implementation of {TRANSACTIONAL cache access strategy.
+ * <p>
+ * It is supposed that this strategy is used in JTA environment and Hibernate and
+ * {@link IgniteInternalCache} corresponding to the L2 cache region are configured to use the same transaction manager.
+ * <p>
+ * Configuration of L2 cache and per-entity cache access strategy can be set in the
+ * Hibernate configuration file:
+ * <pre name="code" class="xml">
+ * &lt;hibernate-configuration&gt;
+ *     &lt;!-- Enable L2 cache. --&gt;
+ *     &lt;property name="cache.use_second_level_cache"&gt;true&lt;/property&gt;
+ *
+ *     &lt;!-- Use Ignite as L2 cache provider. --&gt;
+ *     &lt;property name="cache.region.factory_class"&gt;org.apache.ignite.cache.hibernate.HibernateRegionFactory&lt;/property&gt;
+ *
+ *     &lt;!-- Specify entity. --&gt;
+ *     &lt;mapping class="com.example.Entity"/&gt;
+ *
+ *     &lt;!-- Enable L2 cache with transactional access strategy for entity. --&gt;
+ *     &lt;class-cache class="com.example.Entity" usage="transactional"/&gt;
+ * &lt;/hibernate-configuration&gt;
+ * </pre>
+ * Also cache access strategy can be set using annotations:
+ * <pre name="code" class="java">
+ * &#064;javax.persistence.Entity
+ * &#064;javax.persistence.Cacheable
+ * &#064;org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
+ * public class Entity { ... }
+ * </pre>
+ */
+public class HibernateTransactionalAccessStrategy extends HibernateAccessStrategyAdapter {
+    /**
+     * @param ignite Grid.
+     * @param cache Cache.
+     * @param eConverter Exception converter.
+     */
+    HibernateTransactionalAccessStrategy(Ignite ignite,
+        HibernateCacheProxy cache,
+        HibernateExceptionConverter eConverter) {
+        super(ignite, cache, eConverter);
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Object get(Object key) {
+        try {
+            Object val = cache.get(key);
+
+            if (log.isDebugEnabled())
+                log.debug("Get [cache=" + cache.name() + ", key=" + key + ", val=" + val + ']');
+
+            return val;
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void putFromLoad(Object key, Object val) {
+        try {
+            cache.put(key, val);
+
+            if (log.isDebugEnabled())
+                log.debug("Put [cache=" + cache.name() + ", key=" + key + ", val=" + val + ']');
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void lock(Object key) {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public void unlock(Object key) {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean update(Object key, Object val) {
+        try {
+            boolean res = cache.put(key, val);
+
+            if (log.isDebugEnabled())
+                log.debug("Update [cache=" + cache.name() + ", key=" + key + ", val=" + val + ", res=" + res + ']');
+
+            return res;
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean afterUpdate(Object key, Object val) {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean insert(Object key, Object val) {
+        try {
+            boolean res = cache.put(key, val);
+
+            if (log.isDebugEnabled())
+                log.debug("Insert [cache=" + cache.name() + ", key=" + key + ", val=" + val + ", res=" + res + ']');
+
+            return res;
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean afterInsert(Object key, Object val) {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void remove(Object key) {
+        try {
+            cache.remove(key);
+        }
+        catch (IgniteCheckedException e) {
+            throw convertException(e);
+        }
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteCachedDomainDataAccess.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteCachedDomainDataAccess.java
new file mode 100644
index 0000000..fcf211a
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteCachedDomainDataAccess.java
@@ -0,0 +1,132 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import org.apache.ignite.Ignite;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.spi.DomainDataRegion;
+import org.hibernate.cache.spi.RegionFactory;
+import org.hibernate.cache.spi.access.CachedDomainDataAccess;
+import org.hibernate.cache.spi.access.SoftLock;
+import org.hibernate.engine.spi.SharedSessionContractImplementor;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Implementation of L2 cache access strategy delegating to {@link HibernateAccessStrategyAdapter}.
+ */
+public abstract class IgniteCachedDomainDataAccess extends HibernateRegion implements CachedDomainDataAccess {
+    /** */
+    protected final HibernateAccessStrategyAdapter stgy;
+
+    /** */
+    private DomainDataRegion domainDataRegion;
+
+    /**
+     * @param stgy Access strategy implementation.
+     */
+    protected IgniteCachedDomainDataAccess(HibernateAccessStrategyAdapter stgy,
+        RegionFactory regionFactory,
+        DomainDataRegion domainDataRegion,
+        Ignite ignite, HibernateCacheProxy cache) {
+        super(regionFactory, cache.name(), ignite, cache);
+
+        this.stgy = stgy;
+        this.domainDataRegion = domainDataRegion;
+    }
+
+    /** {@inheritDoc} */
+    @Override public DomainDataRegion getRegion() {
+        return domainDataRegion;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object get(SharedSessionContractImplementor ses, Object key) throws CacheException {
+        return stgy.get(key);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean putFromLoad(
+        SharedSessionContractImplementor ses,
+        Object key,
+        Object value,
+        Object version
+    ) throws CacheException {
+        stgy.putFromLoad(key, value);
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean putFromLoad(
+        SharedSessionContractImplementor ses,
+        Object key,
+        Object value,
+        Object version,
+        boolean minimalPutOverride
+    ) throws CacheException {
+        stgy.putFromLoad(key, value);
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public SoftLock lockItem(SharedSessionContractImplementor ses, Object key, Object version) throws CacheException {
+        stgy.lock(key);
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void unlockItem(SharedSessionContractImplementor ses, Object key, SoftLock lock) throws CacheException {
+        stgy.unlock(key);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void remove(SharedSessionContractImplementor ses, Object key) throws CacheException {
+        stgy.remove(key);
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public SoftLock lockRegion() throws CacheException {
+        stgy.lockRegion();
+
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void unlockRegion(SoftLock lock) throws CacheException {
+        stgy.unlockRegion();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void removeAll(SharedSessionContractImplementor ses) throws CacheException {
+        stgy.removeAll();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void evict(Object key) throws CacheException {
+        stgy.evict(key);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void evictAll() throws CacheException {
+        stgy.evictAll();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean contains(Object key) {
+        return stgy.get(key) != null;
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteCollectionDataAccess.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteCollectionDataAccess.java
new file mode 100644
index 0000000..34d2195
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteCollectionDataAccess.java
@@ -0,0 +1,75 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import org.apache.ignite.Ignite;
+import org.hibernate.cache.spi.DomainDataRegion;
+import org.hibernate.cache.spi.RegionFactory;
+import org.hibernate.cache.spi.access.AccessType;
+import org.hibernate.cache.spi.access.CollectionDataAccess;
+import org.hibernate.engine.spi.SessionFactoryImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+
+/**
+ * Implementation of {@link CollectionDataAccess} contract for managing transactional and concurrent
+ * L2 cache access to cached collection data.
+ */
+public class IgniteCollectionDataAccess extends IgniteCachedDomainDataAccess implements CollectionDataAccess {
+    /** Access strategiy type. */
+    private final AccessType accessType;
+
+    /**
+     * @param stgy Access strategy implementation.
+     * @param accessType Strategy access type.
+     * @param regionFactory Region factory.
+     * @param domainDataRegion Data region.
+     * @param ignite Ignite instance.
+     * @param cache Cache proxy.
+     */
+    public IgniteCollectionDataAccess(
+        HibernateAccessStrategyAdapter stgy,
+        AccessType accessType,
+        RegionFactory regionFactory,
+        DomainDataRegion domainDataRegion,
+        Ignite ignite,
+        HibernateCacheProxy cache
+    ) {
+        super(stgy, regionFactory, domainDataRegion, ignite, cache);
+
+        this.accessType = accessType;
+    }
+
+    /** {@inheritDoc} */
+    @Override public AccessType getAccessType() {
+        return accessType;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object generateCacheKey(
+        Object id,
+        CollectionPersister persister,
+        SessionFactoryImplementor factory,
+        String tenantIdentifier) {
+        return HibernateKeyWrapper.staticCreateCollectionKey(id, persister, tenantIdentifier);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object getCacheKeyId(Object cacheKey) {
+        return ((HibernateKeyWrapper)cacheKey).id();
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteDomainDataRegion.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteDomainDataRegion.java
new file mode 100644
index 0000000..fe6237a
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteDomainDataRegion.java
@@ -0,0 +1,190 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import org.apache.ignite.Ignite;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.cfg.spi.CollectionDataCachingConfig;
+import org.hibernate.cache.cfg.spi.DomainDataRegionBuildingContext;
+import org.hibernate.cache.cfg.spi.DomainDataRegionConfig;
+import org.hibernate.cache.cfg.spi.EntityDataCachingConfig;
+import org.hibernate.cache.cfg.spi.NaturalIdDataCachingConfig;
+import org.hibernate.cache.spi.CacheKeysFactory;
+import org.hibernate.cache.spi.ExtendedStatisticsSupport;
+import org.hibernate.cache.spi.RegionFactory;
+import org.hibernate.cache.spi.access.AccessType;
+import org.hibernate.cache.spi.access.CollectionDataAccess;
+import org.hibernate.cache.spi.access.EntityDataAccess;
+import org.hibernate.cache.spi.access.NaturalIdDataAccess;
+import org.hibernate.cache.spi.support.AbstractDomainDataRegion;
+
+/**
+ * Implementation of a region for cacheable domain data.
+ */
+public class IgniteDomainDataRegion extends AbstractDomainDataRegion implements ExtendedStatisticsSupport {
+    /** Cache proxy. */
+    private final HibernateCacheProxy cache;
+
+    /** Strategy factory. */
+    private HibernateAccessStrategyFactory stgyFactory;
+
+    /**
+     * @param regionCfg Domain data region configuration.
+     * @param regionFactory Region factory.
+     * @param defKeysFactory Default cache keys factory.
+     * @param buildingCtx Domain data region building context.
+     * @param stgyFactory Access strategy factory.
+     */
+    public IgniteDomainDataRegion(DomainDataRegionConfig regionCfg,
+        RegionFactory regionFactory,
+        CacheKeysFactory defKeysFactory,
+        DomainDataRegionBuildingContext buildingCtx,
+        HibernateAccessStrategyFactory stgyFactory) {
+        super(regionCfg, regionFactory, defKeysFactory, buildingCtx);
+
+        this.stgyFactory = stgyFactory;
+
+        cache = stgyFactory.regionCache(getName());
+
+        completeInstantiation(regionCfg, buildingCtx);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected EntityDataAccess generateEntityAccess(EntityDataCachingConfig entityAccessCfg) {
+        AccessType accessType = entityAccessCfg.getAccessType();
+        Ignite ignite = stgyFactory.node();
+        switch (accessType) {
+            case READ_ONLY:
+                HibernateAccessStrategyAdapter readOnlyStgy =
+                    stgyFactory.createReadOnlyStrategy(cache);
+                return new IgniteEntityDataAccess(readOnlyStgy, accessType, getRegionFactory(),
+                    this, ignite, cache);
+
+            case NONSTRICT_READ_WRITE:
+                HibernateAccessStrategyAdapter nonStrictReadWriteStgy =
+                    stgyFactory.createNonStrictReadWriteStrategy(cache);
+                return new IgniteEntityDataAccess(nonStrictReadWriteStgy, accessType, getRegionFactory(),
+                    this, ignite, cache);
+
+            case READ_WRITE:
+                HibernateAccessStrategyAdapter readWriteStgy =
+                    stgyFactory.createReadWriteStrategy(cache);
+                return new IgniteEntityDataAccess(readWriteStgy, accessType, getRegionFactory(),
+                    this, ignite, cache);
+
+            case TRANSACTIONAL:
+                HibernateAccessStrategyAdapter transactionalStgy =
+                    stgyFactory.createTransactionalStrategy(cache);
+                return new IgniteEntityDataAccess(transactionalStgy, accessType, getRegionFactory(),
+                    this, ignite, cache);
+
+            default:
+                throw new IllegalArgumentException("Unknown Hibernate access type: " + accessType);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected CollectionDataAccess generateCollectionAccess(CollectionDataCachingConfig cachingCfg) {
+        HibernateCacheProxy cache = stgyFactory.regionCache(getName());
+        AccessType accessType = cachingCfg.getAccessType();
+        Ignite ignite = stgyFactory.node();
+        switch (accessType) {
+            case READ_ONLY:
+                HibernateAccessStrategyAdapter readOnlyStgy =
+                    stgyFactory.createReadOnlyStrategy(cache);
+                return new IgniteCollectionDataAccess(readOnlyStgy, accessType, getRegionFactory(),
+                    this, ignite, cache);
+
+            case NONSTRICT_READ_WRITE:
+                HibernateAccessStrategyAdapter nonStrictReadWriteStgy =
+                    stgyFactory.createNonStrictReadWriteStrategy(cache);
+                return new IgniteCollectionDataAccess(nonStrictReadWriteStgy, accessType, getRegionFactory(),
+                    this, ignite, cache);
+
+            case READ_WRITE:
+                HibernateAccessStrategyAdapter readWriteStgy =
+                    stgyFactory.createReadWriteStrategy(cache);
+                return new IgniteCollectionDataAccess(readWriteStgy, accessType, getRegionFactory(),
+                    this, ignite, cache);
+
+            case TRANSACTIONAL:
+                HibernateAccessStrategyAdapter transactionalStgy =
+                    stgyFactory.createTransactionalStrategy(cache);
+                return new IgniteCollectionDataAccess(transactionalStgy, accessType, getRegionFactory(),
+                    this, ignite, cache);
+
+            default:
+                throw new IllegalArgumentException("Unknown Hibernate access type: " + accessType);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected NaturalIdDataAccess generateNaturalIdAccess(NaturalIdDataCachingConfig naturalIdAccessCfg) {
+        HibernateCacheProxy cache = stgyFactory.regionCache(getName());
+        AccessType accessType = naturalIdAccessCfg.getAccessType();
+        Ignite ignite = stgyFactory.node();
+        switch (accessType) {
+            case READ_ONLY:
+                HibernateAccessStrategyAdapter readOnlyStgy =
+                    stgyFactory.createReadOnlyStrategy(cache);
+                return new IgniteNaturalIdDataAccess(readOnlyStgy, accessType, getRegionFactory(),
+                    this, ignite, cache);
+
+            case NONSTRICT_READ_WRITE:
+                HibernateAccessStrategyAdapter nonStrictReadWriteStgy =
+                    stgyFactory.createNonStrictReadWriteStrategy(cache);
+                return new IgniteNaturalIdDataAccess(nonStrictReadWriteStgy, accessType, getRegionFactory(),
+                    this, ignite, cache);
+
+            case READ_WRITE:
+                HibernateAccessStrategyAdapter readWriteStgy =
+                    stgyFactory.createReadWriteStrategy(cache);
+                return new IgniteNaturalIdDataAccess(readWriteStgy, accessType, getRegionFactory(),
+                    this, ignite, cache);
+
+            case TRANSACTIONAL:
+                HibernateAccessStrategyAdapter transactionalStgy =
+                    stgyFactory.createTransactionalStrategy(cache);
+                return new IgniteNaturalIdDataAccess(transactionalStgy, accessType, getRegionFactory(),
+                    this, ignite, cache);
+
+            default:
+                throw new IllegalArgumentException("Unknown Hibernate access type: " + accessType);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void destroy() throws CacheException {
+        // no-op
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getElementCountInMemory() {
+        return cache.offHeapEntriesCount();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getElementCountOnDisk() {
+        return cache.sizeLong();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getSizeInMemory() {
+        return cache.offHeapAllocatedSize();
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteEntityDataAccess.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteEntityDataAccess.java
new file mode 100644
index 0000000..cf16139
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteEntityDataAccess.java
@@ -0,0 +1,100 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import org.apache.ignite.Ignite;
+import org.hibernate.cache.spi.DomainDataRegion;
+import org.hibernate.cache.spi.RegionFactory;
+import org.hibernate.cache.spi.access.AccessType;
+import org.hibernate.cache.spi.access.EntityDataAccess;
+import org.hibernate.cache.spi.access.SoftLock;
+import org.hibernate.engine.spi.SessionFactoryImplementor;
+import org.hibernate.engine.spi.SharedSessionContractImplementor;
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * Implementation of contract for managing transactional and concurrent access to cached entity data.
+ */
+public class IgniteEntityDataAccess extends IgniteCachedDomainDataAccess implements EntityDataAccess {
+    /** Strategy access type. */
+    private final AccessType accessType;
+
+    /**
+     * @param stgy Access strategy adapter.
+     * @param accessType Access type.
+     * @param regionFactory Region factory.
+     * @param domainDataRegion Domain data region.
+     * @param ignite Ignite instance.
+     * @param cache Hibernate cache proxy.
+     */
+    public IgniteEntityDataAccess(
+        HibernateAccessStrategyAdapter stgy,
+        AccessType accessType,
+        RegionFactory regionFactory,
+        DomainDataRegion domainDataRegion,
+        Ignite ignite,
+        HibernateCacheProxy cache
+    ) {
+        super(stgy, regionFactory, domainDataRegion, ignite, cache);
+
+        this.accessType = accessType;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object generateCacheKey(Object id, EntityPersister persister, SessionFactoryImplementor factory,
+        String tenantIdentifier) {
+        return HibernateKeyWrapper.staticCreateEntityKey(id, persister, tenantIdentifier);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object getCacheKeyId(Object cacheKey) {
+        return ((HibernateKeyWrapper)cacheKey).id();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean insert(SharedSessionContractImplementor ses, Object key, Object val, Object ver) {
+        return stgy.insert(key, val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean afterInsert(SharedSessionContractImplementor ses, Object key, Object val, Object ver) {
+        return stgy.afterInsert(key, val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean update(SharedSessionContractImplementor ses, Object key, Object val, Object curVer, Object prevVer) {
+        return stgy.update(key, val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean afterUpdate(
+        SharedSessionContractImplementor ses,
+        Object key,
+        Object val,
+        Object curVer,
+        Object prevVer,
+        SoftLock lock
+    ) {
+        return stgy.afterUpdate(key, val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public AccessType getAccessType() {
+        return accessType;
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteGeneralDataRegion.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteGeneralDataRegion.java
new file mode 100644
index 0000000..4762054
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteGeneralDataRegion.java
@@ -0,0 +1,89 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.spi.DirectAccessRegion;
+import org.hibernate.cache.spi.QueryResultsRegion;
+import org.hibernate.cache.spi.RegionFactory;
+import org.hibernate.cache.spi.TimestampsRegion;
+import org.hibernate.engine.spi.SharedSessionContractImplementor;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Implementation of {@link DirectAccessRegion}. This interface defines common contract for {@link QueryResultsRegion}
+ * and {@link TimestampsRegion}.
+ */
+public class IgniteGeneralDataRegion extends HibernateRegion implements DirectAccessRegion {
+    /** */
+    private final IgniteLogger log;
+
+    /**
+     * @param factory Region factory.
+     * @param name Region name.
+     * @param ignite Grid.
+     * @param cache Region cache.
+     */
+    IgniteGeneralDataRegion(RegionFactory factory, String name,
+        Ignite ignite, HibernateCacheProxy cache) {
+        super(factory, name, ignite, cache);
+
+        log = ignite.log().getLogger(getClass());
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Object getFromCache(Object key, SharedSessionContractImplementor ses) throws CacheException {
+        try {
+            Object val = cache.get(key);
+
+            if (log.isDebugEnabled())
+                log.debug("Get [cache=" + cache.name() + ", key=" + key + ", val=" + val + ']');
+
+            return val;
+        }
+        catch (IgniteCheckedException e) {
+            throw new CacheException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void putIntoCache(Object key, Object val, SharedSessionContractImplementor ses) throws CacheException {
+        try {
+            cache.put(key, val);
+
+            if (log.isDebugEnabled())
+                log.debug("Put [cache=" + cache.name() + ", key=" + key + ", val=" + val + ']');
+        }
+        catch (IgniteCheckedException e) {
+            throw new CacheException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clear() {
+        try {
+            cache.clear();
+        }
+        catch (IgniteCheckedException e) {
+            throw new CacheException("Problem clearing cache [name=" + cache.name() + "]", e);
+        }
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteNaturalIdDataAccess.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteNaturalIdDataAccess.java
new file mode 100644
index 0000000..39152f5
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteNaturalIdDataAccess.java
@@ -0,0 +1,98 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import org.apache.ignite.Ignite;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.internal.DefaultCacheKeysFactory;
+import org.hibernate.cache.spi.DomainDataRegion;
+import org.hibernate.cache.spi.RegionFactory;
+import org.hibernate.cache.spi.access.AccessType;
+import org.hibernate.cache.spi.access.NaturalIdDataAccess;
+import org.hibernate.cache.spi.access.SoftLock;
+import org.hibernate.engine.spi.SharedSessionContractImplementor;
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * Implementation of contract for managing transactional and concurrent access to cached naturalId data.
+ */
+public class IgniteNaturalIdDataAccess extends IgniteCachedDomainDataAccess implements NaturalIdDataAccess {
+    /** Strategy access type. */
+    private final AccessType accessType;
+
+    /**
+     * @param stgy Access strategy adapter.
+     * @param accessType Access type.
+     * @param regionFactory Region factory.
+     * @param domainDataRegion Domain data region.
+     * @param ignite Ignite instance.
+     * @param cache Cache proxy.
+     */
+    public IgniteNaturalIdDataAccess(
+        HibernateAccessStrategyAdapter stgy,
+        AccessType accessType,
+        RegionFactory regionFactory,
+        DomainDataRegion domainDataRegion,
+        Ignite ignite,
+        HibernateCacheProxy cache
+    ) {
+        super(stgy, regionFactory, domainDataRegion, ignite, cache);
+
+        this.accessType = accessType;
+    }
+
+    /** {@inheritDoc} */
+    @Override public AccessType getAccessType() {
+        return accessType;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object generateCacheKey(Object[] naturalIdValues, EntityPersister persister, SharedSessionContractImplementor ses) {
+        return DefaultCacheKeysFactory.staticCreateNaturalIdKey(naturalIdValues, persister, ses);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object[] getNaturalIdValues(Object cacheKey) {
+        return DefaultCacheKeysFactory.staticGetNaturalIdValues(cacheKey);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean insert(SharedSessionContractImplementor ses, Object key, Object val) throws CacheException {
+        return stgy.insert(key, val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean afterInsert(SharedSessionContractImplementor ses, Object key, Object val) throws CacheException {
+        return stgy.afterInsert(key, val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean update(SharedSessionContractImplementor ses, Object key, Object val) throws CacheException {
+        return stgy.update(key, val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean afterUpdate(
+        SharedSessionContractImplementor ses,
+        Object key,
+        Object val,
+        SoftLock lock
+    ) throws CacheException {
+        return stgy.afterUpdate(key, val);
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteQueryResultsRegion.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteQueryResultsRegion.java
new file mode 100644
index 0000000..2ce25f3
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteQueryResultsRegion.java
@@ -0,0 +1,70 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import org.apache.ignite.Ignite;
+import org.hibernate.cache.spi.QueryResultsRegion;
+import org.hibernate.query.Query;
+
+/**
+ * Implementation of {@link QueryResultsRegion}. This region is used to store query results.
+ * <p>
+ * Query results caching can be enabled in the Hibernate configuration file:
+ * <pre name="code" class="xml">
+ * &lt;hibernate-configuration&gt;
+ *     &lt;!-- Enable L2 cache. --&gt;
+ *     &lt;property name="cache.use_second_level_cache"&gt;true&lt;/property&gt;
+ *
+ *     &lt;!-- Enable query cache. --&gt;
+ *     &lt;property name="cache.use_second_level_cache"&gt;true&lt;/property&gt;
+
+ *     &lt;!-- Use Ignite as L2 cache provider. --&gt;
+ *     &lt;property name="cache.region.factory_class"&gt;org.apache.ignite.cache.hibernate.HibernateRegionFactory&lt;/property&gt;
+ *
+ *     &lt;!-- Specify entity. --&gt;
+ *     &lt;mapping class="com.example.Entity"/&gt;
+ *
+ *     &lt;!-- Enable L2 cache with nonstrict-read-write access strategy for entity. --&gt;
+ *     &lt;class-cache class="com.example.Entity" usage="nonstrict-read-write"/&gt;
+ * &lt;/hibernate-configuration&gt;
+ * </pre>
+ * By default queries are not cached even after enabling query caching, to enable results caching for a particular
+ * query, call {@link Query#setCacheable(boolean)}:
+ * <pre name="code" class="java">
+ *     Session ses = getSession();
+ *
+ *     Query qry = ses.createQuery("...");
+ *
+ *     qry.setCacheable(true); // Enable L2 cache for query.
+ * </pre>
+ * Note: the query cache does not cache the state of the actual entities in the cache, it caches only identifier
+ * values. For this reason, the query cache should always be used in conjunction with
+ * the second-level cache for those entities expected to be cached as part of a query result cache
+ */
+public class IgniteQueryResultsRegion extends IgniteGeneralDataRegion implements QueryResultsRegion {
+    /**
+     * @param factory Region factory.
+     * @param name Region name.
+     * @param ignite Grid.
+     * @param cache Region cache.
+     */
+    public IgniteQueryResultsRegion(HibernateRegionFactory factory, String name,
+        Ignite ignite, HibernateCacheProxy cache) {
+        super(factory, name, ignite, cache);
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteTimestampsRegion.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteTimestampsRegion.java
new file mode 100644
index 0000000..e1d1e3b
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/IgniteTimestampsRegion.java
@@ -0,0 +1,40 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import org.apache.ignite.Ignite;
+import org.hibernate.cache.spi.RegionFactory;
+import org.hibernate.cache.spi.TimestampsRegion;
+
+/**
+ * Implementation of {@link TimestampsRegion}. This region is automatically created when query
+ * caching is enabled and it holds most recent updates timestamps to queryable tables.
+ * Name of timestamps region is {@link RegionFactory#DEFAULT_UPDATE_TIMESTAMPS_REGION_UNQUALIFIED_NAME}.
+ */
+public class IgniteTimestampsRegion extends IgniteGeneralDataRegion implements TimestampsRegion {
+    /**
+     * @param factory Region factory.
+     * @param regionName Region name.
+     * @param cache Region cache.
+     * @param ignite Ignite instance.
+     */
+    public IgniteTimestampsRegion(RegionFactory factory, String regionName, Ignite ignite, HibernateCacheProxy cache) {
+        super(factory, regionName, ignite, cache);
+    }
+
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/package-info.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/package-info.java
new file mode 100644
index 0000000..48e8ffc
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/hibernate/package-info.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 description. -->
+ * Contains implementation of Hibernate L2 cache. Refer to
+ * <i>org.apache.ignite.examples.datagrid.hibernate.HibernateL2CacheExample</i> for more information on how to
+ * configure and use Ignite with Hibernate.
+ */
+
+package org.apache.ignite.cache.hibernate;
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStore.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStore.java
new file mode 100644
index 0000000..619eabf
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStore.java
@@ -0,0 +1,545 @@
+/*
+ * 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.ignite.cache.store.hibernate;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.cache.integration.CacheLoaderException;
+import javax.cache.integration.CacheWriterException;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cache.store.CacheStore;
+import org.apache.ignite.cache.store.CacheStoreAdapter;
+import org.apache.ignite.cache.store.CacheStoreSession;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.IgniteInterruptedCheckedException;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.marshaller.Marshaller;
+import org.apache.ignite.marshaller.jdk.JdkMarshaller;
+import org.apache.ignite.resources.CacheStoreSessionResource;
+import org.apache.ignite.resources.IgniteInstanceResource;
+import org.apache.ignite.resources.LoggerResource;
+import org.apache.ignite.transactions.Transaction;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.SharedSessionContract;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.resource.transaction.spi.TransactionStatus;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * {@link CacheStore} implementation backed by Hibernate. This implementation
+ * stores objects in underlying database in {@code BLOB} format.
+ * <h2 class="header">Configuration</h2>
+ * Either {@link #setSessionFactory(SessionFactory)} or
+ * {@link #setHibernateConfigurationPath(String)} or
+ * {@link #setHibernateProperties(Properties)} should be set.
+ * <p>
+ * If session factory is provided it should contain
+ * {@link CacheHibernateBlobStoreEntry} persistent class (via provided
+ * mapping file {@code GridCacheHibernateStoreEntry.hbm.xml} or by
+ * adding {@link CacheHibernateBlobStoreEntry} to annotated classes
+ * of session factory.
+ * <p>
+ * Path to hibernate configuration may be either an URL or a file path or
+ * a classpath resource. This configuration file should include provided
+ * mapping {@code GridCacheHibernateStoreEntry.hbm.xml} or include annotated
+ * class {@link CacheHibernateBlobStoreEntry}.
+ * <p>
+ * If hibernate properties are provided, mapping
+ * {@code GridCacheHibernateStoreEntry.hbm.xml} is included automatically.
+ * <p>
+ * Use {@link CacheHibernateBlobStoreFactory} factory to pass {@link CacheHibernateBlobStore} to {@link CacheConfiguration}.
+ */
+public class CacheHibernateBlobStore<K, V> extends CacheStoreAdapter<K, V> {
+    /**
+     * Default connection URL
+     * (value is <tt>jdbc:h2:mem:hibernateCacheStore;DB_CLOSE_DELAY=-1;DEFAULT_LOCK_TIMEOUT=5000</tt>).
+     */
+    public static final String DFLT_CONN_URL = "jdbc:h2:mem:hibernateCacheStore;DB_CLOSE_DELAY=-1;" +
+        "DEFAULT_LOCK_TIMEOUT=5000";
+
+    /** Default show SQL property value (value is <tt>true</tt>). */
+    public static final String DFLT_SHOW_SQL = "true";
+
+    /** Default <tt>hibernate.hbm2ddl.auto</tt> property value (value is <tt>true</tt>). */
+    public static final String DFLT_HBM2DDL_AUTO = "update";
+
+    /** Default <tt>hibernate.connection.pool_size</tt> property value (value is <tt>64</tt>). */
+    public static final String DFLT_CONN_POOL_SIZE = "64";
+
+    /** Session attribute name. */
+    private static final String ATTR_SES = "HIBERNATE_STORE_SESSION";
+
+    /** Name of Hibarname mapping resource. */
+    private static final String MAPPING_RESOURCE =
+            "org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreEntry.hbm.xml";
+
+    /** Marshaller. */
+    private static final Marshaller marsh = new JdkMarshaller();
+
+    /** Init guard. */
+    @GridToStringExclude
+    private final AtomicBoolean initGuard = new AtomicBoolean();
+
+    /** Init latch. */
+    @GridToStringExclude
+    private final CountDownLatch initLatch = new CountDownLatch(1);
+
+    /** Hibernate properties. */
+    @GridToStringExclude
+    private Properties hibernateProps;
+
+    /** Session factory. */
+    @GridToStringExclude
+    private SessionFactory sesFactory;
+
+    /** Path to hibernate configuration file. */
+    private String hibernateCfgPath;
+
+    /** Log. */
+    @LoggerResource
+    private IgniteLogger log;
+
+    /** Auto-injected store session. */
+    @CacheStoreSessionResource
+    private CacheStoreSession ses;
+
+    /** Ignite instance. */
+    @IgniteInstanceResource
+    private Ignite ignite;
+
+    /** {@inheritDoc} */
+    @SuppressWarnings({"unchecked", "RedundantTypeArguments"})
+    @Override public V load(K key) {
+        init();
+
+        Transaction tx = transaction();
+
+        if (log.isDebugEnabled())
+            log.debug("Store load [key=" + key + ", tx=" + tx + ']');
+
+        Session ses = session(tx);
+
+        try {
+            CacheHibernateBlobStoreEntry entry = ses.get(CacheHibernateBlobStoreEntry.class, toBytes(key));
+            if (entry == null)
+                return null;
+
+            return fromBytes(entry.getValue());
+        }
+        catch (IgniteCheckedException | HibernateException e) {
+            rollback(ses, tx);
+
+            throw new CacheLoaderException("Failed to load value from cache store with key: " + key, e);
+        }
+        finally {
+            end(ses, tx);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void write(javax.cache.Cache.Entry<? extends K, ? extends V> entry) {
+        init();
+
+        Transaction tx = transaction();
+
+        K key = entry.getKey();
+        V val = entry.getValue();
+
+        if (log.isDebugEnabled())
+            log.debug("Store put [key=" + key + ", val=" + val + ", tx=" + tx + ']');
+
+        if (val == null) {
+            delete(key);
+
+            return;
+        }
+
+        Session ses = session(tx);
+
+        try {
+            CacheHibernateBlobStoreEntry entry0 = new CacheHibernateBlobStoreEntry(toBytes(key), toBytes(val));
+
+            ses.saveOrUpdate(entry0);
+        }
+        catch (IgniteCheckedException | HibernateException e) {
+            rollback(ses, tx);
+
+            throw new CacheWriterException("Failed to put value to cache store [key=" + key + ", val" + val + "]", e);
+        }
+        finally {
+            end(ses, tx);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings({"JpaQueryApiInspection", "JpaQlInspection"})
+    @Override public void delete(Object key) {
+        init();
+
+        Transaction tx = transaction();
+
+        if (log.isDebugEnabled())
+            log.debug("Store remove [key=" + key + ", tx=" + tx + ']');
+
+        Session ses = session(tx);
+
+        try {
+            Object obj = ses.get(CacheHibernateBlobStoreEntry.class, toBytes(key));
+
+            if (obj != null)
+                ses.delete(obj);
+        }
+        catch (IgniteCheckedException | HibernateException e) {
+            rollback(ses, tx);
+
+            throw new CacheWriterException("Failed to remove value from cache store with key: " + key, e);
+        }
+        finally {
+            end(ses, tx);
+        }
+    }
+
+    /**
+     * Rolls back hibernate session.
+     *
+     * @param ses Hibernate session.
+     * @param tx Cache ongoing transaction.
+     */
+    private void rollback(SharedSessionContract ses, Transaction tx) {
+        // Rollback only if there is no cache transaction,
+        // otherwise sessionEnd() will do all required work.
+        if (tx == null) {
+            org.hibernate.Transaction hTx = ses.getTransaction();
+
+            if (hTx != null && hTx.getStatus().canRollback())
+                hTx.rollback();
+        }
+    }
+
+    /**
+     * Ends hibernate session.
+     *
+     * @param ses Hibernate session.
+     * @param tx Cache ongoing transaction.
+     */
+    private void end(Session ses, Transaction tx) {
+        // Commit only if there is no cache transaction,
+        // otherwise sessionEnd() will do all required work.
+        if (tx == null) {
+            org.hibernate.Transaction hTx = ses.getTransaction();
+
+            if (hTx != null && hTx.getStatus() == TransactionStatus.ACTIVE)
+                hTx.commit();
+
+            ses.close();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void sessionEnd(boolean commit) {
+        init();
+
+        Transaction tx = transaction();
+
+        Map<String, Session> props = session().properties();
+
+        Session ses = props.remove(ATTR_SES);
+
+        if (ses != null) {
+            org.hibernate.Transaction hTx = ses.getTransaction();
+
+            if (hTx != null) {
+                try {
+                    if (commit) {
+                        ses.flush();
+
+                        hTx.commit();
+                    }
+                    else
+                        hTx.rollback();
+
+                    if (log.isDebugEnabled())
+                        log.debug("Transaction ended [xid=" + tx.xid() + ", commit=" + commit + ']');
+                }
+                catch (HibernateException e) {
+                    throw new CacheWriterException("Failed to end transaction [xid=" + tx.xid() +
+                        ", commit=" + commit + ']', e);
+                }
+                finally {
+                    ses.close();
+                }
+            }
+        }
+    }
+
+    /**
+     * Gets Hibernate session.
+     *
+     * @param tx Cache transaction.
+     * @return Session.
+     */
+    Session session(@Nullable Transaction tx) {
+        Session ses;
+
+        if (tx != null) {
+            Map<String, Session> props = session().properties();
+
+            ses = props.get(ATTR_SES);
+
+            if (ses == null) {
+                ses = sesFactory.openSession();
+
+                ses.beginTransaction();
+
+                // Store session in transaction metadata, so it can be accessed
+                // for other operations on the same transaction.
+                props.put(ATTR_SES, ses);
+
+                if (log.isDebugEnabled())
+                    log.debug("Hibernate session open [ses=" + ses + ", tx=" + tx.xid() + "]");
+            }
+        }
+        else {
+            ses = sesFactory.openSession();
+
+            ses.beginTransaction();
+        }
+
+        return ses;
+    }
+
+    /**
+     * Sets session factory.
+     *
+     * @param sesFactory Session factory.
+     */
+    public void setSessionFactory(SessionFactory sesFactory) {
+        this.sesFactory = sesFactory;
+    }
+
+    /**
+     * Sets hibernate configuration path.
+     * <p>
+     * This may be either URL or file path or classpath resource.
+     *
+     * @param hibernateCfgPath URL or file path or classpath resource
+     *      pointing to hibernate configuration XML file.
+     */
+    public void setHibernateConfigurationPath(String hibernateCfgPath) {
+        this.hibernateCfgPath = hibernateCfgPath;
+    }
+
+    /**
+     * Sets Hibernate properties.
+     *
+     * @param hibernateProps Hibernate properties.
+     */
+    public void setHibernateProperties(Properties hibernateProps) {
+        this.hibernateProps = hibernateProps;
+    }
+
+    /**
+     * Initializes store.
+     *
+     * @throws IgniteException If failed to initialize.
+     */
+    private void init() throws IgniteException {
+        if (initGuard.compareAndSet(false, true)) {
+            if (log.isDebugEnabled())
+                log.debug("Initializing cache store.");
+
+            try {
+                // Session factory has been provided - nothing to do.
+                if (sesFactory != null)
+                    return;
+
+                if (!F.isEmpty(hibernateCfgPath)) {
+                    try {
+                        URL url = new URL(hibernateCfgPath);
+
+                        sesFactory = new Configuration().configure(url).buildSessionFactory();
+
+                        if (log.isDebugEnabled())
+                            log.debug("Configured session factory using URL: " + url);
+
+                        // Session factory has been successfully initialized.
+                        return;
+                    }
+                    catch (MalformedURLException e) {
+                        if (log.isDebugEnabled())
+                            log.debug("Caught malformed URL exception: " + e.getMessage());
+                    }
+
+                    // Provided path is not a valid URL. File?
+                    File cfgFile = new File(hibernateCfgPath);
+
+                    if (cfgFile.exists()) {
+                        sesFactory = new Configuration().configure(cfgFile).buildSessionFactory();
+
+                        if (log.isDebugEnabled())
+                            log.debug("Configured session factory using file: " + hibernateCfgPath);
+
+                        // Session factory has been successfully initialized.
+                        return;
+                    }
+
+                    // Provided path is not a file. Classpath resource?
+                    sesFactory = new Configuration().configure(hibernateCfgPath).buildSessionFactory();
+
+                    if (log.isDebugEnabled())
+                        log.debug("Configured session factory using classpath resource: " + hibernateCfgPath);
+                }
+                else {
+                    if (hibernateProps == null) {
+                        U.warn(log, "No Hibernate configuration has been provided for store (will use default).");
+
+                        hibernateProps = new Properties();
+
+                        hibernateProps.setProperty("hibernate.connection.url", DFLT_CONN_URL);
+                        hibernateProps.setProperty("hibernate.show_sql", DFLT_SHOW_SQL);
+                        hibernateProps.setProperty("hibernate.hbm2ddl.auto", DFLT_HBM2DDL_AUTO);
+                        hibernateProps.setProperty("hibernate.connection.pool_size", DFLT_CONN_POOL_SIZE);
+                    }
+
+                    Configuration cfg = new Configuration();
+
+                    cfg.setProperties(hibernateProps);
+
+                    assert resourceAvailable(MAPPING_RESOURCE) : MAPPING_RESOURCE;
+
+                    cfg.addResource(MAPPING_RESOURCE);
+
+                    sesFactory = cfg.buildSessionFactory();
+
+                    if (log.isDebugEnabled())
+                        log.debug("Configured session factory using properties: " + hibernateProps);
+                }
+            }
+            catch (HibernateException e) {
+                throw new IgniteException("Failed to initialize store.", e);
+            }
+            finally {
+                initLatch.countDown();
+            }
+        }
+        else if (initLatch.getCount() > 0) {
+            try {
+                U.await(initLatch);
+            }
+            catch (IgniteInterruptedCheckedException e) {
+                throw new IgniteException(e);
+            }
+        }
+
+        if (sesFactory == null)
+            throw new IgniteException("Cache store was not properly initialized.");
+    }
+
+    /**
+     * Checks availability of a classpath resource.
+     *
+     * @param name Resource name.
+     * @return {@code true} if resource is available and ready for read, {@code false} otherwise.
+     */
+    private boolean resourceAvailable(String name) {
+        InputStream cfgStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(name);
+
+        if (cfgStream == null) {
+            log.error("Classpath resource not found: " + name);
+
+            return false;
+        }
+
+        try {
+            // Read a single byte to force actual content access by JVM.
+            cfgStream.read();
+
+            return true;
+        }
+        catch (IOException e) {
+            log.error("Failed to read classpath resource: " + name, e);
+
+            return false;
+        }
+        finally {
+            U.close(cfgStream, log);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(CacheHibernateBlobStore.class, this);
+    }
+
+    /**
+     * Serialize object to byte array using marshaller.
+     *
+     * @param obj Object to convert to byte array.
+     * @return Byte array.
+     * @throws IgniteCheckedException If failed to convert.
+     */
+    protected byte[] toBytes(Object obj) throws IgniteCheckedException {
+        return U.marshal(marsh, obj);
+    }
+
+    /**
+     * Deserialize object from byte array using marshaller.
+     *
+     * @param bytes Bytes to deserialize.
+     * @param <X> Result object type.
+     * @return Deserialized object.
+     * @throws IgniteCheckedException If failed.
+     */
+    protected <X> X fromBytes(byte[] bytes) throws IgniteCheckedException {
+        if (bytes == null || bytes.length == 0)
+            return null;
+
+        return U.unmarshal(marsh, bytes, getClass().getClassLoader());
+    }
+
+    /**
+     * @return Current transaction.
+     */
+    @Nullable private Transaction transaction() {
+        CacheStoreSession ses = session();
+
+        return ses != null ? ses.transaction() : null;
+    }
+
+    /**
+     * @return Store session.
+     */
+    private CacheStoreSession session() {
+        return ses;
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreEntry.hbm.xml b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreEntry.hbm.xml
new file mode 100644
index 0000000..5b0be43
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreEntry.hbm.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+
+<!DOCTYPE hibernate-mapping PUBLIC
+        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.apache.ignite.examples.datagrid.store" default-access="field">
+    <class name="org.apache.ignite.cache.store.hibernate.CacheHibernateBlobStoreEntry" table="ENTRIES">
+        <id name="key"/>
+
+        <property name="val"/>
+    </class>
+</hibernate-mapping>
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreEntry.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreEntry.java
new file mode 100644
index 0000000..6cc4786
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreEntry.java
@@ -0,0 +1,89 @@
+/*
+ * 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.ignite.cache.store.hibernate;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+/**
+ * Entry that is used by {@link CacheHibernateBlobStore} implementation.
+ * <p>
+ * Note that this is a reference implementation for tests only.
+ * When running on production systems use concrete key-value types to
+ * get better performance.
+ */
+@Entity
+@Table(name = "ENTRIES")
+public class CacheHibernateBlobStoreEntry {
+    /** Key (use concrete key type in production). */
+    @Id
+    @Column(length = 65535)
+    private byte[] key;
+
+    /** Value (use concrete value type in production). */
+    @Column(length = 65535)
+    private byte[] val;
+
+    /**
+     * Constructor.
+     */
+    CacheHibernateBlobStoreEntry() {
+        // No-op.
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param key Key.
+     * @param val Value.
+     */
+    CacheHibernateBlobStoreEntry(byte[] key, byte[] val) {
+        this.key = key;
+        this.val = val;
+    }
+
+    /**
+     * @return Key.
+     */
+    public byte[] getKey() {
+        return key;
+    }
+
+    /**
+     * @param key Key.
+     */
+    public void setKey(byte[] key) {
+        this.key = key;
+    }
+
+    /**
+     * @return Value.
+     */
+    public byte[] getValue() {
+        return val;
+    }
+
+    /**
+     * @param val Value.
+     */
+    public void setValue(byte[] val) {
+        this.val = val;
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreFactory.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreFactory.java
new file mode 100644
index 0000000..7e58f29
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreFactory.java
@@ -0,0 +1,235 @@
+/*
+ * 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.ignite.cache.store.hibernate;
+
+import java.util.Properties;
+import javax.cache.configuration.Factory;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.IgniteComponentType;
+import org.apache.ignite.internal.util.spring.IgniteSpringHelper;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.resources.SpringApplicationContextResource;
+import org.hibernate.SessionFactory;
+
+/**
+ * {@link Factory} implementation for {@link CacheHibernateBlobStore}.
+ *
+ * Use this factory to pass {@link CacheHibernateBlobStore} to {@link CacheConfiguration}.
+ *
+ * <h2 class="header">Java Example</h2>
+ * In this example existing session factory is provided.
+ * <pre name="code" class="java">
+ *     ...
+ *     CacheHibernateBlobStoreFactory&lt;String, String&gt; factory = new CacheHibernateBlobStoreFactory&lt;String, String&gt;();
+ *
+ *     factory.setSessionFactory(sesFactory);
+ *     ...
+ * </pre>
+ *
+ * <h2 class="header">Spring Example (using Spring ORM)</h2>
+ * <pre name="code" class="xml">
+ *   ...
+ *   &lt;bean id=&quot;cache.hibernate.store.factory&quot;
+ *       class=&quot;org.apache.ignite.cache.store.hibernate.CacheHibernateBlobStoreFactory&quot;&gt;
+ *       &lt;property name=&quot;sessionFactory&quot;&gt;
+ *           &lt;bean class=&quot;org.springframework.orm.hibernate3.LocalSessionFactoryBean&quot;&gt;
+ *               &lt;property name=&quot;hibernateProperties&quot;&gt;
+ *                   &lt;value&gt;
+ *                       connection.url=jdbc:h2:mem:
+ *                       show_sql=true
+ *                       hbm2ddl.auto=true
+ *                       hibernate.dialect=org.hibernate.dialect.H2Dialect
+ *                   &lt;/value&gt;
+ *               &lt;/property&gt;
+ *               &lt;property name=&quot;mappingResources&quot;&gt;
+ *                   &lt;list&gt;
+ *                       &lt;value&gt;
+ *                           org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreEntry.hbm.xml
+ *                       &lt;/value&gt;
+ *                   &lt;/list&gt;
+ *               &lt;/property&gt;
+ *           &lt;/bean&gt;
+ *       &lt;/property&gt;
+ *   &lt;/bean&gt;
+ *   ...
+ * </pre>
+ *
+ * <h2 class="header">Spring Example (using Spring ORM and persistent annotations)</h2>
+ * <pre name="code" class="xml">
+ *     ...
+ *     &lt;bean id=&quot;cache.hibernate.store.factory1&quot;
+ *         class=&quot;org.apache.ignite.cache.store.hibernate.CacheHibernateBlobStoreFactory&quot;&gt;
+ *         &lt;property name=&quot;sessionFactory&quot;&gt;
+ *             &lt;bean class=&quot;org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean&quot;&gt;
+ *                 &lt;property name=&quot;hibernateProperties&quot;&gt;
+ *                     &lt;value&gt;
+ *                         connection.url=jdbc:h2:mem:
+ *                         show_sql=true
+ *                         hbm2ddl.auto=true
+ *                         hibernate.dialect=org.hibernate.dialect.H2Dialect
+ *                     &lt;/value&gt;
+ *                 &lt;/property&gt;
+ *                 &lt;property name=&quot;annotatedClasses&quot;&gt;
+ *                     &lt;list&gt;
+ *                         &lt;value&gt;
+ *                             org.apache.ignite.cache.store.hibernate.CacheHibernateBlobStoreEntry
+ *                         &lt;/value&gt;
+ *                     &lt;/list&gt;
+ *                 &lt;/property&gt;
+ *             &lt;/bean&gt;
+ *         &lt;/property&gt;
+ *     &lt;/bean&gt;
+ *     ...
+ * </pre>
+ *
+ * <h2 class="header">Spring Example</h2>
+ * <pre name="code" class="xml">
+ *     ...
+ *     &lt;bean id=&quot;cache.hibernate.store.factory2&quot;
+ *         class=&quot;org.apache.ignite.cache.store.hibernate.CacheHibernateBlobStoreFactory&quot;&gt;
+ *         &lt;property name=&quot;hibernateProperties&quot;&gt;
+ *             &lt;props&gt;
+ *                 &lt;prop key=&quot;connection.url&quot;&gt;jdbc:h2:mem:&lt;/prop&gt;
+ *                 &lt;prop key=&quot;hbm2ddl.auto&quot;&gt;update&lt;/prop&gt;
+ *                 &lt;prop key=&quot;show_sql&quot;&gt;true&lt;/prop&gt;
+ *             &lt;/props&gt;
+ *         &lt;/property&gt;
+ *     &lt;/bean&gt;
+ *     ...
+ * </pre>
+ * <p>
+ * <img src="http://ignite.apache.org/images/spring-small.png">
+ * <br>
+ * For information about Spring framework visit <a href="http://www.springframework.org/">www.springframework.org</a>
+ */
+public class CacheHibernateBlobStoreFactory<K, V> implements Factory<CacheHibernateBlobStore<K, V>> {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Session factory. */
+    @GridToStringExclude
+    private transient SessionFactory sesFactory;
+
+    /** Session factory bean name. */
+    private String sesFactoryBean;
+
+    /** Path to hibernate configuration file. */
+    private String hibernateCfgPath;
+
+    /** Hibernate properties. */
+    @GridToStringExclude
+    private Properties hibernateProps;
+
+    /** Application context. */
+    @SpringApplicationContextResource
+    private Object appCtx;
+
+    /** {@inheritDoc} */
+    @Override public CacheHibernateBlobStore<K, V> create() {
+        CacheHibernateBlobStore<K, V> store = new CacheHibernateBlobStore<>();
+
+        store.setHibernateConfigurationPath(hibernateCfgPath);
+        store.setHibernateProperties(hibernateProps);
+
+        if (sesFactory != null)
+            store.setSessionFactory(sesFactory);
+        else if (sesFactoryBean != null) {
+            if (appCtx == null)
+                throw new IgniteException("Spring application context resource is not injected.");
+
+            IgniteSpringHelper spring;
+
+            try {
+                spring = IgniteComponentType.SPRING.create(false);
+
+                SessionFactory sesFac = spring.loadBeanFromAppContext(appCtx, sesFactoryBean);
+
+                store.setSessionFactory(sesFac);
+            }
+            catch (IgniteCheckedException e) {
+                throw new IgniteException("Failed to load bean in application context [beanName=" + sesFactoryBean +
+                        ", igniteConfig=" + appCtx + ']');
+            }
+        }
+
+        return store;
+    }
+
+    /**
+     * Sets session factory.
+     *
+     * @param sesFactory Session factory.
+     * @return {@code This} for chaining.
+     * @see CacheHibernateBlobStore#setSessionFactory(SessionFactory)
+     */
+    public CacheHibernateBlobStoreFactory<K, V> setSessionFactory(SessionFactory sesFactory) {
+        this.sesFactory = sesFactory;
+
+        return this;
+    }
+
+    /**
+     * Sets name of the data source bean.
+     *
+     * @param sesFactory Session factory bean name.
+     * @return {@code This} for chaining.
+     * @see CacheHibernateBlobStore#setSessionFactory(SessionFactory)
+     */
+    public CacheHibernateBlobStoreFactory<K, V> setSessionFactoryBean(String sesFactory) {
+        this.sesFactoryBean = sesFactory;
+
+        return this;
+    }
+
+    /**
+     * Sets hibernate configuration path.
+     * <p>
+     * This may be either URL or file path or classpath resource.
+     *
+     * @param hibernateCfgPath URL or file path or classpath resource
+     *      pointing to hibernate configuration XML file.
+     * @return {@code This} for chaining.
+     * @see CacheHibernateBlobStore#setHibernateConfigurationPath(String)
+     */
+    public CacheHibernateBlobStoreFactory<K, V> setHibernateConfigurationPath(String hibernateCfgPath) {
+        this.hibernateCfgPath = hibernateCfgPath;
+
+        return this;
+    }
+
+    /**
+     * Sets Hibernate properties.
+     *
+     * @param hibernateProps Hibernate properties.
+     * @return {@code This} for chaining.
+     * @see CacheHibernateBlobStore#setHibernateProperties(Properties)
+     */
+    public CacheHibernateBlobStoreFactory<K, V> setHibernateProperties(Properties hibernateProps) {
+        this.hibernateProps = hibernateProps;
+
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(CacheHibernateBlobStoreFactory.class, this);
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/CacheHibernateStoreSessionListener.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/CacheHibernateStoreSessionListener.java
new file mode 100644
index 0000000..a63a437
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/CacheHibernateStoreSessionListener.java
@@ -0,0 +1,226 @@
+/*
+ * 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.ignite.cache.store.hibernate;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.cache.integration.CacheWriterException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cache.store.CacheStore;
+import org.apache.ignite.cache.store.CacheStoreSession;
+import org.apache.ignite.cache.store.CacheStoreSessionListener;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lifecycle.LifecycleAware;
+import org.apache.ignite.resources.LoggerResource;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.resource.transaction.spi.TransactionStatus;
+
+/**
+ * Hibernate-based cache store session listener.
+ * <p>
+ * This listener creates a new Hibernate session for each store
+ * session. If there is an ongoing cache transaction, a corresponding
+ * Hibernate transaction is created as well.
+ * <p>
+ * The Hibernate session is saved as a store session
+ * {@link CacheStoreSession#attachment() attachment}.
+ * The listener guarantees that the session will be
+ * available for any store operation. If there is an
+ * ongoing cache transaction, all operations within this
+ * transaction will share a DB transaction.
+ * <p>
+ * As an example, here is how the {@link CacheStore#write(javax.cache.Cache.Entry)}
+ * method can be implemented if {@link CacheHibernateStoreSessionListener}
+ * is configured:
+ * <pre name="code" class="java">
+ * private static class Store extends CacheStoreAdapter&lt;Integer, Integer&gt; {
+ *     &#64;CacheStoreSessionResource
+ *     private CacheStoreSession ses;
+ *
+ *     &#64;Override public void write(Cache.Entry&lt;? extends Integer, ? extends Integer&gt; entry) throws CacheWriterException {
+ *         // Get Hibernate session from the current store session.
+ *         Session hibSes = ses.attachment();
+ *
+ *         // Persist the value.
+ *         hibSes.persist(entry.getValue());
+ *     }
+ * }
+ * </pre>
+ * Hibernate session will be automatically created by the listener
+ * at the start of the session and closed when it ends.
+ * <p>
+ * {@link CacheHibernateStoreSessionListener} requires that either
+ * {@link #setSessionFactory(SessionFactory)} session factory}
+ * or {@link #setHibernateConfigurationPath(String) Hibernate configuration file}
+ * is provided. If non of them is set, exception is thrown. Is both are provided,
+ * session factory will be used.
+ */
+public class CacheHibernateStoreSessionListener implements CacheStoreSessionListener, LifecycleAware {
+    /** Hibernate session factory. */
+    private SessionFactory sesFactory;
+
+    /** Hibernate configuration file path. */
+    private String hibernateCfgPath;
+
+    /** Logger. */
+    @LoggerResource
+    private IgniteLogger log;
+
+    /** Whether to close session on stop. */
+    private boolean closeSesOnStop;
+
+    /**
+     * Sets Hibernate session factory.
+     * <p>
+     * Either session factory or configuration file is required.
+     * If none is provided, exception will be thrown on startup.
+     *
+     * @param sesFactory Session factory.
+     */
+    public void setSessionFactory(SessionFactory sesFactory) {
+        this.sesFactory = sesFactory;
+    }
+
+    /**
+     * Gets Hibernate session factory.
+     *
+     * @return Session factory.
+     */
+    public SessionFactory getSessionFactory() {
+        return sesFactory;
+    }
+
+    /**
+     * Sets hibernate configuration path.
+     * <p>
+     * Either session factory or configuration file is required.
+     * If none is provided, exception will be thrown on startup.
+     *
+     * @param hibernateCfgPath Hibernate configuration path.
+     */
+    public void setHibernateConfigurationPath(String hibernateCfgPath) {
+        this.hibernateCfgPath = hibernateCfgPath;
+    }
+
+    /**
+     * Gets hibernate configuration path.
+     *
+     * @return Hibernate configuration path.
+     */
+    public String getHibernateConfigurationPath() {
+        return hibernateCfgPath;
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("deprecation")
+    @Override public void start() throws IgniteException {
+        if (sesFactory == null && F.isEmpty(hibernateCfgPath)) {
+            throw new IgniteException("Either session factory or Hibernate configuration file is required by " +
+                getClass().getSimpleName() + '.');
+        }
+
+        if (!F.isEmpty(hibernateCfgPath)) {
+            if (sesFactory == null) {
+                try {
+                    URL url = new URL(hibernateCfgPath);
+
+                    sesFactory = new Configuration().configure(url).buildSessionFactory();
+                }
+                catch (MalformedURLException ex) {
+                    log.warning("Exception on store listener start", ex);
+                }
+
+                if (sesFactory == null) {
+                    File cfgFile = new File(hibernateCfgPath);
+
+                    if (cfgFile.exists())
+                        sesFactory = new Configuration().configure(cfgFile).buildSessionFactory();
+                }
+
+                if (sesFactory == null)
+                    sesFactory = new Configuration().configure(hibernateCfgPath).buildSessionFactory();
+
+                if (sesFactory == null)
+                    throw new IgniteException("Failed to resolve Hibernate configuration file: " + hibernateCfgPath);
+
+                closeSesOnStop = true;
+            }
+            else {
+                U.warn(log, "Hibernate configuration file configured in " + getClass().getSimpleName() +
+                    " will be ignored (session factory is already set).");
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void stop() throws IgniteException {
+        if (closeSesOnStop && sesFactory != null && !sesFactory.isClosed())
+            sesFactory.close();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onSessionStart(CacheStoreSession ses) {
+        if (ses.attachment() == null) {
+            try {
+                Session hibSes = sesFactory.openSession();
+
+                ses.attach(hibSes);
+
+                if (ses.isWithinTransaction())
+                    hibSes.beginTransaction();
+            }
+            catch (HibernateException e) {
+                throw new CacheWriterException("Failed to start store session [tx=" + ses.transaction() + ']', e);
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onSessionEnd(CacheStoreSession ses, boolean commit) {
+        Session hibSes = ses.attach(null);
+
+        if (hibSes != null) {
+            try {
+                Transaction tx = hibSes.getTransaction();
+
+                if (commit) {
+                    if (hibSes.isDirty())
+                        hibSes.flush();
+
+                    if (tx.getStatus() == TransactionStatus.ACTIVE)
+                        tx.commit();
+                }
+                else if (tx.getStatus().canRollback())
+                    tx.rollback();
+            }
+            catch (HibernateException e) {
+                throw new CacheWriterException("Failed to end store session [tx=" + ses.transaction() + ']', e);
+            }
+            finally {
+                hibSes.close();
+            }
+        }
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/package-info.java b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/package-info.java
new file mode 100644
index 0000000..49af23a
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/main/java/org/apache/ignite/cache/store/hibernate/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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 description. -->
+ * Contains reference Hibernate-based cache store implementation.
+ */
+
+package org.apache.ignite.cache.store.hibernate;
diff --git a/modules/hibernate-ext/hibernate/src/test/config/factory-cache.xml b/modules/hibernate-ext/hibernate/src/test/config/factory-cache.xml
new file mode 100644
index 0000000..a251846
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/test/config/factory-cache.xml
@@ -0,0 +1,59 @@
+<?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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+    <bean id="simpleSessionFactory"
+          class="org.apache.ignite.cache.store.hibernate.CacheHibernateStoreFactorySelfTest$DummySessionFactoryExt"/>
+
+    <bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
+        <property name="cacheConfiguration">
+            <list>
+                <bean class="org.apache.ignite.configuration.CacheConfiguration">
+                    <property name="name" value="test"/>
+                    <property name="atomicityMode" value="ATOMIC"/>
+                    <property name="backups" value="1"/>
+                    <property name="cacheStoreFactory">
+                        <bean class="org.apache.ignite.cache.store.hibernate.CacheHibernateBlobStoreFactory">
+                            <property name="sessionFactoryBean" value = "simpleSessionFactory"/>
+                        </bean>
+                    </property>
+                </bean>
+            </list>
+        </property>
+
+        <!-- Explicitly configure TCP discovery SPI to provide list of initial nodes. -->
+        <property name="discoverySpi">
+            <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+                <property name="ipFinder">
+                    <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">
+                        <property name="addresses">
+                            <list>
+                                <value>127.0.0.1:47500..47509</value>
+                            </list>
+                        </property>
+                    </bean>
+                </property>
+            </bean>
+        </property>
+    </bean>
+</beans>
diff --git a/modules/hibernate-ext/hibernate/src/test/config/factory-cache1.xml b/modules/hibernate-ext/hibernate/src/test/config/factory-cache1.xml
new file mode 100644
index 0000000..7a53b1f
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/test/config/factory-cache1.xml
@@ -0,0 +1,61 @@
+<?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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+    <bean id="simpleSessionFactory1"
+          class="org.apache.ignite.cache.store.hibernate.CacheHibernateStoreFactorySelfTest$DummySessionFactory"/>
+
+    <bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
+        <property name="igniteInstanceName" value="ignite1"/>
+
+        <property name="cacheConfiguration">
+            <list>
+                <bean class="org.apache.ignite.configuration.CacheConfiguration">
+                    <property name="name" value="test"/>
+                    <property name="atomicityMode" value="ATOMIC"/>
+                    <property name="backups" value="1"/>
+                    <property name="cacheStoreFactory">
+                        <bean class="org.apache.ignite.cache.store.hibernate.CacheHibernateBlobStoreFactory">
+                            <property name="sessionFactoryBean" value = "simpleSessionFactory1"/>
+                        </bean>
+                    </property>
+                </bean>
+            </list>
+        </property>
+
+        <!-- Explicitly configure TCP discovery SPI to provide list of initial nodes. -->
+        <property name="discoverySpi">
+            <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+                <property name="ipFinder">
+                    <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">
+                        <property name="addresses">
+                            <list>
+                                <value>127.0.0.1:47500..47509</value>
+                            </list>
+                        </property>
+                    </bean>
+                </property>
+            </bean>
+        </property>
+    </bean>
+</beans>
diff --git a/modules/hibernate-ext/hibernate/src/test/config/factory-incorrect-store-cache.xml b/modules/hibernate-ext/hibernate/src/test/config/factory-incorrect-store-cache.xml
new file mode 100644
index 0000000..4de9fc9
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/test/config/factory-incorrect-store-cache.xml
@@ -0,0 +1,60 @@
+<?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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+    <bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
+        <property name="failureHandler">
+            <bean class="org.apache.ignite.failure.NoOpFailureHandler"/>
+        </property>
+
+        <property name="cacheConfiguration">
+            <list>
+                <bean class="org.apache.ignite.configuration.CacheConfiguration">
+                    <property name="name" value="test"/>
+                    <property name="atomicityMode" value="ATOMIC"/>
+                    <property name="backups" value="1"/>
+                    <property name="cacheStoreFactory">
+                        <bean class="org.apache.ignite.cache.store.hibernate.CacheHibernateBlobStoreFactory">
+                            <property name="sessionFactoryBean" value = "simpleSessionFactory1"/>
+                        </bean>
+                    </property>
+                </bean>
+            </list>
+        </property>
+
+        <!-- Explicitly configure TCP discovery SPI to provide list of initial nodes. -->
+        <property name="discoverySpi">
+            <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+                <property name="ipFinder">
+                    <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">
+                        <property name="addresses">
+                            <list>
+                                <value>127.0.0.1:47500..47509</value>
+                            </list>
+                        </property>
+                    </bean>
+                </property>
+            </bean>
+        </property>
+    </bean>
+</beans>
diff --git a/modules/hibernate-ext/hibernate/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheConfigurationSelfTest.java b/modules/hibernate-ext/hibernate/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheConfigurationSelfTest.java
new file mode 100644
index 0000000..60eb131
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheConfigurationSelfTest.java
@@ -0,0 +1,413 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import javax.cache.Cache;
+import javax.persistence.Cacheable;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Root;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteKernal;
+import org.apache.ignite.internal.processors.cache.IgniteCacheProxy;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
+import org.hibernate.cache.spi.access.AccessType;
+import org.hibernate.cfg.Configuration;
+import org.junit.Test;
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.hibernate.HibernateAccessStrategyFactory.REGION_CACHE_PROPERTY;
+import static org.hibernate.cache.spi.RegionFactory.DEFAULT_QUERY_RESULTS_REGION_UNQUALIFIED_NAME;
+import static org.hibernate.cache.spi.RegionFactory.DEFAULT_UPDATE_TIMESTAMPS_REGION_UNQUALIFIED_NAME;
+import static org.hibernate.cfg.AvailableSettings.CACHE_REGION_FACTORY;
+import static org.hibernate.cfg.AvailableSettings.GENERATE_STATISTICS;
+import static org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO;
+import static org.hibernate.cfg.AvailableSettings.RELEASE_CONNECTIONS;
+import static org.hibernate.cfg.AvailableSettings.USE_QUERY_CACHE;
+import static org.hibernate.cfg.AvailableSettings.USE_SECOND_LEVEL_CACHE;
+
+/**
+ * Tests Hibernate L2 cache configuration.
+ */
+public class HibernateL2CacheConfigurationSelfTest extends GridCommonAbstractTest {
+    /** */
+    public static final String ENTITY1_NAME = Entity1.class.getName();
+
+    /** */
+    public static final String ENTITY2_NAME = Entity2.class.getName();
+
+    /** */
+    public static final String ENTITY3_NAME = Entity3.class.getName();
+
+    /** */
+    public static final String ENTITY4_NAME = Entity4.class.getName();
+
+    /** */
+    public static final String CONNECTION_URL = "jdbc:h2:mem:example;DB_CLOSE_DELAY=-1";
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        startGrid(0);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        for (IgniteCacheProxy<?, ?> cache : ((IgniteKernal)grid(0)).caches())
+            cache.clear();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        TcpDiscoverySpi discoSpi = new TcpDiscoverySpi();
+
+        discoSpi.setIpFinder(new TcpDiscoveryVmIpFinder(true));
+
+        cfg.setDiscoverySpi(discoSpi);
+
+        cfg.setCacheConfiguration(cacheConfiguration(ENTITY3_NAME),
+            cacheConfiguration(ENTITY4_NAME),
+            cacheConfiguration("cache1"),
+            cacheConfiguration("cache2"),
+            cacheConfiguration("cache3"),
+            cacheConfiguration(DEFAULT_UPDATE_TIMESTAMPS_REGION_UNQUALIFIED_NAME),
+            cacheConfiguration(DEFAULT_QUERY_RESULTS_REGION_UNQUALIFIED_NAME));
+
+        return cfg;
+    }
+
+    /**
+     * @param cacheName Cache name.
+     * @return Cache configuration.
+     */
+    private CacheConfiguration cacheConfiguration(String cacheName) {
+        CacheConfiguration cfg = new CacheConfiguration();
+
+        cfg.setName(cacheName);
+
+        cfg.setCacheMode(PARTITIONED);
+
+        cfg.setAtomicityMode(ATOMIC);
+
+        cfg.setStatisticsEnabled(true);
+
+        return cfg;
+    }
+
+    /**
+     * @param igniteInstanceName Ignite instance name.
+     * @return Hibernate configuration.
+     */
+    protected Configuration hibernateConfiguration(String igniteInstanceName) {
+        Configuration cfg = new Configuration();
+
+        cfg.addAnnotatedClass(Entity1.class);
+        cfg.addAnnotatedClass(Entity2.class);
+        cfg.addAnnotatedClass(Entity3.class);
+        cfg.addAnnotatedClass(Entity4.class);
+
+        cfg.setProperty(HibernateAccessStrategyFactory.DFLT_ACCESS_TYPE_PROPERTY, AccessType.NONSTRICT_READ_WRITE.name());
+
+        cfg.setProperty(HBM2DDL_AUTO, "create");
+
+        cfg.setProperty(GENERATE_STATISTICS, "true");
+
+        cfg.setProperty(USE_SECOND_LEVEL_CACHE, "true");
+
+        cfg.setProperty(USE_QUERY_CACHE, "true");
+
+        cfg.setProperty(CACHE_REGION_FACTORY, HibernateRegionFactory.class.getName());
+
+        cfg.setProperty(RELEASE_CONNECTIONS, "on_close");
+
+        cfg.setProperty(HibernateAccessStrategyFactory.IGNITE_INSTANCE_NAME_PROPERTY, igniteInstanceName);
+
+        cfg.setProperty(REGION_CACHE_PROPERTY + ENTITY1_NAME, "cache1");
+        cfg.setProperty(REGION_CACHE_PROPERTY + ENTITY2_NAME, "cache2");
+        cfg.setProperty(
+            REGION_CACHE_PROPERTY + DEFAULT_UPDATE_TIMESTAMPS_REGION_UNQUALIFIED_NAME,
+            DEFAULT_UPDATE_TIMESTAMPS_REGION_UNQUALIFIED_NAME
+        );
+        cfg.setProperty(
+            REGION_CACHE_PROPERTY + DEFAULT_QUERY_RESULTS_REGION_UNQUALIFIED_NAME,
+            DEFAULT_QUERY_RESULTS_REGION_UNQUALIFIED_NAME
+        );
+
+        return cfg;
+    }
+
+    /**
+     * Tests property {@link HibernateAccessStrategyFactory#REGION_CACHE_PROPERTY}.
+     */
+    @Test
+    public void testPerRegionCacheProperty() {
+        testCacheUsage(1, 1, 0, 1, 1);
+    }
+
+    /**
+     * @param expCache1 Expected size of cache with name 'cache1'.
+     * @param expCache2 Expected size of cache with name 'cache2'.
+     * @param expCache3 Expected size of cache with name 'cache3'.
+     * @param expCacheE3 Expected size of cache with name {@link #ENTITY3_NAME}.
+     * @param expCacheE4 Expected size of cache with name {@link #ENTITY4_NAME}.
+     */
+    @SuppressWarnings("unchecked")
+    private void testCacheUsage(int expCache1, int expCache2, int expCache3, int expCacheE3, int expCacheE4) {
+        SessionFactory sesFactory = startHibernate(getTestIgniteInstanceName(0));
+
+        try {
+            Session ses = sesFactory.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                ses.save(new Entity1());
+                ses.save(new Entity2());
+                ses.save(new Entity3());
+                ses.save(new Entity4());
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            ses = sesFactory.openSession();
+
+            try {
+                List<Entity1> list1 = getResultsList(ses, Entity1.class);
+
+                assertEquals(1, list1.size());
+
+                for (Entity1 e : list1) {
+                    ses.load(ENTITY1_NAME, e.getId());
+                    assertNotNull(e.getId());
+                }
+
+                List<Entity2> list2 = getResultsList(ses, Entity2.class);
+
+                assertEquals(1, list2.size());
+
+                for (Entity2 e : list2)
+                    assertNotNull(e.getId());
+
+                List<Entity3> list3 = getResultsList(ses, Entity3.class);
+
+                assertEquals(1, list3.size());
+
+                for (Entity3 e : list3)
+                    assertNotNull(e.getId());
+
+                List<Entity4> list4 = getResultsList(ses, Entity4.class);
+
+                assertEquals(1, list4.size());
+
+                for (Entity4 e : list4)
+                    assertNotNull(e.getId());
+            }
+            finally {
+                ses.close();
+            }
+
+            IgniteCache<Object, Object> cache1 = grid(0).cache("cache1");
+            IgniteCache<Object, Object> cache2 = grid(0).cache("cache2");
+            IgniteCache<Object, Object> cache3 = grid(0).cache("cache3");
+            IgniteCache<Object, Object> cacheE3 = grid(0).cache(ENTITY3_NAME);
+            IgniteCache<Object, Object> cacheE4 = grid(0).cache(ENTITY4_NAME);
+
+            assertEquals("Unexpected entries: " + toSet(cache1.iterator()), expCache1, cache1.size());
+            assertEquals("Unexpected entries: " + toSet(cache2.iterator()), expCache2, cache2.size());
+            assertEquals("Unexpected entries: " + toSet(cache3.iterator()), expCache3, cache3.size());
+            assertEquals("Unexpected entries: " + toSet(cacheE3.iterator()), expCacheE3, cacheE3.size());
+            assertEquals("Unexpected entries: " + toSet(cacheE4.iterator()), expCacheE4, cacheE4.size());
+        }
+        finally {
+            sesFactory.close();
+        }
+    }
+
+    /** */
+    private <T> List<T> getResultsList(Session ses, Class<T> entityClass) {
+        CriteriaBuilder builder = ses.getCriteriaBuilder();
+        CriteriaQuery<T> query = builder.createQuery(entityClass);
+        Root<T> root = query.from(entityClass);
+        query.select(root);
+        return ses.createQuery(query).getResultList();
+    }
+
+    /**
+     *
+     */
+    private <K, V> Set<Cache.Entry<K, V>> toSet(Iterator<Cache.Entry<K, V>> iter) {
+        Set<Cache.Entry<K, V>> set = new HashSet<>();
+
+        while (iter.hasNext())
+            set.add(iter.next());
+
+        return set;
+    }
+
+    /**
+     * @param igniteInstanceName Name of the grid providing caches.
+     * @return Session factory.
+     */
+    private SessionFactory startHibernate(String igniteInstanceName) {
+        Configuration cfg = hibernateConfiguration(igniteInstanceName);
+
+        StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder();
+
+        builder.applySetting("hibernate.connection.url", CONNECTION_URL);
+        builder.applySetting("hibernate.show_sql", false);
+        builder.applySettings(cfg.getProperties());
+
+        return cfg.buildSessionFactory(builder.build());
+    }
+
+    /**
+     * Test Hibernate entity1.
+     */
+    @javax.persistence.Entity
+    @SuppressWarnings({"PublicInnerClass", "UnnecessaryFullyQualifiedName"})
+    @Cacheable
+    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+    public static class Entity1 {
+        /** */
+        private int id;
+
+        /**
+         * @return ID.
+         */
+        @Id
+        @GeneratedValue
+        public int getId() {
+            return id;
+        }
+
+        /**
+         * @param id ID.
+         */
+        public void setId(int id) {
+            this.id = id;
+        }
+    }
+
+    /**
+     * Test Hibernate entity2.
+     */
+    @javax.persistence.Entity
+    @SuppressWarnings({"PublicInnerClass", "UnnecessaryFullyQualifiedName"})
+    @Cacheable
+    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+    public static class Entity2 {
+        /** */
+        private int id;
+
+        /**
+         * @return ID.
+         */
+        @Id
+        @GeneratedValue
+        public int getId() {
+            return id;
+        }
+
+        /**
+         * @param id ID.
+         */
+        public void setId(int id) {
+            this.id = id;
+        }
+    }
+
+    /**
+     * Test Hibernate entity3.
+     */
+    @javax.persistence.Entity
+    @SuppressWarnings({"PublicInnerClass", "UnnecessaryFullyQualifiedName"})
+    @Cacheable
+    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+    public static class Entity3 {
+        /** */
+        private int id;
+
+        /**
+         * @return ID.
+         */
+        @Id
+        @GeneratedValue
+        public int getId() {
+            return id;
+        }
+
+        /**
+         * @param id ID.
+         */
+        public void setId(int id) {
+            this.id = id;
+        }
+    }
+
+    /**
+     * Test Hibernate entity4.
+     */
+    @javax.persistence.Entity
+    @SuppressWarnings({"PublicInnerClass", "UnnecessaryFullyQualifiedName"})
+    @Cacheable
+    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+    public static class Entity4 {
+        /** */
+        private int id;
+
+        /**
+         * @return ID.
+         */
+        @Id
+        @GeneratedValue
+        public int getId() {
+            return id;
+        }
+
+        /**
+         * @param id ID.
+         */
+        public void setId(int id) {
+            this.id = id;
+        }
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheMultiJvmTest.java b/modules/hibernate-ext/hibernate/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheMultiJvmTest.java
new file mode 100644
index 0000000..3fb51df
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheMultiJvmTest.java
@@ -0,0 +1,435 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import java.util.Map;
+import javax.persistence.Cacheable;
+import javax.persistence.Id;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCompute;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.binary.BinaryMarshaller;
+import org.apache.ignite.lang.IgniteRunnable;
+import org.apache.ignite.resources.IgniteInstanceResource;
+import org.apache.ignite.resources.LoggerResource;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+import org.hibernate.boot.MetadataSources;
+import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
+import org.junit.Test;
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+import static org.apache.ignite.cache.hibernate.HibernateL2CacheSelfTest.CONNECTION_URL;
+import static org.apache.ignite.cache.hibernate.HibernateL2CacheSelfTest.hibernateProperties;
+import static org.hibernate.cache.spi.RegionFactory.DEFAULT_UPDATE_TIMESTAMPS_REGION_UNQUALIFIED_NAME;
+import static org.hibernate.cache.spi.access.AccessType.NONSTRICT_READ_WRITE;
+
+/**
+ *
+ */
+public class HibernateL2CacheMultiJvmTest extends GridCommonAbstractTest {
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        cfg.setCacheConfiguration(
+            cacheConfiguration(DEFAULT_UPDATE_TIMESTAMPS_REGION_UNQUALIFIED_NAME),
+            cacheConfiguration(Entity1.class.getName()),
+            cacheConfiguration(Entity2.class.getName()),
+            cacheConfiguration(Entity3.class.getName())
+        );
+
+        cfg.setMarshaller(new BinaryMarshaller());
+
+        cfg.setPeerClassLoadingEnabled(false);
+
+        return cfg;
+    }
+
+    /** */
+    private CacheConfiguration cacheConfiguration(String cacheName) {
+        CacheConfiguration cfg = new CacheConfiguration();
+        cfg.setName(cacheName);
+        cfg.setCacheMode(PARTITIONED);
+        cfg.setAtomicityMode(ATOMIC);
+        cfg.setWriteSynchronizationMode(FULL_SYNC);
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected boolean isMultiJvm() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        startGrid(0);
+
+        startClientGrid(1);
+        startClientGrid(2);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+
+        super.afterTestsStopped();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testL2Cache() throws Exception {
+        Ignite srv = ignite(0);
+
+        {
+            IgniteCompute client1Compute =
+                srv.compute(srv.cluster().forNodeId(ignite(1).cluster().localNode().id()));
+
+            client1Compute.run(new HibernateInsertRunnable());
+        }
+
+        {
+            IgniteCompute client2Compute =
+                srv.compute(srv.cluster().forNodeId(ignite(2).cluster().localNode().id()));
+
+            client2Compute.run(new HibernateLoadRunnable());
+        }
+
+        {
+            IgniteCompute srvCompute = srv.compute(srv.cluster().forLocal());
+
+            srvCompute.run(new HibernateLoadRunnable());
+        }
+    }
+
+    /**
+     *
+     */
+    private static class HibernateInsertRunnable extends HibernateBaseRunnable {
+        /** {@inheritDoc} */
+        @Override public void run() {
+            SessionFactory sesFactory = startHibernate(ignite.name());
+
+            Session ses = sesFactory.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                for (int i = 0; i < 1; i++) {
+                    {
+                        Entity1 e = new Entity1();
+                        e.setId(i);
+                        e.setName("name-" + i);
+
+                        ses.save(e);
+                    }
+
+                    {
+                        Entity2 e = new Entity2();
+                        e.setId(String.valueOf(i));
+                        e.setName("name-" + i);
+
+                        ses.save(e);
+                    }
+
+                    {
+                        Entity3 e = new Entity3();
+                        e.setId(i);
+                        e.setName("name-" + i);
+
+                        ses.save(e);
+                    }
+                }
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+        }
+    }
+
+    /**
+     *
+     */
+    private static class HibernateLoadRunnable extends HibernateBaseRunnable {
+        /** {@inheritDoc} */
+        @Override public void run() {
+            SessionFactory sesFactory = startHibernate(ignite.name());
+
+            Session ses = sesFactory.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                for (int i = 0; i < 1; i++) {
+                    {
+                        Entity1 e = (Entity1)ses.load(Entity1.class, i);
+
+                        log.info("Found: " + e.getName());
+                    }
+                    {
+                        Entity2 e = (Entity2)ses.load(Entity2.class, String.valueOf(i));
+
+                        log.info("Found: " + e.getName());
+                    }
+                    {
+                        Entity3 e = (Entity3)ses.load(Entity3.class, (double)i);
+
+                        log.info("Found: " + e.getName());
+                    }
+                }
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+        }
+    }
+
+    /**
+     *
+     */
+    private abstract static class HibernateBaseRunnable implements IgniteRunnable {
+        /** */
+        @IgniteInstanceResource
+        protected Ignite ignite;
+
+        /** */
+        @LoggerResource
+        IgniteLogger log;
+
+        /**
+         * @param nodeName Name of the grid providing caches.
+         * @return Session factory.
+         */
+        SessionFactory startHibernate(String nodeName) {
+            log.info("Start hibernate on node: " + nodeName);
+
+            StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder();
+
+            for (Map.Entry<String, String> e : hibernateProperties(nodeName, NONSTRICT_READ_WRITE.name()).entrySet())
+                builder.applySetting(e.getKey(), e.getValue());
+
+            builder.applySetting("hibernate.connection.url", CONNECTION_URL);
+
+            MetadataSources metadataSources = new MetadataSources(builder.build());
+
+            metadataSources.addAnnotatedClass(Entity1.class);
+            metadataSources.addAnnotatedClass(Entity2.class);
+            metadataSources.addAnnotatedClass(Entity3.class);
+
+            return metadataSources.buildMetadata().buildSessionFactory();
+        }
+    }
+
+    /**
+     * Test Hibernate entity1.
+     */
+    @javax.persistence.Entity
+    @Cacheable
+    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+    public static class Entity1 {
+        /** */
+        @Id
+        private int id;
+
+        /** */
+        private String name;
+
+        /**
+         * @return ID.
+         */
+        public int getId() {
+            return id;
+        }
+
+        /**
+         * @param id ID.
+         */
+        public void setId(int id) {
+            this.id = id;
+        }
+
+        /**
+         * @return Name.
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * @param name Name.
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            Entity1 entity1 = (Entity1)o;
+
+            return id == entity1.id;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return id;
+        }
+    }
+
+    /**
+     * Test Hibernate entity1.
+     */
+    @javax.persistence.Entity
+    @Cacheable
+    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+    public static class Entity2 {
+        /** */
+        @Id
+        private String id;
+
+        /** */
+        private String name;
+
+        /**
+         * @return ID.
+         */
+        public String getId() {
+            return id;
+        }
+
+        /**
+         * @param id ID.
+         */
+        public void setId(String id) {
+            this.id = id;
+        }
+
+        /**
+         * @return Name.
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * @param name Name.
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            Entity2 entity2 = (Entity2)o;
+
+            return id.equals(entity2.id);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return id.hashCode();
+        }
+    }
+
+    /**
+     * Test Hibernate entity1.
+     */
+    @javax.persistence.Entity
+    @Cacheable
+    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+    public static class Entity3 {
+        /** */
+        @Id
+        private double id;
+
+        /** */
+        private String name;
+
+        /**
+         * @return ID.
+         */
+        public double getId() {
+            return id;
+        }
+
+        /**
+         * @param id ID.
+         */
+        public void setId(double id) {
+            this.id = id;
+        }
+
+        /**
+         * @return Name.
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * @param name Name.
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            Entity3 entity3 = (Entity3)o;
+
+            return Double.compare(entity3.id, id) == 0;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            long temp = Double.doubleToLongBits(id);
+            return (int)(temp ^ (temp >>> 32));
+        }
+    }
+}
diff --git a/modules/hibernate-ext/hibernate/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheSelfTest.java b/modules/hibernate-ext/hibernate/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheSelfTest.java
new file mode 100644
index 0000000..b2b0752
--- /dev/null
+++ b/modules/hibernate-ext/hibernate/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheSelfTest.java
@@ -0,0 +1,1987 @@
+/*
+ * 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.ignite.cache.hibernate;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.OptimisticLockException;
+import javax.persistence.PersistenceException;
+import javax.persistence.SharedCacheMode;
+import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteKernal;
+import org.apache.ignite.internal.processors.cache.IgniteCacheProxy;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.hibernate.ObjectNotFoundException;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.annotations.NaturalId;
+import org.hibernate.annotations.NaturalIdCache;
+import org.hibernate.boot.Metadata;
+import org.hibernate.boot.MetadataSources;
+import org.hibernate.boot.registry.StandardServiceRegistry;
+import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
+import org.hibernate.cache.spi.access.AccessType;
+import org.hibernate.exception.ConstraintViolationException;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.RootClass;
+import org.hibernate.query.Query;
+import org.hibernate.stat.CacheRegionStatistics;
+import org.junit.Test;
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+import static org.apache.ignite.cache.hibernate.HibernateAccessStrategyFactory.DFLT_ACCESS_TYPE_PROPERTY;
+import static org.apache.ignite.cache.hibernate.HibernateAccessStrategyFactory.IGNITE_INSTANCE_NAME_PROPERTY;
+import static org.apache.ignite.cache.hibernate.HibernateAccessStrategyFactory.REGION_CACHE_PROPERTY;
+import static org.hibernate.cache.spi.RegionFactory.DEFAULT_QUERY_RESULTS_REGION_UNQUALIFIED_NAME;
+import static org.hibernate.cache.spi.RegionFactory.DEFAULT_UPDATE_TIMESTAMPS_REGION_UNQUALIFIED_NAME;
+import static org.hibernate.cfg.AvailableSettings.JPA_SHARED_CACHE_MODE;
+import static org.hibernate.cfg.Environment.CACHE_REGION_FACTORY;
+import static org.hibernate.cfg.Environment.GENERATE_STATISTICS;
+import static org.hibernate.cfg.Environment.HBM2DDL_AUTO;
+import static org.hibernate.cfg.Environment.RELEASE_CONNECTIONS;
+import static org.hibernate.cfg.Environment.USE_QUERY_CACHE;
+import static org.hibernate.cfg.Environment.USE_SECOND_LEVEL_CACHE;
+
+/**
+ *
+ * Tests Hibernate L2 cache.
+ */
+public class HibernateL2CacheSelfTest extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    public static final String CONNECTION_URL = "jdbc:h2:mem:example;DB_CLOSE_DELAY=-1";
+
+    /** */
+    public static final String ENTITY_NAME = Entity.class.getName();
+
+    /** */
+    public static final String ENTITY2_NAME = Entity2.class.getName();
+
+    /** */
+    public static final String VERSIONED_ENTITY_NAME = VersionedEntity.class.getName();
+
+    /** */
+    public static final String CHILD_ENTITY_NAME = ChildEntity.class.getName();
+
+    /** */
+    public static final String PARENT_ENTITY_NAME = ParentEntity.class.getName();
+
+    /** */
+    public static final String CHILD_COLLECTION_REGION = ENTITY_NAME + ".children";
+
+    /** */
+    public static final String NATURAL_ID_REGION =
+        "org.apache.ignite.cache.hibernate.HibernateL2CacheSelfTest$Entity##NaturalId";
+
+    /** */
+    public static final String NATURAL_ID_REGION2 =
+        "org.apache.ignite.cache.hibernate.HibernateL2CacheSelfTest$Entity2##NaturalId";
+
+    /** */
+    private SessionFactory sesFactory1;
+
+    /** */
+    private SessionFactory sesFactory2;
+
+    /**
+     * First Hibernate test entity.
+     */
+    @javax.persistence.Entity
+    @NaturalIdCache
+    @SuppressWarnings({"PublicInnerClass", "UnnecessaryFullyQualifiedName"})
+    public static class Entity {
+        /** */
+        private int id;
+
+        /** */
+        private String name;
+
+        /** */
+        private Collection<ChildEntity> children;
+
+        /**
+         * Default constructor required by Hibernate.
+         */
+        public Entity() {
+            // No-op.
+        }
+
+        /**
+         * @param id ID.
+         * @param name Name.
+         */
+        public Entity(int id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        /**
+         * @return ID.
+         */
+        @Id
+        public int getId() {
+            return id;
+        }
+
+        /**
+         * @param id ID.
+         */
+        public void setId(int id) {
+            this.id = id;
+        }
+
+        /**
+         * @return Name.
+         */
+        @NaturalId(mutable = true)
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * @param name Name.
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        /**
+         * @return Children.
+         */
+        @OneToMany(cascade = javax.persistence.CascadeType.ALL, fetch = FetchType.LAZY)
+        @JoinColumn(name = "ENTITY_ID")
+        public Collection<ChildEntity> getChildren() {
+            return children;
+        }
+
+        /**
+         * @param children Children.
+         */
+        public void setChildren(Collection<ChildEntity> children) {
+            this.children = children;
+        }
+    }
+
+    /**
+     * Second Hibernate test entity.
+     */
+    @javax.persistence.Entity
+    @NaturalIdCache
+    @SuppressWarnings({"PublicInnerClass", "UnnecessaryFullyQualifiedName"})
+    public static class Entity2 {
+        /** */
+        private int id;
+
+        /** */
+        private String name;
+
+        /** */
+        private Collection<ChildEntity> children;
+
+        /**
+         * Default constructor required by Hibernate.
+         */
+        public Entity2() {
+            // No-op.
+        }
+
+        /**
+         * @param id ID.
+         * @param name Name.
+         */
+        public Entity2(int id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        /**
+         * @return ID.
+         */
+        @Id
+        public int getId() {
+            return id;
+        }
+
+        /**
+         * @param id ID.
+         */
+        public void setId(int id) {
+            this.id = id;
+        }
+
+        /**
+         * @return Name.
... 3538 lines suppressed ...