这篇文章主要介绍,通过Spring Boot整合Mybatis后如何实现在一个工程中实现多数据源。同时可实现读写分离。

准备工作

环境:

windows
jdk 8
maven 3.0
IDEA

创建数据库表

mysql中创建student库并执行下面查询创建student

-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (`sno` int(15) NOT NULL,`sname` varchar(50) DEFAULT NULL,`sex` char(2) DEFAULT NULL,`dept` varchar(25) DEFAULT NULL,`birth` date DEFAULT NULL,`age` int(3) DEFAULT NULL,PRIMARY KEY (`sno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('1', '李同学', '1', '王同学学习成绩很不错', '2010-07-22', '17');

mysql中创建teacher库并执行下面查询创建teacher

-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (`Tno` varchar(20) NOT NULL DEFAULT '',`Tname` varchar(50) DEFAULT NULL,`sex` char(2) DEFAULT NULL,`dept` varchar(25) DEFAULT NULL,`birth` date DEFAULT NULL,`age` int(3) DEFAULT NULL,PRIMARY KEY (`Tno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES ('1', '王老师', '1', '王老师上课很认真', '2018-07-06', '35');

构建工程

<?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>cn.zhangbox</groupId><artifactId>spring-boot-study</artifactId><version>1.0-SNAPSHOT</version></parent><groupId>cn.zhangbox</groupId><artifactId>spring-boot-mybatis-datasource</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>spring-boot-mybatis-datasource</name><description>this 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><!-- 版本控制 --><commons-lang3.version>3.4</commons-lang3.version><commons-codec.version>1.10</commons-codec.version><mybatis-spring-boot.version>1.2.0</mybatis-spring-boot.version><lombok.version>1.16.14</lombok.version><fastjson.version>1.2.41</fastjson.version><druid.version>1.1.2</druid.version></properties><repositories><!-- 阿里私服 --><repository><id>aliyunmaven</id><url>http://maven.aliyun.com/nexus/content/groups/public/</url></repository></repositories><dependencies><!-- mybatis核心包 start --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>${mybatis-spring-boot.version}</version></dependency><!-- mybatis核心包 end --><!-- SpringWEB核心包 start --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- SpringWEB核心包 end --><!-- mysql驱动核心包 start --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- mysql驱动核心包 end --><!-- sprigTest核心包 start --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- sprigTest核心包 end --><!-- commons工具核心包 start --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>${commons-lang3.version}</version></dependency><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>${commons-codec.version}</version></dependency><!-- commons工具核心包 end --><!-- fastjson核心包 start --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson.version}</version></dependency><!-- fastjson核心包 end --><!-- druid核心包 start --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>${druid.version}</version></dependency><!-- druid核心包 end --><!-- lombok核心包 start --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency><!-- lombok核心包 end --></dependencies><build><finalName>spring-boot-mybatis-datasource</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.5.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.19.1</version></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><dependencies><dependency><groupId>org.springframework</groupId><artifactId>springloaded</artifactId><version>1.2.4.RELEASE</version></dependency></dependencies><configuration><mainClass>cn.zhangbox.admin.SpringBootDruidApplication</mainClass><jvmArguments>-Dfile.encoding=UTF-8 -Xdebug-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005</jvmArguments><executable>true</executable><fork>true</fork></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><mainClass>cn.zhangbox.admin.SpringBootDruidApplication</mainClass><jvmArguments>-Dfile.encoding=UTF-8 -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005</jvmArguments><executable>true</executable><fork>true</fork></configuration></plugin></plugins></build></project>

注意:这里引入了lombok插件节省编写实体类时候写getset方法,这里在idea中进行setget操作需要下载lombok插件,在设置页面的plugins中搜索lombok插件在中央插件库下载后重启idea即可,更详细的lombok使用教程可以查考:

程序员DD的lombok系列教程:

Lombok:让JAVA代码更优雅

修改YML配置

#公共配置
server:port: 80tomcat:uri-encoding: UTF-8
spring:#激活哪一个环境的配置文件profiles:active: dev#连接池配置datasource:#配置student库驱动和连接池student:driver-class-name: com.mysql.jdbc.Driver# 使用druid数据源type: com.alibaba.druid.pool.DruidDataSource#配置teacher库驱动和连接池teacher:driver-class-name: com.mysql.jdbc.Driver# 使用druid数据源type: com.alibaba.druid.pool.DruidDataSourcedruid:# 配置测试查询语句validationQuery: SELECT 1 FROM DUAL# 初始化大小,最小,最大initialSize: 10minIdle: 10maxActive: 200# 配置一个连接在池中最小生存的时间,单位是毫秒minEvictableIdleTimeMillis: 180000testOnBorrow: falsetestWhileIdle: trueremoveAbandoned: trueremoveAbandonedTimeout: 1800logAbandoned: true# 打开PSCache,并且指定每个连接上PSCache的大小poolPreparedStatements: truemaxOpenPreparedStatements: 100# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙filters: stat,wall,log4j# 通过connectProperties属性来打开mergeSql功能;慢SQL记录connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000#mybatis
mybatis:# 实体类扫描type-aliases-package: cn.zhangbox.springboot.entity# 配置映射文件位置mapper-locations: classpath:mapper/*.xml# 开启驼峰匹配mapUnderscoreToCamelCase: true---
#开发环境配置
server:#端口port: 8080
spring:profiles: dev# 数据源配置datasource:student:url: jdbc:mysql://127.0.0.1:3306/student?useUnicode=true&characterEncoding=utf8&useSSL=false&tinyInt1isBit=trueusername: rootpassword: 123456teacher:url: jdbc:mysql://127.0.0.1:3306/teacher?useUnicode=true&characterEncoding=utf8&useSSL=false&tinyInt1isBit=trueusername: rootpassword: 123456
#日志
logging:config: classpath:log/logback.xmlpath: log/spring-boot-mybatis-datasource---
#测试环境配置
server:#端口port: 80
spring:profiles: test# 数据源配置datasource:student:url: jdbc:mysql://127.0.0.1:3306/student?useUnicode=true&characterEncoding=utf8&useSSL=false&tinyInt1isBit=trueusername: rootpassword: 123456teacher:url: jdbc:mysql://127.0.0.1:3306/teacher?useUnicode=true&characterEncoding=utf8&useSSL=false&tinyInt1isBit=trueusername: rootpassword: 123456
#日志
logging:config: classpath:log/logback.xmlpath: /home/log/spring-boot-mybatis-datasource---
#生产环境配置
server:#端口port: 8080
spring:profiles: prod# 数据源配置datasource:student:url: jdbc:mysql://127.0.0.1:3306/student?useUnicode=true&characterEncoding=utf8&useSSL=false&tinyInt1isBit=trueusername: rootpassword: 123456teacher:url: jdbc:mysql://127.0.0.1:3306/teacher?useUnicode=true&characterEncoding=utf8&useSSL=false&tinyInt1isBit=trueusername: rootpassword: 123456
#日志
logging:config: classpath:log/logback.xmlpath: /home/log/spring-boot-mybatis-datasource

这里进行了mybatis整合,如果不会mybatis整合可以参考我写的这篇文章:
SpringBoot非官方教程 | 第六篇:SpringBoot整合mybatis

创建日志配置文件

在工程resources文件夹下新建文件夹log,并在该文件夹下创建logback.xml文件,加入以下配置:

<!-- Logback configuration. See http://logback.qos.ch/manual/index.html -->
<configuration scan="true" scanPeriod="10 seconds"><!--继承spring boot提供的logback配置--><!--<include resource="org/springframework/boot/logging/logback/base.xml" />--><!--设置系统日志目录--><property name="APP_DIR" value="spring-boot-mybatis-datasource" /><!-- 彩色日志 --><!-- 彩色日志依赖的渲染类 --><conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" /><conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" /><conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" /><!-- 彩色日志格式 --><property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/><!-- 控制台输出 --><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><Pattern>${CONSOLE_LOG_PATTERN}</Pattern><charset>UTF-8</charset> <!-- 此处设置字符集 --></encoder><!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息--><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>info</level></filter></appender><!-- 时间滚动输出 level为 DEBUG 日志 --><appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 正在记录的日志文件的路径及文件名 --><file>${LOG_PATH}/log_debug.log</file><!--日志文件输出格式--><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern><charset>UTF-8</charset> <!-- 此处设置字符集 --></encoder><!-- 日志记录器的滚动策略,按日期,按大小记录 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--归档的日志文件的路径,例如今天是2017-04-26日志,当前写的日志文件路径为file节点指定,可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。而2017-04-26的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引--><fileNamePattern>${LOG_PATH}/debug/log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern><!--除按日志记录之外,还配置了日志文件不能超过500M,若超过500M,日志文件会以索引0开始,命名日志文件,例如log-error-2017-04-26.0.log--><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>500MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy><!--日志文件保留天数--><maxHistory>30</maxHistory></rollingPolicy><!-- 此日志文件只记录debug级别的 --><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>debug</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><!-- 时间滚动输出 level为 INFO 日志 --><appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 正在记录的日志文件的路径及文件名 --><file>${LOG_PATH}/log_info.log</file><!--日志文件输出格式--><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern><charset>UTF-8</charset> <!-- 此处设置字符集 --></encoder><!-- 日志记录器的滚动策略,按日期,按大小记录 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--归档的日志文件的路径,例如今天是2017-04-26日志,当前写的日志文件路径为file节点指定,可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。而2017-04-26的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引--><fileNamePattern>${LOG_PATH}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern><!--除按日志记录之外,还配置了日志文件不能超过500M,若超过500M,日志文件会以索引0开始,命名日志文件,例如log-error-2017-04-26.0.log--><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>500MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy><!--日志文件保留天数--><maxHistory>30</maxHistory></rollingPolicy><!-- 此日志文件只记录info级别的 --><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>info</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><!-- 时间滚动输出 level为 WARN 日志 --><appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 正在记录的日志文件的路径及文件名 --><file>${LOG_PATH}/log_warn.log</file><!--日志文件输出格式--><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern><charset>UTF-8</charset> <!-- 此处设置字符集 --></encoder><!-- 日志记录器的滚动策略,按日期,按大小记录 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--归档的日志文件的路径,例如今天是2017-04-26日志,当前写的日志文件路径为file节点指定,可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。而2017-04-26的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引--><fileNamePattern>${LOG_PATH}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern><!--除按日志记录之外,还配置了日志文件不能超过500M,若超过500M,日志文件会以索引0开始,命名日志文件,例如log-error-2017-04-26.0.log--><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>500MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy><!--日志文件保留天数--><maxHistory>30</maxHistory></rollingPolicy><!-- 此日志文件只记录warn级别的 --><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>warn</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><!-- 时间滚动输出 level为 ERROR 日志 --><appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 正在记录的日志文件的路径及文件名 --><file>${LOG_PATH}/log_error.log</file><!--日志文件输出格式--><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern><charset>UTF-8</charset> <!-- 此处设置字符集 --></encoder><!-- 日志记录器的滚动策略,按日期,按大小记录 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--归档的日志文件的路径,例如今天是2017-04-26日志,当前写的日志文件路径为file节点指定,可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。而2017-04-26的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引--><fileNamePattern>${LOG_PATH}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern><!--除按日志记录之外,还配置了日志文件不能超过500M,若超过500M,日志文件会以索引0开始,命名日志文件,例如log-error-2017-04-26.0.log--><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>500MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy><!--日志文件保留天数--><maxHistory>30</maxHistory></rollingPolicy><!-- 此日志文件只记录ERROR级别的 --><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>error</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><logger name="org.springframework.web" level="info"/><logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/><logger name="cn.zhangbox.springboot" level="debug"/><!--开发环境:打印控制台--><springProfile name="dev"><root level="info"><appender-ref ref="CONSOLE" /><appender-ref ref="DEBUG_FILE" /><appender-ref ref="INFO_FILE" /><appender-ref ref="WARN_FILE" /><appender-ref ref="ERROR_FILE" /></root></springProfile><!--测试环境:打印控制台和输出到文件--><springProfile name="test"><root level="info"><appender-ref ref="CONSOLE" /><appender-ref ref="INFO_FILE" /><appender-ref ref="WARN_FILE" /><appender-ref ref="ERROR_FILE" /></root></springProfile><!--生产环境:输出到文件--><springProfile name="prod"><root level="info"><appender-ref ref="DEBUG_FILE" /><appender-ref ref="INFO_FILE" /><appender-ref ref="ERROR_FILE" /></root></springProfile></configuration>

注意:loback配置文件中

<logger name="cn.zhangbox.springboot" level="debug"/>

name的属性值一定要是当前工程的java代码的完整目录,因为mybatis打印的日志级别是debug级别的,因此需要配置debug级别日志扫描的目录。

创建Druid配置类

在工程java代码目录下创建config的目录在下面创建DruidDBConfig类加入以下代码:

@Configuration
public class DruidDBConfig {@Beanpublic ServletRegistrationBean druidServlet() {ServletRegistrationBean reg = new ServletRegistrationBean();reg.setServlet(new StatViewServlet());reg.addUrlMappings("/druid/*");//设置控制台管理用户reg.addInitParameter("loginUsername","root");reg.addInitParameter("loginPassword","root");// 禁用HTML页面上的“Reset All”功能reg.addInitParameter("resetEnable","false");//reg.addInitParameter("allow", "127.0.0.1"); //白名单return reg;}@Beanpublic FilterRegistrationBean filterRegistrationBean() {//创建过滤器FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();filterRegistrationBean.setFilter(new WebStatFilter());Map<String, String> initParams = new HashMap<String, String>();//忽略过滤的形式initParams.put("exclusions", "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*");filterRegistrationBean.setInitParameters(initParams);//设置过滤器过滤路径filterRegistrationBean.addUrlPatterns("/*");return filterRegistrationBean;}
}

注意:这里ServletRegistrationBean 配置bean中通过addInitParameter设置了管控台的用户名密码都是root,可以在这里进行自定义配置也可以将这里的用户名密码通过转移数据库进行定制化配置实现。

创建Student数据源配置类

在工程java代码目录下创建config的目录在下面创建StudentDataSourceConfig类加入以下代码:

@Configuration
@MapperScan(basePackages ="cn.zhangbox.springboot.dao.student",sqlSessionFactoryRef = "studentSqlSessionFactory")//mybatis接口包扫描
public class StudentDataSourceConfig {@Value("${spring.datasource.student.type}")private Class<? extends DataSource> dataSourceType;/***初始化连接池* @return*/@Bean(name = "studentDataSource")@ConfigurationProperties(prefix = "spring.datasource.student")@Primarypublic DataSource writeDataSource() {return DataSourceBuilder.create().type(dataSourceType).build();}/**** 构建 SqlSessionFactory* @return*/@Bean(name = "studentSqlSessionFactory")@Primarypublic SqlSessionFactory studentSqlSessionFactory(@Qualifier("studentDataSource") DataSource dataSource) throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(dataSource);//bean.setTypeAliasesPackage("com.ztzq.data.beans.bigdata");bean.setVfs(SpringBootVFS.class);bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/student/*.xml"));return bean.getObject();}/*** 配置事物* @param dataSource* @return*/@Bean(name = "studentTransactionManager")@Primarypublic DataSourceTransactionManager TransactionManager(@Qualifier("studentDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}/*** 构建 SqlSessionTemplate* @param sqlSessionFactory* @return* @throws Exception*/@Bean(name = "studentSqlSessionTemplate")@Primarypublic SqlSessionTemplate SqlSessionTemplate(@Qualifier("studentSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {return new SqlSessionTemplate(sqlSessionFactory);}
}

创建Teacher数据源配置类

在工程java代码目录下创建config的目录在下面创建TeacherDataSourceConfig类加入以下代码:

@Configuration
@MapperScan(basePackages ="cn.zhangbox.springboot.dao.teacher",sqlSessionFactoryRef = "teacherSqlSessionFactory")//mybatis接口包扫描
public class TecaherDataSourceConfig {@Value("${spring.datasource.teacher.type}")private Class<? extends DataSource> dataSourceType;/***初始化连接池* @return*/@Bean(name = "teacherDataSource")@ConfigurationProperties(prefix = "spring.datasource.teacher")public DataSource writeDataSource() {return DataSourceBuilder.create().type(dataSourceType).build();}/**** 构建 SqlSessionFactory* @return*/@Bean(name = "teacherSqlSessionFactory")public SqlSessionFactory teacherSqlSessionFactory(@Qualifier("teacherDataSource") DataSource dataSource) throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(dataSource);//bean.setTypeAliasesPackage("com.ztzq.data.beans.bigdata");bean.setVfs(SpringBootVFS.class);bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/teacher/*.xml"));return bean.getObject();}/*** 配置事物* @param dataSource* @return*/@Bean(name = "teacherTransactionManager")public DataSourceTransactionManager TransactionManager(@Qualifier("teacherDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}/*** 构建 SqlSessionTemplate* @param sqlSessionFactory* @return* @throws Exception*/@Bean(name = "teacherSqlSessionTemplate")public SqlSessionTemplate SqlSessionTemplate(@Qualifier("teacherSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {return new SqlSessionTemplate(sqlSessionFactory);}
}

创建实体

在工程java代码目录下创建entity的目录在下面创建Student类加入以下代码:

@Data
@EqualsAndHashCode(callSuper = false)
public class Student {private static final long serialVersionUID = 1L;/*** 主键id*/private Integer sno;/*** 学生姓名*/private String sname;/*** 性别*/private String sex;/*** 生日*/private String birth;/*** 年龄*/private String age;/*** 简介*/private String dept;}

在工程java代码目录下创建entity的目录在下面创建Teacher类加入以下代码:

@Data
@EqualsAndHashCode(callSuper = false)
public class Teacher {private static final long serialVersionUID = 1L;/*** 主键id*/private Integer tno;/*** 老师姓名*/private String tname;/*** 性别*/private String sex;/*** 生日*/private String birth;/*** 年龄*/private String age;/*** 简介*/private String dept;}

创建Controller

在工程java代码目录下创建controller的目录在下面创建StudentConteroller类加入以下代码:

@Controller
@RequestMapping("/student")
public class StudentConteroller {private static final Logger LOGGER = LoggerFactory.getLogger(StudentConteroller.class);@Autowiredprotected StudentService studentService;/*** 查询所有的学生信息** @param sname* @param age* @param modelMap* @return*/@ResponseBody@GetMapping("/list")public String list(String sname, Integer age, ModelMap modelMap) {String json = null;try {List<Student> studentList = studentService.getStudentList(sname, age);modelMap.put("ren_code", "0");modelMap.put("ren_msg", "查询成功");modelMap.put("studentList", studentList);json = JSON.toJSONString(modelMap);} catch (Exception e) {e.printStackTrace();modelMap.put("ren_code", "0");modelMap.put("ren_msg", "查询失败===>" + e);LOGGER.error("查询失败===>" + e);json = JSON.toJSONString(modelMap);}return json;}
}

在工程java代码目录下创建controller的目录在下面创建TeacherConteroller类加入以下代码:

@Controller
@RequestMapping("/teacher")
public class TeacherConteroller {private static final Logger LOGGER = LoggerFactory.getLogger(TeacherConteroller.class);@Autowiredprotected TeacherService teacherService;/*** 查询所有的老师信息** @param tname* @param age* @param modelMap* @return*/@ResponseBody@GetMapping("/list")public String list(String tname, Integer age, ModelMap modelMap) {String json = null;try {List<Teacher> teacherList = teacherService.getTeacherList(tname, age);modelMap.put("ren_code", "0");modelMap.put("ren_msg", "查询成功");modelMap.put("teacherList", teacherList);json = JSON.toJSONString(modelMap);} catch (Exception e) {e.printStackTrace();modelMap.put("ren_code", "0");modelMap.put("ren_msg", "查询失败===>" + e);LOGGER.error("查询失败===>" + e);json = JSON.toJSONString(modelMap);}return json;}
}

创建Service

在工程java代码目录下面创建service目录在下面创建StudentService类加入以下代码:

public interface StudentService {/*** 查询所有的学生信息** @param sname* @param age* @return*/List<Student> getStudentList(String sname, Integer age);
}

在工程java代码目录下面创建service目录在下面创建TeacherService类加入以下代码:

public interface TeacherService {/*** 查询所有的老师信息** @param tname* @param age* @return*/List<Teacher> getTeacherList(String tname, Integer age);
}

创建ServiceImpl

在工程java代码目录下的service的目录下面创建impl目录在下面创建StudentServiceImpl类加入以下代码:

@Service("StudentService")
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class StudentServiceImpl implements StudentService {@AutowiredStudentDao studentDao;@Overridepublic List<Student> getStudentList(String sname, Integer age) {return studentDao.getStudentList(sname,age);}
}

在工程java代码目录下的service的目录下面创建impl目录在下面创建TeacherServiceImpl类加入以下代码:

@Service("TeacherService")
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class TeacherServiceImpl implements TeacherService {@AutowiredTeacherDao teacherDao;@Overridepublic List<Teacher> getTeacherList(String tname, Integer age) {return teacherDao.getTeacherList(tname,age);}
}

创建Dao

在工程java代码目录下创建dao的目录下面创建student目录在此目录下创建StudentDao类加入以下代码:

public interface StudentDao {List<Student> getStudentList(@Param("sname")String sname, @Param("age")Integer age);}

在工程java代码目录下创建dao的目录下面创建teacher目录在此目录下创建TeacherDao类加入以下代码:

public interface TeacherDao {List<Teacher> getTeacherList(@Param("tname") String tname, @Param("age") Integer age);}

创建Mapper映射文件

在工程resource目录下创建mapper的目录下创建student目录在此目录下面创建StudentMapper.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="cn.zhangbox.springboot.dao.StudentDao" ><!-- 查询所有的学生信息 --><select id="getStudentList" resultType="cn.zhangbox.springboot.entity.Student">SELECTs.sno,s.sname,s.sex,s.dept,s.birth,s.ageFROMstudent sWHERE1 = 1<if test="sname != null">and s.sname = #{sname}</if><if test="age != null">and s.age = #{age}</if></select>
</mapper>

在工程resource目录下创建mapper的目录下创建teacher目录在此目录下面创建TeacherMapper.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="cn.zhangbox.springboot.dao.teacher.TeacherDao" ><!-- 查询所有的老师信息 --><select id="getTeacherList" resultType="cn.zhangbox.springboot.entity.Teacher">SELECTs.tno,s.tname,s.sex,s.dept,s.birth,s.ageFROMteacher sWHERE1 = 1<if test="tname != null">and s.tname = #{tname}</if><if test="age != null">and s.age = #{age}</if></select>
</mapper>

创建启动类

@SpringBootApplication
public class SpringBootManyDataSourceApplication extends SpringBootServletInitializer {public static void main(String[] args) {SpringApplication.run(SpringBootManyDataSourceApplication.class, args);}
}

启动项目进行测试:

控制台打印

 .   ____          _            __ _ _/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/  ___)| |_)| | | | | || (_| |  ) ) ) )'  |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot ::        (v1.5.3.RELEASE)2018-07-09 19:58:22.757  INFO 10096 --- [           main] .z.s.SpringBootManyDataSourceApplication : Starting SpringBootManyDataSourceApplication on 99IHXFJDHAQ7H7N with PID 10096 (started by Administrator in D:\开源项目\spring-boot-study)
2018-07-09 19:58:22.780  INFO 10096 --- [           main] .z.s.SpringBootManyDataSourceApplication : The following profiles are active: dev
2018-07-09 19:58:22.987  INFO 10096 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@35e5d0e5: startup date [Mon Jul 09 19:58:22 CST 2018]; root of context hierarchy
2018-07-09 19:58:23.460  INFO 10096 --- [kground-preinit] o.h.validator.internal.util.Version      : HV000001: Hibernate Validator 5.3.5.Final
2018-07-09 19:58:24.220  INFO 10096 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'filterRegistrationBean' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=druidDBConfig; factoryMethodName=filterRegistrationBean; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [cn/zhangbox/springboot/config/DruidDBConfig.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=com.alibaba.druid.spring.boot.autoconfigure.stat.DruidWebStatFilterConfiguration; factoryMethodName=filterRegistrationBean; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/alibaba/druid/spring/boot/autoconfigure/stat/DruidWebStatFilterConfiguration.class]]
2018-07-09 19:58:25.440  INFO 10096 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2018-07-09 19:58:25.457  INFO 10096 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2018-07-09 19:58:25.459  INFO 10096 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.14
2018-07-09 19:58:25.594  INFO 10096 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2018-07-09 19:58:25.594  INFO 10096 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2608 ms
2018-07-09 19:58:26.138  INFO 10096 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'statViewServlet' to [/druid/*]
2018-07-09 19:58:26.139  INFO 10096 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
2018-07-09 19:58:26.140  INFO 10096 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'statViewServlet' to [/druid/*]
2018-07-09 19:58:26.141  INFO 10096 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet statViewServlet was not registered (possibly already registered?)
2018-07-09 19:58:26.146  INFO 10096 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-07-09 19:58:26.147  INFO 10096 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-07-09 19:58:26.148  INFO 10096 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-07-09 19:58:26.148  INFO 10096 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2018-07-09 19:58:26.149  INFO 10096 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'webStatFilter' to urls: [/*]
2018-07-09 19:58:27.694  INFO 10096 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@35e5d0e5: startup date [Mon Jul 09 19:58:22 CST 2018]; root of context hierarchy
2018-07-09 19:58:27.792  INFO 10096 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/student/list],methods=[GET]}" onto public java.lang.String cn.zhangbox.springboot.controller.StudentConteroller.list(java.lang.String,java.lang.Integer,org.springframework.ui.ModelMap)
2018-07-09 19:58:27.794  INFO 10096 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/teacher/list],methods=[GET]}" onto public java.lang.String cn.zhangbox.springboot.controller.TeacherConteroller.list(java.lang.String,java.lang.Integer,org.springframework.ui.ModelMap)
2018-07-09 19:58:27.796  INFO 10096 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-07-09 19:58:27.796  INFO 10096 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-07-09 19:58:27.837  INFO 10096 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-07-09 19:58:27.837  INFO 10096 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-07-09 19:58:27.893  INFO 10096 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-07-09 19:58:28.836  INFO 10096 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2018-07-09 19:58:28.837  INFO 10096 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'studentDataSource' has been autodetected for JMX exposure
2018-07-09 19:58:28.838  INFO 10096 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'teacherDataSource' has been autodetected for JMX exposure
2018-07-09 19:58:28.838  INFO 10096 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'statFilter' has been autodetected for JMX exposure
2018-07-09 19:58:28.846  INFO 10096 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located MBean 'studentDataSource': registering with JMX server as MBean [com.alibaba.druid.pool:name=studentDataSource,type=DruidDataSource]
2018-07-09 19:58:28.849  INFO 10096 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located MBean 'teacherDataSource': registering with JMX server as MBean [com.alibaba.druid.pool:name=teacherDataSource,type=DruidDataSource]
2018-07-09 19:58:28.851  INFO 10096 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located MBean 'statFilter': registering with JMX server as MBean [com.alibaba.druid.filter.stat:name=statFilter,type=StatFilter]
2018-07-09 19:58:28.877  INFO 10096 --- [           main] o.a.coyote.http11.Http11NioProtocol      : Initializing ProtocolHandler ["http-nio-8080"]
2018-07-09 19:58:28.905  INFO 10096 --- [           main] o.a.coyote.http11.Http11NioProtocol      : Starting ProtocolHandler ["http-nio-8080"]
2018-07-09 19:58:28.930  INFO 10096 --- [           main] o.a.tomcat.util.net.NioSelectorPool      : Using a shared selector for servlet write/read
2018-07-09 19:58:28.965  INFO 10096 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2018-07-09 19:58:28.972  INFO 10096 --- [           main] .z.s.SpringBootManyDataSourceApplication : Started SpringBootManyDataSourceApplication in 8.519 seconds (JVM running for 12.384)
2018-07-09 19:58:37.626  INFO 10096 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2018-07-09 19:58:37.626  INFO 10096 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2018-07-09 19:58:37.660  INFO 10096 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 33 ms
2018-07-09 19:58:37.981  INFO 10096 --- [nio-8080-exec-1] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
2018-07-09 19:58:41.381  INFO 10096 --- [nio-8080-exec-2] com.alibaba.druid.pool.DruidDataSource   : {dataSource-2} inited

浏览器请求测试

图片.png

图片.png

本地日志打印效果

图片.png

图片.png

2018-07-09 19:58:22.779 [main] DEBUG c.z.springboot.SpringBootManyDataSourceApplication - Running with Spring Boot v1.5.3.RELEASE, Spring v4.3.8.RELEASE
2018-07-09 19:58:38.386 [http-nio-8080-exec-1] DEBUG c.z.s.dao.student.StudentDao.getStudentList - ==>  Preparing: SELECT s.sno, s.sname, s.sex, s.dept, s.birth, s.age FROM student s WHERE 1 = 1
2018-07-09 19:58:38.409 [http-nio-8080-exec-1] DEBUG c.z.s.dao.student.StudentDao.getStudentList - ==> Parameters:
2018-07-09 19:58:38.436 [http-nio-8080-exec-1] DEBUG c.z.s.dao.student.StudentDao.getStudentList - <==      Total: 2
2018-07-09 19:58:41.461 [http-nio-8080-exec-2] DEBUG c.z.s.dao.teacher.TeacherDao.getTeacherList - ==>  Preparing: SELECT s.tno, s.tname, s.sex, s.dept, s.birth, s.age FROM teacher s WHERE 1 = 1
2018-07-09 19:58:41.462 [http-nio-8080-exec-2] DEBUG c.z.s.dao.teacher.TeacherDao.getTeacherList - ==> Parameters:
2018-07-09 19:58:41.472 [http-nio-8080-exec-2] DEBUG c.z.s.dao.teacher.TeacherDao.getTeacherList - <==      Total: 1

这里使用logback配置中将不同级别的日志设置了在不同文件中打印,这样很大程度上方便项目出问题查找问题。

Druid管控台

图片.png

图片.png

图片.png

图片.png

图片.png

druid链接池不知道怎么配置可以参考我写的这篇文章:
SpringBoot进阶教程 | 第三篇:整合Druid连接池以及Druid监控

源码地址

Spring Boot整合Mybatis实现多数据源源码

作者:星缘1314
链接:https://www.jianshu.com/p/61ad5a32a036
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

关注「编程微刊」公众号 ,在微信后台回复「领取资源」,获取IT资源300G干货大全。

公众号回复“1”,拉你进程序员技术讨论群.

SpringBoot进阶教程 | 第四篇:整合Mybatis实现多数据源 1相关推荐

  1. SpringBoot进阶教程 | 第四篇:整合Mybatis实现多数据源

    这篇文章主要介绍,通过Spring Boot整合Mybatis后如何实现在一个工程中实现多数据源.同时可实现读写分离. 准备工作 环境: windows jdk 8 maven 3.0 IDEA 创建 ...

  2. SpringBoot实战教程(4)| 整合Mybatis

    ​MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis .2 ...

  3. SpringCloud核心教程 | 第四篇:服务注册与发现 Consul篇

    Spring Cloud简介 Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中涉及的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全 ...

  4. 史上最简单的SpringCloud教程 | 第四篇:断路器(Hystrix)

    转:https://blog.csdn.net/forezp/article/details/69934399 最新版本: 史上最简单的SpringCloud教程 | 第四篇:断路器(Hystrix) ...

  5. SpringCloud教程 | 第四篇:断路器(Hystrix)

    SpringCloud教程 | 第四篇:断路器(Hystrix) 在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTempl ...

  6. MVC教程第四篇:传递表单数据

    MVC教程第四篇:传递表单数据     摘要 本文将完成我们"MVC公告发布系统"的公告发布功能,以此展示在ASP.NET MVC中如何传递处理表单的数据. 前言 通过前几篇文章, ...

  7. SpringBoot非官方教程 | 第三篇:SpringBoot用JdbcTemplates访问Mysql

    SpringBoot非官方教程 | 第三篇:SpringBoot用JdbcTemplates访问Mysql 本文介绍springboot通过jdbc访问关系型mysql,通过spring的JdbcTe ...

  8. 数据恢复基础和进阶教程(四)---NTFS文件系统的特点及几种常用的数据恢复方法

    数据恢复基础和进阶教程(四)-NTFS文件系统的特点及几种常用的数据恢复方法 *在每逢年底必加班的大环境下,这两天单位比较忙,就连中午和晚上基本都不能按时休息,所以没及时更新最新的课程,抱歉.今天工作 ...

  9. SpringBoot2x整合Mybatis(多数据源)

    SpringBoot2x整合Mybatis(多数据源)现在基本上都有各大项目必须的配置,一个项目需要多个数据源进行操作多个数据库,下面以三数据源为例,如果有更多数据源直接照着弄即可. 1.pom依赖 ...

最新文章

  1. 数据结构与算法 | 队列
  2. Vue--- 一点车项目
  3. DataNode之BlockSender分析
  4. java零碎要点---用java实现生成二维码,与解析代码实现
  5. 绘图添加网格_Stata绘图: 添加虚线网格线
  6. 【OpenCV4】计算对称矩阵特征值和特征向量 cv::eigen() 用法详解和代码示例(c++)
  7. 计算机课翻转课堂教案,翻转课堂式教学
  8. jQuery实践-别踩白块儿网页版
  9. c语言1ms延时程序,请教老师,51微控制器在12M晶振,C语言程式设计时,延时函式120次=1ms(书本上是这样的,不懂),是怎样得来的?...
  10. isfinite函数_isfinite()函数以及C ++中的示例
  11. 如何分组取最新的数据
  12. 284、超详细的光纤熔纤、盘纤教程,值得收藏
  13. 阿里视频云web播放器常见问题汇总
  14. 添加完商品后,点货品显示此商品不存在规格,请为其添加规格?
  15. 快的买大黄蜂是福是祸?
  16. 83. 测试是软件开发的工程严谨度
  17. 虚拟地址和物理地址及其映射
  18. Python爬虫之gif图片抓取
  19. Can't execute code from a freed script
  20. java 日历签到功能_Android 在日历的基础上实现签到功能

热门文章

  1. 电脑桌面云便签账号怎么更换绑定邮箱?
  2. 深度学习GPU环境搭建
  3. 始于20200613关于第一人称射击游戏猿粪计划开发日志
  4. 算术运算符与关系运算符
  5. umount解挂磁盘报*** is busy解决办法
  6. block()/blockFirst()/blockLast() are blocking,which is not supported in thread reactor-http-kqueue-3
  7. 数理统计讲义第二章课后答案12
  8. 【bzoj1690/Usaco2007 Dec】奶牛的旅行——分数规划 最优比率环
  9. 光大银行外包软件测试,文思海辉蝉联光大银行人力外包测试领域优秀服务商
  10. Stata导入数据的几种方法