springboot(2.X)实践----多个数据源并开启事务配置、自定义url拦截过滤器、druid配置、全局异常配置等汇总

目录

一、介绍

二、单个数据源以及事务的配置(springboot中)

三、单个数据源的事务如此简单,那多个呢?

1、这里我只使用两个数据源进行配置操作说明。导入依赖就不用说了吧

2、相关配置

2.1、数据库连接池--druid的配置 (单个的druid监控,看新的配置就可以了)

2.2、全局异常的拦截配置

2.3、过滤拦截的配置  

2.4 自定义本地配置文件读取配置

3、(重点)多个数据源的配置和事务的配置

3.1properties文件的编写

3.2、实际的数据库布局

3.3具体的vo类实现

3.4、多个数据源配置的项目目录结构

3.5 、dao层的开发

3.6 、mapper 的配置  

3.7、service实现类的代码

3.8、数据源的配置

3.9、控制层


一、介绍

平时我们使用的springboot配置单个数据源实现数据的增删改查操作,同时配备事务规则;但实际开发中,并不会只对一个数据库实现操作,如何实现多个数据源的操作的同时,也能都满足事务(同时添加数据到多个数据库中,一条失败,其他均需要失败)呢?这时候我们就需要进行多数据源事务的配置了。

二、单个数据源以及事务的配置(springboot中)

server:
  port: 80
mybatis:
  config-location: classpath:mybatis/mybatis.cfg.xml    # mybatis配置文件所在路径
  type-aliases-package: cn.linkpower.vo                 # 定义所有操作类的别名所在包
  mapper-locations:                                     # 所有的mapper映射文件
  - classpath:mybatis/mapper/**/*.xml
spring:
  datasource:
   type: com.alibaba.druid.pool.DruidDataSource         # 配置当前要使用的数据源的操作类型
   driver-class-name: org.gjt.mm.mysql.Driver           # 配置MySQL的驱动程序类
   url: jdbc:mysql://127.0.0.1:3306/mldn                # 数据库连接地址
   username: root                                       # 数据库用户名
   password: root                                       # 数据库连接密码
   filters: stat,wall,log4j                             # 开启druid 监控 还需要进行一项过滤配置
   dbcp2:                                               # 进行数据库连接池的配置
     min-idle: 5                                        # 数据库连接池的最小维持连接数    
     initial-size: 5                                    # 初始化提供的连接数
     max-total: 10                                      # 最大的连接数
     max-wait-millis: 200                               # 等待连接获取的最大超时时间

只需要在service的实现类上注解@Transactional即可进行事务的处理操作

三、单个数据源的事务如此简单,那多个呢?

1、这里我只使用两个数据源进行配置操作说明。导入依赖就不用说了吧

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.4.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springboottestdemo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<!-- 设置相关导入包的编码属性 -->
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<!-- web开发 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- 测试开发 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- springboot整合MyBatis操作 -->
		<!-- 1.配置druid数据库连接池 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.0.4</version>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.0</version>
		</dependency>
		<!-- 2.导入mybatis库文件 -->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.0</version>
		</dependency>
		<!-- 使用aop拦截器 需要的aop库文件 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<!-- 使用thymeleaf模板 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<!-- 阿里巴巴json -->
		<dependency>
		    <groupId>com.alibaba</groupId>
		    <artifactId>fastjson</artifactId>
		    <version>1.2.44</version>
		</dependency>
		<!-- 省略get/set(开发工具需要安装lomback的插件 https://blog.csdn.net/qq_25646191/article/details/79639633) -->
		<!-- <dependency>
	        <groupId>org.projectlombok</groupId>
	        <artifactId>lombok</artifactId>
	        <version>1.16.10</version>
    	</dependency> -->
		<!-- 热部署  start -->
		<!-- 热部署方法1spring boot devtools 依赖包 -->
		<!-- <dependency>
			<groupId>org.springframework</groupId>
			<artifactId>springloaded</artifactId>
			<version>1.2.6.RELEASE</version>
		</dependency> -->
		<!-- 热部署方式2 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional> <!-- 这个需要为 true 热部署才有效 -->
		</dependency>
		<!-- 热部署  end -->
	</dependencies>

	<build>

		<plugins>
			<!-- 打包成jar 插件 -->
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

2、相关配置

依赖导入成功后,我们需要配置数据源和全局拦截异常等操作(一般的项目都会有像token过滤的处理),此处使用的是springboot2.0及以上的配置方式实现除了数据源和事务的配置之外的配置!

2.1、数据库连接池--druid的配置 (单个的druid监控,看新的配置就可以了)

 旧的配置:配置方面在spring 2.X实际使用时,出现了一些莫名奇妙的问题

package com.example.config;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
/**
 * 拓展技能:进行druid数据库连接池的性能的监控操作  需要做一些配置(例如:你访问的ip地址是否是白名单、数据库sql监控等)<br>
 * 访问方式:   http://localhost/druid/
 * @author 76519
 *
 */
//@Configuration
public class DruidConfig {
	private Logger log = LoggerFactory.getLogger(DruidConfig.class);
	//@Bean
	public ServletRegistrationBean druidServlet() { // 主要实现WEB监控的配置处理
		//System.out.println("WEB监控的配置处理");
		this.log.info("WEB监控的配置处理");
		//想用使用 Servlet 功能,就必须要借用 Spring Boot 提供的 ServletRegistrationBean 接口
		//访问方式http://localhost/druid/getAll   启动alibaba的druid数据库连接池自带的servlet
		ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(
				new StatViewServlet(), "/druid/*"); // 现在要进行druid监控的配置处理操作
		servletRegistrationBean.addInitParameter("allow",
				"127.0.0.1,192.168.28.159"); // 白名单
		servletRegistrationBean.addInitParameter("deny", "192.168.28.200"); // 黑名单
		servletRegistrationBean.addInitParameter("loginUsername", "admin"); // 用户名
		servletRegistrationBean.addInitParameter("loginPassword", "hello"); // 密码
		servletRegistrationBean.addInitParameter("resetEnable", "false"); // 是否可以重置数据源
		return servletRegistrationBean ;
	}
	//@Bean
	public FilterRegistrationBean filterRegistrationBean() {
		FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean() ;
		filterRegistrationBean.setFilter(new WebStatFilter());
		filterRegistrationBean.addUrlPatterns("/*"); // 所有请求进行监控处理
		filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.css,/druid/*");
		return filterRegistrationBean ;
	}
	//@Bean
	//@ConfigurationProperties(prefix = "spring.datasource")
	public DataSource druidDataSource() {
		return new DruidDataSource();
	}
}

新的配置

  • pom文件中新增依赖
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
  • application.yml修改配置项
server:
  port: 80  #设置端口号信息
mybatis:
  config-location: classpath:mybatis/mybatis-config.xml    # mybatis配置文件所在路径
  type-aliases-package: cn.linkpower.vo                 # 定义所有操作类的别名所在包
  mapper-locations:                                     # 所有的mapper映射文件
   - classpath:mybatis/mapper/**/*.xml
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/fhadmin?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    # 配置Druid的其他参数,以下配置必须增加一个配置文件才能有效
    # 初始化大小,最小,最大
    initialSize: 5
    minIdle: 5
    maxActive: 20
    # 获取连接等待超时的时间
    maxWait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    timeBetweenEvictionRunsMillis: 60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat, wall
    # 打开PSCache,并且指定每个连接上PSCache的大小
    maxPoolPreparedStatementPerConnectionSize: 20
    # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    # 合并多个DruidDataSource的监控数据
    useGlobalDataSourceStat: true
  • DruidConfig.java配置类重新定义
@Configuration
public class DruidConfig {
	/**
	 * @function 添加
	 * @return
	 */
	@Bean
	@ConfigurationProperties(prefix = "spring.datasource")
	public DataSource druid(){
		return new DruidDataSource();
	}

	//配置Druid的监控
	//1、配置一个管理后台的Servlet
	@Bean
	public ServletRegistrationBean statViewServlet(){
		ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
		Map<String,String> initParams = new HashMap<>();
		initParams.put("loginUsername","admin");
		initParams.put("loginPassword","123456");
		initParams.put("allow","");//默认就是允许所有访问
		initParams.put("deny","192.168.15.21");
		bean.setInitParameters(initParams);
		return bean;
	}

	//2、配置一个web监控的filter
	@Bean
	public FilterRegistrationBean webStatFilter(){
		FilterRegistrationBean bean = new FilterRegistrationBean();
		bean.setFilter(new WebStatFilter());
		Map<String,String> initParams = new HashMap<>();
		initParams.put("exclusions","*.js,*.css,/druid/*");
		bean.setInitParameters(initParams);
		bean.setUrlPatterns(Arrays.asList("/*"));
		return  bean;
	}
}

 新的druid配置,针对2.X,参考博客

这个配置类我之前使用过,是可以进行sql监控的,只是为了进行多数据源事务的测试将其注释了!

2.2、全局异常的拦截配置

package com.example.config;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

/**
 * 异常拦截页面
 * @author 76519
 *
 */
@Configuration
public class ExceptionConfiguration implements HandlerExceptionResolver{
	private Logger log = LoggerFactory.getLogger(ExceptionConfiguration.class);

	@Override
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
			Exception ex) {
		log.info("监听异常");
		//获取异常信息  跳转异常界面
		String exStr = ex.toString().replaceAll("\n", "<br/>");
		ModelAndView mv = new ModelAndView();
		mv.addObject("exception", exStr);
		mv.setViewName("error");
		return mv;
	}

}

2.3、过滤拦截的配置  

有两部分构成,第一部分需要注册需要拦截的请求是什么;第二部分则对需要过滤的请求实现拦截操作

    注册配置

package com.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.example.config.util.BaseInterceptor;

@Configuration
public class InterceptorConfiguration implements WebMvcConfigurer{
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		System.out.println("****InterceptorConfiguration****");
		registry.addInterceptor(new BaseInterceptor()).addPathPatterns("/**/demo");//拦截处理操作的匹配路径
		//2.0以前的配置需要 继承  WebMvcConfigurerAdapter 抽象类  重写此方法后调用下列代码
		//super.addInterceptors(registry);
	}
}

   实际拦截处理的配置

package com.example.config.util;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * 此处进行   拦截器  的配置操作(如登陆拦截等)----使用拦截器  必须进行配置  springboot配置再config包下
 * @author 76519
 *
 */
public class BaseInterceptor implements HandlerInterceptor {
	// 调用日志进行打印信息处理
	private Logger log = LoggerFactory.getLogger(BaseInterceptor.class);

	/**
	 * 请求暂未进入servlet时的操作拦截处理
	 */
	@Override
	public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object handler) throws Exception {
		HandlerMethod handlerMethod = (HandlerMethod) handler;
		this.log.info("[****BaseInterceptor.preHandle()拦截前****]" + 
				handlerMethod.getBean().getClass().getSimpleName());
		// 放过拦截就返回true 否则拦住就返回false(不继续请求)
		return true;
	}

	/**
	 * 处理中
	 */
	@Override
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object handler, ModelAndView modelAndView)
			throws Exception {
		HandlerMethod handlerMethod = (HandlerMethod) handler;
		this.log.info("[****BaseInterceptor.postHandle()控制器处理中****]" + 
				handlerMethod.getBean().getClass().getSimpleName());
		this.log.info("[****BaseInterceptor.postHandle()****]" + modelAndView);
	}

	/**
	 * 处理后
	 */
	@Override
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
		this.log.info("[****BaseInterceptor.afterCompletion()控制器处理后****]拦截处理完毕");
	}

}

2.4 自定义本地配置文件读取配置

如图所示,我需要读取i18n中的自定义 Messages.properties 配置文件信息

toast.msg=香蕉不拿拿先生爱吃香蕉

①将配置文件信息注册至application.properties中

#读取Messages.properties配置文件的消息信息(多个文件的话  使用","分离)
spring.messages.basename=i18n/Messages  

②编写控制器的父类(抽象类)

package com.example.controller.abs;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;

public abstract class AbsController {
	
	@Autowired
	private MessageSource messageSource;
	
	public String getMessage(String msgKey, Object[] args) {
        return messageSource.getMessage(msgKey, args, LocaleContextHolder.getLocale());
    }

    public String getMessage(String msgKey) {
        return messageSource.getMessage(msgKey, null, LocaleContextHolder.getLocale());
    }
}

③对于需要实现文件读取的控制器类,进行继承操作

package com.example.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSON;
import com.example.controller.abs.AbsController;
import com.example.service.IUserServiceMaster;

import net.sf.json.JSONObject;

@Controller
@RequestMapping("/demo")
public class DemoController extends AbsController{

	private Logger log = LoggerFactory.getLogger(DemoController.class);
	
	@RequestMapping("/show")
	@ResponseBody
	public String show(){
		log.info("----->"+String.valueOf(super.getMessage("toast.msg")));
		return "香蕉不拿拿先生爱吃香蕉2";
	}
}

④实际测试

INFO  com.example.controller.DemoController - ----->香蕉不拿拿先生爱吃香蕉

3、(重点)多个数据源的配置和事务的配置

3.1properties文件的编写

#配置端口号信息
server.port=80
#配置数据源1
datasource.master.driver-class-name: com.mysql.jdbc.Driver
datasource.master.url=jdbc:mysql://localhost:3306/banana1?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8
datasource.master.username=root
datasource.master.password=root
#配置数据源2
datasource.slaver.driver-class-name: com.mysql.jdbc.Driver
datasource.slaver.url=jdbc:mysql://localhost:3306/banana2?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8
datasource.slaver.username=root
datasource.slaver.password=root

#配置阿里巴巴数据库连接池
#spring.datasource.type: com.alibaba.druid.pool.DruidDataSource
#最大活跃数
#spring.datasource.maxActive: 20
#初始化数量
#spring.datasource.initialSize: 1
#最大连接等待超时时间
#spring.datasource.maxWait: 60000
#打开PSCache,并且指定每个连接PSCache的大小
#spring.datasource.poolPreparedStatements: true
#spring.datasource.maxPoolPreparedStatementPerConnectionSize: 20
#通过connectionProperties属性来打开mergeSql功能;慢SQL记录
##connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
#spring.datasource.minIdle: 1
#spring.datasource.timeBetweenEvictionRunsMillis: 60000
#spring.datasource.minEvictableIdleTimeMillis: 300000
#spring.datasource.validationQuery: select 1 from dual
#spring.datasource.testWhileIdle: true
#spring.datasource.testOnBorrow: false
#spring.datasource.testOnReturn: false
#配置监控统计拦截的filters,去掉后监控界面sql将无法统计,'wall'用于防火墙
#filters: stat, wall, log4j

3.2、实际的数据库布局

两个数据库的表结构一样!

3.3具体的vo类实现

package com.example.vo;


//引用lombok实现自动生成get/set
public class Users {
	private Integer userid;
	private String username;
	private String password;
	private String descript;
	
	public Integer getUserid() {
		return userid;
	}
	public void setUserid(Integer userid) {
		this.userid = userid;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getDescript() {
		return descript;
	}
	public void setDescript(String descript) {
		this.descript = descript;
	}
	
	@Override
	public String toString() {
		return "Users [userid=" + userid + ", username=" + username + ", password=" + password + ", descript="
				+ descript + "]";
	}
}

本来是打算使用lomback神器进行自动get/set生成的,但发现自己的开发软件并没有安装相关的插件,想想还是算了。

3.4、多个数据源配置的项目目录结构

3.5 、dao层的开发

(两个dao一样的代码,只是包名和类名不同而已,这里就只写一个,写多了累赘)

package com.example.dao.mapper1;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;

import com.example.vo.Users;

public interface UserMapper1 {
	
	//@Select("select * from users where username = #{userName} and password = #{passWord}")
	public Users selectUsersByTerm(String userName,String passWord);
	
	//@Insert("INSERT INTO users(username, password) VALUES(#{userName}, #{passWord})")
	public int insertUsers(Users users);
}

3.6 、mapper 的配置  

我使用的是mybatis

这是mybatis-config.xml的配置,同理,在classpath:mybatis/mapper1 和 classpath:mybatis/mapper2文件一样

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL Map Config 3.0//EN"  
	"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings> 
        <setting name="cacheEnabled" value="true" /><!-- 全局映射器启用缓存 -->   
        <setting name="useGeneratedKeys" value="true" /> 
        <setting name="defaultExecutorType" value="REUSE" /> 
        <!-- 打印sql语句 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
</configuration>
<?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.example.dao.mapper1.UserMapper1">
	<select id="selectUsersByTerm" parameterType="java.lang.String" resultType="com.example.vo.Users">
		select * 
		from users 
		where 
		username = #{userName} and password = #{passWord}
	</select>
	<insert id="insertUsers" parameterType="com.example.vo.Users">
		INSERT INTO users(username, password) VALUES(#{username}, #{password})
	</insert>
</mapper>

两个mapper映射也一样,只是两个mapper的namespace指向不同而已。

3.7、service实现类的代码

(我将两个mapper的同时添加放在一个业务层中实现,要么都成功要么都失败)

package com.example.service.impl;

import javax.annotation.Resource;

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

import com.example.dao.mapper1.UserMapper1;
import com.example.dao.mapper2.UserMapper2;
import com.example.service.IUserServiceMaster;
import com.example.vo.Users;

@Service
public class UserServiceMasterImpl implements IUserServiceMaster {
	@Resource
	private UserMapper1 userMapper1;
	@Resource
	private UserMapper2 userMapper2;

	@Override
	public Users selectUsersByTerm(String userName, String passWord) {
		return userMapper1.selectUsersByTerm(userName, passWord);
	}

	@Transactional
	@Override
	public boolean insertUsers(String userName, String passWord) {
		Users users = new Users();
		users.setUsername(userName);
		users.setPassword(passWord);
		int user1 = userMapper1.insertUsers(users);
		int a = 10/0;
		int user2 = userMapper2.insertUsers(users);
		if (user1 > 0 && user2 > 0) {
			return true;
		}
		return false;
	}

}

3.8、数据源的配置

master的配置

package com.example.config.datasource;

import java.io.IOException;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
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.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import com.alibaba.druid.pool.DruidDataSource;

@Configuration //标注这是配置类
@MapperScan(basePackages=MasterDataSourceConfig.PACKAGE,sqlSessionFactoryRef="masterSqlSessionFactory") //配置扫描的mapper接口和sqlsessionFactory
public class MasterDataSourceConfig {
	static final String PACKAGE = "com.example.dao.mapper1";								//master 目录
    static final String MAPPER_LOCATION = "classpath:mybatis/mapper1/*/*.xml";			//扫描的 xml 目录
    static final String CONFIG_LOCATION = "classpath:mybatis/mapper1/mybatis-config.xml"; //自定义的mybatis config 文件位置
    static final String TYPE_ALIASES_PACKAGE = "com.example.vo"; 						//扫描的 实体类 目录
   
    //引入相关的配置信息    (关联引用application.properties中的配置信息)
    @Value("${datasource.master.username}")
    private String username;
    
    @Value("${datasource.master.password}")
    private String password;
    
    @Value("${datasource.master.url}")
    private String url;
    
    @Value("${datasource.master.driver-class-name}")
    private String driverclass;
    
    //配置数据源
    @Bean(name="masterDataSource")
    @Primary  //当有多个数据源时,为了避免spring的犹豫不决  采取@Primary注解  告诉spring优先使用
    public DataSource masterDataSource(){
    	DruidDataSource druidDataSource = new DruidDataSource();
    	druidDataSource.setDriverClassName(driverclass);
    	druidDataSource.setUrl(url);
    	druidDataSource.setUsername(username);
    	druidDataSource.setPassword(password);
    	return druidDataSource;
    }
    
    //配置事务管理器
    @Bean(name="masterTransactionManager")
    @Primary
    public DataSourceTransactionManager masterTransactionManager(){
    	return new DataSourceTransactionManager(masterDataSource());
    }
    
    //配置sqlsessionFactory (必须添加   @Qualifier  注解  不然会导致两次不同数据源的数据添加会加入到同一个数据库中)
    @Bean(name = "masterSqlSessionFactory")
    @Primary
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource")DataSource masterDataSource) throws Exception{
    	final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(masterDataSource);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MasterDataSourceConfig.MAPPER_LOCATION));
        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(MasterDataSourceConfig.CONFIG_LOCATION));
        sessionFactory.setTypeAliasesPackage(MasterDataSourceConfig.TYPE_ALIASES_PACKAGE);
        return sessionFactory.getObject();
    }
}

salver的数据源和事务配置

package com.example.config.datasource;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import com.alibaba.druid.pool.DruidDataSource;

@Configuration //标注这是配置类
@MapperScan(basePackages=SlaverDataSourceConfig.PACKAGE,sqlSessionFactoryRef="slaverSqlSessionFactory") //配置扫描的mapper接口和sqlsessionFactory
public class SlaverDataSourceConfig {
	static final String PACKAGE = "com.example.dao.mapper2";								//master 目录
    static final String MAPPER_LOCATION = "classpath:mybatis/mapper2/*/*.xml";			//扫描的 xml 目录
    static final String CONFIG_LOCATION = "classpath:mybatis/mapper2/mybatis-config.xml"; //自定义的mybatis config 文件位置
    static final String TYPE_ALIASES_PACKAGE = "com.example.vo"; 						//扫描的 实体类 目录
   
    //引入相关的配置信息    (关联引用application.properties中的配置信息)
    @Value("${datasource.slaver.username}")
    private String username;
    
    @Value("${datasource.slaver.password}")
    private String password;
    
    @Value("${datasource.slaver.url}")
    private String url;
    
    @Value("${datasource.slaver.driver-class-name}")
    private String driverclass;
    
    //配置数据源
    @Bean(name="slaverDataSource")
    public DataSource slaverDataSource(){
    	DruidDataSource druidDataSource = new DruidDataSource();
    	druidDataSource.setDriverClassName(driverclass);
    	druidDataSource.setUrl(url);
    	druidDataSource.setUsername(username);
    	druidDataSource.setPassword(password);
    	return druidDataSource;
    }
    
    //配置事务管理器
    @Bean(name="slaverTransactionManager")
    public DataSourceTransactionManager masterTransactionManager(){
    	return new DataSourceTransactionManager(slaverDataSource());
    }
    
    //配置sqlsessionFactory(必须添加   @Qualifier  注解  不然会导致两次不同数据源的数据添加会加入到同一个数据库中)
    @Bean(name = "slaverSqlSessionFactory")
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("slaverDataSource")DataSource slaverDataSource) throws Exception{
    	final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(slaverDataSource);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(SlaverDataSourceConfig.MAPPER_LOCATION));
        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(SlaverDataSourceConfig.CONFIG_LOCATION));
        sessionFactory.setTypeAliasesPackage(SlaverDataSourceConfig.TYPE_ALIASES_PACKAGE);
        return sessionFactory.getObject();
    }
}

这两个java类的配置表示踩了不少坑,对此做下总结,方便准备踏坑的童鞋看看,少走弯路(对于我没有总结到的,还望大家多多指出):

坑一、最开始配置数据源时,我没有在其中一个数据源的配置上添加  @Primary  注解,导致spring根本不知道使用哪种事务/数据源进行数据操作,如果对spring的@Primary注解不了解的,可以去看看别的大佬的资料。(@Primary只是当一个接口存在多个实现类时,告诉使用者犹豫不决时,优先考虑哪一个实现类);

坑二、在配置sqlSessionFactory的时候,我没有在方法中具体指明哪一个datasource,导致问题是我添加数据的时候,使得两个mapper的操作都执行到了同一个数据库的同一张表中,对此找了好久的bug,最后才知道没有指明具体的datasource。所以后台添加了 @Qualifier("masterDataSource")DataSource masterDataSource 和  @Qualifier("slaverDataSource")DataSource slaverDataSource

3.9、控制层

package com.example.controller;

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

import com.example.service.IUserServiceMaster;

@Controller
@RequestMapping("/demo")
public class DemoController {
	
	@Autowired
	private IUserServiceMaster userServiceMasterImpl;
	
	@RequestMapping("/demo")
	@ResponseBody
	public String index(){
		//int a = 10/0;
		return "这是一根香蕉";
	}
	
	@RequestMapping("/test")
	@ResponseBody
	public String test(){
		userServiceMasterImpl.insertUsers("xiangjiao", "bunana111");
		return "香蕉不拿拿先生爱吃香蕉1";
	}
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值