SpringBoot入門之基於Druid配置Mybatis多數據源
上一篇了解了Druid進行配置連接池的監控和慢sql處理,這篇了解下使用基於基於Druid配置Mybatis多數據源。SpringBoot默認配置數據庫連接信息時只需設置url等屬性信息就可以了,SpringBoot就會基於約定根據配置信息實例化對象,但是一般大型的項目都是有多個子系統或者多個數據源組成,那怎麽使用SpringBoot進行Mybatis多數據源配置呢?
一、數據庫準備
我們這裏準備使用主從兩個數據庫來進行演示多數據源配置。一個主庫用來寫write,一個從庫用來讀read.至於兩個數據庫的數據同步問題這裏暫時不考慮。兩個數據庫只是數據庫名不一樣,主庫為mybatis1,從庫為mybatis,表結構是一樣的。
主庫(write):
CREATE DATABASE `mybatis1` /*!40100 DEFAULT CHARACTER SET utf8 */; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) DEFAULT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8;
從庫(read):
CREATE DATABASE`mybatis` /*!40100 DEFAULT CHARACTER SET utf8 */; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) DEFAULT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8;
二、引入依賴
這裏主要引入mysql數據庫、mybatis架構、Druid相關的SpringBoot依賴。下面的是由於要使用jsp顯示內容所以也假如了jsp相關的依賴。
![技術分享圖片](/img/jia.gif)
<?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> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demo</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf-spring5 <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.9.RELEASE</version> </dependency> --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>View Code
三、創建Mapper
從這裏開始就比較坑了,為了寫這篇博客昨天搞到夜裏兩點中,Druid官方介紹的比較少,demo也不是與mybatis相結合,就倒置怎麽把mapper與數據源配置對照上也是問題,因為默認單數據源的話,配置下數據源信息以及Mapper就好,但是如果是多數據源那就要手動指定數據源在哪裏,怎麽和Mapper對照上。
這裏先創建兩個Mappe,一個是寫的一個是讀的。這裏要註意的地方是要加上@Mapper註解。
ReadUserMapper:
![技術分享圖片](/img/jia.gif)
package com.example.read.mapper; import java.util.List; import com.example.model.User; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Result; import org.apache.ibatis.annotations.Results; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; @Mapper public interface ReadUserMapper { @Select("SELECT name FROM user") @Results({ @Result(property = "Name", column = "name") }) List<User> getAll(); @Select("SELECT name FROM user WHERE id = #{id}") @Results({ @Result(property = "Name", column = "name") }) User getOne(int id); @Insert("INSERT INTO user(name,age) VALUES(#{name}, #{age})") void insert(User user); @Update("UPDATE user SET name=#{name},age=#{age} WHERE id =#{id}") void update(User user); @Delete("DELETE FROM user WHERE id =#{id}") void delete(int id); }View Code
WriteUserMapper:
![技術分享圖片](/img/jia.gif)
package com.example.write.mapper; import java.util.List; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Result; import org.apache.ibatis.annotations.Results; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import com.example.model.*; @Mapper public interface WriteUserMapper { @Select("SELECT name FROM user") @Results({ @Result(property = "Name", column = "name") }) List<User> getAll(); @Select("SELECT name FROM user WHERE id = #{id}") @Results({ @Result(property = "Name", column = "name") }) User getOne(int id); @Insert("INSERT INTO user(name,age) VALUES(#{name}, #{age})") void insert(User user); @Update("UPDATE user SET name=#{name},age=#{age} WHERE id =#{id}") void update(User user); @Delete("DELETE FROM user WHERE id =#{id}") void delete(int id); }View Code
四、配置數據源
如果使用SpringBoot默認配置類,可以直接在application.properties中配置就好了,它會自動掃描mapper類與數據源進行關聯,但是如果是多個數據源的話,那就需要進行手動配置。這裏分別創建了讀DataSourceReadConfig、寫DataSourceWriteConfig數據源配置類。
DataSourceWriteConfig:
![技術分享圖片](/img/jia.gif)
package com.example.config; import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; 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.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Component; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; @Configuration @MapperScan(basePackages = "com.example.write.mapper", sqlSessionTemplateRef = "writeSqlSessionTemplate") public class DataSourceWriteConfig { @Bean(name = "writeDataSource") @ConfigurationProperties(prefix = "spring.datasource.druid.write") @Qualifier("writeDataSource") @Primary public DataSource writeDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean(name = "writeSqlSessionFactory") @Primary public SqlSessionFactory writeSqlSessionFactory(@Qualifier("writeDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); return bean.getObject(); } @Bean(name = "writeTransactionManager") @Primary public DataSourceTransactionManager writeTransactionManager(@Qualifier("writeDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "writeSqlSessionTemplate") @Primary public SqlSessionTemplate writeSqlSessionTemplate(@Qualifier("writeSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }View Code
DataSourceReadConfig:
![技術分享圖片](/img/jia.gif)
package com.example.config; import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; 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.jdbc.datasource.DataSourceTransactionManager; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; @Configuration @MapperScan(basePackages = "com.example.read.mapper", sqlSessionTemplateRef = "readSqlSessionTemplate") public class DataSourceReadConfig { @Bean(name = "readDataSource") @ConfigurationProperties(prefix = "spring.datasource.druid.read") @Qualifier("readDataSource") public DataSource readDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean(name = "readSqlSessionFactory") public SqlSessionFactory readSqlSessionFactory(@Qualifier("readDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource((javax.sql.DataSource) dataSource); return bean.getObject(); } @Bean(name = "readTransactionManager") public DataSourceTransactionManager readTransactionManager(@Qualifier("readDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "readSqlSessionTemplate") public SqlSessionTemplate readSqlSessionTemplate(@Qualifier("readSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }View Code
這部分是遇到坑最多的地方,由於Druid官方github上並沒有具體參考的demo,看其他的博客又與gitgub介紹的有出入,比如獲取配置類中獲取DataSource方法中,其他我看使用的是DataSourceBuilder,但Druid GitHub上的是DruidDataSourceBuilder,不知道是不是版本的問題,如果使用DataSourceBuilder,配置多數據庫時不起作用。github上也有這句話:Spring Boot 2.X 版本不再支持配置繼承,多數據源的話每個數據源的所有配置都需要單獨配置,否則配置不會生效。還有就是DataSource引入的包名,我開始引入的並不是import javax.sql.DataSource;這個也是一個坑。
五、Druid多數據源配置
這裏也遇到了坑,由於在配置數據源類中並未使用DruidDataSourceBuilder,而是使用的DataSourceBuilder,這就導致下面配置的沒用,而且在設置數據庫url還報錯,需要使用jdbc-url.
![技術分享圖片](/img/jia.gif)
spring.mvc.view.prefix=/view/ spring.mvc.view.suffix=.jsp mybatis.type-aliases-package=com.example.model #mybatis.config-location=classpath:mybatis/mybatis-config.xml #mybatis.mapper-locations=classpath:mybatis/mapper/*.xml spring.datasource.druid.read.web-stat-filter.enabled=true spring.datasource.druid.read.web-stat-filter.url-pattern=/* spring.datasource.druid.read.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/* spring.datasource.druid.read.web-stat-filter.session-stat-enable=true spring.datasource.druid.read.web-stat-filter.session-stat-max-count=1000 spring.datasource.druid.read.stat-view-servlet.enabled= true spring.datasource.druid.read.stat-view-servlet.url-pattern=/druid/* spring.datasource.druid.read.stat-view-servlet.reset-enable=true spring.datasource.druid.read.stat-view-servlet.login-username=druid spring.datasource.druid.read.stat-view-servlet.login-password=123456 spring.datasource.druid.read.stat-view-servlet.allow=127.0.0.1 spring.datasource.druid.read.stat-view-servlet.deny=192.168.0.19 spring.datasource.druid.read.aop-patterns=com.example.read.mapper.* spring.datasource.druid.read.url =jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC spring.datasource.druid.read.username = root spring.datasource.druid.read.password = 123456 spring.datasource.druid.read.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.druid.read.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.druid.write.max-active=20 spring.datasource.druid.write.initial-size=1 spring.datasource.druid.write.max-wait=60000 spring.datasource.druid.write.pool-prepared-statements=true spring.datasource.druid.write.max-pool-prepared-statement-per-connection-size=20 spring.datasource.druid.write.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 spring.datasource.druid.write.min-idle=1 spring.datasource.druid.write.time-between-eviction-runs-millis=60000 spring.datasource.druid.write.min-evictable-idle-time-millis=300000 spring.datasource.druid.write.validation-query=select 1 from dual spring.datasource.druid.write.test-while-idle=true spring.datasource.druid.write.test-on-borrow=true spring.datasource.druid.write.test-on-return=true spring.datasource.druid.write.web-stat-filter.enabled=true spring.datasource.druid.write.web-stat-filter.url-pattern=/* spring.datasource.druid.write.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/* spring.datasource.druid.write.web-stat-filter.session-stat-enable=true spring.datasource.druid.write.web-stat-filter.session-stat-max-count=1000 spring.datasource.druid.write.stat-view-servlet.enabled= true spring.datasource.druid.write.stat-view-servlet.url-pattern=/druid/* spring.datasource.druid.write.stat-view-servlet.reset-enable=true spring.datasource.druid.write.stat-view-servlet.login-username=druid spring.datasource.druid.write.stat-view-servlet.login-password=123456 spring.datasource.druid.write.stat-view-servlet.allow=127.0.0.1 spring.datasource.druid.write.stat-view-servlet.deny=192.168.0.19 spring.datasource.druid.write.url =jdbc:mysql://127.0.0.1:3306/mybatis1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC spring.datasource.druid.write.aop-patterns=com.example.write.mapper.* spring.datasource.druid.write.username = root spring.datasource.druid.write.password = 123456 spring.datasource.druid.write.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.druid.write.type=com.alibaba.druid.pool.DruidDataSourceView Code
六、多數據源的使用
這裏並未設置Service層,而是直接在Controller中使用。在Controller中會裝配一個寫的mapper一個讀的mapper,分別進行查詢和新增操作。
![技術分享圖片](/img/jia.gif)
package com.example.demo; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.example.model.User; import com.example.read.mapper.ReadUserMapper; import com.example.write.mapper.WriteUserMapper; @Controller @RequestMapping("/user") public class UserController { @Autowired private WriteUserMapper userMapperWrite; @Autowired private ReadUserMapper userMapperRead; @RequestMapping(value = "/alluser.do",method = RequestMethod.GET) public String getallusers(Model model) { List<User> users=userMapperRead.getAll(); model.addAttribute("users", users); return "userlist"; } @RequestMapping(value = "/insert.do",method = RequestMethod.GET) public String adduser(Model model) { User user=new User(); user.setName("cuiyw"); user.setAge(27); userMapperWrite.insert(user); List<User> users=userMapperWrite.getAll(); model.addAttribute("users", users); return "userlist"; } }View Code
七、指定數據源配置文件位置
上面基本把配置信息都配置好了,但是如果這樣運行還是會報錯誤,它還是不能找到這個mapper,此時需要在main方法文件增加註解@ComponentScan(basePackages={"com.example.config","com.example.demo"}),讓它掃描配置文件的包,然後在配置文件的包裏面有配置@MapperScan來查找到mapper。
![技術分享圖片](/img/jia.gif)
package com.example.demo; import org.springframework.boot.SpringApplication; //import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; //import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.context.annotation.ComponentScan; //@EnableAutoConfiguration(exclude= {DataSourceAutoConfiguration.class}) @ComponentScan(basePackages={"com.example.config","com.example.demo"}) @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }View Code
Description: Field userMapperWrite in com.example.demo.UserController required a bean of type ‘com.example.write.mapper.WriteUserMapper‘ that could not be found. Action: Consider defining a bean of type ‘com.example.write.mapper.WriteUserMapper‘ in your configuration.
八、其他問題
1.這裏還遇到404找不到路徑的錯誤,這裏還需要在@ComponentScan註解加上Controller對應的包,所以上面代碼有@ComponentScan(basePackages={"com.example.config","com.example.demo"})。
This application has no explicit mapping for /error, so you are seeing this as a fallback. Sun Jul 22 23:58:27 CST 2018 There was an unexpected error (type=Not Found, status=404). No message available
2.設置手動配置問題
由於開始使用的是DataSourceBuilder,但在application.properties還是使用spring.datasource.druid.read這種方法進行配置,並沒spring.datasource.url這樣配置,導致報下面的錯誤。因為使用的是DataSourceBuilder所以SpringBoot還是認為用的默認配置,所以就找spring.datasource.url,此時可以使用@EnableAutoConfiguration(exclude= {DataSourceAutoConfiguration.class})註解來設置手動註解。
九、測試
這裏還是分別輸入http://localhost:8080/user/alluser.do,http://localhost:8080/user/insert.do,然後查看兩個數據庫user表的數據是否有沒有改變,讀數據庫數據未變,寫數據庫數據增加。Druid的數據源監測也是有兩條數據源信息。
SpringBoot入門之基於Druid配置Mybatis多數據源