/* * 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.List; import java.util.Map; import javax.cache.Cache; import javax.persistence.Cacheable; import javax.persistence.Id; 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.hamcrest.core.Is; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.boot.Metadata; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cache.spi.access.AccessType; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.RootClass; import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL; import static org.apache.ignite.cache.CacheMode.PARTITIONED; import static org.apache.ignite.cache.hibernate.HibernateAccessStrategyFactory.REGION_CACHE_PROPERTY; import static org.hibernate.cfg.AvailableSettings.USE_STRUCTURED_CACHE; import static org.junit.Assert.assertThat; /** * Tests Hibernate L2 cache configuration. */ @SuppressWarnings("unchecked") public class HibernateL2CacheStrategySelfTest extends GridCommonAbstractTest { /** */ private static final String ENTITY1_NAME = Entity1.class.getName(); /** */ private static final String ENTITY2_NAME = Entity2.class.getName(); /** */ private static final String ENTITY3_NAME = Entity3.class.getName(); /** */ private static final String ENTITY4_NAME = Entity4.class.getName(); /** */ private static final String TIMESTAMP_CACHE = "org.hibernate.cache.spi.UpdateTimestampsCache"; /** */ private static final String QUERY_CACHE = "org.hibernate.cache.internal.StandardQueryCache"; /** */ private static final String CONNECTION_URL = "jdbc:h2:mem:example;DB_CLOSE_DELAY=-1"; /** */ private SessionFactory sesFactory1; /** {@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)cfg.getDiscoverySpi()).setIpFinder(new TcpDiscoveryVmIpFinder(true)); cfg.setCacheConfiguration(cacheConfiguration(ENTITY3_NAME), cacheConfiguration(ENTITY4_NAME), cacheConfiguration("cache1"), cacheConfiguration("cache2"), cacheConfiguration(TIMESTAMP_CACHE), cacheConfiguration(QUERY_CACHE)); 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(TRANSACTIONAL); return cfg; } /** * @throws Exception If failed. */ public void testEntityCacheReadWrite() throws Exception { for (AccessType accessType : new AccessType[]{AccessType.READ_WRITE, AccessType.NONSTRICT_READ_WRITE}) testEntityCacheReadWrite(accessType); } /** * @param accessType Cache access type. * @throws Exception If failed. */ private void testEntityCacheReadWrite(AccessType accessType) throws Exception { log.info("Test access type: " + accessType); sesFactory1 = startHibernate(accessType, getTestIgniteInstanceName(0)); try { // 1 Adding. Session ses = sesFactory1.openSession(); try { Transaction tr = ses.beginTransaction(); ses.save(new Entity1(1, "entity-1#name-1")); ses.save(new Entity2(1, "entity-2#name-1")); tr.commit(); } finally { ses.close(); } loadEntities(sesFactory1); assertEquals(1, grid(0).cache("cache1").size()); assertEquals(1, grid(0).cache("cache2").size()); assertThat(getEntityNameFromRegion(sesFactory1, "cache1", 1), Is.is("entity-1#name-1")); assertThat(getEntityNameFromRegion(sesFactory1, "cache2", 1), Is.is("entity-2#name-1")); // 2. Updating and adding. ses = sesFactory1.openSession(); try { Transaction tx = ses.beginTransaction(); Entity1 e1 = (Entity1)ses.load(Entity1.class, 1); e1.setName("entity-1#name-1#UPDATED-1"); ses.update(e1); ses.save(new Entity2(2, "entity-2#name-2#ADDED")); tx.commit(); } finally { ses.close(); } loadEntities(sesFactory1); assertEquals(1, grid(0).cache("cache1").size()); assertEquals(2, grid(0).cache("cache2").size()); assertThat(getEntityNameFromRegion(sesFactory1, "cache1", 1), Is.is("entity-1#name-1#UPDATED-1")); assertThat(getEntityNameFromRegion(sesFactory1, "cache2", 1), Is.is("entity-2#name-1")); assertThat(getEntityNameFromRegion(sesFactory1, "cache2", 2), Is.is("entity-2#name-2#ADDED")); // 3. Updating, adding, updating. ses = sesFactory1.openSession(); try { Transaction tx = ses.beginTransaction(); Entity2 e2_1 = (Entity2)ses.load(Entity2.class, 1); e2_1.setName("entity-2#name-1#UPDATED-1"); ses.update(e2_1); ses.save(new Entity1(2, "entity-1#name-2#ADDED")); Entity1 e1_1 = (Entity1)ses.load(Entity1.class, 1); e1_1.setName("entity-1#name-1#UPDATED-2"); ses.update(e1_1); tx.commit(); } finally { ses.close(); } loadEntities(sesFactory1); assertEquals(2, grid(0).cache("cache1").size()); assertEquals(2, grid(0).cache("cache2").size()); assertThat(getEntityNameFromRegion(sesFactory1, "cache2", 1), Is.is("entity-2#name-1#UPDATED-1")); assertThat(getEntityNameFromRegion(sesFactory1, "cache1", 2), Is.is("entity-1#name-2#ADDED")); assertThat(getEntityNameFromRegion(sesFactory1, "cache1", 1), Is.is("entity-1#name-1#UPDATED-2")); ses = sesFactory1.openSession(); sesFactory1.getStatistics().logSummary(); ses.close(); } finally { cleanup(); } } /** * @param sesFactory Session factory. */ private void loadEntities(SessionFactory sesFactory) { Session ses = sesFactory.openSession(); try { List<Entity1> list1 = ses.createCriteria(ENTITY1_NAME).list(); for (Entity1 e1 : list1) assertNotNull(e1.getName()); List<Entity2> list2 = ses.createCriteria(ENTITY2_NAME).list(); for (Entity2 e2 : list2) assertNotNull(e2.getName()); } finally { ses.close(); } } /** * @param sesFactory Session Factory. * @param regionName Region Name. * @param id Id. * @return Entity Name. */ private String getEntityNameFromRegion(SessionFactory sesFactory, String regionName, int id) { Session ses = sesFactory.openSession(); try { for (Cache.Entry<Object, Object> entry : grid(0).cache(regionName)) { if (((HibernateKeyWrapper)entry.getKey()).id().equals(id)) return (String) ((HashMap) entry.getValue()).get("name"); } return null; } finally { ses.close(); } } /** * @param accessType Cache access typr. * @param igniteInstanceName Name of the grid providing caches. * @return Session factory. */ private SessionFactory startHibernate(AccessType accessType, String igniteInstanceName) { StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder(); builder.applySetting("hibernate.connection.url", CONNECTION_URL); for (Map.Entry<String, String> e : HibernateL2CacheSelfTest.hibernateProperties(igniteInstanceName, accessType.name()).entrySet()) builder.applySetting(e.getKey(), e.getValue()); builder.applySetting(USE_STRUCTURED_CACHE, "true"); builder.applySetting(REGION_CACHE_PROPERTY + ENTITY1_NAME, "cache1"); builder.applySetting(REGION_CACHE_PROPERTY + ENTITY2_NAME, "cache2"); builder.applySetting(REGION_CACHE_PROPERTY + TIMESTAMP_CACHE, TIMESTAMP_CACHE); builder.applySetting(REGION_CACHE_PROPERTY + QUERY_CACHE, QUERY_CACHE); MetadataSources metadataSources = new MetadataSources(builder.build()); metadataSources.addAnnotatedClass(Entity1.class); metadataSources.addAnnotatedClass(Entity2.class); metadataSources.addAnnotatedClass(Entity3.class); metadataSources.addAnnotatedClass(Entity4.class); Metadata metadata = metadataSources.buildMetadata(); for (PersistentClass entityBinding : metadata.getEntityBindings()) { if (!entityBinding.isInherited()) ((RootClass)entityBinding).setCacheConcurrencyStrategy(accessType.getExternalName()); } return metadata.buildSessionFactory(); } /** * Test Hibernate entity1. */ @javax.persistence.Entity @SuppressWarnings({"PublicInnerClass", "UnnecessaryFullyQualifiedName"}) @Cacheable public static class Entity1 { /** */ private int id; /** */ private String name; /** * */ public Entity1() { // No-op. } /** * @param id ID. * @param name Name. */ Entity1(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. */ public String getName() { return name; } /** * @param name Name. */ public void setName(String name) { this.name = name; } } /** * Test Hibernate entity2. */ @javax.persistence.Entity @SuppressWarnings({"PublicInnerClass", "UnnecessaryFullyQualifiedName"}) @Cacheable public static class Entity2 { /** */ private int id; /** */ private String name; /** * */ public Entity2() { // No-op. } /** * @param id ID. * @param name Name. */ 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. */ public String getName() { return name; } /** * @param name Name. */ public void setName(String name) { this.name = name; } } /** * Test Hibernate entity3. */ @javax.persistence.Entity @SuppressWarnings({"PublicInnerClass", "UnnecessaryFullyQualifiedName"}) @Cacheable public static class Entity3 { /** */ private int id; /** */ private String name; /** * */ public Entity3() { // No-op. } /** * @param id ID. * @param name Name. */ public Entity3(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. */ public String getName() { return name; } /** * @param name Name. */ public void setName(String name) { this.name = name; } } /** * Test Hibernate entity4. */ @javax.persistence.Entity @SuppressWarnings({"PublicInnerClass", "UnnecessaryFullyQualifiedName"}) @Cacheable public static class Entity4 { /** */ private int id; /** */ private String name; /** * */ public Entity4() { // No-op. } /** * @param id ID. * @param name Name. */ public Entity4(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. */ public String getName() { return name; } /** * @param name Name. */ public void setName(String name) { this.name = name; } } /** * Closes session factories and clears data from caches. * * @throws Exception If failed. */ private void cleanup() throws Exception { if (sesFactory1 != null) sesFactory1.close(); sesFactory1 = null; for (IgniteCacheProxy<?, ?> cache : ((IgniteKernal)grid(0)).caches()) cache.clear(); } }