EJB multiple datasource transaction example

 

EJB multiple datasource transaction example

08 May 2014

By Gon?alo Marques

tags: jpajava-eejtaxaejb

In this article we will see
how to configure EJB transactions across multiple datasources

1. Introduction

Sometimes we may need our EJB transactions to spawn across multiple
datasources. For this to be possible one must take advantage of the enterprise
container Transaction Manager and the Java transaction API (JTA).

The Transaction Manager will be responsible for coordinating the multiple
datasource transaction. This transaction coordination will be transparent for
the participating EJBs.

We must make sure that the Resource Managers (drivers) we will be using are
XA compatible, ie. they are able to participate in distributed
transactions.

Each participating datasource must be associated with a XA compatible driver.
These datasources will be referenced by a distinct JTA
Persistence Unit in the application (persistence.xml).

Since the container will be managing the transaction through the Transaction
Manager, this means that only Container Managed Transaction
(CMT) EJBs may transparently participate in this kind of
distributed transaction.

This article considers the following environment:

  1. Ubuntu 12.04

  2. JDK 1.7.0.21

  3. Glassfish 4.0

2. The Datasources

We will configure a couple of datasources to be used in this article:
dataSource1 and dataSource2.

MySQL will be our database. Since we need a couple of datasources, we will
create a couple of database users, each one of them associated with a distinct
database:

Configured database users

Datasource 1
Database:
DATABASE1
Username: user1
Database:
passwd1

Datasource 2
Database:
DATABASE2
Username: user2
Database:
passwd2

In each database we create a testing table that will hold the records created
by the application:

Configured tables

Datasource 1
CREATE TABLE TABLE_ONE
(
TABLE_ONE_ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
VALUE VARCHAR(32)
NOT NULL
);

Datasource 2
CREATE TABLE TABLE_TWO
(
TABLE_TWO_ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
VALUE VARCHAR(32)
NOT NULL
);

As we have said previously, the datasources must be configured with a
XA compatible driver. So when we create the corresponding
connection pool in Glassfish for each resource, we select the pool Resource Type
as javax.sql.XADataSource:

XA Connection Pool configuration

Now we configure the corresponding JDBC resources - providing the JNDI name -
associating each one with the previously configured connection pools:

JDBC Resource configuration

3. The Persistence Context

Now we configure a couple of Persistence Units in
persistence.xml, each one associated with a distinct datasource
we configured previously:

/META-INF/persistence.xml


<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

<persistence-unit name="PersistenceUnit1" transaction-type="JTA">
<jta-data-source>jdbc/dataSource1</jta-data-source>
<class>com.byteslounge.entity.TableOne</class>
</persistence-unit>

<persistence-unit name="PersistenceUnit2" transaction-type="JTA">
<jta-data-source>jdbc/dataSource2</jta-data-source>
<class>com.byteslounge.entity.TableTwo</class>
</persistence-unit>

</persistence>


Since the distributed transaction will be managed by the container
Transaction Manager, we configure each Persistence Unit transaction type as
JTA. This also means that the Persistence Context associated
with each Persistence Unit will be also managed by the
container.

4. JPA Entities

Now we define a couple of JPA entities that will represent the tables from
each of the databases:

TableOne.java


package com.byteslounge.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "TABLE_ONE")
public class TableOne {

@Id
@Column(name = "TABLE_ONE_ID", nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int tableOneId;

@Column(name = "VALUE", nullable = false)
private String value;

public int getTableOneId() {
return tableOneId;
}

public void setTableOneId(int tableOneId) {
this.tableOneId = tableOneId;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

}

TableTwo.java


package com.byteslounge.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "TABLE_TWO")
public class TableTwo {

@Id
@Column(name = "TABLE_TWO_ID", nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int tableTwoId;

@Column(name = "VALUE", nullable = false)
private String value;

public int getTableTwoId() {
return tableTwoId;
}

public void setTableTwoId(int tableTwoId) {
this.tableTwoId = tableTwoId;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

}

5. The EJBs

Finally we define the EJBs that will participate in the distributed
transaction.

ServiceOne.java


package com.byteslounge.ejb;

import javax.ejb.Local;
import com.byteslounge.entity.TableOne;

@Local
public interface ServiceOne {

void save(TableOne tableOne);

}

ServiceOneBean.java


package com.byteslounge.ejb;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import com.byteslounge.entity.TableOne;

@Stateless
public class ServiceOneBean implements ServiceOne {

@PersistenceContext(unitName = "PersistenceUnit1")
private EntityManager em;

@Override
public void save(TableOne tableOne) {
em.persist(tableOne);
}

}


The first EJB is quite straight forward: We only defined a single method that
will persist an entity of type TableOne. Note that we are
injecting an Entity Manager that will serve as an interface to interact with
PersistenceUnit1 Persistence Context.

Now we define a second EJB:

ServiceTwo.java


package com.byteslounge.ejb;

import javax.ejb.Local;
import com.byteslounge.entity.TableTwo;

@Local
public interface ServiceTwo {

void save(TableTwo tableTwo);

}

ServiceTwoBean.java


package com.byteslounge.ejb;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import com.byteslounge.entity.TableTwo;

@Stateless
public class ServiceTwoBean implements ServiceTwo {

@PersistenceContext(unitName = "PersistenceUnit2")
private EntityManager em;

@Override
public void save(TableTwo tableTwo) {
em.persist(tableTwo);
throw new RuntimeException("Rollback transaction!");
}

}


Once again we defined a single method that will save an entity of type
TableTwo. This method is intentionally throwing a
RuntimeException that will result in the global distributed
transaction being rolled back.

In this EJB we are injecting an Entity Manager that is associated with
PersistenceUnit2 Persistence Context.

Finally we define an EJB that will serve as a facade to the distributed
transaction:

TransactionalService.java


package com.byteslounge.ejb;

import javax.ejb.Local;

import com.byteslounge.entity.TableOne;
import com.byteslounge.entity.TableTwo;

@Local
public interface TransactionalService {

void save(TableOne tableOne, TableTwo tableTwo);

}

TransactionalServiceBean.java


package com.byteslounge.ejb;

import javax.ejb.EJB;
import javax.ejb.Stateless;

import com.byteslounge.entity.TableOne;
import com.byteslounge.entity.TableTwo;

@Stateless
public class TransactionalServiceBean implements TransactionalService {

@EJB
private ServiceOne serviceOne;

@EJB
private ServiceTwo serviceTwo;

@Override
public void save(TableOne tableOne, TableTwo tableTwo) {
serviceOne.save(tableOne);
serviceTwo.save(tableTwo);
}

}


We defined a method that receives a couple of entities, each one of a
distinct type: TableOne and TableTwo. Each
injected EJB is responsible for persisting one of the entities.

The transaction will be propagated to each nested EJB call. If both nested
EJB calls succeed, the container will signal the Transaction Manager that the
distributed transaction may commit (the commit protocol used by the Transaction
Manager and each of the Resource Managers will not be discussed in this
article).

6. Testing

Let us define a simple servlet in order to test the distributed
transaction:

Testing Servlet


package com.byteslounge.servlet;

import java.io.IOException;

import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.byteslounge.ejb.TransactionalService;
import com.byteslounge.entity.TableOne;
import com.byteslounge.entity.TableTwo;

@WebServlet(name = "testingServlet", urlPatterns = { "/testing" })
public class TestingServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@EJB
private TransactionalService transactionalService;

@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {

TableOne tableOne = new TableOne();
tableOne.setValue("value1");

TableTwo tableTwo = new TableTwo();
tableTwo.setValue("value2");

try {
transactionalService.save(tableOne, tableTwo);
} catch (Exception e) {
e.printStackTrace();
}

}

}


When we access the servlet, the ServiceTwo EJB will throw
our intentional exception so the distributed transaction will be rollback:

javax.ejb.EJBTransactionRolledbackException
...
Caused
by: javax.ejb.TransactionRolledbackLocalException: Exception thrown from
bean
...
Caused by: java.lang.RuntimeException: Rollback transaction!

And both tables in each database will still be empty:

Now if we modify the ServiceTwo EJB method to complete
without throwing the runtime exception:

Modified ServiceTwo EJB method


@Override
public void save(TableTwo tableTwo) {
em.persist(tableTwo);
// throw new RuntimeException("Rollback transaction!");
}

When we access the servlet, the distributed transaction will complete
successfully and the data will be persisted in both tables / databases:

The article source code is available for download at the end of this page.

source: http://www.byteslounge.com/tutorials/ejb-multiple-datasource-transaction-example

EJB multiple datasource transaction example,布布扣,bubuko.com

时间: 2024-08-24 23:21:33

EJB multiple datasource transaction example的相关文章

EJB Container Manage Transaction

你脑海中事务的概念是什么? 先用最直白的话解释解释:对数据库操作我们最熟悉的是写SQL语句数据数据库建库.建表.操作表等,我们可以理解事务是一个规范只要操作数据库时按着这个规范做即可满足要求,可以把一条SQL语句加入到这个规范中也可以把很多语句加入规范中,只要加入到这个规范中的语句就会遵循这个规范的原则. 对于数据库不应该仅仅理解为写一条SQL语句是对数据库的操作,对数据库的所有操作都是在事务中进行的,即使仅仅一条插入语句也会开启关闭事务在事务中进行,因此可以把事务看作是操作数据库的最小单元,对

ejb Name Environment

关于EJB3.x的命名空间一直都很迷惑,有些地方规范上写得也不是很清楚.在网上搜索了一些资料做个整理: EJB reference,是指一个EJB引用另一个EJB的business interface, non-interface views或者home接口. 我们可以在部署描述符ejb-jar.xml中: 1.在同一ejb-jar.xml中一个EJB对另一个EJB的引用,因此大多数情况下,. 2.同一应用(企业应用)中一个EJB对另一个EJB的引用(它们在不同的ejb-jar.xml中被声明)

spring Transaction Management --官方

原文链接:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html 12. Transaction Management 12.1 Introduction to Spring Framework transaction management Comprehensive transaction support is among the most compelling rea

System and method for parallel execution of memory transactions using multiple memory models, including SSO, TSO, PSO and RMO

A data processor supports the use of multiple memory models by computer programs. At a device external to a data processor, such as a memory controller, memory transactions requests are received from the data processor. Each memory transaction reques

什么是事物

一.什么是事务 通俗的理解,事务是一组原子操作单元,从数据库角度说,就是一组SQL指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行过的所有指令.更简答的说就是:要么全部执行成功,要么撤销不执行. 事务必须服从ISO/IEC所制定的ACID原则. 原子性(atomicity) 一致性(consistency) 隔离性(isolation) 持久性(durability) 原子性表示事务执行过程中的任何失败都将导致事务所做的任何修改失效. 一致性表示当事务执行失败时,所有

Java事务与JTA

一.什么是JAVA事务 通俗的理解,事务是一组原子操作单元,从数据库角度说,就是一组SQL指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行过的所有指令.更简答的说就是:要么全部执行成功,要么撤销不执行. 事务必须服从ISO/IEC所制定的ACID原则. 原子性(atomicity) 一致性(consistency) 隔离性(isolation) 持久性(durability) 原子性表示事务执行过程中的任何失败都将导致事务所做的任何修改失效. 一致性表示当事务执行失败

Spring之 Aspect Oriented Programming with Spring

1. Concepts Aspect-Oriented Programming (AOP) complements OOP by providing another way of thinking about program structure. While OO decomposes applications into a hierarchy of objects, AOP decomposes programs into aspects or concerns. This enables m

Fragment基础操作

Fragment和Activity类似,同样是具备UI的属性:也就是都能用于规划UI布局... Building a Dynamic UI with Fragments --> Fragments具备有动态UI的属性.为了在Android上为用户提供动态的.多窗口的交互体验,我们需要将UI组件和Activity操作封装成模块进行使用,使得我们可以在activity中对这些模块进行切入切出操作.可以用Fragment来创建这些模块,Fragment就像一个嵌套的activity,拥有自己的布局(l

JPA和分布式事务简介

1. Transaction 分两种,Local Transaction 和 Global Transaction. 涉及到一个Connection的Commit,称为Local Transaction. 涉及到多个Connection的Commit,称为Global Transaction. 楼主提到的是,Global Transaction. 2. Global Transaction 需要XA接口(包括在JTA里面)的支持. import javax.sql.XAConnection; i