1.1、事务的定义:
事务是指多个操作单元组成的合集,多个单元操作是整体不可分割的,要么都操作不成功,要么都成功。其必须遵循四个原则(ACID)
1.2、事务四个原则(ACID)
1、原子性(Atomicity):即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做;
2、一致性(Consistency):在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是应该处于正确的状态,即数据完整性约束没有被破坏;如银行转帐,A转帐给B,必须保证A的钱一定转给B,一定不会出现A的钱转了但B没收到,否则数据库的数据就处于不一致(不正确)的状态。
3、隔离性(Isolation):并发事务执行之间互不影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性;
4、持久性(Durability):事务一旦执行成功,它对数据库的数据的改变必须是永久的,不会因比如遇到系统故障或断电造成数据不一致或丢失。
1.3、隔离级别(isolation level)
隔离级别定义了事务与事务之间的隔离程度、隔离级别与并发性是互为矛盾的:隔离程度越高,数据库的并发性越差;隔离程度越低,数据库的并发性越好。
1.4、事务隔离级别并发现象
1、更新丢失(lost update):
当系统允许两个事务同时更新同一数据时,发生更新丢失。
A事务读取User表中的id为1的记录(id=1,name=zhansan,age=20)
B事务读取User表中的id为1的记录(id=1,name=zhansan,age=20)
A事务将id为1的记录的name修改为lisi,提交事务
B事务将id为1的记录的age修改22,提交事务
则A事务就会出现更新丢失
2、脏读(dirty read)
当一个事务读取另一个事务尚未提交的修改时,产生脏读。
A事务读取User表中的id为1的记录(id=1,name=zhansan,age=20)
A事务将id为1的记录的name修改为lisi(id=1, name=lisi,age=20)
B事务读取User表中的id为1的记录(id=1, name=lisi,age=20)
此时A事务撤销更改,事务回滚(id=1,name=zhansan,age=20)
由于A事务回滚了,数据库内不会有任何操作记录,那么B事务是何时、从哪里读取了错误数据根本无从查起。
3、非重复读(nonrepeatableread)
同一查询在同一事务中多次进行,在此期间,由于其他事务提交了对数据的修改或删除,每次返回不同的结果。
A事务读取某数据,这个数据已经被B事务读取,并做了修改或删除。所以这数据已经不是以前的数据。
4、幻像(phantom read)
同一查询在同一事务中多次进行,由于其他提交事务所做的插入操作,虽然查询条件相同,每次返回的结果集却不同。
A事务重复持行一个查询,由于其他提交事务所做的插入操作,返回不同的结果集,此时发生幻像读。
1.5、隔离级别和对应现象的空值情况
隔离级别 | 脏读 | 非重复读 | 幻读 |
未提交读 | Y | Y | Y |
提交读 | N | Y | Y |
可重复读 | N | N | Y |
序列化 | N | N | N |
1.6、数据库事务分为本地事务跟全局事务
本地事务:普通事务,独立一个数据库,能保证在该数据库上操作的ACID。
分布式事务:涉及两个或多个数据库源的事务,即跨越多台同类或异类数据库的事务(由每台数据库的本地事务组成的),分布式事务旨在保证这些本地事务的所有操作的ACID,使事务可以跨越多台数据库;
1.7、Java事务类型分为JDBC事务跟JTA事务
JDBC事务:即为上面说的数据库事务中的本地事务,通过connection对象控制管理。
JTA事务:JTA指Java事务API(JavaTransaction API),是Java EE数据库事务规范, JTA只提供了事务管理接口,由应用程序服务器厂商(如WebSphere Application Server)提供实现,JTA事务比JDBC更强大,支持分布式事务。
1.8、Spring事务传播行为:有七大传播行为,也是在TransactionDefinition接口中定义。
PROPAGATION_REQUIRED:支持当前事务,如当前没有事务,则新建一个。
PROPAGATION_SUPPORTS:支持当前事务,如当前没有事务,则已非事务性执行(源码中提示有个注意点,看不太明白,留待后面考究)。
PROPAGATION_MANDATORY:支持当前事务,如当前没有事务,则抛出异常(强制一定要在一个已经存在的事务中执行,业务方法不可独自发起自己的事务)。
PROPAGATION_REQUIRES_NEW:始终新建一个事务,如当前原来有事务,则把原事务挂起。
PROPAGATION_NOT_SUPPORTED:不支持当前事务,始终已非事务性方式执行,如当前事务存在,挂起该事务。
PROPAGATION_NEVER:不支持当前事务;如果当前事务存在,则引发异常。
PROPAGATION_NESTED:如果当前事务存在,则在嵌套事务中执行,如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作(注意:当应用到JDBC时,只适用JDBC 3.0以上驱动)。
1.9、Spring多数据源分布式事务spring+atomikos[jta]+druid+mybatis
spring3.0之后不再支持jtom[jta]了,第三方开源软件atomikos(http://www.atomikos.com/)来实现.
atomikos事务控制框架,其中看到有3种数据源,分别是,SimpleDataSourceBean,AtomikosDataSourceBean,AtomikosNonXADataSourceBean。
a:SimpleDataSourceBean: 这个是最简单地数据源配置,需要配置XA驱动。
b:AtomikosDataSourceBean: 分布式数据源,Atomikos实现的数据源,需要配置XA驱动,推荐此配置,可以配置连接池的信息。
c:AtomikosNonXADataSourceBean: 非分布式数据源,该数据源配置需要普通JDBC的驱动,可以配置连接池:
Atomikos支持XA(全局事务)和NON-XA(非全局事务),NON-XA[nonxadatasource]效率高于XA.XA事务往往是包括多个数据源的全局事务,非XA是单个数据源的.
XA连接是一个JTA事务中的参与者。XA连接不支持JDBC的自动提交特性。也就是说应用程序不必在xadatasource[XA]连接上调用Java.sql.Connection.commit()或java.sql.Connection.rollback();而应用程序应该使用UserTransaction.begin(),UserTransaction.commit()和UserTransaction.rollback().
1、 创建两个数据库
1.1、创建第一个数据库multi-db1
CREATE DATABASE `multi-db1` DEFAULT CHARACTER SET utf8;
USE `multi-db1`;
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` varchar(100) DEFAULT NULL,
`name` varchar(200) DEFAULT NULL,
`gender` varchar(2) DEFAULT NULL,
`birthday` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
1.2、创建第二个数据库multi-db2
CREATE DATABASE `multi-db2` DEFAULT CHARACTER SET utf8;
USE `multi-db2`;
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` varchar(100) DEFAULT NULL,
`name` varchar(200) DEFAULT NULL,
`gender` varchar(2) DEFAULT NULL,
`birthday` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2、 创建一个maven的项目类型为jar项目目录为
3、项目的POM文件pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jyd.transaction</groupId>
<artifactId>DTransaction</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.26</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.36</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<!-- transaction -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>atomikos-util</artifactId>
<version>4.0.2</version>
</dependency>
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions</artifactId>
<version>4.0.2</version>
</dependency>
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-jta</artifactId>
<version>4.0.2</version>
</dependency>
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-jdbc</artifactId>
<version>4.0.2</version>
</dependency>
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-api</artifactId>
<version>4.0.2</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 资源文件拷贝插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
4、配置数据源参数db.properties
#mysql-Used to verify the effectiveness of the database connection
validationQuery=SELECT 1
jdbc.initialSize=5
jdbc.maxActive=20
jdbc.maxWait=60000
jdbc.poolPreparedStatements=false
jdbc.poolMaximumIdleConnections=0
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.xaDataSourceClassName=com.alibaba.druid.pool.xa.DruidXADataSource
#jdbc.xaDataSourceClassName=com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
#1.tms business. 2.The db level optimization,data concurrency,desirable.
master.jdbc.url=jdbc:mysql://localhost:3306/multi-db1?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
slave.jdbc.url=jdbc:mysql://localhost:3306/multi-db2?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc.username=root
jdbc.password=123456
5、AtomikosDataSourceBean[XA(全局事务)]数据源配置datasource-context.xml
<?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"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" default-lazy-init="true">
<description>配置DB1-DB2数据源信息</description>
<!-- com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean -->
<bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close" abstract="true">
<property name="xaDataSourceClassName" value="${jdbc.xaDataSourceClassName}"/> <!-- SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase, Hana] -->
<property name="poolSize" value="10" />
<property name="minPoolSize" value="10"/>
<property name="maxPoolSize" value="30"/>
<property name="borrowConnectionTimeout" value="60"/>
<property name="reapTimeout" value="20"/>
<property name="maxIdleTime" value="60"/>
<property name="maintenanceInterval" value="60"/>
<property name="loginTimeout" value="60"/>
<property name="testQuery" value="${validationQuery}"/>
</bean>
<bean id="db1DataSource" parent="abstractXADataSource">
<property name="uniqueResourceName" value="DB1" />
<property name="xaProperties">
<props>
<prop key="driverClassName">${jdbc.driverClassName}</prop>
<prop key="url">${master.jdbc.url}</prop>
<prop key="password">${jdbc.password}</prop>
<!-- <prop key="user">${jdbc.username}</prop> --> <!-- mysql -->
<prop key="username">${jdbc.username}</prop> <!-- durid -->
<prop key="initialSize">0</prop>
<prop key="maxActive">20</prop> <!-- 若不配置则代码执行"{dataSource-1} inited"此处停止 -->
<prop key="minIdle">0</prop>
<prop key="maxWait">60000</prop>
<prop key="validationQuery">${validationQuery}</prop>
<prop key="testOnBorrow">false</prop>
<prop key="testOnReturn">false</prop>
<prop key="testWhileIdle">true</prop>
<prop key="removeAbandoned">true</prop>
<prop key="removeAbandonedTimeout">1800</prop>
<prop key="logAbandoned">true</prop>
<prop key="filters">mergeStat</prop>
</props>
</property>
</bean>
<bean id="db2DataSource" parent="abstractXADataSource">
<property name="uniqueResourceName" value="DB2" />
<property name="xaProperties">
<props>
<prop key="driverClassName">${jdbc.driverClassName}</prop>
<prop key="url">${slave.jdbc.url}</prop>
<prop key="password">${jdbc.password}</prop>
<!-- <prop key="user">${jdbc.username}</prop> -->
<prop key="username">${jdbc.username}</prop>
<prop key="initialSize">0</prop>
<prop key="maxActive">20</prop>
<prop key="minIdle">0</prop>
<prop key="maxWait">60000</prop>
<prop key="validationQuery">${validationQuery}</prop>
<prop key="testOnBorrow">false</prop>
<prop key="testOnReturn">false</prop>
<prop key="testWhileIdle">true</prop>
<prop key="removeAbandoned">true</prop>
<prop key="removeAbandonedTimeout">1800</prop>
<prop key="logAbandoned">true</prop>
<prop key="filters">mergeStat</prop>
</props>
</property>
</bean>
</beans>
6、配置mybatis-config
mybatis-config-db1.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias alias="User" type="com.jyd.entity.User"/>
</typeAliases>
<mappers>
<mapper resource="com/jyd/xml/UserMapper.xml" />
</mappers>
</configuration>
mybatis-config-db2.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias alias="UserInfo" type="com.jyd.entity.UserInfo"/>
</typeAliases>
<mappers>
<mapper resource="com/jyd/xml/UserInfoMapper.xml" />
</mappers>
</configuration>
7、mybatis的配置mybatis-context.xml
<?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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" default-lazy-init="true">
<bean id="db1SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis/mybatis-config-db1.xml" />
<property name="dataSource" ref="db1DataSource" />
</bean>
<bean id="db2SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis/mybatis-config-db2.xml" />
<property name="dataSource" ref="db2DataSource" />
</bean>
</beans>
8、Mapper的管理及注入,为mybatis的dao层mapper接口注入[绑定]sqlSessionFactory
<?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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" default-lazy-init="true">
<description>MyBatis为不同的mapper注入sqlSessionFactory</description>
<!-- Mapper的管理及注入 -->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="db1SqlSessionFactory" />
<property name="mapperInterface" value="com.jyd.dao.UserMapper" />
</bean>
<bean id="userInfoMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="db2SqlSessionFactory" />
<property name="mapperInterface" value="com.jyd.dao.UserInfoMapper" />
</bean>
</beans>
9、atomikos事务配置transaction-context.xml
<?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"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" default-lazy-init="true">
<description>配置事物</description>
<!-- atomikos事务管理器 -->
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
<property name="forceShutdown">
<value>true</value>
</property>
</bean>
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="300" />
</bean>
<!-- spring 事务管理器 -->
<bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="atomikosTransactionManager" />
<property name="userTransaction" ref="atomikosUserTransaction" />
<!-- 必须设置,否则程序出现异常 JtaTransactionManager does not support custom isolation levels by default -->
<property name="allowCustomIsolationLevels" value="true"/>
</bean>
<aop:config proxy-target-class="true">
<aop:advisor pointcut="(execution(* com.jyd.service.*.* (..)))" advice-ref="txAdvice" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="springTransactionManager">
<tx:attributes>
<tx:method name="get*" propagation="REQUIRED" read-only="true" />
<tx:method name="find*" propagation="REQUIRED" read-only="true" />
<tx:method name="has*" propagation="REQUIRED" read-only="true" />
<tx:method name="locate*" propagation="REQUIRED" read-only="true" />
<tx:method name="register*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
</tx:attributes>
</tx:advice>
</beans>
10、配置jta启动参数在jta.properties,最后追加详细
com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
com.atomikos.icatch.console_file_name = /home/logs/tx/tx.out.log
com.atomikos.icatch.log_base_name = txlog
com.atomikos.icatch.tm_unique_name = com.atomikos.spring.jdbc.tm
com.atomikos.icatch.console_log_level=DEBUG
11、spring主配置文件spring-context.xml
<?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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<!-- 使用annotation 自动注册bean,并检查@Required,@Autowired的属性已被注入 -->
<context:component-scan base-package="com.jyd" />
<!-- 使用AspectJ方式配置AOP -->
<aop:aspectj-autoproxy />
<!-- 引入属性配置文件 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:properties/db.properties" />
</bean>
<!--或 <context:property-placeholder location="classpath*:*.properties" /> -->
<import resource="datasource-context.xml"/>
<import resource="mapper-context.xml"/>
<import resource="mybatis-context.xml"/>
<import resource="transaction-context.xml"/>
</beans>
12、创建实体bean类(User.java|UserInfo.java)
User.java
package com.jyd.entity;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private static final long serialVersionUID = -5650864083291329775L;
private String id;
private String name;
private String gender;
private Date birthday;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
UserInfo.java
package com.jyd.entity;
import java.io.Serializable;
import java.util.Date;
public class UserInfo implements Serializable {
private static final long serialVersionUID = 7402046259459506152L;
private String id;
private String name;
private String gender;
private Date birthday;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
13、创建mybatis的mapper和dao接口(UserMapper.java|UserInfoMapper.java)和对应mapper文件
UserMapper.java
package com.jyd.dao;
import org.springframework.stereotype.Repository;
import com.jyd.entity.User;
@Repository
public interface UserMapper {
int insert(User record);
}
UserInfoMapper.java
package com.jyd.dao;
import org.springframework.stereotype.Repository;
import com.jyd.entity.UserInfo;
@Repository
public interface UserInfoMapper {
int insert(UserInfo record);
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.jyd.dao.UserMapper" >
<insert id="insert" parameterType="com.jyd.entity.User" >
insert into t_user (id, name, gender, birthday)
values (#{id}, #{name}, #{gender},#{birthday})
</insert>
</mapper>
UserInfoMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.jyd.dao.UserInfoMapper" >
<insert id="insert" parameterType="com.jyd.entity.UserInfo" >
insert into t_user (id, name, gender, birthday)
values (#{id}, #{name}, #{gender},#{birthday})
</insert>
</mapper>
14、创建UserService服务层和实现(UserService.java|UserServiceImpl.java)
UserService.java
package com.jyd.service;
import com.jyd.entity.User;
import com.jyd.entity.UserInfo;
public interface UserService {
boolean registerUser(User user, UserInfo userInfo);
}
UserServiceImpl.java
package com.jyd.service.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.jyd.dao.UserInfoMapper;
import com.jyd.dao.UserMapper;
import com.jyd.entity.User;
import com.jyd.entity.UserInfo;
import com.jyd.service.UserService;
@Service("userService")
public class UserServiceImpl implements UserService {
//log
private static final Logger LOG = LoggerFactory.getLogger(UserServiceImpl.class);
@Autowired
private UserMapper userMapper;
@Autowired
private UserInfoMapper userInfoMapper;
@Override
public boolean registerUser(User user, UserInfo userInfo) {
boolean resRegister = false;
try {
if(userMapper.insert(user) != 1){
throw new RuntimeException("注册用户:User表数据插入不一致.");
}
if(userInfoMapper.insert(userInfo) != 1){
throw new RuntimeException("注册用户:UserInfo表数据插入不一致.");
}
resRegister = true;
} catch (Exception e) {
LOG.info("注册用户:数据库保存异常." + e.getMessage(), e);
throw new RuntimeException("注册用户:数据库保存异常");
}
return resRegister;
}
}
15、Junit测试代码
package com.jyd.test;
import java.util.Calendar;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.jyd.entity.User;
import com.jyd.entity.UserInfo;
import com.jyd.service.UserService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-context.xml"})
public class JTATest extends AbstractJUnit4SpringContextTests{
//log
private static final Logger LOG = LoggerFactory.getLogger(JTATest.class);
@Autowired
private UserService userService;
@Test
public void testRegister(){
User user = new User();
user.setId("1");
user.setName("张三");
user.setGender("男");
user.setBirthday(Calendar.getInstance().getTime());
UserInfo userInfo = new UserInfo();
userInfo.setId("1");
userInfo.setName("张三");
userInfo.setGender("男");
userInfo.setBirthday(Calendar.getInstance().getTime());
if(userService.registerUser(user, userInfo)){
LOG.info("##用户注册成功");
}else{
LOG.info("##用户注册失败");
}
}
}
可以做异常测试,其中如何方法出错了,都会回滚事务