spring boot学习6之mybatis+PageHelper分页插件+jta多数据源事务整合

        在项目开发中,随着业务的扩展,api可能会操作多个数据库。本博文就学习下spring boot下使用spring-boot-starter-jta-atomikos对mybatis+mysql+PageHelper分页插件的整合。

  项目文件结构

spring boot学习6之mybatis+PageHelper分页插件+jta多数据源事务整合_第1张图片


例子源码,已上传github


 准备两个数据源数据库(如果只有一个数据源,那就新建2个数据库进行测试也是OK的)


pom.xml


	    org.springframework.boot
	    spring-boot-starter-parent
	    1.5.2.RELEASE
	   
  
	
	    
	        org.springframework.boot
	        spring-boot-starter-web

	    
	    
		  
   			 org.mybatis.spring.boot
    		 mybatis-spring-boot-starter
   			 1.3.0
		 
		
		
    			mysql
    			mysql-connector-java
		
		
		
			org.springframework.boot
			spring-boot-starter-jta-atomikos
		
		
		
		
    		com.github.pagehelper
    		pagehelper
    		4.1.6
		

		

准备两个数据源数据库(如果只有一个数据源,那就新建2个数据库进行测试也是OK的)

application.yml

logging:
  config: classpath:logback.xml
  path: d:/logs
server:
  port: 80
  session-timeout: 60

spring:
    datasource:
        db01:
           url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8
           username: root
           password: root
           minPoolSize: 3
           maxPoolSize: 25
           maxLifetime: 20000
           borrowConnectionTimeout: 30
           loginTimeout: 30
           maintenanceInterval: 60
           test.maxIdleTime: 60
           testQuery: select 1
           mapperLocations: classpath:/com/fei/springboot/dao/db01/*.xml
           configLocation: classpath:/mybatis-config.xml
        db02:
           url: jdbc:mysql://192.168.0.213:3306/test?useUnicode=true&characterEncoding=utf-8
           username: root
           password: root
           minPoolSize: 3
           maxPoolSize: 25
           maxLifetime: 20000
           borrowConnectionTimeout: 30
           loginTimeout: 30
           maintenanceInterval: 60
           test.maxIdleTime: 60
           testQuery: select 1
           mapperLocations: classpath:/com/fei/springboot/dao/db02/*.xml
           configLocation: classpath:/mybatis-config.xml
mybatis-config.xml

  


	
	    
		
		
		
		        
                 
         
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
	
	


第一个数据源

TestDb01Config.java

package com.fei.springboot.config.dbconfig;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.github.pagehelper.PageHelper;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;

/**
 * db01 数据库配置
 * @author Jfei
 *
 */

@ConfigurationProperties(prefix="spring.datasource.db01")
@Configuration
@MapperScan(basePackages="com.fei.springboot.dao.db01", sqlSessionTemplateRef="db01SqlSessionTemplate")
public class TestDb01Config {

   private Logger logger = LoggerFactory.getLogger(TestDb01Config.class);
	
    private String url;
	private String username;
	private String password;

	/** min-pool-size 最小连接数 **/
	private int minPoolSize;
	/** max-pool-size 最大连接数 **/
	private int maxPoolSize;
	/** max-lifetime 连接最大存活时间 **/
	private int maxLifetime;
	/** borrow-connection-timeout 获取连接失败重新获等待最大时间,在这个时间内如果有可用连接,将返回 **/
	private int borrowConnectionTimeout;
	/** login-timeout java数据库连接池,最大可等待获取datasouce的时间 **/
	private int loginTimeout;
	/** maintenance-interval 连接回收时间 **/
	private int maintenanceInterval;
	/** max-idle-time 最大闲置时间,超过最小连接池连接的连接将将关闭 **/
	private int maxIdleTime;
	/** test-query 测试SQL **/
	private String testQuery;
    

//  配置mapper的扫描,找到所有的mapper.xml映射文件
    private String mapperLocations;

//  加载全局的配置文件
    private String configLocation;
    
    
 // 配置数据源
 	@Primary
 	@Bean(name = "db01DataSource")
 	public DataSource db01DataSource() throws SQLException {
 		
 		MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
 		mysqlXaDataSource.setUrl(url);
 		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
 		mysqlXaDataSource.setPassword(password);
 		mysqlXaDataSource.setUser(username);
 		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
 		
 		AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
 		xaDataSource.setXaDataSource(mysqlXaDataSource);
 		xaDataSource.setUniqueResourceName("db01DataSource");

 		xaDataSource.setMinPoolSize(minPoolSize);
 		xaDataSource.setMaxPoolSize(maxPoolSize);
 		xaDataSource.setMaxLifetime(maxLifetime);
 		xaDataSource.setBorrowConnectionTimeout(borrowConnectionTimeout);
 		xaDataSource.setLoginTimeout(loginTimeout);
 		xaDataSource.setMaintenanceInterval(maintenanceInterval);
 		xaDataSource.setMaxIdleTime(maxIdleTime);
 		xaDataSource.setTestQuery(testQuery);
        
 		return xaDataSource;
 	}
    
 	
 	@Bean(name = "db01SqlSessionFactory")
	public SqlSessionFactory db01SqlSessionFactory(@Qualifier("db01DataSource") DataSource dataSource)
			throws Exception {
		
		  try {
              SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
              sessionFactoryBean.setDataSource(dataSource);
              
              //设置mapper.xml文件所在位置 
              Resource[] resources = new PathMatchingResourcePatternResolver().getResources(mapperLocations);
              sessionFactoryBean.setMapperLocations(resources);
           //设置mybatis-config.xml配置文件位置
              sessionFactoryBean.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));

              //添加分页插件、打印sql插件
              Interceptor[] plugins = new Interceptor[]{pageHelper(),sqlPrintInterceptor()};
              sessionFactoryBean.setPlugins(plugins);
              
              return sessionFactoryBean.getObject();
          } catch (IOException e) {
              logger.error("mybatis resolver db01 mapper*xml is error",e);
              throw e;
          } catch (Exception e) {
              logger.error("mybatis db01sqlSessionFactoryBean create error",e);
              throw e;
          }
	}

	@Bean(name = "db01SqlSessionTemplate")
	public SqlSessionTemplate db01SqlSessionTemplate(
			@Qualifier("db01SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
 	
	  /**
     * 分页插件
     * @param dataSource
     * @return
     */
    

    public PageHelper pageHelper() {
        PageHelper pageHelper = new PageHelper();
        Properties p = new Properties();
        p.setProperty("offsetAsPageNum", "true");
        p.setProperty("rowBoundsWithCount", "true");
        p.setProperty("reasonable", "true");
        p.setProperty("returnPageInfo", "check");
        p.setProperty("params", "count=countSql");
        pageHelper.setProperties(p);
        return pageHelper;
    }
    
    //将要执行的sql进行日志打印(不想拦截,就把这方法注释掉)
    public SqlPrintInterceptor sqlPrintInterceptor(){
    	return new SqlPrintInterceptor();
    }

// ---  get set 自行补充
  
}
第二个数据源

TestDb02Config.java

package com.fei.springboot.config.dbconfig;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.github.pagehelper.PageHelper;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;

/**
 * db02 数据库配置
 * @author Jfei
 *
 */

@ConfigurationProperties(prefix="spring.datasource.db02")
@Configuration
@MapperScan(basePackages="com.fei.springboot.dao.db02",sqlSessionTemplateRef="db02SqlSessionTemplate")
public class TestDb02Config {

   private Logger logger = LoggerFactory.getLogger(TestDb02Config.class);
	
    private String url;
	private String username;
	private String password;

	/** min-pool-size 最小连接数 **/
	private int minPoolSize;
	/** max-pool-size 最大连接数 **/
	private int maxPoolSize;
	/** max-lifetime 连接最大存活时间 **/
	private int maxLifetime;
	/** borrow-connection-timeout 获取连接失败重新获等待最大时间,在这个时间内如果有可用连接,将返回 **/
	private int borrowConnectionTimeout;
	/** login-timeout java数据库连接池,最大可等待获取datasouce的时间 **/
	private int loginTimeout;
	/** maintenance-interval 连接回收时间 **/
	private int maintenanceInterval;
	/** max-idle-time 最大闲置时间,超过最小连接池连接的连接将将关闭 **/
	private int maxIdleTime;
	/** test-query 测试SQL **/
	private String testQuery;
    

//  配置mapper的扫描,找到所有的mapper.xml映射文件
    private String mapperLocations;

//  加载全局的配置文件
    private String configLocation;
    
    
 // 配置数据源
 //	@Primary  //db01那边配置使用Primary了,这里不能再用了,否则报错
 	@Bean(name = "db02DataSource")
 	public DataSource db02DataSource() throws SQLException {
 		
 		MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
 		mysqlXaDataSource.setUrl(url);
 		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
 		mysqlXaDataSource.setPassword(password);
 		mysqlXaDataSource.setUser(username);
 		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
 		
 		AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
 		xaDataSource.setXaDataSource(mysqlXaDataSource);
 		xaDataSource.setUniqueResourceName("db02DataSource");

 		xaDataSource.setMinPoolSize(minPoolSize);
 		xaDataSource.setMaxPoolSize(maxPoolSize);
 		xaDataSource.setMaxLifetime(maxLifetime);
 		xaDataSource.setBorrowConnectionTimeout(borrowConnectionTimeout);
 		xaDataSource.setLoginTimeout(loginTimeout);
 		xaDataSource.setMaintenanceInterval(maintenanceInterval);
 		xaDataSource.setMaxIdleTime(maxIdleTime);
 		xaDataSource.setTestQuery(testQuery);
        
 		return xaDataSource;
 	}
    
 	
 	@Bean(name = "db02SqlSessionFactory")
	public SqlSessionFactory db02SqlSessionFactory(@Qualifier("db02DataSource") DataSource dataSource)
			throws Exception {
		
		  try {
              SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
              sessionFactoryBean.setDataSource(dataSource);
              
              //设置mapper.xml文件所在位置 
              Resource[] resources = new PathMatchingResourcePatternResolver().getResources(mapperLocations);
              sessionFactoryBean.setMapperLocations(resources);
           //设置mybatis-config.xml配置文件位置
              sessionFactoryBean.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));

              //添加分页插件、打印sql插件
              Interceptor[] plugins = new Interceptor[]{pageHelper(),sqlPrintInterceptor()};
              sessionFactoryBean.setPlugins(plugins);
              
              return sessionFactoryBean.getObject();
          } catch (IOException e) {
              logger.error("mybatis resolver db02 mapper*xml is error",e);
              throw e;
          } catch (Exception e) {
              logger.error("mybatis db02sqlSessionFactoryBean create error",e);
              throw e;
          }
	}

	@Bean(name = "db02SqlSessionTemplate")
	public SqlSessionTemplate db02SqlSessionTemplate(
			@Qualifier("db02SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
 	
	  /**
     * 分页插件
     * @param dataSource
     * @return
     */
 
    public PageHelper pageHelper() {
        PageHelper pageHelper = new PageHelper();
        Properties p = new Properties();
        p.setProperty("offsetAsPageNum", "true");
        p.setProperty("rowBoundsWithCount", "true");
        p.setProperty("reasonable", "true");
        p.setProperty("returnPageInfo", "check");
        p.setProperty("params", "count=countSql");
        pageHelper.setProperties(p);
        return pageHelper;
    }
    
    //将要执行的sql进行日志打印(不想拦截,就把这方法注释掉)
    public SqlPrintInterceptor sqlPrintInterceptor(){
    	return new SqlPrintInterceptor();
    }

//--  get set 自行补充

}

注意 

@MapperScan(basePackages="com.fei.springboot.dao.db02",sqlSessionTemplateRef="db02SqlSessionTemplate")  
要匹配,否则容易出错,不同数据源的mapper接口类和mapper.xml文件最好都分开


事务类的配置TransactionManagerConfig.java

package com.fei.springboot.config.dbconfig;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.jta.JtaTransactionManager;

import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;

/**
 * 事务配置
 * @author Jfei
 *
 */
@Configuration
@EnableTransactionManagement
public class TransactionManagerConfig {
	
	@Bean(name = "userTransaction")
	public UserTransaction userTransaction() throws Throwable {
		UserTransactionImp userTransactionImp = new UserTransactionImp();
		userTransactionImp.setTransactionTimeout(10000);
		return userTransactionImp;
	}

	@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
	public TransactionManager atomikosTransactionManager() throws Throwable {
		UserTransactionManager userTransactionManager = new UserTransactionManager();
		userTransactionManager.setForceShutdown(false);
		return userTransactionManager;
	}

	@Bean(name = "transactionManager")
	@DependsOn({ "userTransaction", "atomikosTransactionManager" })
	public PlatformTransactionManager transactionManager() throws Throwable {
		UserTransaction userTransaction = userTransaction();
		JtaTransactionManager manager = new JtaTransactionManager(userTransaction, atomikosTransactionManager());
		return manager;
	}
}
sql打印的拦截器SqlPrintInterceptor.java

package com.fei.springboot.config.dbconfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;

/**
 * MyBatis 将mybatis要执行的sql拦截打印出来
 *
 * @since 1.0.0
 */
@Intercepts
({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class SqlPrintInterceptor implements Interceptor {

	private static Log logger = LogFactory.getLog(SqlPrintInterceptor.class);
	
    private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameterObject = null;
        if (invocation.getArgs().length > 1) {
            parameterObject = invocation.getArgs()[1];
        }

        long start = System.currentTimeMillis();

        Object result = invocation.proceed();
        
        String statementId = mappedStatement.getId();
        BoundSql boundSql = mappedStatement.getBoundSql(parameterObject);
        Configuration configuration = mappedStatement.getConfiguration();
        String sql = getSql(boundSql, parameterObject, configuration);

        long end = System.currentTimeMillis();
        long timing = end - start;
        if(logger.isInfoEnabled()){
        	logger.info("执行sql耗时:" + timing + " ms" + " - id:" + statementId + " - Sql:" );
        	logger.info("   "+sql);
        }
       
        return result;
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {
    }

    private String getSql(BoundSql boundSql, Object parameterObject, Configuration configuration) {
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        List parameterMappings = boundSql.getParameterMappings();
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        if (parameterMappings != null) {
            for (int i = 0; i < parameterMappings.size(); i++) {
                ParameterMapping parameterMapping = parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    Object value;
                    String propertyName = parameterMapping.getProperty();
                    if (boundSql.hasAdditionalParameter(propertyName)) {
                        value = boundSql.getAdditionalParameter(propertyName);
                    } else if (parameterObject == null) {
                        value = null;
                    } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                        value = parameterObject;
                    } else {
                        MetaObject metaObject = configuration.newMetaObject(parameterObject);
                        value = metaObject.getValue(propertyName);
                    }
                    sql = replacePlaceholder(sql, value);
                }
            }
        }
        return sql;
    }

    private String replacePlaceholder(String sql, Object propertyValue) {
        String result;
        if (propertyValue != null) {
            if (propertyValue instanceof String) {
                result = "'" + propertyValue + "'";
            } else if (propertyValue instanceof Date) {
                result = "'" + DATE_FORMAT.format(propertyValue) + "'";
            } else {
                result = propertyValue.toString();
            }
        } else {
            result = "null";
        }
        return sql.replaceFirst("\\?", Matcher.quoteReplacement(result));
    }
}


核心配置OK了。下面写dao/service/controller进行测试

db01的

package com.fei.springboot.dao.db01;

import java.util.List;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import com.fei.springboot.domain.User;

@Mapper
public interface UserMapper {

	@Insert("insert sys_user(id,user_name) values(#{id},#{userName})")
	void insert(User u);
	
	//注:方法名和要UserMapper.xml中的id一致
	List query(@Param("userName")String userName);
	
}
UserMapper.xml





 

Db02UserMapper.java

package com.fei.springboot.dao.db02;

import java.util.List;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import com.fei.springboot.domain.User;

@Mapper
public interface Db02UserMapper {

	@Insert("insert sys_user(id,user_name) values(#{id},#{userName})")
	void insert(User u);
	
	//注:方法名和要UserMapper.xml中的id一致
	List query(@Param("userName")String userName);
	
}
Db02UserMapper.xml





 



UserService.java
package com.fei.springboot.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.fei.springboot.dao.db01.UserMapper;
import com.fei.springboot.dao.db02.Db02UserMapper;
import com.fei.springboot.domain.User;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

@Service
@Transactional(propagation=Propagation.REQUIRED,readOnly=true,rollbackFor=Exception.class)
public class UserService {

	@Autowired
	private UserMapper userMapper;
	
	@Autowired
	private Db02UserMapper db02UserMapper;
	
	//注意:方法的@Transactional会覆盖类上面声明的事务
	//Propagation.REQUIRED :有事务就处于当前事务中,没事务就创建一个事务
	//isolation=Isolation.DEFAULT:事务数据库的默认隔离级别
	@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,readOnly=false)
	public void insertUser(User u){
		this.userMapper.insert(u);
		this.db02UserMapper.insert(u);
		//如果类上面没有@Transactional,方法上也没有,哪怕throw new RuntimeException,数据库也会成功插入数据
	//	throw new RuntimeException("测试插入事务");
	}
	
	public PageInfo queryPage(String userName,int pageNum,int pageSize){
		Page page = PageHelper.startPage(pageNum, pageSize);
		//PageHelper会自动拦截到下面这查询sql
		this.userMapper.query(userName);
		return page.toPageInfo();
	}
	
	

}
UserController.java

package com.fei.springboot.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fei.springboot.domain.User;
import com.fei.springboot.service.UserService;
import com.github.pagehelper.PageInfo;

@Controller
@RequestMapping("/user")
public class UserController {

	
	@Autowired
	private UserService userService;
	
	@RequestMapping("/hello")
	@ResponseBody
	public String hello(){
		return "hello";
	}
	/**
	 * 测试插入
	 * @return
	 */
	@RequestMapping("/add")
	@ResponseBody
	public String add(String id,String userName){
		User u = new User();
		u.setId(id);
		u.setUserName(userName);
		this.userService.insertUser(u);
		return u.getId()+"    " + u.getUserName();
	}
	/**
	 * 测试分页插件
	 * @return
	 */
	@RequestMapping("/queryPage")
	@ResponseBody
	public String queryPage(){
		PageInfo page = this.userService.queryPage("tes", 1, 2);
		System.out.println("总页数=" + page.getPages());
		System.out.println("总记录数=" + page.getTotal()) ;
		for(User u : page.getList()){
			System.out.println(u.getId() + " \t " + u.getUserName());
		}
		return "success";
	}
	
}

启动类,Application.java

package com.fei.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;

@EnableAutoConfiguration
@ComponentScan(basePackages={"com.fei.springboot"})
@SpringBootApplication
public class Application extends SpringBootServletInitializer implements EmbeddedServletContainerCustomizer{

	 @Override  
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {  
        return application.sources(Application.class);  
    }  

	 
	 public static void main(String[] args) throws Exception {
	        SpringApplication.run(Application.class, args);
	    }

	public void customize(ConfigurableEmbeddedServletContainer configurableEmbeddedServletContainer) {
	//	configurableEmbeddedServletContainer.setPort(9090);
	}
}

执行启动类,浏览器执行 
http://127.0.0.1/user/add?id=12345&userName=test12345

到2个数据库中查看,会发现都有数据了。

测试事务,把UserService类中的insertUser方法中的异常注释去掉,然后在浏览器执行
http://127.0.0.1/user/add?id=789&userName=test789
发现抛出了异常,2个数据库中都没数据

注意:由于事务JtaTransactionManager,是二阶提交,有个缺点就是,第一阶段预提交时候发现2个数据库都没问题,但是第2阶段正真提交时候,第一个数据库提交完成,第二个数据库提交的时候失败了(比如刚好宕机了),抛出了异常,但是第一个数据库没法回滚了,所以可以说产生脏数据了。


完整例子源码,已上传github。







你可能感兴趣的:(spring boot学习6之mybatis+PageHelper分页插件+jta多数据源事务整合)