精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

聊聊 SpringBoot 多數(shù)據(jù)源配置

開(kāi)發(fā) 項(xiàng)目管理
AbstractRoutingDataSource 是在 Spring2.0.1 中引入的(注意是 Spring2.0.1 不是 Spring Boot2.0.1,所以這其實(shí)也算是 Spring 一個(gè)非常古老的特性了), 該類充當(dāng)了 DataSource 的路由中介,它能夠在運(yùn)行時(shí), 根據(jù)某種 key 值來(lái)動(dòng)態(tài)切換到真正的 DataSource 上。

一 預(yù)備知識(shí)

想要自定義動(dòng)態(tài)數(shù)據(jù)源切換,得先了解一個(gè)類 AbstractRoutingDataSource:

AbstractRoutingDataSource 是在 Spring2.0.1 中引入的(注意是 Spring2.0.1 不是 Spring Boot2.0.1,所以這其實(shí)也算是 Spring 一個(gè)非常古老的特性了), 該類充當(dāng)了 DataSource 的路由中介,它能夠在運(yùn)行時(shí), 根據(jù)某種 key 值來(lái)動(dòng)態(tài)切換到真正的 DataSource 上。

大致的用法就是你提前準(zhǔn)備好各種數(shù)據(jù)源,存入到一個(gè) Map 中,Map 的 key 就是這個(gè)數(shù)據(jù)源的名字,Map 的 value 就是這個(gè)具體的數(shù)據(jù)源,然后再把這個(gè) Map 配置到 AbstractRoutingDataSource 中,最后,每次執(zhí)行數(shù)據(jù)庫(kù)查詢的時(shí)候,拿一個(gè) key 出來(lái),AbstractRoutingDataSource 會(huì)找到具體的數(shù)據(jù)源去執(zhí)行這次數(shù)據(jù)庫(kù)操作。

大致思路就是這樣。

接下來(lái)我們就來(lái)看看怎么玩。

二 創(chuàng)建項(xiàng)目

首先我們創(chuàng)建一個(gè) Spring Boot 項(xiàng)目,引入 Web、MyBatis 以及 MySQL 依賴,項(xiàng)目創(chuàng)建成功之后,再手動(dòng)加入 Druid 和 AOP 依賴,如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.9</version>
</dependency>

這塊呢其實(shí)沒(méi)啥好說(shuō)的,都是常規(guī)操作。

三 配置文件

接下來(lái)我們創(chuàng)建一個(gè) application-druid.yaml 用來(lái)配置我們的數(shù)據(jù)源信息,如下:

# 數(shù)據(jù)源配置
spring:
    datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        ds:
            # 主庫(kù)數(shù)據(jù)源,默認(rèn) master 不能變
            master:
                url: jdbc:mysql://127.0.0.1:3306/test08?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai
                username: root
                password: 123
            # 從庫(kù)數(shù)據(jù)源
            slave:
                url: jdbc:mysql://127.0.0.1:3306/test07?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai
                username: root
                password: 123
        # 初始連接數(shù)
        initialSize: 5
        # 最小連接池?cái)?shù)量
        minIdle: 10
        # 最大連接池?cái)?shù)量
        maxActive: 20
        # 配置獲取連接等待超時(shí)的時(shí)間
        maxWait: 60000
        # 配置間隔多久才進(jìn)行一次檢測(cè),檢測(cè)需要關(guān)閉的空閑連接,單位是毫秒
        timeBetweenEvictionRunsMillis: 60000
        # 配置一個(gè)連接在池中最小生存的時(shí)間,單位是毫秒
        minEvictableIdleTimeMillis: 300000
        # 配置一個(gè)連接在池中最大生存的時(shí)間,單位是毫秒
        maxEvictableIdleTimeMillis: 900000
        # 配置檢測(cè)連接是否有效
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        druid:
            webStatFilter:
                enabled: true
            statViewServlet:
                enabled: true
                # 設(shè)置白名單,不填則允許所有訪問(wèn)
                allow:
                url-pattern: /druid/*
                # 控制臺(tái)管理用戶名和密碼
                login-username: javaboy
                login-password: 123456
            filter:
                stat:
                    enabled: true
                    # 慢SQL記錄
                    log-slow-sql: true
                    slow-sql-millis: 1000
                    merge-sql: true
                wall:
                    config:
                        multi-statement-allow: true

都是 Druid 的常規(guī)配置,也沒(méi)啥好說(shuō)的,唯一需要注意的是我們整個(gè)配置文件的格式。ds 里邊配置我們的所有數(shù)據(jù)源,每個(gè)數(shù)據(jù)源都有一個(gè)名字,master 是默認(rèn)數(shù)據(jù)源的名字,不可修改,其他數(shù)據(jù)源都可以自定義名字。最后面我們還配置了 Druid 的監(jiān)控功能,如果小伙伴們還不懂 Druid 的監(jiān)控功能,可以查看Spring Boot 如何監(jiān)控 SQL 運(yùn)行情況?。

不過(guò)小伙伴們知道,YAML 配置不像 properties 配置可以通過(guò) @PropertySource 注解加載自定義的配置文件,YAML 配置沒(méi)有類似的加載機(jī)制。不過(guò)工具是死的人是活的,我們可以利用 Spring Boot 的 profile 機(jī)制來(lái)加載這個(gè)自定義的 application-druid.yaml 配置文件,具體做法就是在 application.yaml 中加一行配置,如下:

spring:
  profiles:
    active: druid

接下來(lái)我們還需要提供一個(gè)配置類,將這個(gè)配置文件的內(nèi)容加載到配置類中,如下:

@ConfigurationProperties(prefix = "spring.datasource")
public class DruidProperties {
    private int initialSize;

    private int minIdle;

    private int maxActive;

    private int maxWait;

    private int timeBetweenEvictionRunsMillis;

    private int minEvictableIdleTimeMillis;

    private int maxEvictableIdleTimeMillis;

    private String validationQuery;

    private boolean testWhileIdle;

    private boolean testOnBorrow;

    private boolean testOnReturn;

    private Map<String, Map<String, String>> ds;

    public DruidDataSource dataSource(DruidDataSource datasource) {
        /** 配置初始化大小、最小、最大 */
        datasource.setInitialSize(initialSize);
        datasource.setMaxActive(maxActive);
        datasource.setMinIdle(minIdle);

        /** 配置獲取連接等待超時(shí)的時(shí)間 */
        datasource.setMaxWait(maxWait);

        /** 配置間隔多久才進(jìn)行一次檢測(cè),檢測(cè)需要關(guān)閉的空閑連接,單位是毫秒 */
        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);

        /** 配置一個(gè)連接在池中最小、最大生存的時(shí)間,單位是毫秒 */
        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);

        /**
         * 用來(lái)檢測(cè)連接是否有效的sql,要求是一個(gè)查詢語(yǔ)句,常用select 'x'。如果validationQuery為null,testOnBorrow、testOnReturn、testWhileIdle都不會(huì)起作用。
         */
        datasource.setValidationQuery(validationQuery);
        /** 建議配置為true,不影響性能,并且保證安全性。申請(qǐng)連接的時(shí)候檢測(cè),如果空閑時(shí)間大于timeBetweenEvictionRunsMillis,執(zhí)行validationQuery檢測(cè)連接是否有效。 */
        datasource.setTestWhileIdle(testWhileIdle);
        /** 申請(qǐng)連接時(shí)執(zhí)行validationQuery檢測(cè)連接是否有效,做了這個(gè)配置會(huì)降低性能。 */
        datasource.setTestOnBorrow(testOnBorrow);
        /** 歸還連接時(shí)執(zhí)行validationQuery檢測(cè)連接是否有效,做了這個(gè)配置會(huì)降低性能。 */
        datasource.setTestOnReturn(testOnReturn);
        return datasource;
    }

    public int getInitialSize() {
        return initialSize;
    }

    public void setInitialSize(int initialSize) {
        this.initialSize = initialSize;
    }

    public int getMinIdle() {
        return minIdle;
    }

    public void setMinIdle(int minIdle) {
        this.minIdle = minIdle;
    }

    public int getMaxActive() {
        return maxActive;
    }

    public void setMaxActive(int maxActive) {
        this.maxActive = maxActive;
    }

    public int getMaxWait() {
        return maxWait;
    }

    public void setMaxWait(int maxWait) {
        this.maxWait = maxWait;
    }

    public int getTimeBetweenEvictionRunsMillis() {
        return timeBetweenEvictionRunsMillis;
    }

    public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
        this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
    }

    public int getMinEvictableIdleTimeMillis() {
        return minEvictableIdleTimeMillis;
    }

    public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
        this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
    }

    public int getMaxEvictableIdleTimeMillis() {
        return maxEvictableIdleTimeMillis;
    }

    public void setMaxEvictableIdleTimeMillis(int maxEvictableIdleTimeMillis) {
        this.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis;
    }

    public String getValidationQuery() {
        return validationQuery;
    }

    public void setValidationQuery(String validationQuery) {
        this.validationQuery = validationQuery;
    }

    public boolean isTestWhileIdle() {
        return testWhileIdle;
    }

    public void setTestWhileIdle(boolean testWhileIdle) {
        this.testWhileIdle = testWhileIdle;
    }

    public boolean isTestOnBorrow() {
        return testOnBorrow;
    }

    public void setTestOnBorrow(boolean testOnBorrow) {
        this.testOnBorrow = testOnBorrow;
    }

    public boolean isTestOnReturn() {
        return testOnReturn;
    }

    public void setTestOnReturn(boolean testOnReturn) {
        this.testOnReturn = testOnReturn;
    }

    public Map<String, Map<String, String>> getDs() {
        return ds;
    }

    public void setDs(Map<String, Map<String, String>> ds) {
        this.ds = ds;
    }
}

這個(gè)配置類沒(méi)啥好說(shuō)的,我們配置的多個(gè)數(shù)據(jù)源我將之讀取到了一個(gè)名為 ds 的 Map 中,將來(lái)就根據(jù)這個(gè) Map 中的數(shù)據(jù)來(lái)構(gòu)造數(shù)據(jù)源。

四 加載數(shù)據(jù)源

接下來(lái)我們要根據(jù)配置文件來(lái)加載數(shù)據(jù)源。加載方式如下:

public interface DynamicDataSourceProvider {
    String DEFAULT_DATASOURCE = "master";
    /**
     * 加載所有的數(shù)據(jù)源
     * @return
     */
    Map<String, DataSource> loadDataSources();
}
@Configuration
@EnableConfigurationProperties(DruidProperties.class)
public class YamlDynamicDataSourceProvider implements DynamicDataSourceProvider {
    @Autowired
    DruidProperties druidProperties;

    @Override
    public Map<String, DataSource> loadDataSources() {
        Map<String, DataSource> ds = new HashMap<>(druidProperties.getDs().size());
        try {
            Map<String, Map<String, String>> map = druidProperties.getDs();
            Set<String> keySet = map.keySet();
            for (String s : keySet) {
                DruidDataSource dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(map.get(s));
                ds.put(s, druidProperties.dataSource(dataSource));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ds;
    }
}

加載的核心工作在 YamlDynamicDataSourceProvider 類中完成的。該類中有一個(gè) loadDataSources 方法表示讀取所有的數(shù)據(jù)源對(duì)象。數(shù)據(jù)源的相關(guān)屬性都在 druidProperties 對(duì)象中,我們先根據(jù)基本的數(shù)據(jù)庫(kù)連接信息創(chuàng)建一個(gè) DataSource 對(duì)象,然后再調(diào)用 druidProperties#dataSource 方法為這些數(shù)據(jù)源連接池配置其他的屬性(最大連接數(shù)、最小空閑數(shù)等),最后,以 key-value 的形式將數(shù)據(jù)源存入一個(gè) Map 集合中,每一個(gè)數(shù)據(jù)源的 key 就是你在 YAML 中配置的數(shù)據(jù)源名稱。

五 數(shù)據(jù)源切換

對(duì)于當(dāng)前數(shù)據(jù)庫(kù)操作使用哪個(gè)數(shù)據(jù)源?我們有很多種不同的設(shè)置方案,當(dāng)然最為省事的辦法是把當(dāng)前使用的數(shù)據(jù)源信息存入到 ThreadLocal 中,ThreadLocal 的特點(diǎn),簡(jiǎn)單說(shuō)就是在哪個(gè)線程中存入的數(shù)據(jù),在哪個(gè)線程才能取出來(lái),換一個(gè)線程就取不出來(lái)了,這樣可以確保多線程環(huán)境下的數(shù)據(jù)安全。

先來(lái)一個(gè)簡(jiǎn)單的工具類,如下:

public class DynamicDataSourceContextHolder {
    public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

    /**
     * 使用ThreadLocal維護(hù)變量,ThreadLocal為每個(gè)使用該變量的線程提供獨(dú)立的變量副本,
     * 所以每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線程所對(duì)應(yīng)的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 設(shè)置數(shù)據(jù)源的變量
     */
    public static void setDataSourceType(String dsType) {
        log.info("切換到{}數(shù)據(jù)源", dsType);
        CONTEXT_HOLDER.set(dsType);
    }

    /**
     * 獲得數(shù)據(jù)源的變量
     */
    public static String getDataSourceType() {
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空數(shù)據(jù)源變量
     */
    public static void clearDataSourceType() {
        CONTEXT_HOLDER.remove();
    }
}

接下來(lái)我們自定義一個(gè)注解用來(lái)標(biāo)記當(dāng)前的數(shù)據(jù)源,如下:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface DataSource {
    String dataSourceName() default DynamicDataSourceProvider.DEFAULT_DATASOURCE;

    @AliasFor("dataSourceName")
    String value() default DynamicDataSourceProvider.DEFAULT_DATASOURCE;
}

這個(gè)注解將來(lái)加在 Service 層的方法上,使用該注解的時(shí)候,需要指定一個(gè)數(shù)據(jù)源名稱,不指定的話,默認(rèn)就使用 master 作為數(shù)據(jù)源。

我們還需要通過(guò) AOP 來(lái)解析當(dāng)前的自定義注解,如下:

@Aspect
@Order(1)
@Component
public class DataSourceAspect {
    @Pointcut("@annotation(org.javaboy.demo.annotation.DataSource)"
            + "|| @within(org.javaboy.demo.annotation.DataSource)")
    public void dsPc() {

    }

    @Around("dsPc()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        DataSource dataSource = getDataSource(point);

        if (Objects.nonNull(dataSource)) {
            DynamicDataSourceContextHolder.setDataSourceType(dataSource.dataSourceName());
        }

        try {
            return point.proceed();
        } finally {
            // 銷毀數(shù)據(jù)源 在執(zhí)行方法之后
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }

    /**
     * 獲取需要切換的數(shù)據(jù)源
     */
    public DataSource getDataSource(ProceedingJoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
        if (Objects.nonNull(dataSource)) {
            return dataSource;
        }
        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
    }
}
  1. 首先,我們?cè)?dsPc() 方法上定義了切點(diǎn),我們攔截下所有帶有 @DataSource 注解的方法,同時(shí)由于該注解也可以加在類上,如果該注解加在類上,就表示類中的所有方法都使用該數(shù)據(jù)源。
  2. 接下來(lái)我們定義了一個(gè)環(huán)繞通知,首先根據(jù)當(dāng)前的切點(diǎn),調(diào)用 getDataSource 方法獲取到 @DataSource 注解,這個(gè)注解可能來(lái)自方法上也可能來(lái)自類上,方法上的優(yōu)先級(jí)高于類上的優(yōu)先級(jí)。如果拿到的注解不為空,則我們?cè)?DynamicDataSourceContextHolder 中設(shè)置當(dāng)前的數(shù)據(jù)源名稱,設(shè)置完成后進(jìn)行方法的調(diào)用;如果拿到的注解為空,那么就直接進(jìn)行方法的調(diào)用,不再設(shè)置數(shù)據(jù)源了(將來(lái)會(huì)自動(dòng)使用默認(rèn)的數(shù)據(jù)源)。最后記得方法調(diào)用完成后,從 ThreadLocal 中移除數(shù)據(jù)源。

六 定義動(dòng)態(tài)數(shù)據(jù)源

接下來(lái)我們來(lái)自定義一個(gè)動(dòng)態(tài)數(shù)據(jù)源:

public class DynamicDataSource extends AbstractRoutingDataSource {

    DynamicDataSourceProvider dynamicDataSourceProvider;

    public DynamicDataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
        this.dynamicDataSourceProvider = dynamicDataSourceProvider;
        Map<Object, Object> targetDataSources = new HashMap<>(dynamicDataSourceProvider.loadDataSources());
        super.setTargetDataSources(targetDataSources);
        super.setDefaultTargetDataSource(dynamicDataSourceProvider.loadDataSources().get(DynamicDataSourceProvider.DEFAULT_DATASOURCE));
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        String dataSourceType = DynamicDataSourceContextHolder.getDataSourceType();
        return dataSourceType;
    }
}

這就是我們文章開(kāi)頭所說(shuō)的 AbstractRoutingDataSource 了,該類有一個(gè)方法名為 determineCurrentLookupKey,當(dāng)需要使用數(shù)據(jù)源的時(shí)候,系統(tǒng)會(huì)自動(dòng)調(diào)用該方法,獲取當(dāng)前數(shù)據(jù)源的標(biāo)記,如 master 或者 slave 或者其他,拿到標(biāo)記之后,就可以據(jù)此獲取到一個(gè)數(shù)據(jù)源了。

當(dāng)我們配置 DynamicDataSource 的時(shí)候,需要配置兩個(gè)關(guān)鍵的參數(shù),一個(gè)是 setTargetDataSources,這個(gè)就是當(dāng)前所有的數(shù)據(jù)源,把當(dāng)前所有的數(shù)據(jù)源都告訴給 AbstractRoutingDataSource,這些數(shù)據(jù)源都是 key-value 的形式(將來(lái)根據(jù) determineCurrentLookupKey 方法返回的 key 就可以獲取到具體的數(shù)據(jù)源了);另一個(gè)方法是 setDefaultTargetDataSource,這個(gè)就是默認(rèn)的數(shù)據(jù)源,當(dāng)我們執(zhí)行一個(gè)數(shù)據(jù)庫(kù)操作的時(shí)候,如果沒(méi)有指定數(shù)據(jù)源(例如 Service 層的方法沒(méi)有加 @DataSource 注解),那么默認(rèn)就使用這個(gè)數(shù)據(jù)源。

最后,再將這個(gè) bean 注冊(cè)到 Spring 容器中,如下:

@Configuration
public class DruidAutoConfiguration {
    @Autowired
    DynamicDataSourceProvider dynamicDataSourceProvider;

    @Bean
    DynamicDataSource dynamicDataSource() {
        return new DynamicDataSource(dynamicDataSourceProvider);
    }

    /**
     * 去除數(shù)據(jù)源監(jiān)控頁(yè)面的廣告
     *
     * @param properties
     * @return
     */
    @Bean
    @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
    public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) {
        // 獲取web監(jiān)控頁(yè)面的參數(shù)
        DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
        // 提取common.js的配置路徑
        String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
        String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
        // 創(chuàng)建filter進(jìn)行過(guò)濾
        Filter filter = new Filter() {
            @Override
            public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
            }

            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                    throws IOException, ServletException {
                String text = Utils.readFromResource("support/http/resources/js/common.js");
                text = text.replace("this.buildFooter();", "");
                response.getWriter().write(text);
            }

            @Override
            public void destroy() {
            }
        };
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(filter);
        registrationBean.addUrlPatterns(commonJsPattern);
        return registrationBean;
    }
}

下面,我們還配置了一個(gè)過(guò)濾器,這個(gè)過(guò)濾器的目的是去除 Druid 監(jiān)控頁(yè)面的阿里廣告,具體原理參考Spring Boot 如何監(jiān)控 SQL 運(yùn)行情況?一文。

七 測(cè)試

好啦,大功告成,我們?cè)賮?lái)測(cè)試一下,寫一個(gè) UserMapper:

@Mapper
public interface UserMapper {
    @Select("select count(*) from user")
    Integer count();
}

一個(gè)很簡(jiǎn)單的數(shù)據(jù)庫(kù)查詢操作。

再來(lái)一個(gè) service:

@Service
public class UserService {

    @Autowired
    UserMapper userMapper;

    @DataSource("master")
    public Integer master() {
        return userMapper.count();
    }

    @DataSource("slave")
    public Integer slave() {
        return userMapper.count();
    }
}

通過(guò) @DataSource 注解來(lái)指定具體操作的數(shù)據(jù)源,如果沒(méi)有使用該注解指定,默認(rèn)就使用 master 數(shù)據(jù)源。

最后去單元測(cè)試中測(cè)一下,如下:

@SpringBootTest
class DynamicDatasourceDemoApplicationTests {

    @Autowired
    UserService userService;

    @Test
    void contextLoads() {
        System.out.println("userService.master() = " + userService.master());
        System.out.println("userService.slave() = " + userService.slave());
    }

}

由于我這里 master 和 slave 分別對(duì)應(yīng)了不同的庫(kù),這里查詢會(huì)展示出不同的結(jié)果。

責(zé)任編輯:武曉燕 來(lái)源: 江南一點(diǎn)雨
相關(guān)推薦

2023-09-07 08:39:39

copy屬性數(shù)據(jù)源

2023-01-04 09:33:31

SpringBootMybatis

2023-06-07 08:08:37

MybatisSpringBoot

2020-12-31 07:55:33

spring bootMybatis數(shù)據(jù)庫(kù)

2020-06-02 07:55:31

SpringBoot多數(shù)據(jù)源

2020-03-13 14:05:14

SpringBoot+數(shù)據(jù)源Java

2022-12-19 07:21:35

Hutool-db數(shù)據(jù)庫(kù)JDBC

2010-12-27 09:59:11

ODBC數(shù)據(jù)源

2009-06-15 13:24:46

JBoss數(shù)據(jù)源

2020-11-24 09:56:12

數(shù)據(jù)源讀寫分離

2025-04-14 01:00:00

Calcite電商系統(tǒng)MySQL

2009-08-14 10:26:27

ibatis多數(shù)據(jù)源

2023-10-31 07:52:53

多數(shù)據(jù)源管理后端

2022-05-18 12:04:19

Mybatis數(shù)據(jù)源Spring

2022-05-10 10:43:35

數(shù)據(jù)源動(dòng)態(tài)切換Spring

2025-02-05 09:17:40

2021-01-22 05:49:41

數(shù)據(jù)源思路規(guī)劃

2014-11-20 09:47:06

Java

2023-01-10 16:30:22

Spring數(shù)據(jù)庫(kù)

2024-11-20 09:12:56

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

中文字幕欧美人与畜| 久久久久久久久久国产| 国产精品乱码久久久久| 在线观看麻豆蜜桃| 国产成人一区在线| 8x海外华人永久免费日韩内陆视频| 黄色正能量网站| 日韩专区视频网站| 亚洲午夜激情网站| 日韩欧美在线一区二区| 97人妻一区二区精品免费视频 | 综合天堂久久久久久久| 中文字幕日韩一区| 精品国产乱码久久久久久久软件| 超碰在线观看91| 欧美三区不卡| 中文字幕日韩av综合精品| 免费观看污网站| 黄页免费欧美| 欧美日韩一区免费| 黄色一级大片免费| jizz视频在线观看| 26uuu亚洲综合色| 亚洲jizzjizz日本少妇| 国产主播第一页| 亚洲乱码久久| 欧美日本黄视频| 精品国产大片大片大片| 亚洲+小说+欧美+激情+另类| 日韩一区二区三区四区| 欧美自拍小视频| 自拍偷拍亚洲视频| 午夜视频在线观看一区二区| 91视频成人免费| 色综合久久av| 天天干天天操天天操| 亚洲午夜极品| 欧美成人激情图片网| 亚洲天堂最新地址| 狠狠做六月爱婷婷综合aⅴ| 亚洲激情在线观看| 亚洲国产综合av| 日韩成人一区| 欧美丝袜丝nylons| 97公开免费视频| 卡通欧美亚洲| 欧美性少妇18aaaa视频| 免费不卡av在线| 欧美78videosex性欧美| 亚洲精品免费在线观看| 亚洲高清在线播放| 最新国产在线观看| 国产精品乱码一区二三区小蝌蚪| 日韩久久在线| 婷婷视频在线| 亚洲男同性恋视频| 草草草视频在线观看| 天堂网在线免费观看| caoporn国产| 亚洲一区二区成人| 欧美与黑人午夜性猛交久久久| 久久网免费视频| 一区在线播放| 欧美孕妇性xx| 高潮无码精品色欲av午夜福利| 三级在线观看一区二区| 国产精品第1页| 亚洲字幕av一区二区三区四区| 人妖欧美一区二区| 成人淫片在线看| 亚洲精品第五页| caoporm超碰国产精品| 久久99精品久久久久久青青日本| 日本人妖在线| 欧美激情一区三区| 中文字幕日韩精品久久| 26uuu亚洲电影在线观看| 亚洲综合精品自拍| aa在线免费观看| 欧美日韩成人影院| 4438成人网| 久久黄色av网站| 亚洲精品国产一区二区三区| 成人污版视频| 亚洲精品国产精品乱码不99按摩| 波多野结衣片子| 国产精品毛片久久| 久久久久中文字幕| 日韩黄色片网站| 国产一区福利在线| 国产一区二区无遮挡 | 亚洲精品一区二区三区婷婷月| 婷婷色一区二区三区| 亚洲午夜精品一区 二区 三区| 欧美精品999| 狠狠躁夜夜躁人人爽视频| 国产一区在线不卡| 免费一区二区三区| 国产三区在线观看| 激情av一区二区| 久久久久久久高清| 亚洲第一二三区| 欧美成人精品影院| 懂色av蜜臀av粉嫩av喷吹| 国产一区中文字幕| 国产精品美女久久久久久| 欧美一级黑人aaaaaaa做受| 中文字幕在线观看第二页| 国产成人综合在线播放| 欧美一级日本a级v片| 18网站在线观看| 欧洲精品一区二区三区在线观看| 国产chinesehd精品露脸| 国产不卡一区| 91高潮在线观看| av av片在线看| 国产日韩综合av| 精品视频免费在线播放| 国产日韩中文在线中文字幕| 尤物tv国产一区| 久草国产精品视频| 国产激情视频一区二区在线观看| 日韩欧美在线电影| 波多视频一区| 亚洲精品电影网| 国产精品成人国产乱| 国产在线播放一区三区四| 日本不卡高清视频一区| 欧美gv在线| 精品动漫一区二区三区在线观看| 亚洲少妇xxx| 中国黄色a级片| 激情五月综合| 人体精品一二三区| 深夜福利视频在线观看| 一区二区三区四区激情| 青青草久久伊人| 日本激情一区| 国产精品美女www| 毛片在线能看| 色婷婷久久99综合精品jk白丝| 老熟妇精品一区二区三区| 欧美日韩四区| 91久久精品一区二区别| 国产一区久久精品| 337p亚洲精品色噜噜噜| 国产精品视频看看| 久久99精品一区二区三区| 亚洲国产另类久久久精品极度| 在线看欧美视频| 在线国产精品播放| 曰批又黄又爽免费视频| 欧美国产一区二区| wwwwxxxx日韩| 99re66热这里只有精品8| 国产精品丝袜高跟| 欧美精品电影| 婷婷成人影院| 欧美日韩亚洲一区二| 精品视频站长推荐| 亚洲久久一区| 欧美在线视频二区| 国产国产一区| 久久夜色精品国产亚洲aⅴ| 国产口爆吞精一区二区| 亚洲人亚洲人成电影网站色| 最好看的中文字幕| 黄色亚洲在线| 免费成人看片网址| 国产成人精选| 欧美老女人xx| 亚洲色图 校园春色| 色噜噜狠狠成人中文综合| 正在播放国产对白害羞| 激情国产一区二区 | 麻豆av免费在线观看| 91精品国产综合久久小美女| 久久久久亚洲av成人片| av爱爱亚洲一区| 亚洲 中文字幕 日韩 无码| 欧美gayvideo| 国产一区二区三区av在线| 激情开心成人网| 久久亚洲精品成人| 国产精品久久久久久久久久小说 | 亚洲国产天堂网精品网站| 狠狠人妻久久久久久| 国产精品三级久久久久三级| 性一交一黄一片| 丝袜诱惑亚洲看片| 久久久无码中文字幕久...| 欧美一性一交| 成人www视频在线观看| 超碰在线最新网址| 国产午夜精品美女视频明星a级| 一二三区在线播放| 午夜在线成人av| 多男操一女视频| 91麻豆国产自产在线观看| 一起操在线视频| 亚洲综合精品| 免费日韩在线观看| 狠狠色丁香婷婷综合影院| 国产精品日韩高清| 日韩有码欧美| 日本久久久久久久久久久| 在线观看三级视频| 在线观看日韩专区| 日本免费一区二区三区最新| 欧美一级二级三级乱码| 日韩国产成人在线| 午夜精品久久久久影视| 久久综合九色综合97_久久久| 久久99久久99精品| 99久久婷婷| 日韩av首页| 欧洲中文字幕精品| 国产亚洲欧美精品久久久久久 | 一区二区三区免费在线观看视频| 国产一区二区在线视频| 久久精品香蕉视频| 亚洲国产精品第一区二区三区| 一区高清视频| 欧美日韩久久精品| 免费观看成人高| 91精品日本| 91精品免费| 亚洲二区av| 国产啪精品视频网站| 台湾佬中文娱乐久久久| 97国产suv精品一区二区62| 在线免费av导航| 久久精品成人动漫| 日韩成人影视| www.欧美三级电影.com| jizzjizz在线观看| 一区二区亚洲欧洲国产日韩| 天堂a√中文在线| 日韩电影中文字幕| 天堂在线视频免费观看| 精品盗摄一区二区三区| www.久久精品.com| 精品三级久久久| 国产成人午夜视频网址| 日韩脚交footjobhdboots| 午夜欧美大片免费观看| heyzo中文字幕在线| 欧美日韩xxxxx| 欧美bbbxxxxx| 午夜精品视频在线| 久九九久频精品短视频| 欧美重口另类videos人妖| 亚洲欧美小说色综合小说一区| 欧美与黑人午夜性猛交久久久| 依依综合在线| 国产精品看片资源| 欧美天堂一区| 91手机视频在线观看| 亚洲成人影音| 国产三级精品在线不卡| 久久精品亚洲成在人线av网址| 狠狠综合久久av| 精品99久久| 偷拍盗摄高潮叫床对白清晰| 亚洲欧美综合久久久| 欧美日韩激情四射| aⅴ色国产欧美| 992kp快乐看片永久免费网址| 日韩成人伦理电影在线观看| 手机av在线网| 成人免费毛片嘿嘿连载视频| 国产又黄又粗又猛又爽的视频| 久久夜色精品一区| 亚洲毛片亚洲毛片亚洲毛片| 91国内精品视频| 亚洲成av人**亚洲成av**| 午夜精品三级久久久有码| 色悠久久久久综合欧美99| 正在播放亚洲精品| 日韩午夜在线影院| 日韩一级中文字幕| 国产一区二区三区视频 | 美国成人xxx| 日韩av高清在线播放| 91精品亚洲| 午夜精品久久久久久久无码| 全部av―极品视觉盛宴亚洲| 午夜免费视频网站| 91麻豆国产自产在线观看| 国产成人免费在线观看视频| 亚洲综合免费观看高清完整版在线| 人妻丰满熟妇av无码区| 91精品国产色综合久久不卡蜜臀| 三级在线观看网站| 日韩在线观看免费高清| 成人女同在线观看| 国产精品免费一区二区三区都可以| 日韩欧美高清一区二区三区| 免费在线国产精品| 欧美+日本+国产+在线a∨观看| 国产黄色一级网站| 国产麻豆精品theporn| 最新中文字幕视频| 一区二区在线观看不卡| 亚洲成人av网址| 国产精品影音先锋| 国产精品精品软件视频| 日本成人7777| 超级碰在线观看| 日本欧美大码aⅴ在线播放| 97精品人妻一区二区三区蜜桃| 国产精品理伦片| 国产一级片av| 国产婷婷色综合av蜜臀av| 中文字幕中文字幕在线十八区| 国产成人久久久| 日韩av三区| www.夜夜爱| 国产精品综合av一区二区国产馆| 久久久久久九九九九九| 亚洲成a人片在线观看中文| 91久久精品无码一区二区| 亚洲午夜激情免费视频| 国产色播av在线| 岛国视频一区| 亚洲一区二区日韩| 怡红院亚洲色图| 国产欧美一二三区| 国语对白永久免费| 亚洲第一区在线| 暧暧视频在线免费观看| 97人人香蕉| 欧美三级黄美女| 波多野结衣中文字幕在线播放| 国产精品美女视频| 少妇又紧又色又爽又刺激视频 | 韩国无码一区二区三区精品| 亚洲影院久久精品| 成 人片 黄 色 大 片| 久久综合五月天| 精品亚洲二区| 伊人网在线免费| 国产凹凸在线观看一区二区| 久久久久久久久久网站| 91精品国产全国免费观看| 欧美jizz18hd性欧美| 91精品久久久久久久久| 久久精品av| 成人性生交视频免费观看| 亚洲三级免费电影| 国产日本精品视频| 免费99精品国产自在在线| 精品中文字幕一区二区三区| 中文字幕不卡每日更新1区2区| 久久成人免费网| 日本黄色片免费观看| 4438亚洲最大| 福利成人导航| 精品无码久久久久国产| 国产精品久久久久久久免费软件 | 国产精品美女www爽爽爽| 亚洲综合一区中| 伦伦影院午夜日韩欧美限制| 久久伊人影院| 精品久久久久久无码中文野结衣| 成人avav影音| 国产午夜精品久久久久| 一区二区三区视频免费| 欧美jizz18| h无码动漫在线观看| 92国产精品观看| 日批视频免费观看| 久久高清视频免费| 免费看成人人体视频| 国产男女激情视频| 亚洲同性gay激情无套| 国产91免费在线观看| 日本不卡免费高清视频| 成人羞羞网站| 自拍偷拍激情视频| 91精品福利视频| 五月花成人网| 欧美日韩国产精品一卡| 久久久久综合| 五月天婷婷色综合| 日韩精品在线视频| 国产电影一区二区| 欧美国产亚洲一区| 中文字幕一区二区三区在线不卡 | 国产一区欧美日韩| 日本一级淫片色费放| 在线丨暗呦小u女国产精品| 国产电影一区| 日韩精品一区中文字幕| 亚洲免费观看高清完整版在线观看| 狠狠综合久久av一区二区| 国产精品www网站| 欧美日本久久| 欧美另类69xxxx| 精品一区二区三区四区在线|