一 事务的基本概念
1 原子性 : 所有操作要么都成功,要么都失败
2 一致性 : 事务不能违反完整性约束(比如虽然事务还未提交,但仍然需要遵守约束规则,这些规则是事务提交之前而
非事务提交之后才能生效)
3 隔离性 : 一个事务的效果,不能影响其它同时执行的事务
4 持久性 : 一旦事务成功完成,则数据必须保证已经正确的持久化
二 JDBC事务和JTA事务对比
1 JDBC事务只能支持一个数据库(单数据源),而JTA可以支持分布式的事务(多数据源)
2 JDBC事务,一般数据库本身来执行提交或者回滚操作(单阶段提交),所有数据库都有它自己的事务管理器,管理执行的
日志,这些管理器只能处理自身的事务(称为本地事务)
3 而JTA不同,JTA要支持多个数据源,站在更高的角度上,提供“事务处理监视器(TPM)”来管理和协调这些数据源之间的事务
操作.它必须执行两个阶段的提交的(2PC)协议.
3.1 准备阶段 : TPM向所有RM(资源管理器,即数据库)确认状态,是否可以提交或回滚。
3.2 提交阶段 : TPM确认提交之后,向所有的RM发出提交指令(或回滚)
4 TPM本身会维护事务日志,以保证持久性(灾难恢复等)
三 事务类型 ; JTA 和RESOURCE_LOCAL
1 使用JAT来控制事务
利用容器提供的JTA事务管理器来管理事务
2 使用RESOURCE_LOCAL来控制事务
可以脱离容器
四 案例
1 项目结构图
Student.java : 学生实体类
StudentManager.java 学生管理接口类
StudentManagerImpl.java 学生管理实现类
JtaTest.java Junit测试类,
测试1 : testWithoutJta 没有JTA时候的效果
测试2 : testJta 使用JTA时候的效果
测试3 : testLocal 测试使用 RESOURCE_LOCAL 的效果
persistence.xml : 配置RESOURCE_LOCAL和JTA的数据源
jndi.properties : 配置JNDI
client : 为JtaTest中testWithoutJta 和 testJta 提供客户端环境
jar包有 : JBOSS_HOME/client/*.jar
RESOURCE_LOCAL 为JtaTest中testLocal 提供环境
jar包有 : JBOSS_HOME/server/default/lib/*.jar
JBOSS_HOME/lib/*.jar
五 代码
Student.java
package leaning;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Student {
@Id
@GeneratedValue
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
StudentManager.java
package leaning;
public interface StudentManager {
public void addStudent(String name);
}
StudentManagerImpl.java
package leaning;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless(name="studentManager")
@Remote
public class StudentManagerImpl implements StudentManager {
@PersistenceContext(unitName="EjbLession11_JTA")
private EntityManager em;
public void addStudent(String name) {
Student s = new Student();
s.setName(name);
em.persist(s);
}
}
JtaTest.java
package test;
import static org.junit.Assert.*;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import leaning.Student;
import leaning.StudentManager;
import org.junit.Test;
public class JtaTest {
/**
* 没有JTA测试
* 每调用studentManager.addStudent("张三"+i)就是一个事物
* 当i==5时候,抛出异常,因此有 张三0,张三1,张三2,张三3,张三4,张三5 这6个实体类被保存在数据库中
* **/
@Test
public void testWithoutJta() {
InitialContext context;
try {
context = new InitialContext();
StudentManager studentManager = (StudentManager)context.lookup("studentManager/remote");
for(int i = 0 ; i < 10 ; i++){
studentManager.addStudent("张三"+i);
if(i==5){
throw new RuntimeException();
}
}
} catch (NamingException e) {
e.printStackTrace();
}
}
/**
* JTA测试
* 与testWithoutJta代码基本一样,不同点在于开启了JTA事物,studentManager.addStudent("张三"+i) (i=0,1,2,3,4,5)都在一个事物中
* 发生throw new RuntimeException()后,事物回滚,因此没有插入任何数据
* **/
@Test
public void testJta() {
InitialContext context;
try {
context = new InitialContext();
//JTA事物管理接口
UserTransaction utx = (UserTransaction)context.lookup("UserTransaction");
StudentManager studentManager = (StudentManager)context.lookup("studentManager/remote");
try{
utx.begin(); //开启事物
for(int i = 0 ; i < 10 ; i++){
studentManager.addStudent("张三"+i);
if(i==5){
throw new RuntimeException();
}
}
utx.commit(); //提交事物
}catch(Exception e){
try {
utx.rollback(); //回滚
} catch (Exception e1 ) {
e1.printStackTrace();
}
e.printStackTrace();
}
} catch (NamingException e) {
e.printStackTrace();
}
}
/**
* 测试本地容器事物
* **/
@Test
public void testLocal() {
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("EjbLession11_RESOURCE_LOCAL");// 取 persistence.xml中 persistence-unit name="EjbLession11_RESOURCE_LOCAL"
EntityManager em = entityManagerFactory.createEntityManager();
try{
em.getTransaction().begin();
Student student = new Student();
student.setName("李四");
em.persist(student);
em.getTransaction().commit();
}catch(Exception e){
em.getTransaction().rollback();
e.printStackTrace();
}
}
}
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence 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_1_0.xsd" version="1.0">
<!-- 采集JTA事物 -->
<persistence-unit name="EjbLession11_JTA" transaction-type="JTA">
<jta-data-source>java:/MySqlDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
<!-- 采用RESOURCE_LOCAL事物 -->
<persistence-unit name="EjbLession11_RESOURCE_LOCAL" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/ejb" />
<property name="hibernate.connection.username" value="root" />
<property name="hibernate.connection.password" value="root" />
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=localhost
java.naming.factory.url.pkgs=org.jboss.naming\:org.jnp.interfaces