原标题:Spring认证中国教育管理中心-Apache Geode 的 Spring 数据教程十八(Spring中国教育管理中心)
7. 使用 Apache Geode API
一旦配置了 Apache Geode Cache 和 Regions,它们就可以被注入并在应用程序对象中使用。本章描述了与 Spring 的事务管理功能和 DAO 异常层次结构的集成。本章还介绍了对 Apache Geode 管理对象的依赖注入的支持。
7.1.Gemfire模板
与 Spring 提供的许多其他高级抽象一样,Spring Data for Apache Geode 提供了一个模板 来简化 Apache Geode 数据访问操作。该类提供了几种包含常见 Region 操作的方法,但也提供了针对本机 Apache Geode API执行代码的功能,而无需使用GemfireCallback.
模板类需要一个 Apache Geode Region,一旦配置,就是线程安全的,并且可以跨多个应用程序类重用:
<bean id="gemfireTemplate" class="org.springframework.data.gemfire.GemfireTemplate" p:region-ref="SomeRegion"/>
配置模板后,开发人员可以将其与GemfireCallbackApache Geode一起使用,Region而无需处理检查异常、线程或资源管理问题:
template.execute(new GemfireCallback<Iterable<String>>() {
public Iterable<String> doInGemfire(Region region)
throws GemFireCheckedException, GemFireException {
Region<String, String> localRegion = (Region<String, String>) region;
localRegion.put("1", "one");
localRegion.put("3", "three");
return localRegion.query("length < 5");
}
});
为了充分发挥Apache Geode查询语言的威力,开发者可以使用findandfindUnique 方法,与方法相比query,可以跨多个Region执行查询,执行投影等。
find当查询选择多个项目(通过SelectResults)时应该使用该方法,而后者, findUnique顾名思义,当只返回一个对象时。
7.2.异常翻译
使用新的数据访问技术不仅需要适应新的 API,还需要处理特定于该技术的异常。
为了适应异常处理的情况,
Spring 框架
提供了一个技术无关且一致的 异常层次结构 ,将应用程序从专有的、通常是“检查的”异常抽象为一组集中的运行时异常。
正如
Spring Framework 的
文档中所提到
的
,通过定义bean 使用注释和 AOP , 异常转换可以透明地应用于您的数据访问对象 (DAO) 。使用 Apache Geode 时,只要声明了相同的异常转换功能,就会启用相同的异常转换功能,例如使用 a 或声明,它充当异常转换器并由 Spring 基础设施自动检测并相应地使用。@
RepositoryPersistenceExceptionTranslationPostProcessorCacheFactoryBeangfe:cache/gfe:client-cache
7.3.本地、缓存事务管理
Spring 框架
最流行的特性之一是 事务管理。
如果你不熟悉Spring的事务抽象那么我们强烈建议您 阅读 有关
Spring的事务管理
的基础设施,因为它提供了一个一致的
编程模型,
跨多个API透明地工作,并且可以通过编程方式或声明(最流行的选择)配置。
对于 Apache Geode,Apache Geode 的 Spring Data 提供了一个专用的每个缓存
PlatformTransactionManager,一旦声明,就允许通过 Spring 原子地执行 Region 操作:
使用 XML 启用事务管理
<gfe:transaction-manager id="txManager" cache-ref="myCache"/>
上面的例子中,可以通过消除甚至进一步简化cache-ref,如果在Apache的Geode缓存下默认名称定义的属性,gemfireCache。与其他 Spring Data for Apache Geode 命名空间元素一样,如果未配置缓存 bean 名称,则将使用上述命名约定。此外,如果未明确指定,事务管理器名称为“gemfireTransactionManager”。
目前,Apache Geode 支持具有读提交隔离的乐观事务。此外,为了保证这种隔离,开发人员应避免进行手动修改缓存中存在的值的就地更改。为了防止这种情况发生,事务管理器默认将缓存配置为在读取时使用复制语义,这意味着每次执行读取时都会创建实际值的克隆。如果需要,可以通过copyOnRead属性禁用此行为。
由于在启用读取时复制时会复制给定键的值,因此您必须随后调用 Region.put(key, value)inorder 以事务方式更新值。
有关底层 Geode 事务管理器的语义和行为的更多信息,请参阅 Geode CacheTransactionManager Javadoc 。
公共接口CacheTransactionManager
CacheTransactionManager 接口允许应用程序在每个Cache基础上管理事务 。
GemFire 事务的生命周期从开始操作开始。生命周期以提交或回滚操作结束。在开始和提交/回滚之间通常是 Region操作。通常,那些创建、销毁、失效或更新的操作 Region.Entry被认为是事务性的,即它们修改事务状态。
一个 GemFire 事务可能涉及对多个区域的操作,每个区域可能具有不同的属性。
在本地 VM 中调用 GemFire 事务及其操作时,根据每个参与区域的属性,在提交时将生成的事务状态分发到其他 VM。
一个事务最多只能有一个与之关联的线程,相反,一个线程在任何给定时间只能对一个事务进行操作。子线程不会继承现有事务。
以下每个方法都在当前线程上运行。CacheClosedException如果 Cache 关闭,则所有方法都会抛出 。
GemFire Transactions 目前只支持 Read Committed 隔离。此外,它们是乐观事务,因为写锁定和冲突检查是作为提交操作的一部分执行的。
为了保证读已提交隔离,避免进行“就地”更改,因为此类更改将被其他事务“看到”并破坏读已提交隔离保证。例如
CacheTransactionManager txMgr = cache.getCacheTransactionManager();
txMgr.begin();
StringBuffer s = (StringBuffer) r.get("stringBuf");
s.append("提交前看到的更改。未提交读取!");
r.put("stringBuf", s);
txMgr.commit();
为了帮助创建副本,提供了“读取时复制”Cache属性和 CopyHelper.copy(T)方法。以下是使用该CopyHelper.copy方法的 Read Committed 安全示例。
CacheTransactionManager txMgr = cache.getCacheTransactionManager();
txMgr.begin();
Object o = r.get("stringBuf");
StringBuffer s = (StringBuffer) CopyHelper.copy(o);
s.append("提交前未见更改。已提交读取。");
r.put("stringBuf", s);
txMgr.commit();
需要注意的是,创建副本会对性能和内存消耗产生负面影响。
支持分区区域、分布式无确认和分布式确认区域(参见 AttributesFactory范围)。对于这两个范围,强制执行一致的配置(每个 VM)。
全局区域、客户端区域(参见 org.apache.geode.cache.client 包)和持久区域(参见 参考资料DiskWriteAttributes)不支持事务。
当 PartitionedRegions 参与到一个事务中时,事务中的所有数据必须共同位于一个数据节点上。有关在分区区域中使用事务的详细信息,请参阅 GemFire 开发人员指南。
7.4.全局,JTA 事务管理
Apache Geode 也可以参与基于 JTA 的全局事务,例如由 Java EE 应用服务器(例如 WebSphere 应用服务器(WAS))使用容器管理事务(CMT)和其他 JTA 资源管理的事务。
但是,与许多其他 JTA“兼容”资源(例如,像 ActiveMQ 这样的 JMS 消息代理)不同,Apache Geode不是 XA 兼容资源。因此,Apache Geode 必须被定位为JTA 事务(
准备阶段
)中的“
最后一个资源
”,因为它没有实现两阶段提交协议,或者更确切地说,它不处理分布式事务。
许多支持 CMT 的托管环境在基于 JTA 的事务中维护对“
最后资源
”、非 XA 兼容资源的支持,尽管 JTA 规范实际上并不需要它。有关不符合 XA 的“
最后资源
”的含义的更多信息可以在 Red Hat 的文档中找到。事实上,Red Hat 的 JBoss 项目Narayana就是这样一种 LGPL 开源实现。
Narayana
将此称为“
最后资源提交优化
”(LRCO)。可以在此处找到更多详细信息。
但是,无论您是在具有支持“
最后资源
”的开源 JTA 事务管理实现的独立环境中使用 Apache Geode ,还是在托管环境(例如 Java EE AS,如 WAS)中使用 Apache Geode,Apache Geode 的 Spring Data 都能满足您的需求。
您必须完成一系列步骤才能在涉及 1 个以上事务资源的 JTA 事务中正确使用 Apache Geode 作为“
最后一个资源
”。此外,在这种安排中只能有 1 个非 XA 兼容资源(例如 Apache Geode)。
- 首先,您必须在此处完成 Apache Geode 文档中的步骤 1-4 。
上面的 #1 独立于您的 Spring [Boot] 和/或 [Data for Apache Geode] 应用程序,并且必须成功完成。
- 参考 Apache Geode文档中的第 5 步,Spring Data for Apache Geode's Annotation support 将尝试 在使用注释时为您设置GemFireCache,copyOnRead属性@
EnableGemFireAsLastResource。
但是,如果 SDG 在这方面的自动配置不成功,那么您必须copy-on-read 在gfe:cache或gfe:client-cacheXML 元素中显式设置该属性或将JavaConfigcopyOnRead中的CacheFactoryBean类的属性设置为true。例如:
ClientCache XML:
使用 XML(客户端)设置读取时复制
<gfe:client-cache ... copy-on-read="true"/>
ClientCache
Java配置
:
使用 JavaConfig(客户端)设置 copyOnRead
@Bean
ClientCacheFactoryBean gemfireCache() {
ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean();
gemfireCache.setCopyOnRead(true);
return gemfireCache;
}
对等CacheXML:
使用 XML(服务器)设置读取时复制
<gfe:cache ... copy-on-read="true"/>
对等Cache
JavaConfig
:
使用 JavaConfig(服务器)设置 copyOnRead
@Bean
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setCopyOnRead(true);
return gemfireCache;
}
显式设置copy-on-read属性或copyOnRead属性实际上是没有必要的。启用事务管理需要在读取时进行复制。
- 此时,您可以跳过Apache Geode文档中的步骤 6-8 ,让
Spring Data Geode
发挥其魔力。您需要做的就是@Configuration使用 Spring Data for Apache Geode 的新 @
EnableGemFireAsLastResource注解来注解您的 Spring类,并且 Spring 的 事务管理基础设施和 Spring Data for Apache Geode 的 @EnableGemFireAsLastResource注解配置的组合就可以解决问题。
配置看起来像这样......
@Configuration
@EnableGemFireAsLastResource
@EnableTransactionManagement(order = 1)
class GeodeConfiguration {
...
}
唯一的要求是……
3.1)@
EnableGemFireAsLastResource注解必须在指定@ConfigurationSpring@EnableTransactionManagement注解的同一个Spring类上声明。
3.2)注释的order属性@
EnableTransactionManagement必须显式设置为整数值,不是Integer.MAX_VALUE或Integer.MIN_VALUE(默认为Integer.MAX_VALUE)。
当然,希望你知道JtaTransactionManager 在使用 JTA 事务时你还需要配置 Spring 的。
@Bean
public JtaTransactionManager transactionManager(UserTransaction userTransaction) {
JtaTransactionManager transactionManager = new JtaTransactionManager();
transactionManager.setUserTransaction(userTransaction);
return transactionManager;
}
在节中的配置本地,缓存事务管理并没有适用于此。Spring Data for Apache Geode 的使用GemfireTransactionManager适用于“仅限本地”、缓存事务, 而不是“全局”、JTA 事务。因此,在这种情况下不配置 SDG GemfireTransactionManager。您JtaTransactionManager如上所示配置 Spring 。
有关将Spring 的事务管理与 JTA 结合使用的更多详细信息,请参见此处。
实际上,Spring Data for Apache Geode 的@
EnableGemFireAsLastResource注解导入了包含 2 个 Aspect bean 定义的配置,这些定义在事务操作期间的适当点处理 Apache Geodeo.a.g.ra.GFConnectionFactory.getConnection() 和o.a.g.ra.GFConnection.close()操作。
具体来说,正确的事件顺序如下:
jtaTransation.begin()
GFConnectionFactory.getConnection()
调用应用程序的@Transactional服务方法
无论是jtaTransaction.commit()或jtaTransaction.rollback()
最后, GFConnection.close()
这与您作为应用程序开发人员在必须自己使用 JTA API + Apache Geode API 时手动编码的方式一致,如 Apache Geode示例 所示。
值得庆幸的是,Spring 为您完成了繁重的工作,在应用适当的配置(如上所示)后,您需要做的就是:
将服务方法声明为@Transactional
@Service
class MyTransactionalService {
@Transactional
public <Return-Type> someTransactionalServiceMethod() {
// perform business logic interacting with and accessing multiple JTA resources atomically
}
...
}
PlatformTransactionManager一旦 @Transactional您的应用程序进入边界(即MyTransactionService.someTransactionalServiceMethod() 调用时),上面的#1 和#4 就会由基于Spring 的JTA 适当地处理。
#2 和 #3 由 Spring Data 处理,用于启用@
EnableGemFireAsLastResource注释的Apache Geode 的新方面。
#3 当然是你的应用程序的责任。
事实上,通过配置适当的日志记录,您将看到正确的事件序列......
事务日志输出
2017-Jun-22 11:11:37 TRACE TransactionInterceptor - Getting transaction for [example.app.service.MessageService.send]
2017-Jun-22 11:11:37 TRACE GemFireAsLastResourceConnectionAcquiringAspect - Acquiring {data-store-name} Connection
from {data-store-name} JCA ResourceAdapter registered at [gfe/jca]
2017-Jun-22 11:11:37 TRACE MessageService - PRODUCER [ Message :
[{ @type = example.app.domain.Message, id= MSG0000000000, message = SENT }],
JSON : [{"id":"MSG0000000000","message":"SENT"}] ]
2017-Jun-22 11:11:37 TRACE TransactionInterceptor - Completing transaction for [example.app.service.MessageService.send]
2017-Jun-22 11:11:37 TRACE GemFireAsLastResourceConnectionClosingAspect - Closed {data-store-name} Connection @ [Reference [...]]