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

版权声明: https://blog.csdn.net/dream_broken/article/details/72691838

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

  项目文件结构



例子源码,已上传github


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


pom.xml

<parent>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-parent</artifactId>
	    <version>1.5.2.RELEASE</version>
	</parent>   
  
	<dependencies>
	    <dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-starter-web</artifactId>

	    </dependency>
	    
		  <dependency>
   			 <groupId>org.mybatis.spring.boot</groupId>
    		 <artifactId>mybatis-spring-boot-starter</artifactId>
   			 <version>1.3.0</version>
		</dependency> 
		
		<dependency>
    			<groupId>mysql</groupId>
    			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jta-atomikos</artifactId>
		</dependency>
		
		<!-- 分页插件 -->
		<dependency>
    		<groupId>com.github.pagehelper</groupId>
    		<artifactId>pagehelper</artifactId>
    		<version>4.1.6</version>
		</dependency>

		</dependencies>

准备两个数据源数据库(如果只有一个数据源,那就新建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

<?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>
	<settings>
	    <!-- 使全局的映射器启用或禁用缓存。 -->
		<setting name="cacheEnabled" value="true" />
		<!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。 -->
		<setting name="lazyLoadingEnabled" value="true" />
		<!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。 -->        
         <setting name="aggressiveLazyLoading" value="true"/>        
         <!-- 是否允许单条sql 返回多个数据集  (取决于驱动的兼容性) default:true -->
		<setting name="multipleResultSetsEnabled" value="true" />
		<!-- 是否可以使用列的别名 (取决于驱动的兼容性) default:true -->
		<setting name="useColumnLabel" value="true" />
		<!-- 允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。  default:false  -->
		<setting name="useGeneratedKeys" value="false" />
		<!-- 指定 MyBatis 如何自动映射 数据基表的列 NONE:不隐射 PARTIAL:部分  FULL:全部  -->
		<setting name="autoMappingBehavior" value="PARTIAL" />
		<!-- 这是默认的执行类型  (SIMPLE: 简单; REUSE: 执行器可能重复使用prepared statements语句;BATCH: 执行器可以重复执行语句和批量更新)  -->
		<setting name="defaultExecutorType" value="SIMPLE" />
		
		<setting name="defaultStatementTimeout" value="25" />
		
		<setting name="defaultFetchSize" value="100" />
		
		<setting name="safeRowBoundsEnabled" value="false" />
		<!-- 使用驼峰命名法转换字段。 -->
		<setting name="mapUnderscoreToCamelCase" value="true" />
		<!-- 设置本地缓存范围 session:就会有数据的共享  statement:语句范围 (这样就不会有数据的共享 ) defalut:session -->
		<setting name="localCacheScope" value="SESSION" />
		<!-- 默认为OTHER,为了解决oracle插入null报错的问题要设置为NULL -->
		<setting name="jdbcTypeForNull" value="NULL" />
		<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" />
	</settings>
	
</configuration>

第一个数据源

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<ParameterMapping> 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<User> query(@Param("userName")String userName);
	
}
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.fei.springboot.dao.db01.UserMapper">
 
<select id="query"  resultType="com.fei.springboot.domain.User">
    select id ,user_name 
    from sys_user 
    where 1=1
    <if test="userName != null">
      and user_name like CONCAT('%',#{userName},'%')
    </if>
</select>
</mapper>
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<User> query(@Param("userName")String userName);
	
}
Db02UserMapper.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.fei.springboot.dao.db02.Db02UserMapper">
 
<select id="query"  resultType="com.fei.springboot.domain.User">
    select id ,user_name 
    from sys_user 
    where 1=1
    <if test="userName != null">
      and user_name like CONCAT('%',#{userName},'%')
    </if>
</select>
</mapper>



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<User> queryPage(String userName,int pageNum,int pageSize){
		Page<User> 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<User> 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。







猜你喜欢

转载自blog.csdn.net/dream_broken/article/details/72691838