http://git-wip-us.apache.org/repos/asf/ignite/blob/7102d532/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateEntityRegion.java ---------------------------------------------------------------------- diff --git a/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateEntityRegion.java b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateEntityRegion.java new file mode 100644 index 0000000..1842a63 --- /dev/null +++ b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateEntityRegion.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.cache.hibernate; + +import org.apache.ignite.Ignite; +import org.hibernate.cache.CacheException; +import org.hibernate.cache.internal.DefaultCacheKeysFactory; +import org.hibernate.cache.spi.CacheDataDescription; +import org.hibernate.cache.spi.EntityRegion; +import org.hibernate.cache.spi.access.AccessType; +import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; +import org.hibernate.cache.spi.access.SoftLock; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.persister.entity.EntityPersister; + +/** + * Implementation of {@link EntityRegion}. This region is used to store entity data. + * <p> + * L2 cache for entity can be enabled in the Hibernate configuration file: + * <pre name="code" class="xml"> + * <hibernate-configuration> + * <!-- Enable L2 cache. --> + * <property name="cache.use_second_level_cache">true</property> + * + * <!-- Use Ignite as L2 cache provider. --> + * <property name="cache.region.factory_class">org.apache.ignite.cache.hibernate.HibernateRegionFactory</property> + * + * <!-- Specify entity. --> + * <mapping class="com.example.Entity"/> + * + * <!-- Enable L2 cache with nonstrict-read-write access strategy for entity. --> + * <class-cache class="com.example.Entity" usage="nonstrict-read-write"/> + * </hibernate-configuration> + * </pre> + * Also cache for entity can be enabled using annotations: + * <pre name="code" class="java"> + * @javax.persistence.Entity + * @javax.persistence.Cacheable + * @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) + * public class Entity { ... } + * </pre> + */ +public class HibernateEntityRegion extends HibernateTransactionalDataRegion implements EntityRegion { + /** + * @param factory Region factory. + * @param name Region name. + * @param ignite Grid. + * @param cache Region cache, + * @param dataDesc Region data description. + */ + public HibernateEntityRegion(HibernateRegionFactory factory, String name, Ignite ignite, + HibernateCacheProxy cache, CacheDataDescription dataDesc) { + super(factory, name, ignite, cache, dataDesc); + } + + /** {@inheritDoc} */ + @Override public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException { + return new AccessStrategy(createAccessStrategy(accessType)); + } + + /** + * Entity region access strategy. + */ + private class AccessStrategy extends HibernateAbstractRegionAccessStrategy + implements EntityRegionAccessStrategy { + /** + * @param stgy Access strategy implementation. + */ + private AccessStrategy(HibernateAccessStrategyAdapter stgy) { + super(stgy); + } + + /** {@inheritDoc} */ + @Override public Object generateCacheKey(Object id, + EntityPersister persister, + SessionFactoryImplementor factory, + String tenantIdentifier) { + return HibernateKeyWrapper.staticCreateEntityKey(id, persister, tenantIdentifier); + } + + /** {@inheritDoc} */ + @Override public Object getCacheKeyId(Object cacheKey) { + return ((HibernateKeyWrapper)cacheKey).id(); + } + + /** {@inheritDoc} */ + @Override public EntityRegion getRegion() { + return HibernateEntityRegion.this; + } + + /** {@inheritDoc} */ + @Override public boolean insert(SharedSessionContractImplementor ses, Object key, Object val, Object ver) throws CacheException { + return stgy.insert(key, val); + } + + /** {@inheritDoc} */ + @Override public boolean afterInsert(SharedSessionContractImplementor ses, Object key, Object val, Object ver) throws CacheException { + return stgy.afterInsert(key, val); + } + + /** {@inheritDoc} */ + @Override public boolean update(SharedSessionContractImplementor ses, Object key, Object val, Object currVer, Object previousVer) + throws CacheException { + return stgy.update(key, val); + } + + /** {@inheritDoc} */ + @Override public boolean afterUpdate(SharedSessionContractImplementor ses, Object key, Object val, Object currVer, Object previousVer, SoftLock lock) + throws CacheException { + return stgy.afterUpdate(key, val, lock); + } + } +} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/7102d532/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateGeneralDataRegion.java ---------------------------------------------------------------------- diff --git a/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateGeneralDataRegion.java b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateGeneralDataRegion.java new file mode 100644 index 0000000..2b34804 --- /dev/null +++ b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateGeneralDataRegion.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.cache.hibernate; + +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCheckedException; +import org.hibernate.cache.CacheException; +import org.hibernate.cache.spi.GeneralDataRegion; +import org.hibernate.cache.spi.QueryResultsRegion; +import org.hibernate.cache.spi.TimestampsRegion; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.jetbrains.annotations.Nullable; + +/** + * Implementation of {@link GeneralDataRegion}. This interface defines common contract for {@link QueryResultsRegion} + * and {@link TimestampsRegion}. + */ +public class HibernateGeneralDataRegion extends HibernateRegion implements GeneralDataRegion { + /** + * @param factory Region factory. + * @param name Region name. + * @param ignite Grid. + * @param cache Region cache. + */ + public HibernateGeneralDataRegion(HibernateRegionFactory factory, String name, + Ignite ignite, HibernateCacheProxy cache) { + super(factory, name, ignite, cache); + } + + /** {@inheritDoc} */ + @Nullable @Override public Object get(SharedSessionContractImplementor ses, Object key) throws CacheException { + try { + return cache.get(key); + } catch (IgniteCheckedException e) { + throw new CacheException(e); + } + } + + /** {@inheritDoc} */ + @Override public void put(SharedSessionContractImplementor ses, Object key, Object val) throws CacheException { + try { + cache.put(key, val); + } catch (IgniteCheckedException e) { + throw new CacheException(e); + } + } + + /** {@inheritDoc} */ + @Override public void evict(Object key) throws CacheException { + HibernateAccessStrategyAdapter.evict(ignite, cache, key); + } + + /** {@inheritDoc} */ + @Override public void evictAll() throws CacheException { + HibernateAccessStrategyAdapter.evictAll(cache); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/7102d532/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateKeyTransformer.java ---------------------------------------------------------------------- diff --git a/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateKeyTransformer.java b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateKeyTransformer.java new file mode 100644 index 0000000..2922f7f --- /dev/null +++ b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateKeyTransformer.java @@ -0,0 +1,28 @@ +/* + * 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; + +/** + * + */ +public interface HibernateKeyTransformer { + /** + * @param key Hibernate key. + */ + public Object transform(Object key); +} http://git-wip-us.apache.org/repos/asf/ignite/blob/7102d532/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateKeyWrapper.java ---------------------------------------------------------------------- diff --git a/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateKeyWrapper.java b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateKeyWrapper.java new file mode 100644 index 0000000..3f2b97f --- /dev/null +++ b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateKeyWrapper.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.cache.hibernate; + +import org.apache.ignite.internal.util.typedef.internal.S; +import org.hibernate.cache.internal.DefaultCacheKeysFactory; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.EntityPersister; + +/** + * + */ +public class HibernateKeyWrapper { + /** Key. */ + private final Object key; + + /** Entry. */ + private final String entry; + + /** */ + private final String tenantId; + + /** + * @param key Key. + * @param entry Entry. + * @param tenantId Tenant ID. + */ + HibernateKeyWrapper(Object key, String entry, String tenantId) { + this.key = key; + this.entry = entry; + this.tenantId = tenantId; + } + + /** + * @return ID. + */ + Object id() { + return key; + } + + /** + * @param id ID. + * @param persister Persister. + * @param tenantIdentifier Tenant ID. + * @return Cache key. + * @see DefaultCacheKeysFactory#staticCreateCollectionKey(Object, CollectionPersister, SessionFactoryImplementor, String) + */ + static Object staticCreateCollectionKey(Object id, + CollectionPersister persister, + String tenantIdentifier) { + return new HibernateKeyWrapper(id, persister.getRole(), tenantIdentifier); + } + + /** + * @param id ID. + * @param persister Persister. + * @param tenantIdentifier Tenant ID. + * @return Cache key. + * @see DefaultCacheKeysFactory#staticCreateEntityKey(Object, EntityPersister, SessionFactoryImplementor, String) + */ + public static Object staticCreateEntityKey(Object id, EntityPersister persister, String tenantIdentifier) { + return new HibernateKeyWrapper(id, persister.getRootEntityName(), tenantIdentifier); + } + + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || getClass() != o.getClass()) + return false; + + HibernateKeyWrapper that = (HibernateKeyWrapper) o; + + return (key != null ? key.equals(that.key) : that.key == null) && + (entry != null ? entry.equals(that.entry) : that.entry == null) && + (tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + int res = key != null ? key.hashCode() : 0; + res = 31 * res + (entry != null ? entry.hashCode() : 0); + res = 31 * res + (tenantId != null ? tenantId.hashCode() : 0); + return res; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(HibernateKeyWrapper.class, this); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/7102d532/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateNaturalIdRegion.java ---------------------------------------------------------------------- diff --git a/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateNaturalIdRegion.java b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateNaturalIdRegion.java new file mode 100644 index 0000000..73ed23a --- /dev/null +++ b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateNaturalIdRegion.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.hibernate; + +import org.apache.ignite.Ignite; +import org.hibernate.cache.CacheException; +import org.hibernate.cache.internal.DefaultCacheKeysFactory; +import org.hibernate.cache.spi.CacheDataDescription; +import org.hibernate.cache.spi.NaturalIdRegion; +import org.hibernate.cache.spi.access.AccessType; +import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy; +import org.hibernate.cache.spi.access.SoftLock; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.persister.entity.EntityPersister; + +/** + * Implementation of {@link NaturalIdRegion}. This region is used to store naturalId data. + * <p> + * L2 cache for entity naturalId and target cache region can be set using annotations: + * <pre name="code" class="java"> + * @javax.persistence.Entity + * @javax.persistence.Cacheable + * @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) + * @org.hibernate.annotations.NaturalIdCache + * public class Entity { + * @org.hibernate.annotations.NaturalId + * private String entityCode; + * + * ... + * } + * </pre> + */ +public class HibernateNaturalIdRegion extends HibernateTransactionalDataRegion implements NaturalIdRegion { + /** + * @param factory Region factory. + * @param name Region name. + * @param ignite Grid. + * @param cache Region cache, + * @param dataDesc Region data description. + */ + public HibernateNaturalIdRegion(HibernateRegionFactory factory, String name, + Ignite ignite, HibernateCacheProxy cache, CacheDataDescription dataDesc) { + super(factory, name, ignite, cache, dataDesc); + } + + /** {@inheritDoc} */ + @Override public NaturalIdRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException { + return new AccessStrategy(createAccessStrategy(accessType)); + } + + /** + * NaturalId region access strategy. + */ + private class AccessStrategy extends HibernateAbstractRegionAccessStrategy implements + NaturalIdRegionAccessStrategy { + /** + * @param stgy Access strategy implementation. + */ + private AccessStrategy(HibernateAccessStrategyAdapter stgy) { + super(stgy); + } + + /** {@inheritDoc} */ + @Override public Object generateCacheKey(Object[] naturalIdValues, EntityPersister persister, SharedSessionContractImplementor ses) { + return DefaultCacheKeysFactory.staticCreateNaturalIdKey(naturalIdValues, persister, ses); + } + + /** {@inheritDoc} */ + @Override public Object[] getNaturalIdValues(Object cacheKey) { + return DefaultCacheKeysFactory.staticGetNaturalIdValues(cacheKey); + } + + /** {@inheritDoc} */ + @Override public NaturalIdRegion getRegion() { + return HibernateNaturalIdRegion.this; + } + + /** {@inheritDoc} */ + @Override public boolean insert(SharedSessionContractImplementor ses, Object key, Object val) throws CacheException { + return stgy.insert(key, val); + } + + /** {@inheritDoc} */ + @Override public boolean afterInsert(SharedSessionContractImplementor ses, Object key, Object val) throws CacheException { + return stgy.afterInsert(key, val); + } + + /** {@inheritDoc} */ + @Override public boolean update(SharedSessionContractImplementor ses, Object key, Object val) throws CacheException { + return stgy.update(key, val); + } + + /** {@inheritDoc} */ + @Override public boolean afterUpdate(SharedSessionContractImplementor ses, Object key, Object val, SoftLock lock) throws CacheException { + return stgy.afterUpdate(key, val, lock); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/7102d532/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateNonStrictAccessStrategy.java ---------------------------------------------------------------------- diff --git a/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateNonStrictAccessStrategy.java b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateNonStrictAccessStrategy.java new file mode 100644 index 0000000..a36d7e7 --- /dev/null +++ b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateNonStrictAccessStrategy.java @@ -0,0 +1,222 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.cache.hibernate; + +import java.util.Map; +import java.util.Set; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.internal.util.GridLeanMap; +import org.apache.ignite.internal.util.GridLeanSet; +import org.apache.ignite.internal.util.typedef.F; +import org.hibernate.cache.CacheException; +import org.hibernate.cache.spi.access.AccessType; +import org.hibernate.cache.spi.access.SoftLock; +import org.jetbrains.annotations.Nullable; + +/** + * Implementation of {@link AccessType#NONSTRICT_READ_WRITE} cache access strategy. + * <p> + * Configuration of L2 cache and per-entity cache access strategy can be set in the + * Hibernate configuration file: + * <pre name="code" class="xml"> + * <hibernate-configuration> + * <!-- Enable L2 cache. --> + * <property name="cache.use_second_level_cache">true</property> + * + * <!-- Use Ignite as L2 cache provider. --> + * <property name="cache.region.factory_class">org.apache.ignite.cache.hibernate.HibernateRegionFactory</property> + * + * <!-- Specify entity. --> + * <mapping class="com.example.Entity"/> + * + * <!-- Enable L2 cache with nonstrict-read-write access strategy for entity. --> + * <class-cache class="com.example.Entity" usage="nonstrict-read-write"/> + * </hibernate-configuration> + * </pre> + * Also cache access strategy can be set using annotations: + * <pre name="code" class="java"> + * @javax.persistence.Entity + * @javax.persistence.Cacheable + * @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) + * public class Entity { ... } + * </pre> + */ +public class HibernateNonStrictAccessStrategy extends HibernateAccessStrategyAdapter { + /** */ + private final ThreadLocal<WriteContext> writeCtx; + + /** + * @param ignite Grid. + * @param cache Cache. + * @param writeCtx Thread local instance used to track updates done during one Hibernate transaction. + */ + protected HibernateNonStrictAccessStrategy(Ignite ignite, HibernateCacheProxy cache, ThreadLocal writeCtx) { + super(ignite, cache); + + this.writeCtx = (ThreadLocal<WriteContext>)writeCtx; + } + + /** {@inheritDoc} */ + @Nullable @Override protected SoftLock lock(Object key) throws CacheException { + WriteContext ctx = writeCtx.get(); + + if (ctx == null) + writeCtx.set(ctx = new WriteContext()); + + ctx.locked(key); + + return null; + } + + /** {@inheritDoc} */ + @Override protected void unlock(Object key, SoftLock lock) throws CacheException { + try { + WriteContext ctx = writeCtx.get(); + + if (ctx != null && ctx.unlocked(key)) { + writeCtx.remove(); + + ctx.updateCache(cache); + } + } + catch (IgniteCheckedException e) { + throw new CacheException(e); + } + } + + /** {@inheritDoc} */ + @Override protected boolean update(Object key, Object val) throws CacheException { + return false; + } + + /** {@inheritDoc} */ + @Override protected boolean afterUpdate(Object key, Object val, SoftLock lock) throws CacheException { + WriteContext ctx = writeCtx.get(); + + if (ctx != null) { + ctx.updated(key, val); + + unlock(key, lock); + + return true; + } + + return false; + } + + /** {@inheritDoc} */ + @Override protected boolean insert(Object key, Object val) throws CacheException { + return false; + } + + /** {@inheritDoc} */ + @Override protected boolean afterInsert(Object key, Object val) throws CacheException { + try { + cache.put(key, val); + + return true; + } + catch (IgniteCheckedException e) { + throw new CacheException(e); + } + } + + /** {@inheritDoc} */ + @Override protected void remove(Object key) throws CacheException { + WriteContext ctx = writeCtx.get(); + + if (ctx != null) + ctx.removed(key); + } + + /** + * Information about updates done during single database transaction. + */ + @SuppressWarnings("TypeMayBeWeakened") + private static class WriteContext { + /** */ + private Map<Object, Object> updates; + + /** */ + private Set<Object> rmvs; + + /** */ + private Set<Object> locked = new GridLeanSet<>(); + + /** + * Marks key as locked. + * + * @param key Key. + */ + void locked(Object key) { + locked.add(key); + } + + /** + * Marks key as unlocked. + * + * @param key Key. + * @return {@code True} if last locked key was unlocked. + */ + boolean unlocked(Object key) { + locked.remove(key); + + return locked.isEmpty(); + } + + /** + * Marks key as updated. + * + * @param key Key. + * @param val Value. + */ + void updated(Object key, Object val) { + if (updates == null) + updates = new GridLeanMap<>(); + + updates.put(key, val); + } + + /** + * Marks key as removed. + * + * @param key Key. + */ + void removed(Object key) { + if (rmvs == null) + rmvs = new GridLeanSet<>(); + + rmvs.add(key); + } + + /** + * Updates cache. + * + * @param cache Cache. + * @throws IgniteCheckedException If failed. + */ + void updateCache(HibernateCacheProxy cache) throws IgniteCheckedException { + if (!F.isEmpty(rmvs)) + cache.removeAll(rmvs); + + if (!F.isEmpty(updates)) + cache.putAll(updates); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/7102d532/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateQueryResultsRegion.java ---------------------------------------------------------------------- diff --git a/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateQueryResultsRegion.java b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateQueryResultsRegion.java new file mode 100644 index 0000000..0b9a43d --- /dev/null +++ b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateQueryResultsRegion.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.cache.hibernate; + +import org.apache.ignite.Ignite; +import org.hibernate.Query; +import org.hibernate.cache.spi.QueryResultsRegion; + +/** + * Implementation of {@link QueryResultsRegion}. This region is used to store query results. + * <p> + * Query results caching can be enabled in the Hibernate configuration file: + * <pre name="code" class="xml"> + * <hibernate-configuration> + * <!-- Enable L2 cache. --> + * <property name="cache.use_second_level_cache">true</property> + * + * <!-- Enable query cache. --> + * <property name="cache.use_second_level_cache">true</property> + + * <!-- Use Ignite as L2 cache provider. --> + * <property name="cache.region.factory_class">org.apache.ignite.cache.hibernate.HibernateRegionFactory</property> + * + * <!-- Specify entity. --> + * <mapping class="com.example.Entity"/> + * + * <!-- Enable L2 cache with nonstrict-read-write access strategy for entity. --> + * <class-cache class="com.example.Entity" usage="nonstrict-read-write"/> + * </hibernate-configuration> + * </pre> + * By default queries are not cached even after enabling query caching, to enable results caching for a particular + * query, call {@link Query#setCacheable(boolean)}: + * <pre name="code" class="java"> + * Session ses = getSession(); + * + * Query qry = ses.createQuery("..."); + * + * qry.setCacheable(true); // Enable L2 cache for query. + * </pre> + * Note: the query cache does not cache the state of the actual entities in the cache, it caches only identifier + * values. For this reason, the query cache should always be used in conjunction with + * the second-level cache for those entities expected to be cached as part of a query result cache + */ +public class HibernateQueryResultsRegion extends HibernateGeneralDataRegion implements QueryResultsRegion { + /** + * @param factory Region factory. + * @param name Region name. + * @param ignite Grid. + * @param cache Region cache. + */ + public HibernateQueryResultsRegion(HibernateRegionFactory factory, String name, + Ignite ignite, HibernateCacheProxy cache) { + super(factory, name, ignite, cache); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/7102d532/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateReadOnlyAccessStrategy.java ---------------------------------------------------------------------- diff --git a/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateReadOnlyAccessStrategy.java b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateReadOnlyAccessStrategy.java new file mode 100644 index 0000000..cdef80e --- /dev/null +++ b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateReadOnlyAccessStrategy.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.cache.hibernate; + +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCheckedException; +import org.hibernate.cache.CacheException; +import org.hibernate.cache.spi.access.AccessType; +import org.hibernate.cache.spi.access.SoftLock; +import org.jetbrains.annotations.Nullable; + +/** + * Implementation of {@link AccessType#READ_ONLY} cache access strategy. + * <p> + * Configuration of L2 cache and per-entity cache access strategy can be set in the + * Hibernate configuration file: + * <pre name="code" class="xml"> + * <hibernate-configuration> + * <!-- Enable L2 cache. --> + * <property name="cache.use_second_level_cache">true</property> + * + * <!-- Use Ignite as L2 cache provider. --> + * <property name="cache.region.factory_class">org.apache.ignite.cache.hibernate.HibernateRegionFactory</property> + * + * <!-- Specify entity. --> + * <mapping class="com.example.Entity"/> + * + * <!-- Enable L2 cache with read-only access strategy for entity. --> + * <class-cache class="com.example.Entity" usage="read-only"/> + * </hibernate-configuration> + * </pre> + * Also cache access strategy can be set using annotations: + * <pre name="code" class="java"> + * @javax.persistence.Entity + * @javax.persistence.Cacheable + * @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY) + * public class Entity { ... } + * </pre> + + * + */ +public class HibernateReadOnlyAccessStrategy extends HibernateAccessStrategyAdapter { + /** + * @param ignite Grid. + * @param cache Cache. + */ + public HibernateReadOnlyAccessStrategy(Ignite ignite, HibernateCacheProxy cache) { + super(ignite, cache); + } + + /** {@inheritDoc} */ + @Override protected boolean insert(Object key, Object val) throws CacheException { + return false; + } + + /** {@inheritDoc} */ + @Override protected boolean afterInsert(Object key, Object val) throws CacheException { + try { + cache.put(key, val); + + return true; + } + catch (IgniteCheckedException e) { + throw new CacheException(e); + } + } + + /** {@inheritDoc} */ + @Nullable @Override protected SoftLock lock(Object key) throws CacheException { + return null; + } + + /** {@inheritDoc} */ + @Override protected void unlock(Object key, SoftLock lock) throws CacheException { + // No-op. + } + + /** {@inheritDoc} */ + @Override protected void remove(Object key) throws CacheException { + // No-op. + } + + /** {@inheritDoc} */ + @Override protected boolean update(Object key, Object val) throws CacheException { + throw new UnsupportedOperationException("Updates are not supported for read-only access strategy."); + } + + /** {@inheritDoc} */ + @Override protected boolean afterUpdate(Object key, Object val, SoftLock lock) throws CacheException { + throw new UnsupportedOperationException("Updates are not supported for read-only access strategy."); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/7102d532/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateReadWriteAccessStrategy.java ---------------------------------------------------------------------- diff --git a/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateReadWriteAccessStrategy.java b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateReadWriteAccessStrategy.java new file mode 100644 index 0000000..ae9bd71 --- /dev/null +++ b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateReadWriteAccessStrategy.java @@ -0,0 +1,328 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.cache.hibernate; + +import java.util.Set; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.IgniteException; +import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal; +import org.apache.ignite.internal.util.GridLeanSet; +import org.hibernate.cache.CacheException; +import org.hibernate.cache.spi.access.AccessType; +import org.hibernate.cache.spi.access.SoftLock; + +import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC; +import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ; + +/** + * Implementation of {@link AccessType#READ_WRITE} cache access strategy. + * <p> + * Configuration of L2 cache and per-entity cache access strategy can be set in the + * Hibernate configuration file: + * <pre name="code" class="xml"> + * <hibernate-configuration> + * <!-- Enable L2 cache. --> + * <property name="cache.use_second_level_cache">true</property> + * + * <!-- Use Ignite as L2 cache provider. --> + * <property name="cache.region.factory_class">org.apache.ignite.cache.hibernate.HibernateRegionFactory</property> + * + * <!-- Specify entity. --> + * <mapping class="com.example.Entity"/> + * + * <!-- Enable L2 cache with read-write access strategy for entity. --> + * <class-cache class="com.example.Entity" usage="read-write"/> + * </hibernate-configuration> + * </pre> + * Also cache access strategy can be set using annotations: + * <pre name="code" class="java"> + * @javax.persistence.Entity + * @javax.persistence.Cacheable + * @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) + * public class Entity { ... } + * </pre> + */ +public class HibernateReadWriteAccessStrategy extends HibernateAccessStrategyAdapter { + /** */ + private final ThreadLocal<TxContext> txCtx; + + /** + * @param ignite Grid. + * @param cache Cache. + * @param txCtx Thread local instance used to track updates done during one Hibernate transaction. + */ + protected HibernateReadWriteAccessStrategy(Ignite ignite, HibernateCacheProxy cache, ThreadLocal txCtx) { + super(ignite, cache); + + this.txCtx = (ThreadLocal<TxContext>)txCtx; + } + + /** {@inheritDoc} */ + @Override protected Object get(Object key) throws CacheException { + boolean success = false; + + try { + Object o = cache.get(key); + + success = true; + + return o; + } + catch (IgniteCheckedException e) { + throw new CacheException(e); + } + finally { + if (!success) + rollbackCurrentTx(); + } + } + + /** {@inheritDoc} */ + @Override protected void putFromLoad(Object key, Object val) throws CacheException { + boolean success = false; + + try { + cache.put(key, val); + + success = true; + } + catch (IgniteCheckedException e) { + throw new CacheException(e); + } + finally { + if (!success) + rollbackCurrentTx(); + } + } + + /** {@inheritDoc} */ + @Override protected SoftLock lock(Object key) throws CacheException { + boolean success = false; + + try { + TxContext ctx = txCtx.get(); + + if (ctx == null) + txCtx.set(ctx = new TxContext()); + + lockKey(key); + + ctx.locked(key); + + success = true; + + return null; + } + catch (IgniteCheckedException e) { + throw new CacheException(e); + } + finally { + if (!success) + rollbackCurrentTx(); + } + } + + /** {@inheritDoc} */ + @Override protected void unlock(Object key, SoftLock lock) throws CacheException { + boolean success = false; + + try { + TxContext ctx = txCtx.get(); + + if (ctx != null) + unlock(ctx, key); + + success = true; + } + catch (Exception e) { + throw new CacheException(e); + } + finally { + if (!success) + rollbackCurrentTx(); + } + } + + /** {@inheritDoc} */ + @Override protected boolean update(Object key, Object val) throws CacheException { + return false; + } + + /** {@inheritDoc} */ + @Override protected boolean afterUpdate(Object key, Object val, SoftLock lock) throws CacheException { + boolean success = false; + boolean res = false; + + try { + TxContext ctx = txCtx.get(); + + if (ctx != null) { + cache.put(key, val); + + unlock(ctx, key); + + res = true; + } + + success = true; + + return res; + } + catch (Exception e) { + throw new CacheException(e); + } + finally { + if (!success) + rollbackCurrentTx(); + } + } + + /** {@inheritDoc} */ + @Override protected boolean insert(Object key, Object val) throws CacheException { + return false; + } + + /** {@inheritDoc} */ + @Override protected boolean afterInsert(Object key, Object val) throws CacheException { + boolean success = false; + + try { + cache.put(key, val); + + success = true; + + return true; + } + catch (IgniteCheckedException e) { + throw new CacheException(e); + } + finally { + if (!success) + rollbackCurrentTx(); + } + } + + /** {@inheritDoc} */ + @Override protected void remove(Object key) throws CacheException { + boolean success = false; + + try { + TxContext ctx = txCtx.get(); + + if (ctx != null) + cache.remove(key); + + success = true; + } + catch (IgniteCheckedException e) { + throw new CacheException(e); + } + finally { + if (!success) + rollbackCurrentTx(); + } + } + + /** + * + * @param ctx Transaction context. + * @param key Key. + * @throws CacheException If failed. + */ + private void unlock(TxContext ctx, Object key) throws CacheException { + if (ctx.unlocked(key)) { // Finish transaction if last key is unlocked. + txCtx.remove(); + + GridNearTxLocal tx = cache.tx(); + + assert tx != null; + + try { + tx.proxy().commit(); + } + finally { + tx.proxy().close(); + } + + assert cache.tx() == null; + } + } + + /** + * Roll backs current transaction. + */ + private void rollbackCurrentTx() { + try { + TxContext ctx = txCtx.get(); + + if (ctx != null) { + txCtx.remove(); + + GridNearTxLocal tx = cache.tx(); + + if (tx != null) + tx.proxy().rollback(); + } + } + catch (IgniteException e) { + log.error("Failed to rollback cache transaction.", e); + } + } + + /** + * @param key Key. + * @throws IgniteCheckedException If failed. + */ + private void lockKey(Object key) throws IgniteCheckedException { + if (cache.tx() == null) + cache.txStart(PESSIMISTIC, REPEATABLE_READ); + + cache.get(key); // Acquire distributed lock. + } + + /** + * Information about updates done during single database transaction. + */ + @SuppressWarnings("TypeMayBeWeakened") + private static class TxContext { + /** */ + private Set<Object> locked = new GridLeanSet<>(); + + /** + * Marks key as locked. + * + * @param key Key. + */ + void locked(Object key) { + locked.add(key); + } + + /** + * Marks key as unlocked. + * + * @param key Key. + * @return {@code True} if last locked key was unlocked. + */ + boolean unlocked(Object key) { + locked.remove(key); + + return locked.isEmpty(); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/7102d532/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateRegion.java ---------------------------------------------------------------------- diff --git a/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateRegion.java b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateRegion.java new file mode 100644 index 0000000..11a96d0 --- /dev/null +++ b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateRegion.java @@ -0,0 +1,99 @@ +/* + * 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 java.util.Map; +import org.apache.ignite.Ignite; +import org.hibernate.cache.CacheException; +import org.hibernate.cache.spi.Region; + +/** + * Implementation of {@link Region}. This interface defines base contract for all L2 cache regions. + */ +public class HibernateRegion implements Region { + /** */ + protected final HibernateRegionFactory factory; + + /** */ + private final String name; + + /** Cache instance. */ + protected final HibernateCacheProxy cache; + + /** Grid instance. */ + protected Ignite ignite; + + /** + * @param factory Region factory. + * @param name Region name. + * @param ignite Grid. + * @param cache Region cache. + */ + public HibernateRegion(HibernateRegionFactory factory, String name, Ignite ignite, HibernateCacheProxy cache) { + this.factory = factory; + this.name = name; + this.ignite = ignite; + this.cache = cache; + } + + /** {@inheritDoc} */ + @Override public String getName() { + return name; + } + + /** {@inheritDoc} */ + @Override public void destroy() throws CacheException { + // No-op. + } + + /** {@inheritDoc} */ + @Override public boolean contains(Object key) { + return cache.containsKey(key); + } + + /** {@inheritDoc} */ + @Override public long getSizeInMemory() { + return -1; + } + + /** {@inheritDoc} */ + @Override public long getElementCountInMemory() { + return cache.size(); + } + + /** {@inheritDoc} */ + @Override public long getElementCountOnDisk() { + return -1; + } + + /** {@inheritDoc} */ + @Override public Map toMap() { + return Collections.emptyMap(); + } + + /** {@inheritDoc} */ + @Override public long nextTimestamp() { + return System.currentTimeMillis(); + } + + /** {@inheritDoc} */ + @Override public int getTimeout() { + return 0; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/7102d532/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateRegionFactory.java ---------------------------------------------------------------------- diff --git a/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateRegionFactory.java b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateRegionFactory.java new file mode 100644 index 0000000..0cf03d7 --- /dev/null +++ b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateRegionFactory.java @@ -0,0 +1,255 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.cache.hibernate; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteException; +import org.apache.ignite.IgniteLogger; +import org.apache.ignite.Ignition; +import org.apache.ignite.internal.IgniteKernal; +import org.apache.ignite.internal.processors.cache.IgniteInternalCache; +import org.apache.ignite.internal.util.typedef.G; +import org.hibernate.boot.spi.SessionFactoryOptions; +import org.hibernate.cache.CacheException; +import org.hibernate.cache.spi.CacheDataDescription; +import org.hibernate.cache.spi.CollectionRegion; +import org.hibernate.cache.spi.EntityRegion; +import org.hibernate.cache.spi.NaturalIdRegion; +import org.hibernate.cache.spi.QueryResultsRegion; +import org.hibernate.cache.spi.RegionFactory; +import org.hibernate.cache.spi.TimestampsRegion; +import org.hibernate.cache.spi.access.AccessType; + +import static org.hibernate.cache.spi.access.AccessType.NONSTRICT_READ_WRITE; + +/** + * Hibernate L2 cache region factory. + * <p> + * Following Hibernate settings should be specified to enable second level cache and to use this + * region factory for caching: + * <pre name="code" class="brush: xml; gutter: false;"> + * hibernate.cache.use_second_level_cache=true + * hibernate.cache.region.factory_class=org.apache.ignite.cache.hibernate.HibernateRegionFactory + * </pre> + * Note that before region factory is started you need to start properly configured Ignite node in the same JVM. + * For example to start Ignite node one of loader provided in {@code org.apache.ignite.grid.startup} package can be used. + * <p> + * Name of Ignite instance to be used for region factory must be specified as following Hibernate property: + * <pre name="code" class="brush: xml; gutter: false;"> + * org.apache.ignite.hibernate.ignite_instance_name=<Ignite instance name> + * </pre> + * Each Hibernate cache region must be associated with some {@link IgniteInternalCache}, by default it is assumed that + * for each cache region there is a {@link IgniteInternalCache} with the same name. Also it is possible to define + * region to cache mapping using properties with prefix {@code org.apache.ignite.hibernate.region_cache}. + * For example if for region with name "region1" cache with name "cache1" should be used then following + * Hibernate property should be specified: + * <pre name="code" class="brush: xml; gutter: false;"> + * org.apache.ignite.hibernate.region_cache.region1=cache1 + * </pre> + */ +public class HibernateRegionFactory implements RegionFactory { + /** */ + private static final long serialVersionUID = 0L; + + /** + * Hibernate L2 cache grid name property name. + * + * @deprecated Use {@link #IGNITE_INSTANCE_NAME_PROPERTY}. + * If {@link #IGNITE_INSTANCE_NAME_PROPERTY} is specified it takes precedence. + */ + @Deprecated + public static final String GRID_NAME_PROPERTY = "org.apache.ignite.hibernate.grid_name"; + + /** Hibernate L2 cache Ignite instance name property name. */ + public static final String IGNITE_INSTANCE_NAME_PROPERTY = "org.apache.ignite.hibernate.ignite_instance_name"; + + /** Default cache property name. */ + public static final String DFLT_CACHE_NAME_PROPERTY = "org.apache.ignite.hibernate.default_cache"; + + /** Property prefix used to specify region name to cache name mapping. */ + public static final String REGION_CACHE_PROPERTY = "org.apache.ignite.hibernate.region_cache."; + + /** */ + public static final String DFLT_ACCESS_TYPE_PROPERTY = "org.apache.ignite.hibernate.default_access_type"; + + /** */ + public static final String GRID_CONFIG_PROPERTY = "org.apache.ignite.hibernate.grid_config"; + + /** Grid providing caches. */ + private Ignite ignite; + + /** Default cache. */ + private HibernateCacheProxy dfltCache; + + /** Default region access type. */ + private AccessType dfltAccessType; + + /** Region name to cache name mapping. */ + private final Map<String, String> regionCaches = new HashMap<>(); + + /** Map needed to provide the same transaction context for different regions. */ + private final ThreadLocal threadLoc = new ThreadLocal(); + + /** Key transformer. */ + private final HibernateKeyTransformer hibernate4transformer = new HibernateKeyTransformer() { + @Override public Object transform(Object key) { + return key; + } + }; + + /** {@inheritDoc} */ + @Override public void start(SessionFactoryOptions settings, Properties props) throws CacheException { + String gridCfg = props.getProperty(GRID_CONFIG_PROPERTY); + String igniteInstanceName = props.getProperty(IGNITE_INSTANCE_NAME_PROPERTY); + + if (igniteInstanceName == null) + igniteInstanceName = props.getProperty(GRID_NAME_PROPERTY); + + if (gridCfg != null) { + try { + ignite = G.start(gridCfg); + } + catch (IgniteException e) { + throw new CacheException(e); + } + } + else + ignite = Ignition.ignite(igniteInstanceName); + + String accessType = props.getProperty(DFLT_ACCESS_TYPE_PROPERTY, NONSTRICT_READ_WRITE.name()); + + dfltAccessType = AccessType.valueOf(accessType); + + for (Map.Entry<Object, Object> prop : props.entrySet()) { + String key = prop.getKey().toString(); + + if (key.startsWith(REGION_CACHE_PROPERTY)) { + String regionName = key.substring(REGION_CACHE_PROPERTY.length()); + + String cacheName = prop.getValue().toString(); + + if (((IgniteKernal)ignite).getCache(cacheName) == null) + throw new CacheException("Cache '" + cacheName + "' specified for region '" + regionName + "' " + + "is not configured."); + + regionCaches.put(regionName, cacheName); + } + } + + String dfltCacheName = props.getProperty(DFLT_CACHE_NAME_PROPERTY); + + if (dfltCacheName != null) { + IgniteInternalCache<Object, Object> dfltCache = ((IgniteKernal)ignite).getCache(dfltCacheName); + + if (dfltCache == null) + throw new CacheException("Cache specified as default is not configured: " + dfltCacheName); + + this.dfltCache = new HibernateCacheProxy(dfltCache, hibernate4transformer); + } + + IgniteLogger log = ignite.log().getLogger(HibernateRegionFactory.class); + + if (log.isDebugEnabled()) + log.debug("HibernateRegionFactory started [igniteInstanceName=" + igniteInstanceName + ']'); + } + + /** {@inheritDoc} */ + @Override public void stop() { + // No-op. + } + + /** {@inheritDoc} */ + @Override public boolean isMinimalPutsEnabledByDefault() { + return false; + } + + /** {@inheritDoc} */ + @Override public AccessType getDefaultAccessType() { + return dfltAccessType; + } + + /** {@inheritDoc} */ + @Override public long nextTimestamp() { + return System.currentTimeMillis(); + } + + /** {@inheritDoc} */ + @Override public EntityRegion buildEntityRegion(String regionName, Properties props, CacheDataDescription metadata) + throws CacheException { + return new HibernateEntityRegion(this, regionName, ignite, regionCache(regionName), metadata); + } + + /** {@inheritDoc} */ + @Override public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties props, + CacheDataDescription metadata) throws CacheException { + return new HibernateNaturalIdRegion(this, regionName, ignite, regionCache(regionName), metadata); + } + + /** {@inheritDoc} */ + @Override public CollectionRegion buildCollectionRegion(String regionName, Properties props, + CacheDataDescription metadata) throws CacheException { + return new HibernateCollectionRegion(this, regionName, ignite, regionCache(regionName), metadata); + } + + /** {@inheritDoc} */ + @Override public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties props) + throws CacheException { + return new HibernateQueryResultsRegion(this, regionName, ignite, regionCache(regionName)); + } + + /** {@inheritDoc} */ + @Override public TimestampsRegion buildTimestampsRegion(String regionName, Properties props) throws CacheException { + return new HibernateTimestampsRegion(this, regionName, ignite, regionCache(regionName)); + } + + /** + * Reuse same thread local for the same cache across different regions. + * + * @param cacheName Cache name. + * @return Thread local instance used to track updates done during one Hibernate transaction. + */ + ThreadLocal threadLocalForCache(String cacheName) { + return threadLoc; + } + + /** + * @param regionName L2 cache region name. + * @return Cache for given region. + * @throws CacheException If cache for given region is not configured. + */ + private HibernateCacheProxy regionCache(String regionName) throws CacheException { + String cacheName = regionCaches.get(regionName); + + if (cacheName == null) { + if (dfltCache != null) + return dfltCache; + + cacheName = regionName; + } + + IgniteInternalCache<Object, Object> cache = ((IgniteKernal)ignite).getCache(cacheName); + + if (cache == null) + throw new CacheException("Cache '" + cacheName + "' for region '" + regionName + "' is not configured."); + + return new HibernateCacheProxy(cache, hibernate4transformer); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/7102d532/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateTimestampsRegion.java ---------------------------------------------------------------------- diff --git a/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateTimestampsRegion.java b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateTimestampsRegion.java new file mode 100644 index 0000000..8b4c243 --- /dev/null +++ b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateTimestampsRegion.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.cache.hibernate; + +import org.apache.ignite.Ignite; +import org.hibernate.cache.spi.TimestampsRegion; + +/** + * Implementation of {@link TimestampsRegion}. This region is automatically created when query + * caching is enabled and it holds most recent updates timestamps to queryable tables. + * Name of timestamps region is {@code "org.hibernate.cache.spi.UpdateTimestampsCache"}. + */ +public class HibernateTimestampsRegion extends HibernateGeneralDataRegion implements TimestampsRegion { + /** + * @param factory Region factory. + * @param name Region name. + * @param ignite Grid. + * @param cache Region cache. + */ + public HibernateTimestampsRegion(HibernateRegionFactory factory, String name, + Ignite ignite, HibernateCacheProxy cache) { + super(factory, name, ignite, cache); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/7102d532/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateTransactionalAccessStrategy.java ---------------------------------------------------------------------- diff --git a/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateTransactionalAccessStrategy.java b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateTransactionalAccessStrategy.java new file mode 100644 index 0000000..ca52849 --- /dev/null +++ b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateTransactionalAccessStrategy.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.cache.hibernate; + +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.internal.processors.cache.IgniteInternalCache; +import org.hibernate.cache.CacheException; +import org.hibernate.cache.spi.access.AccessType; +import org.hibernate.cache.spi.access.SoftLock; +import org.jetbrains.annotations.Nullable; + +/** + * Implementation of {@link AccessType#TRANSACTIONAL} cache access strategy. + * <p> + * It is supposed that this strategy is used in JTA environment and Hibernate and + * {@link IgniteInternalCache} corresponding to the L2 cache region are configured to use the same transaction manager. + * <p> + * Configuration of L2 cache and per-entity cache access strategy can be set in the + * Hibernate configuration file: + * <pre name="code" class="xml"> + * <hibernate-configuration> + * <!-- Enable L2 cache. --> + * <property name="cache.use_second_level_cache">true</property> + * + * <!-- Use Ignite as L2 cache provider. --> + * <property name="cache.region.factory_class">org.apache.ignite.cache.hibernate.HibernateRegionFactory</property> + * + * <!-- Specify entity. --> + * <mapping class="com.example.Entity"/> + * + * <!-- Enable L2 cache with transactional access strategy for entity. --> + * <class-cache class="com.example.Entity" usage="transactional"/> + * </hibernate-configuration> + * </pre> + * Also cache access strategy can be set using annotations: + * <pre name="code" class="java"> + * @javax.persistence.Entity + * @javax.persistence.Cacheable + * @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) + * public class Entity { ... } + * </pre> + */ +public class HibernateTransactionalAccessStrategy extends HibernateAccessStrategyAdapter { + /** + * @param ignite Grid. + * @param cache Cache. + */ + public HibernateTransactionalAccessStrategy(Ignite ignite, HibernateCacheProxy cache) { + super(ignite, cache); + } + + /** {@inheritDoc} */ + @Nullable @Override protected Object get(Object key) throws CacheException { + try { + return cache.get(key); + } + catch (IgniteCheckedException e) { + throw new CacheException(e); + } + } + + /** {@inheritDoc} */ + @Override protected void putFromLoad(Object key, Object val) throws CacheException { + try { + cache.put(key, val); + } + catch (IgniteCheckedException e) { + throw new CacheException(e); + } + } + + /** {@inheritDoc} */ + @Override protected SoftLock lock(Object key) throws CacheException { + return null; + } + + /** {@inheritDoc} */ + @Override protected void unlock(Object key, SoftLock lock) throws CacheException { + // No-op. + } + + /** {@inheritDoc} */ + @Override protected boolean update(Object key, Object val) throws CacheException { + try { + cache.put(key, val); + + return true; + } + catch (IgniteCheckedException e) { + throw new CacheException(e); + } + } + + /** {@inheritDoc} */ + @Override protected boolean afterUpdate(Object key, Object val, SoftLock lock) throws CacheException { + return false; + } + + /** {@inheritDoc} */ + @Override protected boolean insert(Object key, Object val) throws CacheException { + try { + cache.put(key, val); + + return true; + } + catch (IgniteCheckedException e) { + throw new CacheException(e); + } + } + + /** {@inheritDoc} */ + @Override protected boolean afterInsert(Object key, Object val) throws CacheException { + return false; + } + + /** {@inheritDoc} */ + @Override protected void remove(Object key) throws CacheException { + try { + cache.remove(key); + } + catch (IgniteCheckedException e) { + throw new CacheException(e); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/7102d532/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateTransactionalDataRegion.java ---------------------------------------------------------------------- diff --git a/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateTransactionalDataRegion.java b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateTransactionalDataRegion.java new file mode 100644 index 0000000..581076a --- /dev/null +++ b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/HibernateTransactionalDataRegion.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.cache.hibernate; + +import org.apache.ignite.Ignite; +import org.apache.ignite.configuration.TransactionConfiguration; +import org.hibernate.cache.CacheException; +import org.hibernate.cache.spi.CacheDataDescription; +import org.hibernate.cache.spi.CollectionRegion; +import org.hibernate.cache.spi.EntityRegion; +import org.hibernate.cache.spi.NaturalIdRegion; +import org.hibernate.cache.spi.TransactionalDataRegion; +import org.hibernate.cache.spi.access.AccessType; + +import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL; + +/** + * Implementation of {@link TransactionalDataRegion} (transactional means that + * data in the region is updated in connection with database transaction). + * This interface defines base contract for {@link EntityRegion}, {@link CollectionRegion} + * and {@link NaturalIdRegion}. + */ +public class HibernateTransactionalDataRegion extends HibernateRegion implements TransactionalDataRegion { + /** */ + private final CacheDataDescription dataDesc; + + /** + * @param factory Region factory. + * @param name Region name. + * @param ignite Grid. + * @param cache Region cache. + * @param dataDesc Region data description. + */ + public HibernateTransactionalDataRegion(HibernateRegionFactory factory, String name, + Ignite ignite, HibernateCacheProxy cache, CacheDataDescription dataDesc) { + super(factory, name, ignite, cache); + + this.dataDesc = dataDesc; + } + + /** {@inheritDoc} */ + @Override public boolean isTransactionAware() { + return false; // This method is not used by Hibernate. + } + + /** {@inheritDoc} */ + @Override public CacheDataDescription getCacheDataDescription() { + return dataDesc; + } + + /** + * @param accessType Hibernate L2 cache access type. + * @return Access strategy for given access type. + */ + protected HibernateAccessStrategyAdapter createAccessStrategy(AccessType accessType) { + switch (accessType) { + case READ_ONLY: + return new HibernateReadOnlyAccessStrategy(ignite, cache); + + case NONSTRICT_READ_WRITE: + return new HibernateNonStrictAccessStrategy(ignite, cache, factory.threadLocalForCache(cache.name())); + + case READ_WRITE: + if (cache.configuration().getAtomicityMode() != TRANSACTIONAL) + throw new CacheException("Hibernate READ-WRITE access strategy must have Ignite cache with " + + "'TRANSACTIONAL' atomicity mode: " + cache.name()); + + return new HibernateReadWriteAccessStrategy(ignite, cache, factory.threadLocalForCache(cache.name())); + + case TRANSACTIONAL: + if (cache.configuration().getAtomicityMode() != TRANSACTIONAL) + throw new CacheException("Hibernate TRANSACTIONAL access strategy must have Ignite cache with " + + "'TRANSACTIONAL' atomicity mode: " + cache.name()); + + TransactionConfiguration txCfg = ignite.configuration().getTransactionConfiguration(); + + if (txCfg == null || + (txCfg.getTxManagerFactory() == null + && txCfg.getTxManagerLookupClassName() == null + && cache.configuration().getTransactionManagerLookupClassName() == null)) { + throw new CacheException("Hibernate TRANSACTIONAL access strategy must have Ignite with " + + "Factory<TransactionManager> configured (see IgniteConfiguration." + + "getTransactionConfiguration().setTxManagerFactory()): " + cache.name()); + } + + return new HibernateTransactionalAccessStrategy(ignite, cache); + + default: + throw new IllegalArgumentException("Unknown Hibernate access type: " + accessType); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/7102d532/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/package-info.java ---------------------------------------------------------------------- diff --git a/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/package-info.java b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/package-info.java new file mode 100644 index 0000000..1179aec --- /dev/null +++ b/modules/hibernate5/src/main/java/org/apache/ignite/cache/hibernate/package-info.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * <!-- Package description. --> + * Contains implementation of Hibernate L2 cache. Refer to + * <i>org.apache.ignite.examples.datagrid.hibernate.HibernateL2CacheExample</i> for more information on how to + * configure and use Ignite with Hibernate. + */ +package org.apache.ignite.cache.hibernate; \ No newline at end of file
