Once the GemFire cache and regions have been configured they can
injected and used inside application objects. This chapter describes the
integration with Spring's transaction management functionality and
DaoException
hierarchy. It also covers support for
dependency injection of GemFire managed objects.
Using a new data access technology requires not just accommodating
to a new API but also handling exceptions specific to that technology. To
accommodate this case, Spring Framework provides a technology agnostic,
consistent exception hierarchy
that abstracts one from proprietary (and usually checked) exceptions to a
set of focused runtime exceptions. As mentioned in the Spring Framework
documentation, exception
translation can be applied transparently to your data access
objects through the use of the @Repository
annotation
and AOP by defining a PersistenceExceptionTranslationPostProcessor bean.
The same exception translation functionality is enabled when using Gemfire
as long as at least a CacheFactoryBean
is declared.
The Cache
factory acts as an exception
translator which is automatically detected by the Spring infrastructure
and used accordingly.
As with many other high-level abstractions provided by the Spring
Framework and related projects, Spring GemFire provides a
template that plays a central role when working with
the GemFire API. The class provides several one-liner
methods, for popular operations but also the ability to
execute code against the native GemFire API without
having to deal with exceptions for example through the
GemfireCallback
.
The template class requires a GemFire
Region
instance and once configured is
thread-safe and should be reused across multiple classes:
<bean id="gemfireTemplate" class="org.springframework.data.gemfire.GemfireTemplate" p:region-ref="someRegion"/>
Once the template is configured, one can use it alongside
GemfireCallback
to work directly with the
GemFire Region
, without having to deal with
checked exceptions, threading or resource management concerns:
template.execute(new GemfireCallback<Iterable<String>>() { public Iterable<String> doInGemfire(Region reg) throws GemFireCheckedException, GemFireException { // working against a Region of String Region<String, String> region = reg; region.put("1", "one"); region.put("3", "three"); return region.query("length < 5"); } });
For accessing the full power of the GemFire query language, one can use the find
and findUnique
which, as oppose to the
query
method, can execute queries inside across multiple regions, execute projections just to name a few features. find
method should be
used when the query selects multiple items (through SelectResults
) and the latter, findUnique
, as the name suggests when only one object is returned.
Since 1.1, Spring GemFire provides an implementation for Spring 3.1
cache abstraction through the
org.springframework.data.gemfire.support
package. To use GemFire
as a backing implementation, simply add GemfireCacheManager
to your configuration:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:gfe="http://www.springframework.org/schema/gemfire" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <!-- turn on declarative caching --> <cache:annotation-driven /> <gfe:cache id="gemfire-cache" /> <!-- declare GemFire Cache Manager --> <bean id="cacheManager" class="org.springframework.data.gemfire.support.GemfireCacheManager" p:cache-ref="gemfire-cache"> </beans>
One of the most popular features of Spring Framework is transaction management. If you are not familiar with it, we strongly recommend looking into it as it offers a consistent programming model that works transparently across multiple APIs that can be configured either programmatically or declaratively (the most popular choice).
For GemFire, SGF provides a dedicated, per-cache, transaction
manager that once declared, allows actions on the
Region
s to be grouped and executed
atomically through Spring:
<gfe:transaction-manager id="tx-manager" cache-ref="cache"/>
![]() | Note |
---|---|
The example above can be simplified even more by eliminating the cache-ref attribute if the GemFire cache is defined under the default
name gemfire-cache . As with the other SGF namespace elements, if the cache name is not configured, the aforementioned naming convention will used.
Additionally, the transaction manager name, if not specified is gemfire-transaction-manager . |
or if you prefer bean
declarations:
<bean id="tx-manager" class="org.springframework.data.gemfire.GemfireTransactionManager" p:cache-ref="cache"/>
Note that currently GemFire supports optimistic transactions with
read committed isolation. Furthermore, to guarantee
this isolation, developers should avoid making
in-place changes, that is manually modifying the
values present in the cache. To prevent this from happening, the
transaction manager configured the cache to use copy on
read semantics, meaning a clone of the actual value is created,
each time a read is performed. This behaviour can be disabled if needed
through the copyOnRead
property. For more information
on the semantics of the underlying GemFire transaction manager, see the
GemFire documentation.
GemFire XML configuration (usually named
cache.xml
allows user objects to
be declared as part of the fabric configuration. Usually these objects are
CacheLoader
s or other pluggable components
into GemFire. Out of the box in GemFire, each such type declared through
XML must implement the Declarable
interface
which allows arbitrary parameters to be passed to the declared class
through a Properties
instance.
In this section we describe how you can configure the pluggable
components defined in cache.xml
using Spring while
keeping your Cache/Region configuration defined in
cache.xml
This allows your pluggable components to
focus on the application logic and not the location or creation of
DataSources or other collaboration object.
However, if you are starting on a green-field project, it is
recommended that you configure Cache, Region, and other pluggable
components directly in Spring. This avoids inheriting from the
Declarable
interface or the base class
presented in this section. See the following sidebar for more information
on this approach.
As an example of configuring a
Declarable
component using Spring, consider
the following declaration (taken from the
Declarable
javadoc):
<cache-loader> <class-name>com.company.app.DBLoader</class-name> <parameter name="URL"> <string>jdbc://12.34.56.78/mydb</string> </parameter> </cache-loader>
To simplify the task of parsing, converting the parameters and
initializing the object, SGF offers a base class
(WiringDeclarableSupport
) that allows GemFire user
objects to be wired through a template bean
definition or, in case that is missing perform autowiring through the
Spring container. To take advantage of this feature, the user objects need
to extend WiringDeclarableSupport
which
automatically locates the declaring
BeanFactory
and performs wiring as part of
the initialization process.
When used WiringDeclarableSupport
tries to
first locate an existing bean definition and use that as wiring
template. Unless specified, the component class name will be used as an
implicit bean definition name. Let's see how our
DBLoader
declaration would look in that
case:
public class DBLoader extends WiringDeclarableSupport implements CacheLoader { private DataSource dataSource; public void setDataSource(DataSource ds){ this.dataSource = ds; } public Object load(LoaderHelper helper) { ... } }
<cache-loader> <class-name>com.company.app.DBLoader</class-name> <!-- no parameter is passed (use the bean implicit name that is the class name) --> </cache-loader>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="dataSource" ... /> <!-- template bean definition --> <bean id="com.company.app.DBLoader" abstract="true" p:dataSource-ref="dataSource"/> </beans>
In the scenario above, as no parameter was specified, a bean with
id/name com.company.app.DBLoader
was searched for.
The found bean definition is used as a template for wiring the instance
created by GemFire. For cases where the bean name uses a different
convention, one can pass in the bean-name
parameter
in the GemFire configuration:
<cache-loader> <class-name>com.company.app.DBLoader</class-name> <!-- pass the bean definition template name as parameter --> <parameter name="bean-name"> <string>template-bean</string> </parameter> </cache-loader>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="dataSource" ... /> <!-- template bean definition --> <bean id="template-bean" abstract="true" p:dataSource-ref="dataSource"/> </beans>
![]() | Note |
---|---|
The template bean definitions do not have to be declared in XML - any format is allowed (Groovy, annotations, etc..). |
If no bean definition is found, by default,
WiringDeclarableSupport
will autowire
the declaring instance. This means that unless any dependency injection
metadata is offered by the instance, the container
will find the object setters and try to automatically satisfy these
dependencies. However, one can also use JDK 5 annotations to provide
additional information to the auto-wiring process. We strongly recommend
reading the dedicated chapter
in the Spring documentation for more information on the supported
annotations and enabling factors.
For example, the hypothetical DBLoader
declaration above can be injected with a Spring-configured
DataSource in the following way:
public class DBLoader extends WiringDeclarableSupport implements CacheLoader { // use annotations to 'mark' the needed dependencies @javax.inject.Inject private DataSource dataSource; public Object load(LoaderHelper helper) { ... } }
<cache-loader> <class-name>com.company.app.DBLoader</class-name> <!-- no need to declare any parameters anymore since the class is auto-wired --> </cache-loader>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- enable annotation processing --> <context:annotation-config/> </beans>
By using the JSR-330 annotations, the cache loader code has been
simplified since the location and creation of the DataSource has been
externalized and the user code is concerned only with the loading
process. The DataSource
might be
transactional, created lazily, shared between multiple objects or
retrieved from JNDI - these aspects can be easily configured and changed
through the Spring container without touching the
DBLoader
code.