You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2017/04/26 11:23:50 UTC

[14/16] ignite git commit: ignite-1794 Refactored hibernate modules, switched to hibernate 5.1

http://git-wip-us.apache.org/repos/asf/ignite/blob/ee1b19d3/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheSelfTest.java b/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheSelfTest.java
new file mode 100644
index 0000000..7ac790c
--- /dev/null
+++ b/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheSelfTest.java
@@ -0,0 +1,1954 @@
+/*
+ * 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 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.Query;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.StaleObjectStateException;
+import org.hibernate.Transaction;
+import org.hibernate.annotations.NaturalId;
+import org.hibernate.annotations.NaturalIdCache;
+import org.hibernate.cache.spi.GeneralDataRegion;
+import org.hibernate.cache.spi.TransactionalDataRegion;
+import org.hibernate.cache.spi.access.AccessType;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.exception.ConstraintViolationException;
+import org.hibernate.service.ServiceRegistryBuilder;
+import org.hibernate.stat.NaturalIdCacheStatistics;
+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.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.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 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.
+         */
+        @NaturalId(mutable = true)
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * @param name Name.
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+    }
+
+    /**
+     * Hibernate child entity referenced by {@link Entity}.
+     */
+    @javax.persistence.Entity
+    @SuppressWarnings("PublicInnerClass")
+    public static class ChildEntity {
+        /** */
+        private int id;
+
+        /**
+         * Default constructor required by Hibernate.
+         */
+        public ChildEntity() {
+            // No-op.
+        }
+
+        /**
+         * @param id ID.
+         */
+        public ChildEntity(int id) {
+            this.id = id;
+        }
+
+        /**
+         * @return ID.
+         */
+        @Id
+        @GeneratedValue
+        public int getId() {
+            return id;
+        }
+
+        /**
+         * @param id ID.
+         */
+        public void setId(int id) {
+            this.id = id;
+        }
+    }
+
+    /**
+     * Hibernate entity referencing {@link Entity}.
+     */
+    @javax.persistence.Entity
+    @SuppressWarnings("PublicInnerClass")
+    public static class ParentEntity {
+        /** */
+        private int id;
+
+        /** */
+        private Entity entity;
+
+        /**
+         * Default constructor required by Hibernate.
+         */
+        public ParentEntity() {
+            // No-op.
+        }
+
+        /**
+         * @param id ID.
+         * @param entity Referenced entity.
+         */
+        public ParentEntity(int id, Entity entity) {
+            this.id = id;
+            this.entity = entity;
+        }
+
+        /**
+         * @return ID.
+         */
+        @Id
+        public int getId() {
+            return id;
+        }
+
+        /**
+         * @param id ID.
+         */
+        public void setId(int id) {
+            this.id = id;
+        }
+
+        /**
+         * @return Referenced entity.
+         */
+        @OneToOne
+        public Entity getEntity() {
+            return entity;
+        }
+
+        /**
+         * @param entity Referenced entity.
+         */
+        public void setEntity(Entity entity) {
+            this.entity = entity;
+        }
+    }
+
+    /**
+     * Hibernate entity.
+     */
+    @javax.persistence.Entity
+    @SuppressWarnings({"PublicInnerClass", "UnnecessaryFullyQualifiedName"})
+    public static class VersionedEntity {
+        /** */
+        private int id;
+
+        /** */
+        private long ver;
+
+        /**
+         * Default constructor required by Hibernate.
+         */
+        public VersionedEntity() {
+        }
+
+        /**
+         * @param id ID.
+         */
+        public VersionedEntity(int id) {
+            this.id = id;
+        }
+
+        /**
+         * @return ID.
+         */
+        @Id
+        public int getId() {
+            return id;
+        }
+
+        /**
+         * @param id ID.
+         */
+        public void setId(int id) {
+            this.id = id;
+        }
+
+        /**
+         * @return Version.
+         */
+        @javax.persistence.Version
+        public long getVersion() {
+            return ver;
+        }
+
+        /**
+         * @param ver Version.
+         */
+        public void setVersion(long ver) {
+            this.ver = ver;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        TcpDiscoverySpi discoSpi = new TcpDiscoverySpi();
+
+        discoSpi.setIpFinder(IP_FINDER);
+
+        cfg.setDiscoverySpi(discoSpi);
+
+        cfg.setCacheConfiguration(generalRegionConfiguration("org.hibernate.cache.spi.UpdateTimestampsCache"),
+            generalRegionConfiguration("org.hibernate.cache.internal.StandardQueryCache"),
+            transactionalRegionConfiguration(ENTITY_NAME),
+            transactionalRegionConfiguration(ENTITY2_NAME),
+            transactionalRegionConfiguration(VERSIONED_ENTITY_NAME),
+            transactionalRegionConfiguration(PARENT_ENTITY_NAME),
+            transactionalRegionConfiguration(CHILD_COLLECTION_REGION),
+            transactionalRegionConfiguration(NATURAL_ID_REGION),
+            transactionalRegionConfiguration(NATURAL_ID_REGION2));
+
+        return cfg;
+    }
+
+    /**
+     * @param regionName Region name.
+     * @return Cache configuration for {@link GeneralDataRegion}.
+     */
+    private CacheConfiguration generalRegionConfiguration(String regionName) {
+        CacheConfiguration cfg = new CacheConfiguration();
+
+        cfg.setName(regionName);
+
+        cfg.setCacheMode(PARTITIONED);
+
+        cfg.setAtomicityMode(ATOMIC);
+
+        cfg.setWriteSynchronizationMode(FULL_SYNC);
+
+        cfg.setBackups(1);
+
+        cfg.setAffinity(new RendezvousAffinityFunction(false, 10));
+
+        return cfg;
+    }
+
+    /**
+     * @param regionName Region name.
+     * @return Cache configuration for {@link TransactionalDataRegion}.
+     */
+    protected CacheConfiguration transactionalRegionConfiguration(String regionName) {
+        CacheConfiguration cfg = new CacheConfiguration();
+
+        cfg.setName(regionName);
+
+        cfg.setCacheMode(PARTITIONED);
+
+        cfg.setAtomicityMode(TRANSACTIONAL);
+
+        cfg.setWriteSynchronizationMode(FULL_SYNC);
+
+        cfg.setBackups(1);
+
+        cfg.setAffinity(new RendezvousAffinityFunction(false, 10));
+
+        return cfg;
+    }
+
+    /**
+     * @param accessType Hibernate L2 cache access type.
+     * @param igniteInstanceName Ignite instance name.
+     * @return Hibernate configuration.
+     */
+    private Configuration hibernateConfiguration(AccessType accessType,
+        String igniteInstanceName) {
+        Configuration cfg = new Configuration();
+
+        cfg.addAnnotatedClass(Entity.class);
+        cfg.addAnnotatedClass(Entity2.class);
+        cfg.addAnnotatedClass(VersionedEntity.class);
+        cfg.addAnnotatedClass(ChildEntity.class);
+        cfg.addAnnotatedClass(ParentEntity.class);
+
+        cfg.setCacheConcurrencyStrategy(ENTITY_NAME, accessType.getExternalName());
+        cfg.setCacheConcurrencyStrategy(ENTITY2_NAME, accessType.getExternalName());
+        cfg.setCacheConcurrencyStrategy(VERSIONED_ENTITY_NAME, accessType.getExternalName());
+        cfg.setCacheConcurrencyStrategy(PARENT_ENTITY_NAME, accessType.getExternalName());
+        cfg.setCollectionCacheConcurrencyStrategy(CHILD_COLLECTION_REGION, accessType.getExternalName());
+
+        for (Map.Entry<String, String> e : hibernateProperties(igniteInstanceName, accessType.name()).entrySet())
+            cfg.setProperty(e.getKey(), e.getValue());
+
+        // Use the same cache for Entity and Entity2.
+        cfg.setProperty(REGION_CACHE_PROPERTY + ENTITY2_NAME, ENTITY_NAME);
+
+        return cfg;
+    }
+
+    /**
+     * @return Hibernate registry builder.
+     */
+    protected ServiceRegistryBuilder registryBuilder() {
+        ServiceRegistryBuilder builder = new ServiceRegistryBuilder();
+
+        builder.applySetting("hibernate.connection.url", CONNECTION_URL);
+
+        return builder;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        startGrids(2);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        cleanup();
+    }
+
+    /**
+     * @return Hibernate L2 cache access types to test.
+     */
+    protected AccessType[] accessTypes() {
+        return new AccessType[]{AccessType.READ_ONLY, AccessType.NONSTRICT_READ_WRITE, AccessType.READ_WRITE};
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCollectionCache() throws Exception {
+        for (AccessType accessType : accessTypes())
+            testCollectionCache(accessType);
+    }
+
+    /**
+     * @param accessType Cache access type.
+     * @throws Exception If failed.
+     */
+    @SuppressWarnings("unchecked")
+    private void testCollectionCache(AccessType accessType) throws Exception {
+        createSessionFactories(accessType);
+
+        Map<Integer, Integer> idToChildCnt = new HashMap<>();
+
+        try {
+            Session ses = sesFactory1.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                for (int i = 0; i < 3; i++) {
+                    Entity e = new Entity(i, "name-" + i);
+
+                    Collection<ChildEntity> children = new ArrayList<>();
+
+                    for (int j = 0; j < 3; j++)
+                        children.add(new ChildEntity());
+
+                    e.setChildren(children);
+
+                    idToChildCnt.put(e.getId(), e.getChildren().size());
+
+                    ses.save(e);
+                }
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            // Load children, this should populate cache.
+
+            ses = sesFactory1.openSession();
+
+            try {
+                List<Entity> list = ses.createCriteria(ENTITY_NAME).list();
+
+                assertEquals(idToChildCnt.size(), list.size());
+
+                for (Entity e : list)
+                    assertEquals((int)idToChildCnt.get(e.getId()), e.getChildren().size());
+            }
+            finally {
+                ses.close();
+            }
+
+            assertCollectionCache(sesFactory2, idToChildCnt, 3, 0);
+            assertCollectionCache(sesFactory1, idToChildCnt, 3, 0);
+
+            if (accessType == AccessType.READ_ONLY)
+                return;
+
+            // Update children for one entity.
+
+            ses = sesFactory1.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                Entity e1 = (Entity)ses.load(Entity.class, 1);
+
+                e1.getChildren().remove(e1.getChildren().iterator().next());
+
+                ses.update(e1);
+
+                idToChildCnt.put(e1.getId(), e1.getChildren().size());
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertCollectionCache(sesFactory2, idToChildCnt, 2, 1); // After update collection cache entry is removed.
+            assertCollectionCache(sesFactory1, idToChildCnt, 3, 0); // 'assertCollectionCache' loads children in cache.
+
+            // Update children for the same entity using another SessionFactory.
+
+            ses = sesFactory2.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                Entity e1 = (Entity)ses.load(Entity.class, 1);
+
+                e1.getChildren().remove(e1.getChildren().iterator().next());
+
+                ses.update(e1);
+
+                idToChildCnt.put(e1.getId(), e1.getChildren().size());
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertCollectionCache(sesFactory2, idToChildCnt, 2, 1); // After update collection cache entry is removed.
+            assertCollectionCache(sesFactory1, idToChildCnt, 3, 0); // 'assertCollectionCache' loads children in cache.
+        }
+        finally {
+            cleanup();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testEntityCache() throws Exception {
+        for (AccessType accessType : accessTypes())
+            testEntityCache(accessType);
+    }
+
+    /**
+     * @param accessType Cache access type.
+     * @throws Exception If failed.
+     */
+    private void testEntityCache(AccessType accessType) throws Exception {
+        createSessionFactories(accessType);
+
+        Map<Integer, String> idToName = new HashMap<>();
+
+        try {
+            Session ses = sesFactory1.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                for (int i = 0; i < 2; i++) {
+                    String name = "name-" + i;
+
+                    ses.save(new Entity(i, name));
+
+                    idToName.put(i, name);
+                }
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEntityCache(ENTITY_NAME, sesFactory2, idToName, 100);
+            assertEntityCache(ENTITY_NAME, sesFactory1, idToName, 100);
+
+            if (accessType == AccessType.READ_ONLY)
+                return;
+
+            ses = sesFactory1.openSession();
+
+            try {
+                // Updates and inserts in single transaction.
+
+                Transaction tx = ses.beginTransaction();
+
+                Entity e0 = (Entity)ses.load(Entity.class, 0);
+
+                e0.setName("name-0-changed1");
+
+                ses.update(e0);
+
+                idToName.put(0, e0.getName());
+
+                ses.save(new Entity(2, "name-2"));
+
+                idToName.put(2, "name-2");
+
+                Entity e1 = (Entity)ses.load(Entity.class, 1);
+
+                e1.setName("name-1-changed1");
+
+                ses.update(e1);
+
+                idToName.put(1, e1.getName());
+
+                ses.save(new Entity(3, "name-3"));
+
+                idToName.put(3, "name-3");
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEntityCache(ENTITY_NAME, sesFactory2, idToName);
+            assertEntityCache(ENTITY_NAME, sesFactory1, idToName);
+
+            ses = sesFactory1.openSession();
+
+            try {
+                // Updates, inserts and deletes in single transaction.
+
+                Transaction tx = ses.beginTransaction();
+
+                ses.save(new Entity(4, "name-4"));
+
+                idToName.put(4, "name-4");
+
+                Entity e0 = (Entity)ses.load(Entity.class, 0);
+
+                e0.setName("name-0-changed2");
+
+                ses.update(e0);
+
+                idToName.put(e0.getId(), e0.getName());
+
+                ses.delete(ses.load(Entity.class, 1));
+
+                idToName.remove(1);
+
+                Entity e2 = (Entity)ses.load(Entity.class, 2);
+
+                e2.setName("name-2-changed1");
+
+                ses.update(e2);
+
+                idToName.put(e2.getId(), e2.getName());
+
+                ses.delete(ses.load(Entity.class, 3));
+
+                idToName.remove(3);
+
+                ses.save(new Entity(5, "name-5"));
+
+                idToName.put(5, "name-5");
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEntityCache(ENTITY_NAME, sesFactory2, idToName, 1, 3);
+            assertEntityCache(ENTITY_NAME, sesFactory1, idToName, 1, 3);
+
+            // Try to update the same entity using another SessionFactory.
+
+            ses = sesFactory2.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                Entity e0 = (Entity)ses.load(Entity.class, 0);
+
+                e0.setName("name-0-changed3");
+
+                ses.update(e0);
+
+                idToName.put(e0.getId(), e0.getName());
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEntityCache(ENTITY_NAME, sesFactory2, idToName);
+            assertEntityCache(ENTITY_NAME, sesFactory1, idToName);
+        }
+        finally {
+            cleanup();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testTwoEntitiesSameCache() throws Exception {
+        for (AccessType accessType : accessTypes())
+            testTwoEntitiesSameCache(accessType);
+    }
+
+    /**
+     * @param accessType Cache access type.
+     * @throws Exception If failed.
+     */
+    private void testTwoEntitiesSameCache(AccessType accessType) throws Exception {
+        createSessionFactories(accessType);
+
+        try {
+            Session ses = sesFactory1.openSession();
+
+            Map<Integer, String> idToName1 = new HashMap<>();
+            Map<Integer, String> idToName2 = new HashMap<>();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                for (int i = 0; i < 2; i++) {
+                    String name = "name-" + i;
+
+                    ses.save(new Entity(i, name));
+                    ses.save(new Entity2(i, name));
+
+                    idToName1.put(i, name);
+                    idToName2.put(i, name);
+                }
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEntityCache(ENTITY_NAME, sesFactory2, idToName1, 100);
+            assertEntityCache(ENTITY_NAME, sesFactory1, idToName1, 100);
+
+            assertEntityCache(ENTITY2_NAME, sesFactory2, idToName2, 100);
+            assertEntityCache(ENTITY2_NAME, sesFactory1, idToName2, 100);
+
+            if (accessType == AccessType.READ_ONLY)
+                return;
+
+            ses = sesFactory1.openSession();
+
+            try {
+                // Updates both entities in single transaction.
+
+                Transaction tx = ses.beginTransaction();
+
+                Entity e = (Entity)ses.load(Entity.class, 0);
+
+                e.setName("name-0-changed1");
+
+                ses.update(e);
+
+                Entity2 e2 = (Entity2)ses.load(Entity2.class, 0);
+
+                e2.setName("name-e2-0-changed1");
+
+                ses.update(e2);
+
+                idToName1.put(0, e.getName());
+                idToName2.put(0, e2.getName());
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEntityCache(ENTITY_NAME, sesFactory2, idToName1, 100);
+            assertEntityCache(ENTITY_NAME, sesFactory1, idToName1, 100);
+
+            assertEntityCache(ENTITY2_NAME, sesFactory2, idToName2, 100);
+            assertEntityCache(ENTITY2_NAME, sesFactory1, idToName2, 100);
+
+            ses = sesFactory1.openSession();
+
+            try {
+                // Remove entity1 and insert entity2 in single transaction.
+
+                Transaction tx = ses.beginTransaction();
+
+                Entity e = (Entity)ses.load(Entity.class, 0);
+
+                ses.delete(e);
+
+                Entity2 e2 = new Entity2(2, "name-2");
+
+                ses.save(e2);
+
+                idToName1.remove(0);
+                idToName2.put(2, e2.getName());
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEntityCache(ENTITY_NAME, sesFactory2, idToName1, 0, 100);
+            assertEntityCache(ENTITY_NAME, sesFactory1, idToName1, 0, 100);
+
+            assertEntityCache(ENTITY2_NAME, sesFactory2, idToName2, 100);
+            assertEntityCache(ENTITY2_NAME, sesFactory1, idToName2, 100);
+
+            ses = sesFactory1.openSession();
+
+            Transaction tx = ses.beginTransaction();
+
+            try {
+                // Update, remove, insert in single transaction, transaction fails.
+
+                Entity e = (Entity)ses.load(Entity.class, 1);
+
+                e.setName("name-1-changed1");
+
+                ses.update(e); // Valid update.
+
+                ses.save(new Entity(2, "name-2")); // Valid insert.
+
+                ses.delete(ses.load(Entity2.class, 0)); // Valid delete.
+
+                Entity2 e2 = (Entity2)ses.load(Entity2.class, 1);
+
+                e2.setName("name-2");  // Invalid update, not-unique name.
+
+                ses.update(e2);
+
+                tx.commit();
+
+                fail("Commit must fail.");
+            }
+            catch (ConstraintViolationException e) {
+                log.info("Expected exception: " + e);
+
+                tx.rollback();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEntityCache(ENTITY_NAME, sesFactory2, idToName1, 0, 2, 100);
+            assertEntityCache(ENTITY_NAME, sesFactory1, idToName1, 0, 2, 100);
+
+            assertEntityCache(ENTITY2_NAME, sesFactory2, idToName2, 100);
+            assertEntityCache(ENTITY2_NAME, sesFactory1, idToName2, 100);
+
+            ses = sesFactory2.openSession();
+
+            try {
+                // Update, remove, insert in single transaction.
+
+                tx = ses.beginTransaction();
+
+                Entity e = (Entity)ses.load(Entity.class, 1);
+
+                e.setName("name-1-changed1");
+
+                ses.update(e);
+
+                idToName1.put(1, e.getName());
+
+                ses.save(new Entity(2, "name-2"));
+
+                idToName1.put(2, "name-2");
+
+                ses.delete(ses.load(Entity2.class, 0));
+
+                idToName2.remove(0);
+
+                Entity2 e2 = (Entity2)ses.load(Entity2.class, 1);
+
+                e2.setName("name-e2-2-changed");
+
+                ses.update(e2);
+
+                idToName2.put(1, e2.getName());
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEntityCache(ENTITY_NAME, sesFactory2, idToName1, 0, 100);
+            assertEntityCache(ENTITY_NAME, sesFactory1, idToName1, 0, 100);
+
+            assertEntityCache(ENTITY2_NAME, sesFactory2, idToName2, 0, 100);
+            assertEntityCache(ENTITY2_NAME, sesFactory1, idToName2, 0, 100);
+        }
+        finally {
+            cleanup();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testVersionedEntity() throws Exception {
+        for (AccessType accessType : accessTypes())
+            testVersionedEntity(accessType);
+    }
+
+    /**
+     * @param accessType Cache access type.
+     * @throws Exception If failed.
+     */
+    private void testVersionedEntity(AccessType accessType) throws Exception {
+        createSessionFactories(accessType);
+
+        try {
+            Session ses = sesFactory1.openSession();
+
+            VersionedEntity e0 = new VersionedEntity(0);
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                ses.save(e0);
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            ses = sesFactory1.openSession();
+
+            long ver;
+
+            try {
+                ver = ((VersionedEntity)ses.load(VersionedEntity.class, 0)).getVersion();
+            }
+            finally {
+                ses.close();
+            }
+
+            SecondLevelCacheStatistics stats1 =
+                sesFactory1.getStatistics().getSecondLevelCacheStatistics(VERSIONED_ENTITY_NAME);
+            SecondLevelCacheStatistics stats2 =
+                sesFactory2.getStatistics().getSecondLevelCacheStatistics(VERSIONED_ENTITY_NAME);
+
+            assertEquals(1, stats1.getElementCountInMemory());
+            assertEquals(1, stats2.getElementCountInMemory());
+
+            ses = sesFactory2.openSession();
+
+            try {
+                assertEquals(ver, ((VersionedEntity)ses.load(VersionedEntity.class, 0)).getVersion());
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEquals(1, stats2.getElementCountInMemory());
+            assertEquals(1, stats2.getHitCount());
+
+            if (accessType == AccessType.READ_ONLY)
+                return;
+
+            e0.setVersion(ver - 1);
+
+            ses = sesFactory1.openSession();
+
+            Transaction tx = ses.beginTransaction();
+
+            try {
+                ses.update(e0);
+
+                tx.commit();
+
+                fail("Commit must fail.");
+            }
+            catch (StaleObjectStateException e) {
+                log.info("Expected exception: " + e);
+            }
+            finally {
+                tx.rollback();
+
+                ses.close();
+            }
+
+            sesFactory1.getStatistics().clear();
+
+            stats1 = sesFactory1.getStatistics().getSecondLevelCacheStatistics(VERSIONED_ENTITY_NAME);
+
+            ses = sesFactory1.openSession();
+
+            try {
+                assertEquals(ver, ((VersionedEntity)ses.load(VersionedEntity.class, 0)).getVersion());
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEquals(1, stats1.getElementCountInMemory());
+            assertEquals(1, stats1.getHitCount());
+            assertEquals(1, stats2.getElementCountInMemory());
+            assertEquals(1, stats2.getHitCount());
+        }
+        finally {
+            cleanup();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testNaturalIdCache() throws Exception {
+        for (AccessType accessType : accessTypes())
+            testNaturalIdCache(accessType);
+    }
+
+    /**
+     * @param accessType Cache access type.
+     * @throws Exception If failed.
+     */
+    private void testNaturalIdCache(AccessType accessType) throws Exception {
+        createSessionFactories(accessType);
+
+        Map<String, Integer> nameToId = new HashMap<>();
+
+        try {
+            Session ses = sesFactory1.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                for (int i = 0; i < 3; i++) {
+                    String name = "name-" + i;
+
+                    ses.save(new Entity(i, name));
+
+                    nameToId.put(name, i);
+                }
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            ses = sesFactory1.openSession();
+
+            try {
+                for (Map.Entry<String, Integer> e : nameToId.entrySet())
+                    ((Entity)ses.bySimpleNaturalId(Entity.class).load(e.getKey())).getId();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertNaturalIdCache(sesFactory2, nameToId, "name-100");
+            assertNaturalIdCache(sesFactory1, nameToId, "name-100");
+
+            if (accessType == AccessType.READ_ONLY)
+                return;
+
+            // Update naturalId.
+
+            ses = sesFactory1.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                Entity e1 = (Entity)ses.load(Entity.class, 1);
+
+                nameToId.remove(e1.getName());
+
+                e1.setName("name-1-changed1");
+
+                nameToId.put(e1.getName(), e1.getId());
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertNaturalIdCache(sesFactory2, nameToId, "name-1");
+            assertNaturalIdCache(sesFactory1, nameToId, "name-1");
+
+            // Update entity using another SessionFactory.
+
+            ses = sesFactory2.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                Entity e1 = (Entity)ses.load(Entity.class, 1);
+
+                nameToId.remove(e1.getName());
+
+                e1.setName("name-1-changed2");
+
+                nameToId.put(e1.getName(), e1.getId());
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertNaturalIdCache(sesFactory2, nameToId, "name-1-changed1");
+            assertNaturalIdCache(sesFactory1, nameToId, "name-1-changed1");
+
+            // Try invalid NaturalId update.
+
+            ses = sesFactory1.openSession();
+
+            Transaction tx = ses.beginTransaction();
+
+            try {
+                Entity e1 = (Entity)ses.load(Entity.class, 1);
+
+                e1.setName("name-0"); // Invalid update (duplicated name).
+
+                tx.commit();
+
+                fail("Commit must fail.");
+            }
+            catch (ConstraintViolationException e) {
+                log.info("Expected exception: " + e);
+
+                tx.rollback();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertNaturalIdCache(sesFactory2, nameToId);
+            assertNaturalIdCache(sesFactory1, nameToId);
+
+            // Delete entity.
+
+            ses = sesFactory2.openSession();
+
+            try {
+                tx = ses.beginTransaction();
+
+                Entity e2 = (Entity)ses.load(Entity.class, 2);
+
+                ses.delete(e2);
+
+                nameToId.remove(e2.getName());
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertNaturalIdCache(sesFactory2, nameToId, "name-2");
+            assertNaturalIdCache(sesFactory1, nameToId, "name-2");
+        }
+        finally {
+            cleanup();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testEntityCacheTransactionFails() throws Exception {
+        for (AccessType accessType : accessTypes())
+            testEntityCacheTransactionFails(accessType);
+    }
+
+    /**
+     * @param accessType Cache access type.
+     * @throws Exception If failed.
+     */
+    private void testEntityCacheTransactionFails(AccessType accessType) throws Exception {
+        createSessionFactories(accessType);
+
+        Map<Integer, String> idToName = new HashMap<>();
+
+        try {
+            Session ses = sesFactory1.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                for (int i = 0; i < 3; i++) {
+                    String name = "name-" + i;
+
+                    ses.save(new Entity(i, name));
+
+                    idToName.put(i, name);
+                }
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEntityCache(ENTITY_NAME, sesFactory2, idToName, 100);
+            assertEntityCache(ENTITY_NAME, sesFactory1, idToName, 100);
+
+            ses = sesFactory1.openSession();
+
+            Transaction tx = ses.beginTransaction();
+
+            try {
+                ses.save(new Entity(3, "name-3")); // Valid insert.
+
+                ses.save(new Entity(0, "name-0")); // Invalid insert (duplicated ID).
+
+                tx.commit();
+
+                fail("Commit must fail.");
+            }
+            catch (ConstraintViolationException e) {
+                log.info("Expected exception: " + e);
+
+                tx.rollback();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEntityCache(ENTITY_NAME, sesFactory2, idToName, 3);
+            assertEntityCache(ENTITY_NAME, sesFactory1, idToName, 3);
+
+            if (accessType == AccessType.READ_ONLY)
+                return;
+
+            ses = sesFactory1.openSession();
+
+            tx = ses.beginTransaction();
+
+            try {
+                Entity e0 = (Entity)ses.load(Entity.class, 0);
+                Entity e1 = (Entity)ses.load(Entity.class, 1);
+
+                e0.setName("name-10"); // Valid update.
+                e1.setName("name-2"); // Invalid update (violates unique constraint).
+
+                ses.update(e0);
+                ses.update(e1);
+
+                tx.commit();
+
+                fail("Commit must fail.");
+            }
+            catch (ConstraintViolationException e) {
+                log.info("Expected exception: " + e);
+
+                tx.rollback();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEntityCache(ENTITY_NAME, sesFactory2, idToName);
+            assertEntityCache(ENTITY_NAME, sesFactory1, idToName);
+
+            ses = sesFactory1.openSession();
+
+            try {
+                // Create parent entity referencing Entity with ID = 0.
+
+                tx = ses.beginTransaction();
+
+                ses.save(new ParentEntity(0, (Entity) ses.load(Entity.class, 0)));
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            ses = sesFactory1.openSession();
+
+            tx = ses.beginTransaction();
+
+            try {
+                ses.save(new Entity(3, "name-3")); // Valid insert.
+
+                Entity e1 = (Entity)ses.load(Entity.class, 1);
+
+                e1.setName("name-10"); // Valid update.
+
+                ses.delete(ses.load(Entity.class, 0)); // Invalid delete (there is a parent entity referencing it).
+
+                tx.commit();
+
+                fail("Commit must fail.");
+            }
+            catch (ConstraintViolationException e) {
+                log.info("Expected exception: " + e);
+
+                tx.rollback();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEntityCache(ENTITY_NAME, sesFactory2, idToName, 3);
+            assertEntityCache(ENTITY_NAME, sesFactory1, idToName, 3);
+
+            ses = sesFactory1.openSession();
+
+            tx = ses.beginTransaction();
+
+            try {
+                ses.delete(ses.load(Entity.class, 1)); // Valid delete.
+
+                idToName.remove(1);
+
+                ses.delete(ses.load(Entity.class, 0)); // Invalid delete (there is a parent entity referencing it).
+
+                tx.commit();
+
+                fail("Commit must fail.");
+            }
+            catch (ConstraintViolationException e) {
+                log.info("Expected exception: " + e);
+
+                tx.rollback();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEntityCache(ENTITY_NAME, sesFactory2, idToName);
+            assertEntityCache(ENTITY_NAME, sesFactory1, idToName);
+        }
+        finally {
+            cleanup();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testQueryCache() throws Exception {
+        for (AccessType accessType : accessTypes())
+            testQueryCache(accessType);
+    }
+
+    /**
+     * @param accessType Cache access type.
+     * @throws Exception If failed.
+     */
+    private void testQueryCache(AccessType accessType) throws Exception {
+        createSessionFactories(accessType);
+
+        try {
+            Session ses = sesFactory1.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                for (int i = 0; i < 5; i++)
+                    ses.save(new Entity(i, "name-" + i));
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            // Run some queries.
+
+            ses = sesFactory1.openSession();
+
+            try {
+                Query qry1 = ses.createQuery("from " + ENTITY_NAME + " where id > 2");
+
+                qry1.setCacheable(true);
+
+                assertEquals(2, qry1.list().size());
+
+                Query qry2 = ses.createQuery("from " + ENTITY_NAME + " where name = 'name-0'");
+
+                qry2.setCacheable(true);
+
+                assertEquals(1, qry2.list().size());
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEquals(0, sesFactory1.getStatistics().getQueryCacheHitCount());
+            assertEquals(2, sesFactory1.getStatistics().getQueryCacheMissCount());
+            assertEquals(2, sesFactory1.getStatistics().getQueryCachePutCount());
+
+            // Run queries using another SessionFactory.
+
+            ses = sesFactory2.openSession();
+
+            try {
+                Query qry1 = ses.createQuery("from " + ENTITY_NAME + " where id > 2");
+
+                qry1.setCacheable(true);
+
+                assertEquals(2, qry1.list().size());
+
+                Query qry2 = ses.createQuery("from " + ENTITY_NAME + " where name = 'name-0'");
+
+                qry2.setCacheable(true);
+
+                assertEquals(1, qry2.list().size());
+
+                Query qry3 = ses.createQuery("from " + ENTITY_NAME + " where id > 1");
+
+                qry3.setCacheable(true);
+
+                assertEquals(3, qry3.list().size());
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEquals(2, sesFactory2.getStatistics().getQueryCacheHitCount());
+            assertEquals(1, sesFactory2.getStatistics().getQueryCacheMissCount());
+            assertEquals(1, sesFactory2.getStatistics().getQueryCachePutCount());
+
+            // Update entity, it should invalidate query cache.
+
+            ses = sesFactory1.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                ses.save(new Entity(5, "name-5"));
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            // Run queries.
+
+            ses = sesFactory1.openSession();
+
+            sesFactory1.getStatistics().clear();
+
+            try {
+                Query qry1 = ses.createQuery("from " + ENTITY_NAME + " where id > 2");
+
+                qry1.setCacheable(true);
+
+                assertEquals(3, qry1.list().size());
+
+                Query qry2 = ses.createQuery("from " + ENTITY_NAME + " where name = 'name-0'");
+
+                qry2.setCacheable(true);
+
+                assertEquals(1, qry2.list().size());
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEquals(0, sesFactory1.getStatistics().getQueryCacheHitCount());
+            assertEquals(2, sesFactory1.getStatistics().getQueryCacheMissCount());
+            assertEquals(2, sesFactory1.getStatistics().getQueryCachePutCount());
+
+            // Clear query cache using another SessionFactory.
+
+            sesFactory2.getCache().evictDefaultQueryRegion();
+
+            ses = sesFactory1.openSession();
+
+            // Run queries again.
+
+            sesFactory1.getStatistics().clear();
+
+            try {
+                Query qry1 = ses.createQuery("from " + ENTITY_NAME + " where id > 2");
+
+                qry1.setCacheable(true);
+
+                assertEquals(3, qry1.list().size());
+
+                Query qry2 = ses.createQuery("from " + ENTITY_NAME + " where name = 'name-0'");
+
+                qry2.setCacheable(true);
+
+                assertEquals(1, qry2.list().size());
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEquals(0, sesFactory1.getStatistics().getQueryCacheHitCount());
+            assertEquals(2, sesFactory1.getStatistics().getQueryCacheMissCount());
+            assertEquals(2, sesFactory1.getStatistics().getQueryCachePutCount());
+        }
+        finally {
+            cleanup();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testRegionClear() throws Exception {
+        for (AccessType accessType : accessTypes())
+            testRegionClear(accessType);
+    }
+
+    /**
+     * @param accessType Cache access type.
+     * @throws Exception If failed.
+     */
+    private void testRegionClear(AccessType accessType) throws Exception {
+        createSessionFactories(accessType);
+
+        try {
+            final int ENTITY_CNT = 100;
+
+            Session ses = sesFactory1.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                for (int i = 0; i < ENTITY_CNT; i++) {
+                    Entity e = new Entity(i, "name-" + i);
+
+                    Collection<ChildEntity> children = new ArrayList<>();
+
+                    for (int j = 0; j < 3; j++)
+                        children.add(new ChildEntity());
+
+                    e.setChildren(children);
+
+                    ses.save(e);
+                }
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            loadEntities(sesFactory2, ENTITY_CNT);
+
+            SecondLevelCacheStatistics stats1 = sesFactory1.getStatistics().getSecondLevelCacheStatistics(ENTITY_NAME);
+            SecondLevelCacheStatistics stats2 = sesFactory2.getStatistics().getSecondLevelCacheStatistics(ENTITY_NAME);
+
+            NaturalIdCacheStatistics idStats1 =
+                sesFactory1.getStatistics().getNaturalIdCacheStatistics(NATURAL_ID_REGION);
+            NaturalIdCacheStatistics idStats2 =
+                sesFactory2.getStatistics().getNaturalIdCacheStatistics(NATURAL_ID_REGION);
+
+            SecondLevelCacheStatistics colStats1 =
+                sesFactory1.getStatistics().getSecondLevelCacheStatistics(CHILD_COLLECTION_REGION);
+            SecondLevelCacheStatistics colStats2 =
+                sesFactory2.getStatistics().getSecondLevelCacheStatistics(CHILD_COLLECTION_REGION);
+
+            assertEquals(ENTITY_CNT, stats1.getElementCountInMemory());
+            assertEquals(ENTITY_CNT, stats2.getElementCountInMemory());
+
+            assertEquals(ENTITY_CNT, idStats1.getElementCountInMemory());
+            assertEquals(ENTITY_CNT, idStats2.getElementCountInMemory());
+
+            assertEquals(ENTITY_CNT, colStats1.getElementCountInMemory());
+            assertEquals(ENTITY_CNT, colStats2.getElementCountInMemory());
+
+            // Test cache is cleared after update query.
+
+            ses = sesFactory1.openSession();
+
+            try {
+                Transaction tx = ses.beginTransaction();
+
+                ses.createQuery("delete from " + ENTITY_NAME + " where name='no such name'").executeUpdate();
+
+                ses.createQuery("delete from " + ChildEntity.class.getName() + " where id=-1").executeUpdate();
+
+                tx.commit();
+            }
+            finally {
+                ses.close();
+            }
+
+            assertEquals(0, stats1.getElementCountInMemory());
+            assertEquals(0, stats2.getElementCountInMemory());
+
+            assertEquals(0, idStats1.getElementCountInMemory());
+            assertEquals(0, idStats2.getElementCountInMemory());
+
+            assertEquals(0, colStats1.getElementCountInMemory());
+            assertEquals(0, colStats2.getElementCountInMemory());
+
+            // Load some data in cache.
+
+            loadEntities(sesFactory1, 10);
+
+            assertEquals(10, stats1.getElementCountInMemory());
+            assertEquals(10, stats2.getElementCountInMemory());
+            assertEquals(10, idStats1.getElementCountInMemory());
+            assertEquals(10, idStats2.getElementCountInMemory());
+
+            // Test evictAll method.
+
+            sesFactory2.getCache().evictEntityRegion(ENTITY_NAME);
+
+            assertEquals(0, stats1.getElementCountInMemory());
+            assertEquals(0, stats2.getElementCountInMemory());
+
+            sesFactory2.getCache().evictNaturalIdRegion(ENTITY_NAME);
+
+            assertEquals(0, idStats1.getElementCountInMemory());
+            assertEquals(0, idStats2.getElementCountInMemory());
+
+            sesFactory2.getCache().evictCollectionRegion(CHILD_COLLECTION_REGION);
+
+            assertEquals(0, colStats1.getElementCountInMemory());
+            assertEquals(0, colStats2.getElementCountInMemory());
+        }
+        finally {
+            cleanup();
+        }
+    }
+
+    /**
+     * @param sesFactory Session factory.
+     * @param nameToId Name-ID mapping.
+     * @param absentNames Absent entities' names.
+     */
+    private void assertNaturalIdCache(SessionFactory sesFactory, Map<String, Integer> nameToId, String... absentNames) {
+        sesFactory.getStatistics().clear();
+
+        NaturalIdCacheStatistics stats =
+            sesFactory.getStatistics().getNaturalIdCacheStatistics(NATURAL_ID_REGION);
+
+        long hitBefore = stats.getHitCount();
+
+        long missBefore = stats.getMissCount();
+
+        final Session ses = sesFactory.openSession();
+
+        try {
+            for (Map.Entry<String, Integer> e : nameToId.entrySet())
+                assertEquals((int)e.getValue(), ((Entity)ses.bySimpleNaturalId(Entity.class).load(e.getKey())).getId());
+
+            for (String name : absentNames)
+                assertNull((ses.bySimpleNaturalId(Entity.class).load(name)));
+
+            assertEquals(nameToId.size() + hitBefore, stats.getHitCount());
+
+            assertEquals(absentNames.length + missBefore, stats.getMissCount());
+        }
+        finally {
+            ses.close();
+        }
+    }
+
+    /**
+     * @param sesFactory Session factory.
+     * @param idToChildCnt Number of children per entity.
+     * @param expHit Expected cache hits.
+     * @param expMiss Expected cache misses.
+     */
+    @SuppressWarnings("unchecked")
+    private void assertCollectionCache(SessionFactory sesFactory, Map<Integer, Integer> idToChildCnt, int expHit,
+        int expMiss) {
+        sesFactory.getStatistics().clear();
+
+        Session ses = sesFactory.openSession();
+
+        try {
+            for(Map.Entry<Integer, Integer> e : idToChildCnt.entrySet()) {
+                Entity entity = (Entity)ses.load(Entity.class, e.getKey());
+
+                assertEquals((int)e.getValue(), entity.getChildren().size());
+            }
+        }
+        finally {
+            ses.close();
+        }
+
+        SecondLevelCacheStatistics stats =
+            sesFactory.getStatistics().getSecondLevelCacheStatistics(CHILD_COLLECTION_REGION);
+
+        assertEquals(expHit, stats.getHitCount());
+
+        assertEquals(expMiss, stats.getMissCount());
+    }
+
+    /**
+     * @param sesFactory Session factory.
+     * @param cnt Number of entities to load.
+     */
+    private void loadEntities(SessionFactory sesFactory, int cnt) {
+        Session ses = sesFactory.openSession();
+
+        try {
+            for (int i = 0; i < cnt; i++) {
+                Entity e = (Entity)ses.load(Entity.class, i);
+
+                assertEquals("name-" + i, e.getName());
+
+                assertFalse(e.getChildren().isEmpty());
+
+                ses.bySimpleNaturalId(Entity.class).load(e.getName());
+            }
+        }
+        finally {
+            ses.close();
+        }
+    }
+
+    /**
+     * @param entityName Entity name.
+     * @param sesFactory Session factory.
+     * @param idToName ID to name mapping.
+     * @param absentIds Absent entities' IDs.
+     */
+    private void assertEntityCache(String entityName, SessionFactory sesFactory, Map<Integer, String> idToName,
+        Integer... absentIds) {
+        assert entityName.equals(ENTITY_NAME) || entityName.equals(ENTITY2_NAME) : entityName;
+
+        sesFactory.getStatistics().clear();
+
+        final Session ses = sesFactory.openSession();
+
+        final boolean entity1 = entityName.equals(ENTITY_NAME);
+
+        try {
+            if (entity1) {
+                for (Map.Entry<Integer, String> e : idToName.entrySet())
+                    assertEquals(e.getValue(), ((Entity)ses.load(Entity.class, e.getKey())).getName());
+            }
+            else {
+                for (Map.Entry<Integer, String> e : idToName.entrySet())
+                    assertEquals(e.getValue(), ((Entity2)ses.load(Entity2.class, e.getKey())).getName());
+            }
+
+            for (final int id : absentIds) {
+                GridTestUtils.assertThrows(log, new Callable<Void>() {
+                    @Override public Void call() throws Exception {
+                        if (entity1)
+                            ((Entity)ses.load(Entity.class, id)).getName();
+                        else
+                            ((Entity2)ses.load(Entity2.class, id)).getName();
+
+                        return null;
+                    }
+                }, ObjectNotFoundException.class, null);
+            }
+
+            SecondLevelCacheStatistics stats = sesFactory.getStatistics().getSecondLevelCacheStatistics(entityName);
+
+            assertEquals(idToName.size(), stats.getHitCount());
+
+            assertEquals(absentIds.length, stats.getMissCount());
+        }
+        finally {
+            ses.close();
+        }
+    }
+
+    /**
+     * Creates session factories.
+     *
+     * @param accessType Cache access type.
+     */
+    private void createSessionFactories(AccessType accessType) {
+        sesFactory1 = startHibernate(accessType, getTestIgniteInstanceName(0));
+
+        sesFactory2 = startHibernate(accessType, getTestIgniteInstanceName(1));
+    }
+
+    /**
+     * Starts Hibernate.
+     *
+     * @param accessType Cache access type.
+     * @param igniteInstanceName Ignite instance name.
+     * @return Session factory.
+     */
+    private SessionFactory startHibernate(AccessType accessType, String igniteInstanceName) {
+        Configuration cfg = hibernateConfiguration(accessType, igniteInstanceName);
+
+        ServiceRegistryBuilder builder = registryBuilder();
+
+        builder.applySetting("hibernate.show_sql", false);
+
+        return cfg.buildSessionFactory(builder.buildServiceRegistry());
+    }
+
+    /**
+     * Closes session factories and clears data from caches.
+     *
+     * @throws Exception If failed.
+     */
+    private void cleanup() throws Exception {
+        if (sesFactory1 != null)
+            sesFactory1.close();
+
+        sesFactory1 = null;
+
+        if (sesFactory2 != null)
+            sesFactory2.close();
+
+        sesFactory2 = null;
+
+        for (IgniteCacheProxy<?, ?> cache : ((IgniteKernal)grid(0)).caches())
+            cache.clear();
+    }
+
+    /**
+     * @param igniteInstanceName Node name.
+     * @param dfltAccessType Default cache access type.
+     * @return Properties map.
+     */
+    static Map<String, String> hibernateProperties(String igniteInstanceName, String dfltAccessType) {
+        Map<String, String> map = new HashMap<>();
+
+        map.put(HBM2DDL_AUTO, "create");
+        map.put(GENERATE_STATISTICS, "true");
+        map.put(USE_SECOND_LEVEL_CACHE, "true");
+        map.put(USE_QUERY_CACHE, "true");
+        map.put(CACHE_REGION_FACTORY, HibernateRegionFactory.class.getName());
+        map.put(RELEASE_CONNECTIONS, "on_close");
+        map.put(IGNITE_INSTANCE_NAME_PROPERTY, igniteInstanceName);
+        map.put(DFLT_ACCESS_TYPE_PROPERTY, dfltAccessType);
+
+        return map;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ee1b19d3/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheTransactionalSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheTransactionalSelfTest.java b/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheTransactionalSelfTest.java
new file mode 100644
index 0000000..deca893
--- /dev/null
+++ b/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheTransactionalSelfTest.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.cache.hibernate;
+
+import java.util.Collections;
+import javax.cache.configuration.Factory;
+import javax.transaction.Synchronization;
+import javax.transaction.TransactionManager;
+import javax.transaction.UserTransaction;
+import org.apache.commons.dbcp.managed.BasicManagedDataSource;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.h2.jdbcx.JdbcDataSource;
+import org.hibernate.cache.spi.access.AccessType;
+import org.hibernate.engine.transaction.internal.jta.JtaTransactionFactory;
+import org.hibernate.engine.transaction.spi.TransactionFactory;
+import org.hibernate.service.ServiceRegistryBuilder;
+import org.hibernate.service.jdbc.connections.internal.DatasourceConnectionProviderImpl;
+import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
+import org.hibernate.service.jta.platform.internal.AbstractJtaPlatform;
+import org.hibernate.service.jta.platform.spi.JtaPlatform;
+import org.jetbrains.annotations.Nullable;
+import org.objectweb.jotm.Jotm;
+
+/**
+ *
+ * Tests Hibernate L2 cache with TRANSACTIONAL access mode (Hibernate and Cache are configured
+ * to used the same TransactionManager).
+ */
+public class HibernateL2CacheTransactionalSelfTest extends HibernateL2CacheSelfTest {
+    /** */
+    private static Jotm jotm;
+
+    /**
+     */
+    private static class TestJtaPlatform extends AbstractJtaPlatform {
+        /** {@inheritDoc} */
+        @Override protected TransactionManager locateTransactionManager() {
+            return jotm.getTransactionManager();
+        }
+
+        /** {@inheritDoc} */
+        @Override protected UserTransaction locateUserTransaction() {
+            return jotm.getUserTransaction();
+        }
+    }
+
+    /**
+     */
+    @SuppressWarnings("PublicInnerClass")
+    public static class TestTmFactory implements Factory<TransactionManager> {
+        /** */
+        private static final long serialVersionUID = 0;
+
+        /** {@inheritDoc} */
+        @Override public TransactionManager create() {
+            return jotm.getTransactionManager();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        jotm = new Jotm(true, false);
+
+        super.beforeTestsStarted();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        super.afterTestsStopped();
+
+        if (jotm != null)
+            jotm.stop();
+
+        jotm = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        cfg.getTransactionConfiguration().setTxManagerFactory(new TestTmFactory());
+        cfg.getTransactionConfiguration().setUseJtaSynchronization(useJtaSynchronization());
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected CacheConfiguration transactionalRegionConfiguration(String regionName) {
+        CacheConfiguration cfg = super.transactionalRegionConfiguration(regionName);
+
+        cfg.setNearConfiguration(null);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override protected ServiceRegistryBuilder registryBuilder() {
+        ServiceRegistryBuilder builder = new ServiceRegistryBuilder();
+
+        DatasourceConnectionProviderImpl connProvider = new DatasourceConnectionProviderImpl();
+
+        BasicManagedDataSource dataSrc = new BasicManagedDataSource(); // JTA-aware data source.
+
+        dataSrc.setTransactionManager(jotm.getTransactionManager());
+
+        dataSrc.setDefaultAutoCommit(false);
+
+        JdbcDataSource h2DataSrc = new JdbcDataSource();
+
+        h2DataSrc.setURL(CONNECTION_URL);
+
+        dataSrc.setXaDataSourceInstance(h2DataSrc);
+
+        connProvider.setDataSource(dataSrc);
+
+        connProvider.configure(Collections.emptyMap());
+
+        builder.addService(ConnectionProvider.class, connProvider);
+
+        builder.addService(JtaPlatform.class, new TestJtaPlatform());
+
+        builder.addService(TransactionFactory.class, new JtaTransactionFactory());
+
+        return builder;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected AccessType[] accessTypes() {
+        return new AccessType[]{AccessType.TRANSACTIONAL};
+    }
+
+    /**
+     * @return Whether to use {@link Synchronization}.
+     */
+    protected boolean useJtaSynchronization() {
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ee1b19d3/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheTransactionalUseSyncSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheTransactionalUseSyncSelfTest.java b/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheTransactionalUseSyncSelfTest.java
new file mode 100644
index 0000000..44899f9
--- /dev/null
+++ b/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/hibernate/HibernateL2CacheTransactionalUseSyncSelfTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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 javax.transaction.Synchronization;
+
+/**
+ * Tests Hibernate L2 cache with TRANSACTIONAL access mode and {@link Synchronization}
+ * instead of XA resource.
+ */
+public class HibernateL2CacheTransactionalUseSyncSelfTest extends HibernateL2CacheTransactionalSelfTest {
+    /** {@inheritDoc} */
+    @Override protected boolean useJtaSynchronization() {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ee1b19d3/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreNodeRestartTest.java
----------------------------------------------------------------------
diff --git a/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreNodeRestartTest.java b/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreNodeRestartTest.java
new file mode 100644
index 0000000..d5496af
--- /dev/null
+++ b/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreNodeRestartTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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 org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.store.CacheStore;
+import org.apache.ignite.configuration.NearCacheConfiguration;
+import org.apache.ignite.internal.processors.cache.integration.IgniteCacheStoreNodeRestartAbstractTest;
+
+public class CacheHibernateBlobStoreNodeRestartTest extends IgniteCacheStoreNodeRestartAbstractTest {
+    /** {@inheritDoc} */
+    @Override protected CacheStore getStore() {
+        return new CacheHibernateBlobStore();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected CacheMode cacheMode() {
+        return CacheMode.PARTITIONED;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected CacheAtomicityMode atomicityMode() {
+        return CacheAtomicityMode.ATOMIC;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected NearCacheConfiguration nearConfiguration() {
+        return null;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ee1b19d3/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreSelfTest.java b/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreSelfTest.java
new file mode 100644
index 0000000..77025a1
--- /dev/null
+++ b/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/store/hibernate/CacheHibernateBlobStoreSelfTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.URL;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.testframework.junits.cache.GridAbstractCacheStoreSelfTest;
+import org.hibernate.FlushMode;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+
+/**
+ * Cache store test.
+ */
+public class CacheHibernateBlobStoreSelfTest extends
+    GridAbstractCacheStoreSelfTest<CacheHibernateBlobStore<Object, Object>> {
+    /**
+     * @throws Exception If failed.
+     */
+    public CacheHibernateBlobStoreSelfTest() throws Exception {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        super.afterTest();
+
+        Session s = store.session(null);
+
+        if (s == null)
+            return;
+
+        try {
+            s.createQuery("delete from " + CacheHibernateBlobStoreEntry.class.getSimpleName())
+                    .setFlushMode(FlushMode.ALWAYS).executeUpdate();
+
+            Transaction hTx = s.getTransaction();
+
+            if (hTx != null && hTx.isActive())
+                hTx.commit();
+        }
+        finally {
+            s.close();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected CacheHibernateBlobStore<Object, Object> store() {
+        return new CacheHibernateBlobStore<>();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testConfigurationByUrl() throws Exception {
+        URL url = U.resolveIgniteUrl(CacheHibernateStoreFactorySelfTest.MODULE_PATH +
+            "/src/test/java/org/apache/ignite/cache/store/hibernate/hibernate.cfg.xml");
+
+        assert url != null;
+
+        store.setHibernateConfigurationPath(url.toString());
+
+        // Store will be implicitly initialized.
+        store.load("key");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testConfigurationByFile() throws Exception {
+        URL url = U.resolveIgniteUrl(CacheHibernateStoreFactorySelfTest.MODULE_PATH +
+                "/src/test/java/org/apache/ignite/cache/store/hibernate/hibernate.cfg.xml");
+
+        assert url != null;
+
+        File file = new File(url.toURI());
+
+        store.setHibernateConfigurationPath(file.getAbsolutePath());
+
+        // Store will be implicitly initialized.
+        store.load("key");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testConfigurationByResource() throws Exception {
+        store.setHibernateConfigurationPath("/org/apache/ignite/cache/store/hibernate/hibernate.cfg.xml");
+
+        // Store will be implicitly initialized.
+        store.load("key");
+    }
+
+    @Override public void testSimpleMultithreading() throws Exception {
+        fail("https://issues.apache.org/jira/browse/IGNITE-1757");
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ee1b19d3/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/store/hibernate/CacheHibernateStoreFactorySelfTest.java
----------------------------------------------------------------------
diff --git a/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/store/hibernate/CacheHibernateStoreFactorySelfTest.java b/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/store/hibernate/CacheHibernateStoreFactorySelfTest.java
new file mode 100644
index 0000000..f6c8a2d
--- /dev/null
+++ b/modules/hibernate-4.2/src/test/java/org/apache/ignite/cache/store/hibernate/CacheHibernateStoreFactorySelfTest.java
@@ -0,0 +1,288 @@
+/*
+ * 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.Serializable;
+import java.sql.Connection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.hibernate.Cache;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.SessionBuilder;
+import org.hibernate.SessionFactory;
+import org.hibernate.StatelessSession;
+import org.hibernate.StatelessSessionBuilder;
+import org.hibernate.TypeHelper;
+import org.hibernate.engine.spi.FilterDefinition;
+import org.hibernate.metadata.ClassMetadata;
+import org.hibernate.metadata.CollectionMetadata;
+import org.hibernate.stat.Statistics;
+
+/**
+ * Test for Cache jdbc blob store factory.
+ */
+public class CacheHibernateStoreFactorySelfTest extends GridCommonAbstractTest {
+    /** Cache name. */
+    private static final String CACHE_NAME = "test";
+
+    /** */
+    static final String MODULE_PATH = "modules/hibernate-4.2/";
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheConfiguration() throws Exception {
+        try (Ignite ignite1 = startGrid(0)) {
+            IgniteCache<Integer, String> cache1 = ignite1.getOrCreateCache(cacheConfiguration());
+
+            checkStore(cache1);
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testXmlConfiguration() throws Exception {
+        try (Ignite ignite = Ignition.start(MODULE_PATH + "/src/test/config/factory-cache.xml")) {
+            try(Ignite ignite1 = Ignition.start(MODULE_PATH + "/src/test/config/factory-cache1.xml")) {
+                checkStore(ignite.<Integer, String>cache(CACHE_NAME), DummySessionFactoryExt.class);
+
+                checkStore(ignite1.<Integer, String>cache(CACHE_NAME), DummySessionFactory.class);
+            }
+        }
+    }
+
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testIncorrectBeanConfiguration() throws Exception {
+        GridTestUtils.assertThrows(log, new Callable<Object>() {
+            @Override public Object call() throws Exception {
+                try(Ignite ignite =
+                    Ignition.start(MODULE_PATH + "/src/test/config/factory-incorrect-store-cache.xml")) {
+                    ignite.cache(CACHE_NAME).getConfiguration(CacheConfiguration.class).
+                            getCacheStoreFactory().create();
+                }
+                return null;
+            }
+        }, IgniteException.class, "Failed to load bean in application context");
+    }
+
+    /**
+     * @return Cache configuration with store.
+     */
+    private CacheConfiguration<Integer, String> cacheConfiguration() {
+        CacheConfiguration<Integer, String> cfg = new CacheConfiguration<>();
+
+        CacheHibernateBlobStoreFactory<Integer, String> factory = new CacheHibernateBlobStoreFactory();
+
+        factory.setHibernateConfigurationPath("/org/apache/ignite/cache/store/hibernate/hibernate.cfg.xml");
+
+        cfg.setCacheStoreFactory(factory);
+
+        return cfg;
+    }
+
+    /**
+     * @param cache Ignite cache.
+     * @param dataSrcClass Data source class.
+     * @throws Exception If store parameters is not the same as in configuration xml.
+     */
+    private void checkStore(IgniteCache<Integer, String> cache, Class<?> dataSrcClass) throws Exception {
+        CacheHibernateBlobStore store = (CacheHibernateBlobStore)cache
+            .getConfiguration(CacheConfiguration.class).getCacheStoreFactory().create();
+
+        assertEquals(dataSrcClass,
+            GridTestUtils.getFieldValue(store, CacheHibernateBlobStore.class, "sesFactory").getClass());
+    }
+
+    /**
+     * @param cache Ignite cache.
+     * @throws Exception If store parameters is not the same as in configuration xml.
+     */
+    private void checkStore(IgniteCache<Integer, String> cache) throws Exception {
+        CacheHibernateBlobStore store = (CacheHibernateBlobStore)cache.getConfiguration(CacheConfiguration.class)
+            .getCacheStoreFactory().create();
+
+        assertEquals("/org/apache/ignite/cache/store/hibernate/hibernate.cfg.xml",
+            GridTestUtils.getFieldValue(store, CacheHibernateBlobStore.class, "hibernateCfgPath"));
+    }
+
+    /**
+     *
+     */
+    public static class DummySessionFactoryExt extends DummySessionFactory {
+        /** */
+        public DummySessionFactoryExt() {
+            // No-op.
+        }
+    }
+
+    /**
+     *
+     */
+    public static class DummySessionFactory implements SessionFactory {
+        /** {@inheritDoc} */
+        @Override public SessionFactoryOptions getSessionFactoryOptions() {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public SessionBuilder withOptions() {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Session openSession() throws HibernateException {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Session getCurrentSession() throws HibernateException {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public StatelessSessionBuilder withStatelessOptions() {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public StatelessSession openStatelessSession() {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public StatelessSession openStatelessSession(Connection conn) {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public ClassMetadata getClassMetadata(Class entityCls) {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public ClassMetadata getClassMetadata(String entityName) {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public CollectionMetadata getCollectionMetadata(String roleName) {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Map<String, ClassMetadata> getAllClassMetadata() {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Map getAllCollectionMetadata() {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Statistics getStatistics() {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void close() throws HibernateException {
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean isClosed() {
+            return false;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Cache getCache() {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void evict(Class persistentCls) throws HibernateException {
+        }
+
+        /** {@inheritDoc} */
+        @Override public void evict(Class persistentCls, Serializable id) throws HibernateException {
+        }
+
+        /** {@inheritDoc} */
+        @Override public void evictEntity(String entityName) throws HibernateException {
+        }
+
+        /** {@inheritDoc} */
+        @Override public void evictEntity(String entityName, Serializable id) throws HibernateException {
+        }
+
+        /** {@inheritDoc} */
+        @Override public void evictCollection(String roleName) throws HibernateException {
+        }
+
+        /** {@inheritDoc} */
+        @Override public void evictCollection(String roleName, Serializable id) throws HibernateException {
+        }
+
+        /** {@inheritDoc} */
+        @Override public void evictQueries(String cacheRegion) throws HibernateException {
+        }
+
+        /** {@inheritDoc} */
+        @Override public void evictQueries() throws HibernateException {
+        }
+
+        /** {@inheritDoc} */
+        @Override public Set getDefinedFilterNames() {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public FilterDefinition getFilterDefinition(String filterName) throws HibernateException {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean containsFetchProfileDefinition(String name) {
+            return false;
+        }
+
+        /** {@inheritDoc} */
+        @Override public TypeHelper getTypeHelper() {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Reference getReference() throws NamingException {
+            return null;
+        }
+    }
+}
\ No newline at end of file