【mybatis-plus】Springboot+AOP+自定义注解实现多数据源操作(数据源信息存在数据库)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 【mybatis-plus】Springboot+AOP+自定义注解实现多数据源操作(数据源信息存在数据库)

背景

本文主要讲述的是如何实现动态切换数据源,数据源信息是存储在数据库表里,与在配置文件中写好数据库信息然后用@DS("XX")注解不同。


前言

本文是为了解决多数据源切换执行任务等,希望对大家有所帮助,有问题的可以在评论区留言,看到一定回复您!

一、准备工作

1.依赖

 <dependency>
     <groupId>com.baomidou</groupId>
     <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
     <version>3.4.0</version>
 </dependency>

2.数据库表(脚本)

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
 
-- ----------------------------
-- Table structure for sys_data_resource
-- ----------------------------
DROP TABLE IF EXISTS `sys_data_resource`;
CREATE TABLE `sys_data_resource`  (
  `uuid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键',
  `data_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '数据源别名',
  `ip_address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '服务器地址(支持ip和域名)',
  `port` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '端口号',
  `data_resource_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '数据库名称',
  `user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名',
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码',
  `parent_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '父id,默认0,0:代表父级 用于树形结构',
  `data_resource_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'mysql' COMMENT '数据库类型,默认:mysql 用于区分不同数据库驱动',
  `create_user_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建用户id',
  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
  `update_user_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '修改用户id',
  `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',
  `url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '数据库链接信息',
  `status` int(2) NULL DEFAULT 0 COMMENT '0:未删除,1:已删除',
  `data_desc` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '数据描述',
  PRIMARY KEY (`uuid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '动态数据源管理' ROW_FORMAT = Dynamic;
 
SET FOREIGN_KEY_CHECKS = 1;

3.配置文件

#配置数据源
spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源,默认值为master
      strict: false
      datasource:
        master: # 数据源1配置
          url: jdbc:log4jdbc:mysql://192.168.1.202:3306/xxxxxx?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
          username: xxxxx
          password: xxx123!
          driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
      druid:
        db-type: com.alibaba.druid.pool.DruidDataSource
        break-after-acquire-failure: true
        # 初始化配置
        initial-size: 3
        # 最小连接数
        min-idle: 3
        # 最大连接数
        max-active: 15
        # 获取连接超时时间
        max-wait: 5000
        # 连接有效性检测时间
        time-between-eviction-runs-millis: 90000
        # 最大空闲时间
        min-evictable-idle-time-millis: 1800000
        test-while-idle: true
        test-on-borrow: false
        test-on-return: false
 
        validation-query: select 1
        # 配置监控统计拦截的filters
        filters: stat
        stat-view-servlet:
          url-pattern: /druid/*
          reset-enable: false
        web-stat-filter:
          url-pattern: /*
          exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
  redis:
    #数据库索引
    database: 0
    host: 192.168.1.202
    port: 6379
    password: xxxx23!

url和driver-class-name 大家可以自行换成mysql的驱动就行。

4.自定义注解@ChangeDB

当方法上使用@ChangeDB时,代表切换数据源,不加时,默认走主数据源。

 
import java.lang.annotation.*;
 
/**
 * 自定义多数据源切换注解
 * <p>
 * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准
 * 如果传递value,则切换到value对应的数据源
 *
 * @author 曹震
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ChangeDB {
    String value() default "";
}

5.定义AOP切面

 
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.hvit.data_governance.dataComparison.dbhelp.DataSourceHelp;
import com.hvit.data_governance.dataComparison.entity.SysDataResource;
import com.hvit.data_governance.dataComparison.service.SysDataResourceService;
import com.hvit.data_governance.utils.DBUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
 
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.sql.DataSource;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Set;
 
/**
 * 动态数据源切面拦截
 * 请注意:这里order一定要小于tx:annotation-driven的order,即先执行DynamicDataSourceAspectAdvice切面,再执行事务切面,才能获取到最终的数据源
 *
 * @author 曹震
 * @version 1.0
 */
@Slf4j
@Aspect
@Component
@Order(1)
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DynamicDataSourceAspect {
    @Autowired
    private DataSource dataSource;
    @Resource
    private DefaultDataSourceCreator defaultDataSourceCreator;
    @Autowired
    private SysDataResourceService sysDataResourceService;
 
    @Pointcut("@annotation(com.hvit.data_governance.dataComparison.dbhelp.ChangeDB) || @within(com.hvit.data_governance.dataComparison.dbhelp.ChangeDB)")
    public void dsPointCut() {
 
    }
 
    @Around("dsPointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        String dynamicDataSource = "";
        try {
            //获取参数,切换数据源参数固定都传poolName参数
            //get请求获取参数
            dynamicDataSource = request.getParameter(DBUtils.DATA_RESOURCE_NAME);
            if (StringUtils.isEmpty(dynamicDataSource)) {
                //如果get请求没获取到参数,就那用post请求body获取参数
                dynamicDataSource = getDataResourceName(request);
            }
            //如果get、post都没获取到,支持动态传参切换数据源
            if (StringUtils.isEmpty(dynamicDataSource)) {
                dynamicDataSource = (String) request.getAttribute("poolName");
            }
            if (StringUtils.isNotEmpty(dynamicDataSource)) {
                //先获取传入的数据源是不是存在数据池当中
                Set<String> sets = ds.getCurrentDataSources().keySet();
                boolean flag = false;
                for (String str : sets) {
                    //首先排除主数据源
                    if (!"master".equals(str)) {
                        if (str.equals(dynamicDataSource)) {
                            DynamicDataSourceContextHolder.push(dynamicDataSource);
                            flag = true;
                            break;
                        }
                    }
                }
                //如果数据池中没有找到当前数据源,那么开始进行新增数据源
                //poolName其实就是数据源id
                if (!flag) {
                    SysDataResource sysDataResource = sysDataResourceService.getById(dynamicDataSource);
                    if (sysDataResource != null) {
                        manualAddSource(sysDataResource);
                        DynamicDataSourceContextHolder.push(dynamicDataSource);
                    }
                }
                return joinPoint.proceed();
            }
            return joinPoint.proceed();
        } finally {
            //每次使用完成,清空数据源
            //DynamicDataSourceContextHolder.poll();
            if (StringUtils.isNotEmpty(dynamicDataSource)) {
                ds.removeDataSource(dynamicDataSource);
            }
        }
    }
 
    /***
     * post请求获取HttpServletRequest 内 poolName参数
     * @param request
     * @return
     */
    public String getDataResourceName(HttpServletRequest request) throws IOException {
        StringBuilder data = new StringBuilder();
        String line;
        BufferedReader reader;
        try {
            reader = request.getReader();
            while (null != (line = reader.readLine())) {
                data.append(line);
            }
        } catch (IOException e) {
            return null;
        }
        JSONObject jsonObject = JSONObject.parseObject(data.toString());
        if (jsonObject == null) {
            return "";
        }
        return jsonObject.getString(DBUtils.DATA_RESOURCE_NAME);
    }
 
    /***
     * 手动加入数据源池
     * @param sysDataResource
     */
    public void manualAddSource(SysDataResource sysDataResource) {
        DataSourceHelp dataSourceHelp = new DataSourceHelp();
        dataSourceHelp.setDriverClassName(DBUtils.getDriverClassName(sysDataResource.getDataResourceType()));
        dataSourceHelp.setPassword(sysDataResource.getPassword());
        dataSourceHelp.setPoolName(sysDataResource.getUuid());
        dataSourceHelp.setUsername(sysDataResource.getUserName());
        dataSourceHelp.setUrl(sysDataResource.getUrl());
        addSource(dataSourceHelp);
    }
 
    public Set<String> addSource(DataSourceHelp dataSourceHelp) {
        DataSourceProperty dataSourceProperty = new DataSourceProperty();
        BeanUtils.copyProperties(dataSourceHelp, dataSourceProperty);
        DynamicRoutingDataSource source = (DynamicRoutingDataSource) dataSource;
        DataSource dataSource = defaultDataSourceCreator.createDataSource(dataSourceProperty);
        source.addDataSource(dataSourceHelp.getPoolName(), dataSource);
        return source.getCurrentDataSources().keySet();
    }
}

这里解释下,我们会先做页面中保存数据库链接信息,然后在树结构中点击查询时,会固定传一个参数poolName,我们传的是记录数据库信息的id,作为poolName,用于我们后续切换数据源的时候的一个标识。

注意: 切面中,我们会在get请求里拿到poolName进行数据源切换,但是在日常考虑中,有post请求也需要切换数据源,所以代码中也加入了从body里取得poolName进行切换参数。为了进一步实现自定义切换参数,我们也可以在  request.getAttribute("poolName");取得参数进行切换!


6.DataSourceHelp类

import lombok.Data;
 
@Data
public class DataSourceHelp {
    private String poolName;
    private String driverClassName;
    private String url;
    private String username;
    private String password;
}

二、开发代码

1.controller层

 
import com.hvit.data_governance.dataComparison.request.*;
import com.hvit.data_governance.dataComparison.service.data.DBService;
import com.hvit.data_governance.dataComparison.service.data.SysDataResourceDataService;
import com.hvit.data_governance.utils.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.SQLException;
 
/**
 * <p>
 * 动态数据源管理 前端控制器
 * </p>
 *
 * @author 曹震
 * @since 2022-12-06
 */
@Api(tags = "动态数据源管理")
@RestController
@RequestMapping("/hvit/dataResource/")
public class SysDataResourceController {
 
    @Autowired
    private SysDataResourceDataService sysDataResourceDataService;
    @Autowired
    private DBService dbService;
 
    @ApiOperation("获取数据集的字段信息")
    @GetMapping("/getDataTableColumns")
    public ResponseEntity getDataTableColumns(String tableName, String poolName) {
        return ResponseEntity.ok(sysDataResourceDataService.getDataTableColumns(tableName, poolName));
    }
}

2.service层

@Slf4j
@Service
public class SysDataResourceDataService {
    
    @Autowired
    private SysDataResourceService sysDataResourceService;
    @Autowired
    private DBService dbService;
 
    public R getDataTableColumns(String tableName, String poolName) {
        if (StringUtils.isAnyEmpty(tableName, poolName)) {
            return R.error("缺少参数!");
        }
        getDataTableFramework(tableName, poolName);
        QueryWrapper<SysTableStructure> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(SysTableStructure::getDataResourceUuid, poolName);
        queryWrapper.lambda().eq(SysTableStructure::getTableName, tableName);
        queryWrapper.lambda().orderByAsc(SysTableStructure::getSort);
        return R.ok().put("data", sysTableStructureService.list(queryWrapper));
    }
 
 /***
     * 注意点:当方法内调用其他需要数据源切换的方法时,需要将类本身注入进来,然后调用才能生效(数据源切换)
     * 处理字段入库
     * @param tableName
     * @param poolName
     */
    @Transactional
    public void getDataTableFramework(String tableName, String poolName) {
        //获取表字段结构信息
        List<TableFramework> tableFrameworks = dbService.getTableFramework(tableName);
        //获取不在表中的字段信息
        List<String> stringList = getDifferenceSet(tableFrameworks, tableName, poolName);
        //装备剔除没用的字段
        if (!CollectionUtils.isEmpty(stringList)) {
            QueryWrapper<SysTableStructure> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(SysTableStructure::getDataResourceUuid, poolName);
            queryWrapper.lambda().eq(SysTableStructure::getTableName, tableName);
            queryWrapper.lambda().in(SysTableStructure::getField, stringList);
            sysTableStructureService.remove(queryWrapper);
        }
        AtomicInteger i = new AtomicInteger();
        if (!CollectionUtils.isEmpty(tableFrameworks)) {
            tableFrameworks.forEach(x -> {
                QueryWrapper<SysTableStructure> wrapper = new QueryWrapper<>();
                wrapper.lambda().eq(SysTableStructure::getDataResourceUuid, poolName);
                wrapper.lambda().eq(SysTableStructure::getTableName, tableName);
                wrapper.lambda().eq(SysTableStructure::getField, x.getField());
                if (sysTableStructureService.count(wrapper) == 0) {
                    SysTableStructure sysTableStructure = new SysTableStructure();
                    sysTableStructure.setField(x.getField());
                    sysTableStructure.setTableName(tableName);
                    sysTableStructure.setHidden(1);
                    sysTableStructure.setType(x.getType());
                    sysTableStructure.setSort(i.get());
                    sysTableStructure.setLength(DBUtils.getContent(x.getType()));
                    sysTableStructure.setDataResourceUuid(poolName);
                    sysTableStructure.setPriKey(x.getKey());
                    sysTableStructureService.save(sysTableStructure);
                }
                i.getAndIncrement();
            });
        }
    }
 
/***
     * 智能匹配字段差集
     * 当表字段存在记录表中,而实际数据库表字段通过其他手动修改过时,智能剔除。
     * @param tableFrameworks
     * @param tableName
     * @return
     */
    public List<String> getDifferenceSet(List<TableFramework> tableFrameworks, String tableName, String poolName) {
        QueryWrapper<SysTableStructure> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(SysTableStructure::getTableName, tableName);
        queryWrapper.lambda().eq(SysTableStructure::getDataResourceUuid, poolName);
        List<SysTableStructure> structureList = sysTableStructureService.list(queryWrapper);
        List<String> retList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(structureList)) {
            List<String> strings = sysTableStructureService.list(queryWrapper).stream().map(SysTableStructure::getField).collect(Collectors.toList());
            List<String> stringList = tableFrameworks.stream().map(TableFramework::getField).collect(Collectors.toList());
            retList = strings.stream().filter(e -> {
                return !stringList.contains(e);
            }).collect(Collectors.toList());
        }
        return retList;
    }
}

3. dbService类

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hvit.data_governance.dataComparison.dao.SysDataResourceMapper;
import com.hvit.data_governance.dataComparison.dbhelp.ChangeDB;
import com.hvit.data_governance.dataComparison.entity.SysDataResource;
import com.hvit.data_governance.dataComparison.entity.TableFramework;
import com.hvit.data_governance.dataComparison.request.HandleMianDataReq;
import com.hvit.data_governance.dataComparison.request.RowDataReq;
import com.hvit.data_governance.dataComparison.service.SysDataResourceService;
import com.hvit.data_governance.utils.DBUtils;
import com.hvit.data_governance.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
 
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
 
@Slf4j
@Service
public class DBService {
    @Resource
    private SysDataResourceMapper sysDataResourceMapper;
    @Autowired
    private SysDataResourceDataService sysDataResourceDataService;
 
   /***
     * 获取表结构信息
     * @param tableName
     * @return
     */
    @ChangeDB
    public List<TableFramework> getTableFramework(String tableName) {
        return sysDataResourceMapper.getTableFramework(tableName);
    }
}

4.sysDataResourceMapper类

import com.hvit.data_governance.dataComparison.entity.SysDataResource;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hvit.data_governance.dataComparison.entity.TableFramework;
import org.apache.ibatis.annotations.*;
 
import java.util.LinkedHashMap;
import java.util.List;
 
/**
 * <p>
 * 动态数据源管理 Mapper 接口
 * </p>
 *
 * @author 曹震
 * @since 2022-12-06
 */
@Mapper
public interface SysDataResourceMapper extends BaseMapper<SysDataResource> {
 
/***
     * 获取表结构信息
     * @param tableName
     * @return
     */
    @Select("DESC `${tableName}`")
    List<TableFramework> getTableFramework(@Param("tableName") String tableName);
 
 
}

5.看下调用过程

我们可以看到日志输出中,已经进行的数据源的切换,在调用完后也及时的清除了切换的数据源。

可以看到表的结构信息已经获取到了。同样postman中也有,给大家看下。

那么到此处就已经做好了动态数据源的切换了。

如果大家喜欢的话,点个关注,点个赞,谢谢。

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
8月前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
527 0
|
3月前
|
Java 测试技术 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
本课主要讲解Spring Boot项目中的属性配置方法。在实际开发中,测试与生产环境的配置往往不同,因此不应将配置信息硬编码在代码中,而应使用配置文件管理,如`application.yml`。例如,在微服务架构下,可通过配置文件设置调用其他服务的地址(如订单服务端口8002),并利用`@Value`注解在代码中读取这些配置值。这种方式使项目更灵活,便于后续修改和维护。
45 0
|
8月前
|
开发框架 前端开发 网络协议
Spring Boot结合Netty和WebSocket,实现后台向前端实时推送信息
【10月更文挑战第18天】 在现代互联网应用中,实时通信变得越来越重要。WebSocket作为一种在单个TCP连接上进行全双工通信的协议,为客户端和服务器之间的实时数据传输提供了一种高效的解决方案。Netty作为一个高性能、事件驱动的NIO框架,它基于Java NIO实现了异步和事件驱动的网络应用程序。Spring Boot是一个基于Spring框架的微服务开发框架,它提供了许多开箱即用的功能和简化配置的机制。本文将详细介绍如何使用Spring Boot集成Netty和WebSocket,实现后台向前端推送信息的功能。
1891 1
|
4月前
|
Java Maven 开发者
编写SpringBoot的自定义starter包
通过本文的介绍,我们详细讲解了如何创建一个Spring Boot自定义Starter包,包括自动配置类、配置属性类、`spring.factories`文件的创建和配置。通过自定义Starter,可以有效地复用公共配置和组件,提高开发效率。希望本文能帮助您更好地理解和应用Spring Boot自定义Starter,在实际项目中灵活使用这一强大的功能。
143 17
|
3月前
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于注解的整合
本文介绍了Spring Boot集成MyBatis的两种方式:基于XML和注解的形式。重点讲解了注解方式,包括@Select、@Insert、@Update、@Delete等常用注解的使用方法,以及多参数时@Param注解的应用。同时,针对字段映射不一致的问题,提供了@Results和@ResultMap的解决方案。文章还提到实际项目中常结合XML与注解的优点,灵活使用两者以提高开发效率,并附带课程源码供下载学习。
74 0
|
3月前
|
JSON Java 数据格式
微服务——SpringBoot使用归纳——Spring Boot中的全局异常处理——拦截自定义异常
本文介绍了在实际项目中如何拦截自定义异常。首先,通过定义异常信息枚举类 `BusinessMsgEnum`,统一管理业务异常的代码和消息。接着,创建自定义业务异常类 `BusinessErrorException`,并在其构造方法中传入枚举类以实现异常信息的封装。最后,利用 `GlobalExceptionHandler` 拦截并处理自定义异常,返回标准的 JSON 响应格式。文章还提供了示例代码和测试方法,展示了全局异常处理在 Spring Boot 项目中的应用价值。
76 0
|
3月前
|
Java 微服务 Spring
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
在微服务架构中,随着业务复杂度增加,项目可能需要调用多个微服务。为避免使用`@Value`注解逐一引入配置的繁琐,可通过定义配置类(如`MicroServiceUrl`)并结合`@ConfigurationProperties`注解实现批量管理。此方法需在配置文件中设置微服务地址(如订单、用户、购物车服务),并通过`@Component`将配置类纳入Spring容器。最后,在Controller中通过`@Resource`注入配置类即可便捷使用,提升代码可维护性。
55 0
|
4月前
|
JSON Java 关系型数据库
Hutool创建数据源工厂动态查询不同数据库不同数据表的数据
Hutool创建数据源工厂动态查询不同数据库不同数据表的数据
79 2
|
5月前
|
前端开发 Java 数据库连接
Java后端开发-使用springboot进行Mybatis连接数据库步骤
本文介绍了使用Java和IDEA进行数据库操作的详细步骤,涵盖从数据库准备到测试类编写及运行的全过程。主要内容包括: 1. **数据库准备**:创建数据库和表。 2. **查询数据库**:验证数据库是否可用。 3. **IDEA代码配置**:构建实体类并配置数据库连接。 4. **测试类编写**:编写并运行测试类以确保一切正常。
208 2
|
6月前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
647 14