17.从零开始学springboot-整合mybatisPlus-druid多源案例

本文详细介绍了如何在SpringBoot项目中实现多数据源的动态切换,通过使用Druid和AOP方式,并配合MyBatis Plus进行数据库操作,同时为后续支持事务管理预留了扩展方案。

背景

之前写了个springboot整合mybatis-plus和dynamic-datasource-spring-boot-starter做多数据源切换的案列 http://wrsee.com/articles/79 ,但是呢,考虑到在复杂的业务场景中,多数据源必须对事务有很好的支持,这种情况下dynamic-datasource-spring-boot-starter目前就不适用了,因为dynamic-datasource-spring-boot-starter目前不支持多数据源事务(作者称后续会支持),所以,我们这次采用druid和aop的方式来做多数据源切换,后续业务场景需要支持事务的话直接加入Atomikos 、Bitronix、Narayana依赖即可。

多数据源事务(分布式事务)概念

这边简单介绍下,假设有A、B两个数据库,有这么一个事务A库中某表插入一条记录然后B库某表插入一天记录,此时,事务执行中发生错误的话,应该AB库都需要回退。

创建父项目

1.png

2.png

删除src目录,因为父项目不写代码

创建子项目

3.png

4.png

5.png

建立pom父子关系

SpringBootDemo/pom.xml:

<?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.mrcoder</groupId>
    <artifactId>SpringBootDemo</artifactId>
    <version>1.0.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
    </parent>
    <packaging>pom</packaging>
    <modules>
        <module>sbmp-multidb-druid</module>
    </modules>
</project>

sbmp-multidb-druid/pom.xml:

<?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>

    <parent>
        <groupId>com.mrcoder</groupId>
        <artifactId>SpringBootDemo</artifactId>
        <version>1.0.0</version>
        <relativePath>../pom.xml</relativePath>
    </parent>


    <groupId>com.mrcoder</groupId>
    <artifactId>sbmp-multidb-druid</artifactId>
    <version>0.0.1</version>
    <name>sbmp-multidb-druid</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <spring.boot.version>2.1.0.RELEASE</spring.boot.version>
        <druid.version>1.1.10</druid.version>
        <mybatisplus.version>3.0.7.1</mybatisplus.version>
        <lombok.version>1.16.14</lombok.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <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>${druid.version}</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatisplus.version}</version>
        </dependency>
        <!-- mybatis-plus代码生成器 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatisplus.version}</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>${mybatisplus.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

添加配置

application.yml:

spring:
  aop:
    proxy-target-class: true
    auto: true
  datasource:
    druid:
      db1:
        username: root
        password: 123456
        url: jdbc:mysql://192.168.145.131:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
        driver-class-name: com.mysql.cj.jdbc.Driver
      db2:
        username: root
        password: 123456
        url: jdbc:mysql://192.168.145.131:3306/test2?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
        driver-class-name: com.mysql.cj.jdbc.Driver

建库

新增test、test2库
test新增表:

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(16) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `modify_time` datetime DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'admin', '22', '2018-02-10 10:11:18', '2018-02-10 10:11:20');

test2新增表:

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for orders
-- ----------------------------
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_no` varchar(11) DEFAULT NULL COMMENT '订单号',
  `user_id` int(11) DEFAULT NULL COMMENT '用户ID',
  `price` decimal(11,2) DEFAULT NULL COMMENT '支付金额',
  `paid_time` datetime DEFAULT NULL COMMENT '支付时间',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `modify_time` datetime DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of orders
-- ----------------------------
INSERT INTO `orders` VALUES ('1', '20180210001', '1', '100.00', '2018-02-10 10:16:11', '2018-02-10 10:16:15', '2018-02-10 10:16:17');

目录结构

6.png

标红部分请手动新建package和class(也可以使用mybatis-plus-generate代码生成器,这边因为上章使用代码生成器了,这次我们手动创建)

完善

config/DataSourceSwitch

package com.mrcoder.sbmpmultidbdruid.config;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DataSourceSwitch {
    DBTypeEnum value() default DBTypeEnum.db1;
}

config/DataSourceSwitchAspect

package com.mrcoder.sbmpmultidbdruid.config;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Order(value = -100)
@Slf4j
@Aspect
public class DataSourceSwitchAspect {

    @Pointcut("execution(* com.mrcoder.sbmpmultidbdruid.mapper.db1.*.*(..))")
    private void db1Aspect() {
    }

    @Pointcut("execution(* com.mrcoder.sbmpmultidbdruid.mapper.db2.*.*(..))")
    private void db2Aspect() {
    }

    @Before("db1Aspect()")
    public void db1() {
        log.info("切换到db1 数据源...");
        DbContextHolder.setDbType(DBTypeEnum.db1);
    }

    @Before("db2Aspect()")
    public void db2() {
        log.info("切换到db2 数据源...");
        DbContextHolder.setDbType(DBTypeEnum.db2);
    }

}

config/DbContextHolder

package com.mrcoder.sbmpmultidbdruid.config;

public class DbContextHolder {
    private static final ThreadLocal contextHolder = new ThreadLocal<>();
    /**
     * 设置数据源
     * @param dbTypeEnum
     */
    public static void setDbType(DBTypeEnum dbTypeEnum) {
        contextHolder.set(dbTypeEnum.getValue());
    }

    /**
     * 取得当前数据源
     * @return
     */
    public static String getDbType() {
        return (String) contextHolder.get();
    }

    /**
     * 清除上下文数据
     */
    public static void clearDbType() {
        contextHolder.remove();
    }
}

config/DBTypeEnum

package com.mrcoder.sbmpmultidbdruid.config;

public enum DBTypeEnum {
    db1("db1"), db2("db2");
    private String value;

    DBTypeEnum(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }
}

config/DynamicDataSource

package com.mrcoder.sbmpmultidbdruid.config;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return  DbContextHolder.getDbType();
    }
}

config/MybatisPlusConfig

package com.mrcoder.sbmpmultidbdruid.config;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
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.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@EnableTransactionManagement
@Configuration
@MapperScan("com.mrcoder.webapi.mapper")
public class MybatisPlusConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        //paginationInterceptor.setLocalPage(true);
        return paginationInterceptor;
    }

    @Bean(name = "db1")
    @ConfigurationProperties(prefix = "spring.datasource.druid.db1")
    public DataSource db1() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "db2")
    @ConfigurationProperties(prefix = "spring.datasource.druid.db2")
    public DataSource db2() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 动态数据源配置
     *
     * @return
     */
    @Bean
    @Primary
    public DataSource multipleDataSource(@Qualifier("db1") DataSource db1,
                                         @Qualifier("db2") DataSource db2) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DBTypeEnum.db1.getValue(), db1);
        targetDataSources.put(DBTypeEnum.db2.getValue(), db2);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(db1);
        return dynamicDataSource;
    }

    @Bean("sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(multipleDataSource(db1(), db2()));

        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setCacheEnabled(false);
        sqlSessionFactory.setConfiguration(configuration);
        //PerformanceInterceptor(),OptimisticLockerInterceptor()
        //添加分页功能
        sqlSessionFactory.setPlugins(new Interceptor[]{
                paginationInterceptor()
        });
//        sqlSessionFactory.setGlobalConfig(globalConfiguration());
        return sqlSessionFactory.getObject();
    }

 /*   @Bean
    public GlobalConfiguration globalConfiguration() {
        GlobalConfiguration conf = new GlobalConfiguration(new LogicSqlInjector());
        conf.setLogicDeleteValue("-1");
        conf.setLogicNotDeleteValue("1");
        conf.setIdType(0);
        conf.setMetaObjectHandler(new MyMetaObjectHandler());
        conf.setDbColumnUnderline(true);
        conf.setRefresh(true);
        return conf;
    }*/
}

controller/IndexController

package com.mrcoder.sbmpmultidbdruid.controller;

import com.mrcoder.sbmpmultidbdruid.entity.Order;
import com.mrcoder.sbmpmultidbdruid.entity.User;
import com.mrcoder.sbmpmultidbdruid.service.OrderService;
import com.mrcoder.sbmpmultidbdruid.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.List;

/**
 * 前端控制器
 */
@RestController
public class IndexController {
    @Autowired
    private UserService userService;
    @Autowired
    private OrderService orderService;

    @GetMapping("/user")
    public ResponseEntity<List<User>> getUserList() {
        return ResponseEntity.ok(userService.getUserList());
    }

    @GetMapping("/price")
    public ResponseEntity<BigDecimal> getPrice() {
        return ResponseEntity.ok(userService.getOrderPriceByUserId(1));
    }

    @GetMapping("/order")
    public ResponseEntity<List<Order>> getOrderList() {
        return ResponseEntity.ok(orderService.getOrderList());
    }

    @GetMapping("/price2")
    public ResponseEntity<BigDecimal> getPrice2() {
        return ResponseEntity.ok(orderService.getOrderPriceByUserId(1));
    }

}

entity/Order

package com.mrcoder.sbmpmultidbdruid.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;

@Data
@TableName("orders")
public class Order extends Model<Order> {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    /**
     * 订单号
     */
    @TableField("order_no")
    private String orderNo;
    /**
     * 用户ID
     */
    @TableField("user_id")
    private Integer userId;
    /**
     * 支付金额
     */
    private BigDecimal price;
    /**
     * 支付时间
     */
    @TableField("paid_time")
    private Date paidTime;
    /**
     * 创建时间
     */
    @TableField("create_time")
    private Date createTime;
    /**
     * 修改时间
     */
    @TableField("modify_time")
    private Date modifyTime;

    @Override
    protected Serializable pkVal() {
        return this.id;
    }
}

entity/User

package com.mrcoder.sbmpmultidbdruid.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

@Data
public class User extends Model<User> {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    /**
     * 姓名
     */
    private String name;
    /**
     * 年龄
     */
    private Integer age;
    /**
     * 创建时间
     */
    @TableField(value = "create_time",fill = FieldFill.INSERT)
    private Date createTime;
    /**
     * 修改时间
     */
    @TableField(value = "modify_time",fill = FieldFill.INSERT_UPDATE)
    private Date modifyTime;

    @Override
    protected Serializable pkVal() {
        return this.id;
    }
}

mapper/db1/UserMapper

package com.mrcoder.sbmpmultidbdruid.mapper.db1;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mrcoder.sbmpmultidbdruid.entity.User;
import org.apache.ibatis.annotations.Select;
import java.util.List;

/**
 *  Mapper 接口
 */
public interface UserMapper extends BaseMapper<User> {

}

mapper/db2/OrderMapper

package com.mrcoder.sbmpmultidbdruid.mapper.db2;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mrcoder.sbmpmultidbdruid.entity.Order;
import org.apache.ibatis.annotations.Select;
import java.math.BigDecimal;
import java.util.List;

/**
 *  Mapper 接口
 */
public interface OrderMapper extends BaseMapper<Order> {

    @Select("SELECT price from orders WHERE user_id = #{userId}")
    BigDecimal getPriceByUserId(Integer userId);
}

service/impl/OrderServiceImpl

package com.mrcoder.sbmpmultidbdruid.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mrcoder.sbmpmultidbdruid.entity.Order;
import com.mrcoder.sbmpmultidbdruid.mapper.db2.OrderMapper;
import com.mrcoder.sbmpmultidbdruid.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;

/**
 * 服务实现类
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    @Autowired
    private OrderMapper orderMapper;

    @Override
    public List<Order> getOrderList() {
        return orderMapper.selectList(null);
    }

    @Override
    public BigDecimal getOrderPriceByUserId(Integer userId) {
        return orderMapper.getPriceByUserId(userId);
    }
}

service/impl/UserServiceImpl

package com.mrcoder.sbmpmultidbdruid.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mrcoder.sbmpmultidbdruid.config.DBTypeEnum;
import com.mrcoder.sbmpmultidbdruid.config.DataSourceSwitch;
import com.mrcoder.sbmpmultidbdruid.entity.User;
import com.mrcoder.sbmpmultidbdruid.mapper.db2.OrderMapper;
import com.mrcoder.sbmpmultidbdruid.mapper.db1.UserMapper;
import com.mrcoder.sbmpmultidbdruid.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;

/**
 *  服务实现类
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private UserMapper userMapper;

    @Override
    @DataSourceSwitch(DBTypeEnum.db2)
    public List<User> getUserList() {
        return userMapper.selectList(null);
    }

    @Override
    //@DataSourceSwitch(DBTypeEnum.db2)
    public BigDecimal getOrderPriceByUserId(Integer userId) {
        return orderMapper.getPriceByUserId(userId);
    }
}

service/OrderService

package com.mrcoder.sbmpmultidbdruid.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.mrcoder.sbmpmultidbdruid.entity.Order;
import java.math.BigDecimal;
import java.util.List;

/**
 *  服务类
 */
public interface OrderService extends IService<Order> {
    List<Order> getOrderList();
    BigDecimal getOrderPriceByUserId(Integer userId);
}

service/UserService

package com.mrcoder.sbmpmultidbdruid.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.mrcoder.sbmpmultidbdruid.entity.User;
import java.math.BigDecimal;
import java.util.List;

/**
 *  服务类
 */
public interface UserService extends IService<User> {
    List<User> getUserList();

    BigDecimal getOrderPriceByUserId(Integer userId);
}

SbmpMultidbDruidApplication

package com.mrcoder.sbmpmultidbdruid;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.mrcoder.sbmpmultidbdruid.mapper")
public class SbmpMultidbDruidApplication {

    public static void main(String[] args) {
        SpringApplication.run(SbmpMultidbDruidApplication.class, args);
    }

}

运行

最后我们运行,访问

http://localhost:8080/order
7.png

http://localhost:8080/user
8.png

http://localhost:8080/price
9.png

http://localhost:8080/price2
10.png

项目地址

https://github.com/MrCoderStack/SpringBootDemo/tree/master/sbmp-multidb-druid

https://gitee.com/MrCoderStack/MrCoderStackBlog/tree/master/sbmp-multidb-druid

请关注我的订阅号

订阅号.png

<think>我们正在整合Spring Boot 3.5、MyBatis-Plus、Oracle数据库和Druid连接池。以下是步骤: 1. **添加依赖**:在pom.xml中添加MyBatis-Plus StarterDruid Starter和Oracle JDBC驱动。 2. **配置数据**:在application.yml中配置Druid连接池和Oracle数据库连接。 3. **配置MyBatis-Plus**:配置MyBatis-Plus的相关设置,如mapper扫描路径等。 4. **使用Druid监控**:配置Druid的监控页面。 5. **编写代码**:创建实体类、Mapper接口,并测试多数据源(如果需要)。 注意:由于Oracle JDBC驱动不在Maven中央仓库,可能需要手动下载并安装到本地仓库,或者使用公司私有仓库。这里我们假设已经处理好了驱动依赖。 ### 详细步骤 #### 1. 添加依赖(pom.xml) ```xml <dependencies> <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MyBatis-Plus Starter --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> <!-- 请使用与Spring Boot 3兼容的最新版本 --> </dependency> <!-- Druid Starter --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.18</version> <!-- 检查是否有新版本 --> </dependency> <!-- Oracle JDBC驱动 --> <dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc8</artifactId> <version>21.9.0.0</version> <!-- 根据实际Oracle版本选择 --> </dependency> <!-- Lombok(可选) --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> ``` #### 2. 配置数据(application.yml) ```yaml spring: datasource: type: com.alibaba.druid.pool.DruidDataSource url: jdbc:oracle:thin:@//localhost:1521/ORCLCDB username: your_username password: your_password driver-class-name: oracle.jdbc.OracleDriver # Druid连接池配置 druid: # 初始化大小、最小、最大 initial-size: 5 min-idle: 5 max-active: 20 # 配置获取连接等待超时的时间 max-wait: 60000 # 配置间隔久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 time-between-eviction-runs-millis: 60000 # 配置一个连接在池中最小生存的时间,单位是毫秒 min-evictable-idle-time-millis: 300000 validation-query: SELECT 1 FROM DUAL test-while-idle: true test-on-borrow: false test-on-return: false # 打开PSCache,并且指定每个连接上PSCache的大小 pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 filters: stat,wall,slf4j # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # 合并DruidDataSource的监控数据 use-global-data-source-stat: true # MyBatis-Plus配置 mybatis-plus: configuration: # 开启驼峰命名转换 map-underscore-to-camel-case: true # 日志中打印SQL log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # mapper.xml文件位置(如果没有可以省略) mapper-locations: classpath:mapper/*.xml # 实体扫描包路径 type-aliases-package: com.example.entity ``` #### 3. 配置Druid监控 在Spring Boot主类或配置类中添加以下代码,开启Druid监控页面: ```java import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; import com.alibaba.druid.util.Utils; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.*; import java.io.IOException; @Configuration @AutoConfigureAfter(DruidDataSourceAutoConfigure.class) public class DruidConfig { /** * 配置Druid监控页面 */ @Bean public FilterRegistrationBean<Filter> statViewFilter() { FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new StatViewFilter()); registrationBean.addUrlPatterns("/druid/*"); return registrationBean; } // 自定义Filter,用于处理Druid监控页面的请求 public static class StatViewFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); } } // 移除Druid监控页面的广告 @Bean public FilterRegistrationBean<Filter> removeDruidAdFilter() { FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new RemoveDruidAdFilter()); registrationBean.addUrlPatterns("/druid/*"); return registrationBean; } public static class RemoveDruidAdFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); // 移除广告 String text = Utils.readFromResource("support/http/resources/js/common.js"); text = text.replace("this.buildFooter();", ""); response.getWriter().write(text); } } } ``` 然后在`application.yml`中配置Druid监控页面的访问账号和权限: ```yaml spring: datasource: druid: # 监控页面配置 stat-view-servlet: enabled: true url-pattern: /druid/* login-username: admin login-password: admin reset-enable: false ``` #### 4. 编写代码 创建实体类和Mapper接口: ```java // 实体类 @Data // Lombok注解 public class User { private Long id; private String name; private Integer age; private String email; } ``` ```java // Mapper接口 @Mapper public interface UserMapper extends BaseMapper<User> { // 继承BaseMapper,已经包含了基本的CRUD方法 } ``` 在启动类上添加`@MapperScan`注解扫描Mapper接口: ```java @SpringBootApplication @MapperScan("com.example.mapper") // 替换为你的Mapper接口所在包 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` #### 5. 测试 编写一个简单的Controller进行测试: ```java @RestController @RequestMapping("/user") public class UserController { @Autowired private UserMapper userMapper; @GetMapping("/list") public List<User> list() { return userMapper.selectList(null); } } ``` 启动应用,访问`http://localhost:8080/user/list`,如果配置正确,应该能查询到数据库中的用户数据。 同时,可以访问Druid监控页面:`http://localhost:8080/druid`,使用配置的用户名和密码(admin/admin)登录,查看数据状态和SQL监控。 ### 注意事项 1. **Oracle驱动版本**:确保使用的Oracle驱动版本与你的数据库版本兼容。这里使用的是`ojdbc8`,适用于Oracle 12c及以上版本。如果使用Oracle 11g,可能需要使用`ojdbc6`。 2. **多数据源配置**:如果需要配置多数据源,可以参考引用[3]中的方法,使用`@DS`注解来切换数据。但请注意,多数据源配置需要额外的配置类来定义个数据,并在每个Mapper上使用`@DS`注解指定数据名称。 3. **Spring Boot 3兼容性**:确保MyBatis-Plus和Druid的版本与Spring Boot 3.5兼容。目前MyBatis-Plus 3.5.3.1和Druid 1.2.18支持Spring Boot 3。 4. **Druid广告过滤**:上述配置中移除了Druid监控页面的广告,这是可选的。 ### 常见问题 - **驱动类找不到**:检查Oracle驱动依赖是否正确,以及驱动类名是否配置正确(`oracle.jdbc.OracleDriver`)。 - **连接失败**:检查Oracle服务是否启动,网络是否可达,以及用户名、密码、SID/service name是否正确。 - **Druid监控页面无法访问**:检查是否配置了`stat-view-servlet`,并且没有安全框架(如Spring Security)拦截了`/druid/*`路径。如果使用了安全框架,需要放行该路径。 ### 参考引用 - [1] MyBatis-Plus&Druid数据- [2] Druid连接池介绍。 - [3] 多数据源配置方法。 §§相关问题§§ 1. 如何在Spring Boot中配置多数据源并实现动态切换? 2. 如何优化Druid连接池配置以提高性能? 3. MyBatis-Plus有哪些常用的高级功能? 4. 如何整合Spring Security保护Druid监控页面?</think>### Spring Boot 3.5 整合 MyBatis-Plus、Oracle 和 Druid 连接池 以下是完整的整合步骤,基于 Spring Boot 3.5 + MyBatis-Plus + Oracle + Druid 技术栈: --- #### 1️⃣ 添加依赖 (`pom.xml`) ```xml <dependencies> <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MyBatis-Plus Starter --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.4.1</version> <!-- 兼容 Spring Boot 3.5 的最新版本 --> </dependency> <!-- Druid 连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.21</version> </dependency> <!-- Oracle JDBC 驱动 --> <dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc11</artifactId> <version>21.9.0.0</version> </dependency> <!-- Lombok(可选) --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> ``` > **注意**:Oracle JDBC 驱动需从 Oracle 官方下载或配置企业私服,Maven 中央仓库不提供。 --- #### 2️⃣ 配置数据 (`application.yml`) ```yaml spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: oracle.jdbc.OracleDriver url: jdbc:oracle:thin:@//localhost:1521/ORCLCDB username: your_username password: your_password # Druid 连接池配置 druid: initial-size: 5 min-idle: 5 max-active: 20 max-wait: 60000 validation-query: SELECT 1 FROM DUAL test-while-idle: true test-on-borrow: false test-on-return: false filters: stat,wall,slf4j # 监控页面配置 stat-view-servlet: enabled: true url-pattern: /druid/* login-username: admin login-password: admin # MyBatis-Plus 配置 mybatis-plus: configuration: map-underscore-to-camel-case: true # 自动驼峰转换 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL global-config: db-config: id-type: auto # 主键生成策略 type-aliases-package: com.example.entity # 实体类包路径 ``` --- #### 3️⃣ 配置 Druid 监控(可选) 创建配置类 `DruidConfig.java`: ```java @Configuration public class DruidConfig { @Bean public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() { ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); // 监控页面访问控制 Map<String, String> initParams = new HashMap<>(); initParams.put("loginUsername", "admin"); initParams.put("loginPassword", "admin"); initParams.put("allow", ""); // 允许所有IP bean.setInitParameters(initParams); return bean; } @Bean public FilterRegistrationBean<WebStatFilter> druidWebStatFilter() { FilterRegistrationBean<WebStatFilter> bean = new FilterRegistrationBean<>(new WebStatFilter()); // 排除静态资和不需要监控的请求 Map<String, String> initParams = new HashMap<>(); initParams.put("exclusions", "*.js,*.css,/druid/*"); bean.setInitParameters(initParams); bean.addUrlPatterns("/*"); return bean; } } ``` --- #### 4️⃣ 实体类与 Mapper ```java // 实体类 @Data @TableName("USERS") // 指定Oracle表名 public class User { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; private String email; } ``` ```java // Mapper接口 @Mapper public interface UserMapper extends BaseMapper<User> { // 继承BaseMapper即获得CRUD能力 } ``` --- #### 5️⃣ 多数据源配置(参考引用[3]) ```java @Configuration @MapperScan(basePackages = "com.example.mapper") public class DataSourceConfig { @Bean @ConfigurationProperties("spring.datasource.druid.master") public DataSource masterDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.druid.slave") public DataSource slaveDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean public DataSource dynamicDataSource( @Qualifier("masterDataSource") DataSource master, @Qualifier("slaveDataSource") DataSource slave) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("master", master); targetDataSources.put("slave", slave); DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSources); dataSource.setDefaultTargetDataSource(master); return dataSource; } } ``` 使用 `@DS` 注解切换数据: ```java @Service public class UserService { @DS("master") // 指定主库 public void addUser(User user) { userMapper.insert(user); } @DS("slave") // 指定从库 public User getUserById(Long id) { return userMapper.selectById(id); } } ``` --- #### 6️⃣ 验证整合结果 ```java @SpringBootTest public class OracleIntegrationTest { @Autowired private UserMapper userMapper; @Test void testInsert() { User user = new User(); user.setName("OracleTest"); user.setAge(30); user.setEmail("test@oracle.com"); int result = userMapper.insert(user); assertEquals(1, result); // 验证插入成功 User savedUser = userMapper.selectById(user.getId()); assertNotNull(savedUser); // 验证查询结果 } } ``` --- ### 关键配置说明 1. **Druid 连接池优势**: - 内置 SQL 防火墙和监控统计功能 - 支持加密数据库密码 - 提供详细的连接池运行状态监控(通过 `/druid` 访问) 2. **Oracle 特殊配置**: - 使用 `ojdbc11` 驱动兼容 JDK 17+ - 连接 URL 格式:`jdbc:oracle:thin:@//host:port/service_name` - 验证查询使用 `SELECT 1 FROM DUAL` 3. **MyBatis-Plus 特性**: - 自动生成基础 CRUD 操作 - 支持 Lambda 表达式条件构造器 - 内置分页插件和性能分析插件 > **访问监控页面**:启动应用后访问 `http://localhost:8080/druid`,使用配置的用户名密码登录查看实时监控[^2]。 --- ### 常见问题解决 1. **Oracle 驱动问题**: ```log java.lang.ClassNotFoundException: oracle.jdbc.OracleDriver ``` **解决方案**:确认 `ojdbc11` 已正确安装到本地 Maven 仓库 2. **时区问题**: ```log ORA-01882: timezone region not found ``` **解决方案**:启动时添加 JVM 参数: ``` -Duser.timezone=GMT+08 ``` 3. **Druid 监控不显示 SQL**: 在配置中启用 filters: ```yaml spring: datasource: druid: filters: stat,wall,slf4j ``` --- ### 最佳实践建议 1. **连接池调优**: ```yaml druid: max-active: 50 # 根据DB服务器配置调整 min-idle: 10 validation-query: SELECT 1 FROM DUAL test-on-borrow: true ``` 2. **MyBatis-Plus 分页配置**: ```java @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; } } ``` 3. **SQL 性能监控**: ```yaml druid: filter: stat: log-slow-sql: true slow-sql-millis: 2000 # 慢SQL阈值(2秒) ``` > 生产环境建议关闭 `mybatis-plus.configuration.log-impl` 的 SQL 打印,改用 Druid 的 SQL 监控功能[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码哥说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值