Spring Boot 集成 Druid + Mybatis

最近把一个项目的框架由 SpringMVC 转为 Spring Boot,顺便作为学习 Spring Boot 的入门实践。框架的使用入门很快,尤其是 Spring Boot 其实相当于对 SpringMVC 做了一些改进,去除配置,改为代码约定。

但是,没了配置,第三方库如何集成进来就是我 Spring Boot 入门学习遇到的第一个坎,尤其是一些没有官方支持 Spring Boot 的库。我把我的项目框架转为 Spring Boot 的时候,就遇到了 Druid 和 Mybatis 集成的问题,Mybatis 有支持 Spring Boot 的包,Druid 没有,所以 Druid 的集成就略显麻烦一点。我搜索了很多其他人写的相关 Blog,结果发现很多人要么是互相抄,要么是语焉不详,只是贴出大版的代码,你照抄他的代码虽然也能集成成功,但是你只知其然,不知其所以然,或者有些代码根本是不必要的。

在走了不少弯路以及持续改进后,我最终以比较满意的结果集成 Druid + Mybatis,为此记录下来,作为个人对近期 Spring Boot 学习总结。本文是针对如何在 Spring Boot 中集成 Druid 和 Mybatis,所以读者必须先熟悉 Spring Boot、Druid 和 Mybatis,对于这三者的细节,不再展开描述。

引入 Druid 和 Mybatis包:

在项目 pom.xml 文件中,引入这两个库的 jar 包,其中数据库我是用 MySQL,所以我同时也引入了 MySQL 的驱动 jar 包

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>6.0.6</version>
</dependency>

<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid</artifactId>
   <version>1.1.5</version>
</dependency>

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.1</version>
</dependency>

<!-- Mybatis 的 jar 包引入的是直接支持 Spring Boot 的,如果是引入是下面的这个 Mybatis 包,就需要自己去实现 Mybais 的配置代码 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.5</version>
</dependency>

集成 Druid:

Druid 是数据库连接池,负责管理数据库的连接,给Mybatis、JPA提供数据库的连接,所以先集成他进来。
首先在 Spring Boot 的配置文件中,配置好数据源的各种参数,我的配置文件使用的是 yml,如果你的配置文件为 Properties,请更换为对应的格式即可,我的配置如下:

spring:
    datasource:
        #DruidDataSource 所需参数
        type: com.alibaba.druid.pool.DruidDataSource 
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/test
        username: 数据库用户名 
        password: 数据库密码 
        initialSize: 3 
        minIdle: 3 
        maxActive: 30 
        maxWait: 15000 
        timeBetweenEvictionRunsMillis: 120000 
        minEvictableIdleTimeMillis: 300000 
        validationQuery: SELECT 'x'
        validationQueryTimeout: 1
        testWhileIdle: true 
        testOnBorrow: false 
        testOnReturn: false 
        poolPreparedStatements: false 
        maxPoolPreparedStatementPerConnectionSize: 20 
        removeAbandoned: true 
        removeAbandonedTimeoutMillis: 20000 
        logAbandoned: true 
        logDifferentThread: true 
        filters: log4j,stat 
        connectionProperties:  druid.stat.mergeSql=true;druid.stat.logSlowSql=true;druid.stat.slowSqlMillis=3000 
        useGlobalDataSourceStat: true 
        # Druid 监控 Servlet 配置参数 
        druidRegistrationUrl: /druid/* 
        resetEnable: true 
        loginUsername: 监控后台登录名称 
        loginPassword: 监控后台登录密码 
        # Druid 监控过滤相关配置参数 
        filtersUrlPatterns: /* 
        exclusions: '*.js,*.gif,*.jpg,*.jpeg,*.png,*.css,*.ico,*.jsp,/druid/*'
        sessionStatMaxCount: 2000 
        sessionStatEnable: true 
        principalSessionName: session_user_key 
        profileEnable: true

以上配置参数写好后,Druid 并未按照我们配置的参数进行初始化,因为 Druid 官方并未去实现相关代码,所以我们要自己去实现。

创建一个 DruidConfig 类,我们在这个类中获取配置文件里面的 spring.datasource 中配置的数据,通过 @Component 和 @ConfigurationProperties(prefix = "spring.datasource") 注解,获取到了配置文件中的配置参数代码如下:

@Component
@ConfigurationProperties(prefix = "spring.datasource")
public class DruidConfig {
    private final Logger logger = LoggerFactory.getLogger(DruidConfig.class);

    private String driverClassName;
    private String url;
    private String username;
    private String password;
    private int initialSize;
    private int minIdle;
    private int maxActive;
    private long maxWait;
    private long timeBetweenEvictionRunsMillis;
    private long minEvictableIdleTimeMillis;
    private String validationQuery;
    private boolean testWhileIdle;
    private boolean testOnBorrow;
    private boolean testOnReturn;
    private boolean poolPreparedStatements;
    private int maxPoolPreparedStatementPerConnectionSize;
    private long removeAbandonedTimeoutMillis;
    private boolean removeAbandoned;
    private boolean logAbandoned;
    private boolean logDifferentThread;
    private String filters;
    private String connectionProperties;
    private boolean useGlobalDataSourceStat;

    //Druid 监控 Servlet 配置参数
    private String druidRegistrationUrl;
    private boolean resetEnable;
    private String loginUsername;
    private String loginPassword;

    // Filters 配置参数
    private String filtersUrlPatterns;
    private String exclusions;
    private int sessionStatMaxCount;
    private boolean sessionStatEnable;
    private String principalSessionName;
    private boolean profileEnable;

    …… setter & getter
}

紧接着就是根据参数,创建 DruidDataSource 了,在 DruidConfig 中,创建一个方法:

@Bean(initMethod = "init", destroyMethod = "close")
@Primary
public DruidDataSource dataSource() {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName(driverClassName);
    dataSource.setUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(password);
    dataSource.setInitialSize(initialSize);
    dataSource.setMinIdle(minIdle);
    dataSource.setMaxActive(maxActive);
    dataSource.setMaxWait(maxWait);
    dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
    dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
    dataSource.setValidationQuery(validationQuery);
    dataSource.setTestWhileIdle(testWhileIdle);
    dataSource.setTestOnBorrow(testOnBorrow);
    dataSource.setTestOnReturn(testOnReturn);
    dataSource.setPoolPreparedStatements(poolPreparedStatements);
    dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
    dataSource.setRemoveAbandonedTimeoutMillis(removeAbandonedTimeoutMillis);
    dataSource.setRemoveAbandoned(removeAbandoned);
    dataSource.setLogDifferentThread(logDifferentThread);

    try {
        dataSource.setFilters(filters);
    }
    catch(SQLException e) {
        e.printStackTrace();
        logger.error("Druid URL过滤设置失败", e);
    }
    dataSource.setConnectionProperties(connectionProperties);
    dataSource.setUseGlobalDataSourceStat(useGlobalDataSourceStat);

    return dataSource;
}

这个方法,就是告诉框架,数据源是由这个方法来提供,注解 @Primary 意思是告诉框架,如果开发者没有指定其他数据源,那么就默认调用这个方法来提供数据源。这一步进行完,其实 Druid 的最核心的配置已经进行完毕,可以使用 Druid 来给 Mybatis 或者 JPA 提供数据库连接了,下面的 Druid 监控可根据实际需求进行配置。

  • 让 Druid 支持事务:通过上面的配置,虽然已经能提供数据库的连接来进行数据库操作了,但是开发过程中,除了调用 SQL,还会遇到调用数据库的事务,所以我们要让 Druid 支持事务才行,让 Druid 支持事务很简单,实现 Spring 的 TransactionManagementConfigurer 接口,重写该接口下的 annotationDrivenTransactionManager 方法,在这个方法里面给 DruidDataSource 开启事务,同时让这个类在 DruidConfig 被 Spring 加载之后,再去被 Spring 加载即可,代码如下:

@Configuration
@EnableTransactionManagement
@AutoConfigureAfter({DruidConfig.class})
@MapperScan(basePackages = {"Mybatis 的 DAO 接口所在的包路径"})
public class TransactionConfig implements   TransactionManagementConfigurer {
    @Autowired
    private DruidDataSource mDataSource;

    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return new DataSourceTransactionManager(mDataSource);
    }
}
  • 配置 Druid 监控页面。Druid 提供了数据库链接状态和 SQL 执行的页面,方便开发者查看自己的程序的数据库连接与操作状态,这个监控后台的页面 Servlet 是 StatViewServlet,我们配置好他,就能打开监控后台查看数据了,在 DruidConfig 中,新增一个如下的方法:

@Bean
public ServletRegistrationBean druidServlet() {
    ServletRegistrationBean servletBean = new ServletRegistrationBean(new StatViewServlet(), druidRegistrationUrl);
    servletBean.addInitParameter("resetEnable", String.valueOf(resetEnable));
    servletBean.addInitParameter("loginUsername", loginUsername);
    servletBean.addInitParameter("loginPassword", loginPassword);

    return servletBean;
}

添加这个方法后,重新启动项目,你就能在浏览器中输入 项目URL/你配置的Druid监控后台路径(就是上面druidRegistrationUrl变量) 来访问到 Druid 的监控后台了。

  • 现在是可以访问 Druid 的监控后台了,但是这样的配置不完美,还需要给监控后台加上一些过滤,避免一些没必要的资源和页面被统计进去,所以我们还要配置一个 WebStatFilter,关于 WebStatFilter 的配置参数,请查阅相关文档,我这里只是部分配置参数。我们在 DruidConfig 中,新增一个如下的方法:

@Bean
public FilterRegistrationBean filterRegistration() {
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
    filterRegistrationBean.addUrlPatterns(filtersUrlPatterns);
    filterRegistrationBean.addInitParameter("exclusions", exclusions);
    filterRegistrationBean.addInitParameter("sessionStatMaxCount", String.valueOf(sessionStatMaxCount));
    filterRegistrationBean.addInitParameter("sessionStatEnable", String.valueOf(sessionStatEnable));
    filterRegistrationBean.addInitParameter("principalSessionName", principalSessionName);
    filterRegistrationBean.addInitParameter("profileEnable", String.valueOf(profileEnable));

    return filterRegistrationBean;
}

至此,Druid 的配置已全部完成。

集成 Mybatis:

Mybatis 的集成就要比 Druid 简单很多,因为我们引入的 org.mybatis.spring.boot 得包,已经帮我们实现了类似于 DruidConfig 的配置类了
image

所以我们只要在 Spring Boot 中配置好 Mybatis 的参数,写好你的 DAO 和 Service(如果你的 Mybatis 是使用 xml 来写 SQL 方式,就不用实现 Service,如果是注解,就要实现 DAO 的 Service),然后就能使用 Mybatis 了,配置如下,在你的 Spring Boot 中写上如下配置,注意,配置的参数要换上自己的,别照搬代码又不改成自己的参数,然后调用 Mybatis 进行操作发生异常时一头雾水:

mybatis:
    config-location: classpath:/db/mybatis-config.xml
    mapper-locations: classpath:/db/mappers/**/*Mapper.xml
    type-aliases-package: com.java.springboot.db.entity

结尾:

至此,Spring Boot 集成 Druid 和 Mybatis 的工作已完成,至于里面的各个参数是什么意思,如何优化,不在本文范围之内,该文只说如何集成。如果文中有任何错误、改进方式,欢迎一起探讨。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容