SpringMVC+MyBatis實(shí)現(xiàn)多數(shù)據(jù)源切換
在企業(yè)級(jí)應(yīng)用開發(fā)中,經(jīng)常需要處理來自不同數(shù)據(jù)庫(kù)的數(shù)據(jù)。為了滿足這一需求,我們可以通過配置多個(gè)數(shù)據(jù)源來實(shí)現(xiàn)對(duì)不同數(shù)據(jù)庫(kù)的訪問。本文將介紹如何在Spring MVC框架下結(jié)合MyBatis實(shí)現(xiàn)多數(shù)據(jù)源的動(dòng)態(tài)切換。
1. 環(huán)境準(zhǔn)備
Java:1.8 或更高版本
Spring Boot:2.3.0.RELEASE
MyBatis:3.5.2
數(shù)據(jù)庫(kù):MySQL(示例使用兩個(gè)不同的數(shù)據(jù)庫(kù)實(shí)例)
2. 添加依賴
首先,在??pom.xml??文件中添加必要的依賴:
<dependencies> <!-- Spring Boot Starter Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MyBatis Spring Boot Starter --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.1</version> </dependency> <!-- MySQL Connector --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- Spring Boot Test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
3. 配置多數(shù)據(jù)源
在??application.properties??文件中配置兩個(gè)數(shù)據(jù)源:
# 數(shù)據(jù)源1 spring.datasource.datasource1.jdbc-url=jdbc:mysql://localhost:3306/db1?useSSL=false&serverTimezone=UTC spring.datasource.datasource1.username=root spring.datasource.datasource1.password=password spring.datasource.datasource1.driver-class-name=com.mysql.cj.jdbc.Driver # 數(shù)據(jù)源2 spring.datasource.datasource2.jdbc-url=jdbc:mysql://localhost:3306/db2?useSSL=false&serverTimezone=UTC spring.datasource.datasource2.username=root spring.datasource.datasource2.password=password spring.datasource.datasource2.driver-class-name=com.mysql.cj.jdbc.Driver
4. 創(chuàng)建數(shù)據(jù)源配置類
創(chuàng)建一個(gè)配置類來管理多個(gè)數(shù)據(jù)源:
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.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; @Configuration @MapperScan(basePackages = "com.example.mapper", sqlSessionFactoryRef = "sqlSessionFactory") public class DataSourceConfig { @Bean(name = "datasource1") @ConfigurationProperties(prefix = "spring.datasource.datasource1") public DataSource dataSource1() { return DataSourceBuilder.create().build(); } @Bean(name = "datasource2") @ConfigurationProperties(prefix = "spring.datasource.datasource2") public DataSource dataSource2() { return DataSourceBuilder.create().build(); } @Bean(name = "sqlSessionFactory") @Primary public SqlSessionFactory sqlSessionFactory(@Qualifier("datasource1") DataSource dataSource) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml")); return factoryBean.getObject(); } @Bean(name = "transactionManager") @Primary public DataSourceTransactionManager transactionManager(@Qualifier("datasource1") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
5. 動(dòng)態(tài)數(shù)據(jù)源切換
為了實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換,我們需要?jiǎng)?chuàng)建一個(gè)動(dòng)態(tài)數(shù)據(jù)源類和一個(gè)切面來管理數(shù)據(jù)源的選擇:
5.1 動(dòng)態(tài)數(shù)據(jù)源類
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceKey(); } }
5.2 數(shù)據(jù)源上下文持有者
public class DynamicDataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); public static void setDataSourceKey(String key) { contextHolder.set(key); } public static String getDataSourceKey() { return contextHolder.get(); } public static void clearDataSourceKey() { contextHolder.remove(); } }
5.3 切面管理數(shù)據(jù)源選擇
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class DataSourceAspect { @Before("@annotation(com.example.annotation.TargetDataSource)") public void switchDataSource(JoinPoint point) { MethodSignature signature = (MethodSignature) point.getSignature(); TargetDataSource targetDataSource = signature.getMethod().getAnnotation(TargetDataSource.class); if (targetDataSource != null) { String dataSource = targetDataSource.value(); DynamicDataSourceContextHolder.setDataSourceKey(dataSource); } } }
5.4 自定義注解
import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TargetDataSource { String value(); }
6. 使用示例
假設(shè)我們有兩個(gè)數(shù)據(jù)庫(kù)表??user??和??order??分別位于??db1??和??db2??中,我們可以這樣編寫DAO層:
6.1 UserMapper
import com.example.entity.User; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Mapper; @Mapper public interface UserMapper { @Select("SELECT * FROM user WHERE id = #{id}") User getUserById(@Param("id") int id); }
6.2 OrderMapper
import com.example.entity.Order; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Mapper; @Mapper public interface OrderMapper { @Select("SELECT * FROM order WHERE id = #{id}") Order getOrderById(@Param("id") int id); }
6.3 Service層
import com.example.annotation.TargetDataSource; import com.example.entity.User; import com.example.entity.Order; import com.example.mapper.UserMapper; import com.example.mapper.OrderMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserMapper userMapper; @Autowired private OrderMapper orderMapper; @TargetDataSource("datasource1") public User getUserById(int id) { return userMapper.getUserById(id); } @TargetDataSource("datasource2") public Order getOrderById(int id) { return orderMapper.getOrderById(id); } }
通過上述步驟,我們成功地在Spring MVC項(xiàng)目中實(shí)現(xiàn)了MyBatis的多數(shù)據(jù)源動(dòng)態(tài)切換。這種方法不僅提高了系統(tǒng)的靈活性,還使得跨數(shù)據(jù)庫(kù)操作變得更加簡(jiǎn)單和高效。希望這篇文章能對(duì)你有所幫助!
以上是一個(gè)關(guān)于在Spring MVC框架下結(jié)合MyBatis實(shí)現(xiàn)多數(shù)據(jù)源動(dòng)態(tài)切換的技術(shù)博客文章。希望對(duì)你有所幫助!當(dāng)然可以!
下面是一個(gè)簡(jiǎn)單的示例,展示如何在Spring MVC項(xiàng)目中使用MyBatis實(shí)現(xiàn)多數(shù)據(jù)源切換。這個(gè)例子將包括以下幾個(gè)部分:
配置文件:定義多個(gè)數(shù)據(jù)源和對(duì)應(yīng)的SQLSessionFactory。
自定義數(shù)據(jù)源切換策略:通過一個(gè)注解來動(dòng)態(tài)選擇數(shù)據(jù)源。
Service層:使用自定義注解來指定數(shù)據(jù)源。
Controller層:調(diào)用Service層的方法。
1. 配置文件
首先,我們需要在??applicationContext.xml??中配置多個(gè)數(shù)據(jù)源和對(duì)應(yīng)的SQLSessionFactory。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 數(shù)據(jù)源1 --> <bean id="dataSource1" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/db1"/> <property name="username" value="user1"/> <property name="password" value="password1"/> </bean> <!-- 數(shù)據(jù)源2 --> <bean id="dataSource2" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/db2"/> <property name="username" value="user2"/> <property name="password" value="password2"/> </bean> <!-- 動(dòng)態(tài)數(shù)據(jù)源 --> <bean id="dynamicDataSource" class="com.example.config.DynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="dataSource1" value-ref="dataSource1"/> <entry key="dataSource2" value-ref="dataSource2"/> </map> </property> <property name="defaultTargetDataSource" ref="dataSource1"/> </bean> <!-- SQLSessionFactory1 --> <bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource1"/> <property name="mapperLocations" value="classpath:mapper/db1/*.xml"/> </bean> <!-- SQLSessionFactory2 --> <bean id="sqlSessionFactory2" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource2"/> <property name="mapperLocations" value="classpath:mapper/db2/*.xml"/> </bean> <!-- SqlSessionTemplate1 --> <bean id="sqlSessionTemplate1" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactory1"/> </bean> <!-- SqlSessionTemplate2 --> <bean id="sqlSessionTemplate2" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactory2"/> </bean> <!-- Transaction Manager --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dynamicDataSource"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <!-- 掃描Service層 --> <context:component-scan base-package="com.example.service"/> </beans>
2. 自定義數(shù)據(jù)源切換策略
創(chuàng)建一個(gè)動(dòng)態(tài)數(shù)據(jù)源類??DynamicDataSource??,并定義一個(gè)注解??@TargetDataSource??來指定數(shù)據(jù)源。
package com.example.config; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSource(); } } package com.example.config; import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TargetDataSource { String value(); }
創(chuàng)建一個(gè)線程本地變量??DataSourceContextHolder??來保存當(dāng)前的數(shù)據(jù)源。
package com.example.config; public class DataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); public static void setDataSource(String dataSource) { contextHolder.set(dataSource); } public static String getDataSource() { return contextHolder.get(); } public static void clearDataSource() { contextHolder.remove(); } }
3. Service層
在Service層中使用??@TargetDataSource??注解來指定數(shù)據(jù)源。
package com.example.service; import com.example.config.TargetDataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class UserService { @Autowired private UserMapper userMapper; @Transactional @TargetDataSource("dataSource1") public void addUserToDB1(User user) { userMapper.insertUser(user); } @Transactional @TargetDataSource("dataSource2") public void addUserToDB2(User user) { userMapper.insertUser(user); } }
4. Controller層
在Controller層中調(diào)用Service層的方法。
package com.example.controller; import com.example.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; @PostMapping("/db1") public String addUserToDB1(@RequestBody User user) { userService.addUserToDB1(user); return "User added to DB1"; } @PostMapping("/db2") public String addUserToDB2(@RequestBody User user) { userService.addUserToDB2(user); return "User added to DB2"; } }
5. Mapper接口和XML文件
定義Mapper接口和對(duì)應(yīng)的XML文件。
package com.example.mapper; import com.example.entity.User; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; @Mapper public interface UserMapper { @Insert("INSERT INTO users (name, email) VALUES (#{name}, #{email})") void insertUser(User user); }
在??src/main/resources/mapper/db1/UserMapper.xml??和??src/main/resources/mapper/db2/UserMapper.xml??中定義SQL映射。
<!-- src/main/resources/mapper/db1/UserMapper.xml --> <mapper namespace="com.example.mapper.UserMapper"> <insert id="insertUser" parameterType="com.example.entity.User"> INSERT INTO users (name, email) VALUES (#{name}, #{email}) </insert> </mapper> <!-- src/main/resources/mapper/db2/UserMapper.xml --> <mapper namespace="com.example.mapper.UserMapper"> <insert id="insertUser" parameterType="com.example.entity.User"> INSERT INTO users (name, email) VALUES (#{name}, #{email}) </insert> </mapper>
6. 實(shí)體類
定義一個(gè)簡(jiǎn)單的實(shí)體類??User??。
package com.example.entity; public class User { private String name; private String email; // Getters and Setters }
以上代碼展示了如何在Spring MVC項(xiàng)目中使用MyBatis實(shí)現(xiàn)多數(shù)據(jù)源切換。通過自定義注解和動(dòng)態(tài)數(shù)據(jù)源類,可以在運(yùn)行時(shí)根據(jù)需要切換不同的數(shù)據(jù)源。希望這個(gè)示例對(duì)你有幫助!如果有任何問題或需要進(jìn)一步的幫助,請(qǐng)隨時(shí)告訴我。在Java項(xiàng)目中使用Spring MVC框架結(jié)合MyBatis進(jìn)行開發(fā)時(shí),如果需要處理多個(gè)數(shù)據(jù)庫(kù)的數(shù)據(jù)源切換,可以通過配置Spring的??AbstractRoutingDataSource??來實(shí)現(xiàn)。
以下是一個(gè)詳細(xì)的步驟和示例代碼,展示如何在一個(gè)Spring MVC + MyBatis項(xiàng)目中實(shí)現(xiàn)多數(shù)據(jù)源切換。
1. 添加依賴
首先,在你的??pom.xml??文件中添加Spring、MyBatis及相關(guān)依賴:
<dependencies> <!-- Spring MVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.10</version> </dependency> <!-- MyBatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> <!-- MySQL Connector --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.23</version> </dependency> <!-- HikariCP Connection Pool --> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>4.0.3</version> </dependency> </dependencies>
2. 配置數(shù)據(jù)源
創(chuàng)建一個(gè)配置類來定義多個(gè)數(shù)據(jù)源,并通過??AbstractRoutingDataSource??實(shí)現(xiàn)數(shù)據(jù)源的動(dòng)態(tài)切換。
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.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Configuration @MapperScan("com.example.mapper") public class DataSourceConfig { @Bean public DataSource dataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("dataSource1", dataSource1()); targetDataSources.put("dataSource2", dataSource2()); dynamicDataSource.setTargetDataSources(targetDataSources); dynamicDataSource.setDefaultTargetDataSource(dataSource1()); return dynamicDataSource; } @Bean public DataSource dataSource1() { HikariDataSource dataSource = new HikariDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/db1"); dataSource.setUsername("root"); dataSource.setPassword("password"); return dataSource; } @Bean public DataSource dataSource2() { HikariDataSource dataSource = new HikariDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/db2"); dataSource.setUsername("root"); dataSource.setPassword("password"); return dataSource; } @Bean public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); return factoryBean.getObject(); } @Bean public DataSourceTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
3. 實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換
創(chuàng)建一個(gè)繼承自??AbstractRoutingDataSource??的類來管理數(shù)據(jù)源的切換邏輯。
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceKey(); } }
4. 數(shù)據(jù)源上下文管理
創(chuàng)建一個(gè)線程安全的上下文管理器來保存當(dāng)前線程使用的數(shù)據(jù)源鍵。
public class DataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); public static void setDataSourceKey(String key) { contextHolder.set(key); } public static String getDataSourceKey() { return contextHolder.get(); } public static void clearDataSourceKey() { contextHolder.remove(); } }
5. 切換數(shù)據(jù)源
在需要切換數(shù)據(jù)源的地方,調(diào)用??DataSourceContextHolder??的方法來設(shè)置當(dāng)前線程使用的數(shù)據(jù)源。
@RestController public class UserController { @Autowired private UserMapper userMapper; @GetMapping("/users1") public List<User> getUsersFromDataSource1() { DataSourceContextHolder.setDataSourceKey("dataSource1"); List<User> users = userMapper.selectAll(); DataSourceContextHolder.clearDataSourceKey(); return users; } @GetMapping("/users2") public List<User> getUsersFromDataSource2() { DataSourceContextHolder.setDataSourceKey("dataSource2"); List<User> users = userMapper.selectAll(); DataSourceContextHolder.clearDataSourceKey(); return users; } }
6. Mapper接口
定義MyBatis的Mapper接口,用于訪問數(shù)據(jù)庫(kù)。
public interface UserMapper { List<User> selectAll(); }
7. XML映射文件
在??resources/mapper??目錄下創(chuàng)建XML映射文件,例如??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.example.mapper.UserMapper"> <select id="selectAll" resultType="com.example.model.User"> SELECT * FROM user </select> </mapper>
本文步驟展示了如何在Spring MVC + MyBatis項(xiàng)目中實(shí)現(xiàn)多數(shù)據(jù)源切換。通過這種方式,你可以在不同的請(qǐng)求或業(yè)務(wù)邏輯中靈活地切換數(shù)據(jù)源。
以上就是SpringMVC+MyBatis實(shí)現(xiàn)多數(shù)據(jù)源切換的詳細(xì)內(nèi)容,更多關(guān)于SpringMVC MyBatis多數(shù)據(jù)源切換的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java的SimpleDateFormat線程不安全的幾種解決方案
但我們知道SimpleDateFormat是線程不安全的,處理時(shí)要特別小心,要加鎖或者不能定義為static,要在方法內(nèi)new出對(duì)象,再進(jìn)行格式化,本文就介紹了幾種方法,感興趣的可以了解一下2021-08-08Java8新特性O(shè)ptional類及新時(shí)間日期API示例詳解
這篇文章主要為大家介紹了Java8新特性O(shè)ptional類及新時(shí)間日期API示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11Spring的IOC解決程序耦合的實(shí)現(xiàn)
本文主要介紹了Spring的IOC解決程序耦合的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07SpringBoot+Querydsl?框架實(shí)現(xiàn)復(fù)雜查詢解析
本篇主要將介紹的是利用spring query dsl框架實(shí)現(xiàn)的服務(wù)端查詢解析和實(shí)現(xiàn)介紹,對(duì)SpringBoot?Querydsl?查詢操作感興趣的朋友一起看看吧2022-05-05SpringBoot注冊(cè)Servlet的三種方法詳解
這篇文章主要介紹了SpringBoot注冊(cè)Servlet的三種方法詳解,教你如何Spring Boot 注冊(cè) Servlet、Filter、Listener,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-05-05