diff --git a/src/main/asciidoc/reference/data.adoc b/src/main/asciidoc/reference/data.adoc index 299a1bec..9f8bf4a5 100644 --- a/src/main/asciidoc/reference/data.adoc +++ b/src/main/asciidoc/reference/data.adoc @@ -79,18 +79,19 @@ about _Spring's Transaction Management_ infrastructure as it offers a consistent transparently across multiple APIs and can be configured either programmatically or declaratively (the most popular choice). -For {data-store-name}, {sdg-name} provides a dedicated, per-cache, `PlatformTransactionManager` that, -once declared, allows Region operations to be executed atomically through Spring: +For {data-store-name}, {sdg-name} provides a dedicated, per-cache, `PlatformTransactionManager` that, once declared, +allows Region operations to be executed atomically through Spring: +.Enable Transaction Management using XML [source,xml] ---- ---- -NOTE: The example above can be simplified even further by eliminating the `cache-ref` attribute if the {data-store-name} cache -is defined under the default name, `gemfireCache`. As with the other {sdg-name} namespace elements, -if the cache bean name is not configured, the aforementioned naming convention will be used. -Additionally, the transaction manager name is "`gemfireTransactionManager`" if not explicitly specified. +NOTE: The example above can be simplified even further by eliminating the `cache-ref` attribute if the {data-store-name} +cache is defined under the default name, `gemfireCache`. As with the other {sdg-name} namespace elements, if the cache +bean name is not configured, the aforementioned naming convention will be used. Additionally, the transaction manager +name is "`gemfireTransactionManager`" if not explicitly specified. Currently, {data-store-name} supports optimistic transactions with *read committed* isolation. Furthermore, to guarantee this isolation, developers should avoid making *in-place* changes that manually modify values present in the cache. @@ -98,6 +99,9 @@ To prevent this from happening, the transaction manager configures the cache to meaning a clone of the actual value is created each time a read is performed. This behavior can be disabled if needed through the `copyOnRead` property. +Since a copy of the value for a given key is made when *copy on read* is enabled, you must subsequently call +`Region.put(key, value)` inorder for the value to be updated, transactionally. + For more information on the semantics and behavior of the underlying Geode transaction manager, please refer to the Geode {x-data-store-javadoc}/org/apache/geode/cache/CacheTransactionManager.html[CacheTransactionManager Javadoc] as well as the {x-data-store-docs}/developing/transactions/chapter_overview.html[documentation]. @@ -105,76 +109,54 @@ as well as the {x-data-store-docs}/developing/transactions/chapter_overview.html [[apis:global-transaction-management]] == Global, JTA Transaction Management -It is also possible for {data-store-name} to participate in a Global, JTA based transaction, such as a transaction managed -by an Java EE Application Server (e.g. WebSphere Application Server, a.k.a. WAS) using Container Managed Transactions +It is also possible for {data-store-name} to participate in Global, JTA-based transactions, such as a transaction +managed by an Java EE Application Server (e.g. WebSphere Application Server (WAS)) using Container Managed Transactions (CMT) along with other JTA resources. However, unlike many other JTA "compliant" resources (e.g. JMS Message Brokers like ActiveMQ), {data-store-name} is *not* an XA compliant resource. Therefore, {data-store-name} must be positioned as the "_Last Resource_" in a JTA transaction -(_prepare phase_) since it does not implement the 2-phase commit protocol, or rather does not handle -distributed transactions. +(_prepare phase_) since it does not implement the 2-phase commit protocol, or rather does not handle distributed +transactions. -Many managed environments with CMT maintain support for "_Last Resource_", non-XA compliant resources in JTA transactions -though it is not actually required in the JTA spec. More information on what a non-XA compliant, "_Last Resource_" means -can be found in Red Hat's https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/5/html/Administration_And_Configuration_Guide/lrco-overview.html[documentation]. +Many managed environments capable of CMT maintain support for "_Last Resource_", non-XA compliant resources in JTA-based +transactions, though it is not actually required in the JTA spec. More information on what a non-XA compliant, +"_Last Resource_" means can be found in Red Hat's https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/5/html/Administration_And_Configuration_Guide/lrco-overview.html[documentation]. In fact, Red Hat's JBoss project, https://narayana.io/[Narayana] is one such LGPL Open Source implementation. _Narayana_ -refers to this as "_Last Resource Commit Optimization_" (LRCO). More details can be found -https://narayana.io//docs/project/index.html#d0e1859[here]. +refers to this as "_Last Resource Commit Optimization_" (LRCO). More details can be found https://narayana.io//docs/project/index.html#d0e1859[here]. -However, whether you are using {data-store-name} in a standalone environment with an Open Source JTA Transaction Management -implementation that supports "_Last Resource_", or a managed environment (e.g. Java EE AS such as WAS), -_Spring Data Geode_ has you covered. +However, whether you are using {data-store-name} in a standalone environment with an Open Source JTA Transaction +Management implementation that supports "_Last Resource_", or a managed environment (e.g. Java EE AS such as WAS), +{sdg-name} has you covered. -There are a series of steps you must complete to properly use {data-store-name} as a "_Last Resource_" in a JTA transaction -involving more than 1 transactional resource. Additionally, there can only be 1 non-XA compliant resource +There are a series of steps you must complete to properly use {data-store-name} as a "_Last Resource_" in a JTA +transaction involving more than 1 transactional resource. Additionally, there can only be 1 non-XA compliant resource (e.g. {data-store-name}) in such an arrangement. 1) First, you must complete Steps 1-4 in {data-store-name}'s documentation -https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[here]. +{apache-geode-docs}/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[here]. -NOTE: #1 above is independent of your Spring [Boot] and/or [Data for {data-store-name}] application -and must be completed successfully. +NOTE: #1 above is independent of your Spring [Boot] and/or [Data for {data-store-name}] application and must be +completed successfully. -2) Referring to Step 5 in {data-store-name}'s https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[documentation], +2) Referring to Step 5 in {data-store-name}'s {apache-geode-docs}/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[documentation], {sdg-name}'s Annotation support will attempt to set the `GemFireCache`, {x-data-store-javadoc}/org/apache/geode/cache/GemFireCache.html#setCopyOnRead-boolean-[`copyOnRead`] property for you when using the `@EnableGemFireAsLastResource` annotation. -However, if SDG's auto-configuration is unsuccessful then you must explicitly set the `copy-on-read` attribute on the -`` or `` element in XML or the `copyOnRead` property of the SDG `CacheFactoryBean` class -in JavaConfig to *true*. For example... +However, if SDG's auto-configuration is unsuccessful in this regard, then you must explicitly set the `copy-on-read` +attribute in the `` or `` XML element or set the `copyOnRead` property of +the `CacheFactoryBean` class in JavaConfig to *true*. For example: -Peer Cache XML: - -[source,xml] ----- - ----- - -Peer Cache JavaConfig: - -[source,java] ----- -@Bean -CacheFactoryBean gemfireCache() { - - CacheFactoryBean gemfireCache = new CacheFactoryBean(); - - gemfireCache.setClose(true); - gemfireCache.setCopyOnRead(true); - - return gemfireCache; -} ----- - -Client Cache XML: +`ClientCache` XML: +.Set copy-on-read using XML (client) [source,xml] ---- ---- -Client Cache JavaConfig: +`ClientCache` _JavaConfig_: +.Set copyOnRead using JavaConfig (client) [source,java] ---- @Bean @@ -182,21 +164,44 @@ ClientCacheFactoryBean gemfireCache() { ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean(); - gemfireCache.setClose(true); gemfireCache.setCopyOnRead(true); return gemfireCache; } ---- -NOTE: explicitly setting the `copy-on-read` attribute or optionally the `copyOnRead` property -really should not be necessary. +Peer `Cache` XML: -3) At this point, you *skip* Steps 6-8 in {data-store-name}'s https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[documentation] -and let _Spring Data Geode_ work its magic. All you need do is annotate your Spring `@Configuration` class +.Set copy-on-read using XML (server) +[source,xml] +---- + +---- + +Peer `Cache` _JavaConfig_: + +.Set copyOnRead using JavaConfig (server) +[source,java] +---- +@Bean +CacheFactoryBean gemfireCache() { + + CacheFactoryBean gemfireCache = new CacheFactoryBean(); + + gemfireCache.setCopyOnRead(true); + + return gemfireCache; +} +---- + +NOTE: Explicitly setting the `copy-on-read` attribute or the `copyOnRead` property is really not necessary. Enabling +transaction management takes case of copying on reads. + +3) At this point, you *skip* Steps 6-8 in {data-store-name}'s {apache-geode-docs}/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[documentation] +and let _Spring Data Geode_ work its magic. All you need to do is annotate your Spring `@Configuration` class with {sdg-name}'s *new* `@EnableGemFireAsLastResource` annotation and a combination of Spring's -https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#transaction[Transaction Management] -infrastructure and {sdg-name}'s `@EnableGemFireAsLastResource` configuration does the trick. +{spring-framework-docs}/#transaction[Transaction Management] infrastructure and {sdg-name}'s +`@EnableGemFireAsLastResource` annotation configuration does the trick. The configuration looks like this... @@ -206,7 +211,6 @@ The configuration looks like this... @EnableGemFireAsLastResource @EnableTransactionManagement(order = 1) class GeodeConfiguration { - ... } ---- @@ -220,7 +224,7 @@ where Spring's `@EnableTransactionManagement` annotation is also specified. that is not `Integer.MAX_VALUE` or `Integer.MIN_VALUE` (defaults to `Integer.MAX_VALUE`). Of course, hopefully you are aware that you also need to configure Spring's `JtaTransactionManager` -when using JTA Transactions like so.. +when using JTA transactions like so.. [source,java] ---- @@ -236,18 +240,18 @@ public JtaTransactionManager transactionManager(UserTransaction userTransaction) ---- NOTE: The configuration in section <> does *not* apply here. -The use of {sdg-name}'s `GemfireTransactionManager` is applicable only in "Local", Cache Transactions, +The use of {sdg-name}'s `GemfireTransactionManager` is applicable in "Local-only", Cache Transactions, *not* "Global", JTA Transactions. Therefore, you do *not* configure the SDG `GemfireTransactionManager` in this case. You configure Spring's `JtaTransactionManager` as shown above. For more details on using _Spring's Transaction Management_ with JTA, -see https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#transaction-application-server-integration[here]. +see {spring-framework-docs}/#transaction-application-server-integration[here]. Effectively, {sdg-name}'s `@EnableGemFireAsLastResource` annotation imports configuration containing 2 Aspect bean definitions that handles the {data-store-name} `o.a.g.ra.GFConnectionFactory.getConnection()` and `o.a.g.ra.GFConnection.close()` operations at the appropriate points during the transactional operation. -Specifically, the correct sequence of events are... +Specifically, the correct sequence of events follow: 1. `jtaTransation.begin()` @@ -261,19 +265,20 @@ Specifically, the correct sequence of events are... This is consistent with how you, as the application developer, would code this manually if you had to use the JTA API + {data-store-name} API yourself, as shown in the -{data-store-name} https://gemfire90.docs.pivotal.io/geode/developing/transactions/jca_adapter_example.html#concept_swv_z2p_wk[example]. +{data-store-name} {apache-geode-docs}/developing/transactions/jca_adapter_example.html#concept_swv_z2p_wk[example]. -Thankfully, Spring does the heavy lifting for you and all you need do after applying the appropriate configuration -(shown above) is... +Thankfully, Spring does the heavy lifting for you and all you need to do after applying the appropriate configuration +(shown above) is: +.Declaring a service method as @Transactional [source,java] ---- @Service -class MyTransactionalService ... { +class MyTransactionalService { @Transactional public someTransactionalServiceMethod() { - // perform business logic interacting with and accessing multiple JTA resources atomically, here + // perform business logic interacting with and accessing multiple JTA resources atomically } ... @@ -281,7 +286,7 @@ class MyTransactionalService ... { ---- #1 & #4 above are appropriately handled for you by Spring's JTA based `PlatformTransactionManager` once the -`@Transactional` boundary is entered by your application (i.e. when the `MyTransactionSerivce.someTransactionalServiceMethod()` +`@Transactional` boundary is entered by your application (i.e. when the `MyTransactionService.someTransactionalServiceMethod()` is called). #2 & #3 are handled by {sdg-name}'s new Aspects enabled with the `@EnableGemFireAsLastResource` annotation. @@ -290,6 +295,7 @@ is called). Indeed, with the appropriate logging configured, you will see the correct sequence of events... +.Transaction Log Output [source,xml] ---- 2017-Jun-22 11:11:37 TRACE TransactionInterceptor - Getting transaction for [example.app.service.MessageService.send] @@ -306,12 +312,25 @@ JSON : [{"id":"MSG0000000000","message":"SENT"}] ] 2017-Jun-22 11:11:37 TRACE GemFireAsLastResourceConnectionClosingAspect - Closed {data-store-name} Connection @ [Reference [...]] ---- -When using transactions, it may be desirable to register a listener to perform certain actions before or after the -transaction commits, or after a rollback occurs. {sdg-name} makes it easy to create listeners that will be invoked -during specific phases of a transaction with the `@TransactionalEventListener` annotation. Methods annotated with -`@TransactionalEventListener` (as shown below) will be notified of events published from transactional methods, during -the specified `phase`. +For more details on using {data-store-name} cache-level transactions, see <>. +For more details on using {data-store-name} in JTA transactions, +see https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html[here]. + +For more details on configuring {data-store-name} as a "_Last Resource_", +see https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[here]. + +[[apis:transaction-event-listener]] +== TransactionEventListener + +When using transactions, it may be desirable to register a listener to perform certain actions before or after the +transaction commits, or after a rollback occurs. + +{sdg-name} makes it easy to create listeners that will be invoked during specific phases of a transaction with the +`@TransactionalEventListener` annotation. Methods annotated with `@TransactionalEventListener` (as shown below) will be +notified of events published from transactional methods, during the specified `phase`. + +.After Transaction Commit Event Listener [source,java] ---- @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) @@ -320,36 +339,33 @@ public void handleAfterCommit(MyEvent event) { } ---- -Inorder for the above method to be invoked, you must publish an event from within your transaction, like below. +Inorder for the above method to be invoked, you must publish an event from within your transaction, like below: +.Publishing a Transactional Event [source,java] ---- @Service -class MyTransactionalService ... { +class MyTransactionalService { @Autowired - private final ApplicationEventPublisher publisher; + private final ApplicationEventPublisher applicationEventPublisher; @Transactional public someTransactionalServiceMethod() { - // perform business logic interacting with and accessing multiple JTA resources atomically, here - publisher.publishEvent(new MyEvent(...)); + + // Perform business logic interacting with and accessing multiple transactional resources atomically, then... + + applicationEventPublisher.publishEvent(new MyEvent(...)); } ... } ---- -The `@TransactionalEventListener` annotation allows you to specify the transaction `phase` at which it will be invoked; -the options are `AFTER_COMMIT`, `AFTER_COMPLETION`, `AFTER_ROLLBACK`, and `BEFORE_COMMIT`. If not specified, `phase` -defaults to `AFTER_COMMIT`. If you wish the listener to be called even when no transaction is running, you may set -`fallbackExecution` to true. - -For more details on using {data-store-name} in JTA transactions, -see https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html[here]. - -For more details on configuring {data-store-name} as a "_Last Resource_", -see https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[here]. +The `@TransactionalEventListener` annotation allows you to specify the transaction `phase` in which the event handler +method will be invoked. Options include: `AFTER_COMMIT`, `AFTER_COMPLETION`, `AFTER_ROLLBACK`, and `BEFORE_COMMIT`. +If not specified, the `phase` defaults to `AFTER_COMMIT`. If you wish the listener to be called even when no transaction +is present, you may set `fallbackExecution` to `true`. :leveloffset: +1