<form id="dlljd"></form>
        <address id="dlljd"><address id="dlljd"><listing id="dlljd"></listing></address></address>

        <em id="dlljd"><form id="dlljd"></form></em>

          <address id="dlljd"></address>
            <noframes id="dlljd">

              聯系我們 - 廣告服務 - 聯系電話:
              您的當前位置: > 關注 > > 正文

              全球快消息!自定義注解+AOPP 簡化數據源的切換操作

              來源:CSDN 時間:2023-01-12 10:34:48

              項目中經常會出現需要同時連接兩個數據源的情況,這里還是演示基于MyBatis來配置兩個數據源,并演示如何切換不同的數據源。


              (資料圖片)

              網上的一些例子都寫的有點冗余,這里我通過自定義注解+AOP的方式,來簡化這種數據源的切換操作。

              maven依賴

              1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950

              UTF-8UTF-81.81.1.28.0.7-dmr2.1.81.0.5org.springframework.bootspring-boot-starter-aopmysqlmysql-connector-java${mysql-connector.version}runtimecom.alibabadruid${druid.version}com.baomidoumybatis-plus${mybatis-plus.version}com.baomidoumybatisplus-spring-boot-starter${mybatisplus-spring-boot-starter.version}org.springframework.bootspring-boot-starter-testtestorg.hamcresthamcrest-all1.3test

              初始化數據庫

              這里我們需要創建兩個數據庫,初始化腳本如下:

              12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849

              # -------------------------------------以下是pos業務庫開始-------------------------------------------CREATE DATABASE IF NOT EXISTS pos default charset utf8 COLLATE utf8_general_ci;SET FOREIGN_KEY_CHECKS=0;USE pos;-- 后臺管理用戶表DROP TABLE IF EXISTS `t_user`;CREATE TABLE `t_user` (  `id`                        INT(11) PRIMARY KEY AUTO_INCREMENT COMMENT "主鍵ID",  `username`                  VARCHAR(32) NOT NULL COMMENT "賬號",  `name`                      VARCHAR(16) DEFAULT "" COMMENT "名字",  `password`                  VARCHAR(128) DEFAULT "" COMMENT "密碼",  `salt`                      VARCHAR(64) DEFAULT "" COMMENT "md5密碼鹽",  `phone`                     VARCHAR(32) DEFAULT "" COMMENT "聯系電話",  `tips`                      VARCHAR(255) COMMENT "備注",  `state`                     TINYINT(1) DEFAULT 1 COMMENT "狀態 1:正常 2:禁用",  `created_time`              DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT "創建時間",  `updated_time`              DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT "更新時間") ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT="后臺管理用戶表";# 下面是pos數據庫中的插入數據INSERT INTO `t_user` VALUES (1,"admin","系統管理員","123456","www", "17890908889", "系統管理員", 1, "2017-12-12 09:46:12", "2017-12-12 09:46:12");INSERT INTO `t_user` VALUES (2,"aix","張三","123456","eee", "17859569358", "", 1, "2017-12-12 09:46:12", "2017-12-12 09:46:12");# -------------------------------------以下biz業務庫開始-------------------------------------------CREATE DATABASE IF NOT EXISTS pos default charset utf8 COLLATE utf8_general_ci;SET FOREIGN_KEY_CHECKS=0;USE biz;-- 后臺管理用戶表DROP TABLE IF EXISTS `t_user`;CREATE TABLE `t_user` (  `id`                        INT(11) PRIMARY KEY AUTO_INCREMENT COMMENT "主鍵ID",  `username`                  VARCHAR(32) NOT NULL COMMENT "賬號",  `name`                      VARCHAR(16) DEFAULT "" COMMENT "名字",  `password`                  VARCHAR(128) DEFAULT "" COMMENT "密碼",  `salt`                      VARCHAR(64) DEFAULT "" COMMENT "md5密碼鹽",  `phone`                     VARCHAR(32) DEFAULT "" COMMENT "聯系電話",  `tips`                      VARCHAR(255) COMMENT "備注",  `state`                     TINYINT(1) DEFAULT 1 COMMENT "狀態 1:正常 2:禁用",  `created_time`              DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT "創建時間",  `updated_time`              DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT "更新時間") ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT="后臺管理用戶表";# 下面是biz數據庫中的插入數據INSERT INTO `t_user` VALUES (1,"admin1","系統管理員","123456","www", "17890908889", "系統管理員", 1, "2017-12-12 09:46:12", "2017-12-12 09:46:12");INSERT INTO `t_user` VALUES (2,"aix1","張三","123456","eee", "17859569358", "", 1, "2017-12-12 09:46:12", "2017-12-12 09:46:12");

              可以看到我創建了兩個數據庫pos和biz,同時還初始化了用戶表,并分別插入兩條初始數據。注意用戶名數據不相同。

              配置文件

              接下來修改application.yml配置文件,如下:

              1234567891011121314151617181920212223242526272829303132

              ###################  自定義配置  ###################xncoding:  muti-datasource-open: true #是否開啟多數據源(true/false)###################  mybatis-plus配置  ###################mybatis-plus:  mapper-locations: classpath*:com/xncoding/pos/common/dao/repository/mapping/*.xml  typeAliasesPackage: >    com.xncoding.pos.common.dao.entity  global-config:    id-type: 0  # 0:數據庫ID自增   1:用戶輸入id  2:全局唯一id(IdWorker)  3:全局唯一ID(uuid)    db-column-underline: false    refresh-mapper: true  configuration:    map-underscore-to-camel-case: true    cache-enabled: true #配置的緩存的全局開關    lazyLoadingEnabled: true #延時加載的開關    multipleResultSetsEnabled: true #開啟的話,延時加載一個屬性時會加載該對象全部屬性,否則按需加載屬性#默認數據源spring:  datasource:    url: jdbc:mysql://127.0.0.1:3306/pos?useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8    username: root    password: 123456#多數據源biz:  datasource:    url: jdbc:mysql://127.0.0.1:3306/biz?useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8    username: root    password: 123456

              解釋一下:

              這里我添加了一個自定義配置項muti-datasource-open,用來控制是否開啟多數據源支持。這個配置項后面我會用到。 接下來定義MyBatis的配置,最后定義了兩個MySQL數據庫的連接信息,一個是pos庫,一個是biz庫。

              動態切換數據源

              這里通過Spring的AOP技術實現數據源的動態切換。

              多數據源的常量類:

              1234

              public interface DSEnum {    String DATA_SOURCE_CORE = "dataSourceCore";         //核心數據源    String DATA_SOURCE_BIZ = "dataSourceBiz";            //其他業務的數據源}

              datasource的上下文,用來存儲當前線程的數據源類型:

              1234567891011121314151617181920212223242526

              public class DataSourceContextHolder {    private static final ThreadLocalcontextHolder = new ThreadLocal();    /**     * @param dataSourceType 數據庫類型     * @Description: 設置數據源類型     */    public static void setDataSourceType(String dataSourceType) {        contextHolder.set(dataSourceType);    }    /**     * @Description: 獲取數據源類型     */    public static String getDataSourceType() {        return contextHolder.get();    }    /**     * @Description: 清除數據源類型     */    public static void clearDataSourceType() {        contextHolder.remove();    }}

              定義動態數據源,繼承AbstractRoutingDataSource:

              1234567

              public class DynamicDataSource extends AbstractRoutingDataSource {    @Override    protected Object determineCurrentLookupKey() {        return DataSourceContextHolder.getDataSourceType();    }}

              接下來自定義一個注解,用來在Service方法上面注解使用哪個數據源:

              1234567891011

              /** * 多數據源標識 * * @author xiongneng */@Inherited@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface DataSource {    String name() default "";}

              最后,最核心的AOP類定義:

              1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556

              /** * 多數據源切換的aop * * @author xiongneng */@Aspect@Component@ConditionalOnProperty(prefix = "xncoding", name = "muti-datasource-open", havingValue = "true")public class MultiSourceExAop implements Ordered {    private Logger log = Logger.getLogger(this.getClass());    @Pointcut(value = "@annotation(com.xncoding.pos.common.annotion.DataSource)")    private void cut() {    }    @Around("cut()")    public Object around(ProceedingJoinPoint point) throws Throwable {        Signature signature = point.getSignature();        MethodSignature methodSignature = null;        if (!(signature instanceof MethodSignature)) {            throw new IllegalArgumentException("該注解只能用于方法");        }        methodSignature = (MethodSignature) signature;        Object target = point.getTarget();        Method currentMethod = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());        DataSource datasource = currentMethod.getAnnotation(DataSource.class);        if (datasource != null) {            DataSourceContextHolder.setDataSourceType(datasource.name());            log.debug("設置數據源為:" + datasource.name());        } else {            DataSourceContextHolder.setDataSourceType(DSEnum.DATA_SOURCE_CORE);            log.debug("設置數據源為:dataSourceCore");        }        try {            return point.proceed();        } finally {            log.debug("清空數據源信息!");            DataSourceContextHolder.clearDataSourceType();        }    }    /**     * aop的順序要早于spring的事務     */    @Override    public int getOrder() {        return 1;    }}

              這里使用到了注解@ConditionalOnProperty,只有當我的配置文件中muti-datasource-open=true的時候注解才會生效。

              另外還有一個要注意的地方,就是order,aop的順序一定要早于spring的事務,這里我將它設置成1,后面你會看到我將spring事務順序設置成2。

              配置類

              首先有兩個屬性類:

              DruidProperties連接池的屬性類MutiDataSourcePropertiesbiz數據源的屬性類

              然后定義配置類:

              1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465

              @Configuration@EnableTransactionManagement(order = 2)@MapperScan(basePackages = {"com.xncoding.pos.common.dao.repository"})public class MybatisPlusConfig {    @Autowired    DruidProperties druidProperties;    @Autowired    MutiDataSourceProperties mutiDataSourceProperties;    /**     * 核心數據源     */    private DruidDataSource coreDataSource() {        DruidDataSource dataSource = new DruidDataSource();        druidProperties.config(dataSource);        return dataSource;    }    /**     * 另一個數據源     */    private DruidDataSource bizDataSource() {        DruidDataSource dataSource = new DruidDataSource();        druidProperties.config(dataSource);        mutiDataSourceProperties.config(dataSource);        return dataSource;    }    /**     * 單數據源連接池配置     */    @Bean    @ConditionalOnProperty(prefix = "xncoding", name = "muti-datasource-open", havingValue = "false")    public DruidDataSource singleDatasource() {        return coreDataSource();    }    /**     * 多數據源連接池配置     */    @Bean    @ConditionalOnProperty(prefix = "xncoding", name = "muti-datasource-open", havingValue = "true")    public DynamicDataSource mutiDataSource() {        DruidDataSource coreDataSource = coreDataSource();        DruidDataSource bizDataSource = bizDataSource();        try {            coreDataSource.init();            bizDataSource.init();        } catch (SQLException sql) {            sql.printStackTrace();        }        DynamicDataSource dynamicDataSource = new DynamicDataSource();        HashMaphashMap = new HashMap<>();        hashMap.put(DSEnum.DATA_SOURCE_CORE, coreDataSource);        hashMap.put(DSEnum.DATA_SOURCE_BIZ, bizDataSource);        dynamicDataSource.setTargetDataSources(hashMap);        dynamicDataSource.setDefaultTargetDataSource(coreDataSource);        return dynamicDataSource;    }}

              代碼其實很好理解,我就不再多做解釋了。

              然后步驟跟普通的集成MyBatis是一樣的,我簡單的過一遍。

              實體類

              123456789101112131415161718192021222324252627282930313233343536373839404142434445464748

              @TableName(value = "t_user")public class User extends Model{private static final long serialVersionUID = 1L;    /**     * 主鍵ID     */    @TableId(value="id", type= IdType.AUTO)    private Integer id;    /**     * 賬號     */    private String username;    /**     * 名字     */    private String name;    /**     * 密碼     */    private String password;    /**     * md5密碼鹽     */    private String salt;    /**     * 聯系電話     */    private String phone;    /**     * 備注     */    private String tips;    /**     * 狀態 1:正常 2:禁用     */    private Integer state;    /**     * 創建時間     */    private Date createdTime;    /**     * 更新時間     */    private Date updatedTime;    // 省略getter/setter方法}

              定義DAO

              123

              public interface UserMapper extends BaseMapper{}

              定義Service

              1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950

              @Servicepublic class UserService {    @Resource    private UserMapper userMapper;    /**     * 通過ID查找用戶     * @param id     * @return     */    public User findById(Integer id) {        return userMapper.selectById(id);    }    /**     * 通過ID查找用戶     * @param id     * @return     */    @DataSource(name = DSEnum.DATA_SOURCE_BIZ)    public User findById1(Integer id) {        return userMapper.selectById(id);    }    /**     * 新增用戶     * @param user     */    public void insertUser(User user) {        userMapper.insert(user);    }    /**     * 修改用戶     * @param user     */    public void updateUser(User user) {        userMapper.updateById(user);    }    /**     * 刪除用戶     * @param id     */    public void deleteUser(Integer id) {        userMapper.deleteById(id);    }}

              這里唯一要說明的就是我在方法findById1()上面增加了注解@DataSource(name = DSEnum.DATA_SOURCE_BIZ),這樣這個方法就會訪問biz數據庫。

              注意,不加注解就會訪問默認數據庫pos。

              測試

              最后編寫一個簡單的測試,我只測試findById()方法和findById1()方法,看它們是否訪問的是不同的數據源。

              12345678910111213141516171819202122

              @RunWith(SpringRunner.class)@SpringBootTestpublic class ApplicationTests {    private static final Logger log = LoggerFactory.getLogger(ApplicationTests.class);    @Resource    private UserService userService;    /**     * 測試增刪改查     */    @Test    public void test() {        // 核心數據庫中的用戶id=1        User user = userService.findById(1);        assertThat(user.getUsername(), is("admin"));        // biz數據庫中的用戶id=1        User user1 = userService.findById1(1);        assertThat(user1.getUsername(), is("admin1"));    }}

              運行測試,結果顯示為green bar,成功!

              責任編輯:

              標簽:

              相關推薦:

              精彩放送:

              新聞聚焦
              Top 中文字幕在线观看亚洲日韩