Springboot+Mybatis处理复杂数据类型(Map、List等)

Springboot+Mybatis处理复杂参数类型(Map、List等)

在实际场景使用springboot+mybatis来完成数据库的增删改查时,可能会面对接收的参数比较复杂的情况。比如在接收restful风格的请求参数时,可能filter里的过滤条件比较复杂,包含有数字、字符串、List等类型混合的请求。同时为了使得mybatis的查询写得更通用,需要覆盖各种场景,这就要借助于Mybatis的各种特性来实现。

接下来,就以实际的例子来给出代码参考。该部分的代码可以直接沿用到未来的其它实际场景中。

需求场景

接收Restful风格的复杂参数请求,利用mybatis处理包含的Map、List、String等数据类型,并根据请求参数进行排序和分页等操作

请求参数

在实际场景中遇到的一个复杂restful风格接口中的请求参数,filter里的过滤条件比较多,且参数结构较为复杂:

{
	"appKey": "appKey",
	"filter": {
		"id": ["609a3d7242ff880018b9e26b"],
		"eventName": "免费",
		"eventKey": "click",
		"positionInfo": {"posId":"5fd9f7b71b52740011016b75", "levelId":"5fd9f7b71b5274001103740"},
		"clientVersion": ["210f63dd6b6c4c09b4cf55a4"],
        "priority":0
	},
	"limit": 999,
	"populate": "",
	"projectId": "5fd9f67840e8580047582075",
	"skip": 0,
	"sort": {
		"id": 1
	}
}

对部分数据做了脱敏,但仍然保留了filter中的复杂参数场景。可以看到filter中包含了Map、List、String、Integer,基本包含了主要的数据类型。因此,对这个问题的解决可以延用到许多其它的场景。

解决方案

首先给出Mapper.java的代码

import com.example.data.pojo.Statsevents;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

@Component
public interface StatseventsMapper {

    int deleteByPrimaryKey(String id);

    int insert(Statsevents record);

    Statsevents selectByPrimaryKey(String id);

    List<Statsevents> selectAll(@Param("param") Map<String, Object> paramMap);

    int updateByPrimaryKey(Statsevents record);
}

上述代码包含了增删改查的四个功能。主要关注于seleteAll查找指定条件的所有数据,这里利用了@Param("param")注解来命令导入的参数,这样在xml文件中就可以用param来使用引入的参数。通过上面的请求参数,确定引入的参数类型为Map<String, Object>。

接下来就是主要的mybatis的xml文件的编写

<select id="selectAll" parameterType="java.util.Map" resultMap="BaseResultMap">

    select id, eventName, eventKey, priority, positionInfo, clientVersion
    from statsevents
    <where>
      <!--根据filter筛选数据-->
      <if test="param.containsKey('filter') and param.get('filter').size() > 0">
        <foreach collection="param.get('filter').entrySet()" index="key" item='val' separator="and" open=" " close=" ">
        <choose>
            <!--处理List类型参数-->
          <when test="val instanceof com.alibaba.fastjson.JSONArray and val.size()>0">
            ${key} in
            <foreach collection="val" item="_key" open="(" close=")" separator=",">
              #{_key}
            </foreach>
          </when>
            <!--处理Map类型参数,like是因为需要模糊搜索-->
          <when test="val instanceof com.alibaba.fastjson.JSONObject and val.size() > 0">
            <foreach collection="val" index="pKey" item="pVal" open=" " separator="and" close=" ">
              ${key} like "%${pVal}%"
            </foreach>
          </when>
          <!--需要找到同时包含指定clientVersion值,因此不能用in,更改为and-->
          <when test="key == 'clientVersion' and val instanceof com.alibaba.fastjson.JSONArray and val.size() > 0">
            <foreach collection="val" item="cVal" open=" " separator="and" close=" ">
              ${key} like '%${cVal}%'
            </foreach>
          </when>
            <!--处理Integer类型参数-->
          <when test="val instanceof Integer and val != null">
            ${key} like '%"status": ${val}%'
          </when>
            <!--处理String类型参数-->
          <when test="val instanceof String and val != ''">
            ${key} like '%${val}%'
          </when>
        </choose>
        </foreach>
      </if>
    </where>

    <!--根据sort的键进行排序,键可以指定为任意值-->
    <if test="param.containsKey('sort')">
      <foreach collection="param.get('sort').entrySet()" index="key" item="val">
        <if test="val == -1">
          order by ${key} asc
        </if>
        <if test="val == 1">
          order by ${key} desc
        </if>
      </foreach>
    </if>

    <!--根据skip和limit做分页-->
    <if test="param.containsKey('limit') and param.containsKey('skip')">
      limit ${param.get("skip")}, ${param.get("limit")}
    </if>
  </select>

上述代码基本完整展示了基于Restful风格的请求参数下,mybatis的处理方式。包含了处理复杂的参数请求,以及根据请求参数进行排序和分页的功能。

代码讲解

本次主要是处理Restful风格的请求参数,根据filter里的筛选条件,查询指定数据。

由于传入的是一个Map<String, Object>的参数类型,因此,最外层使用了处理Map数据类型时的方法。

mybatis处理Map数据类型的方法

利用foreach标签进行循环,其中colleaction参数表示待遍历的Map数据集index表示索引(也就是key)item表示每个键的值,index-item就表示了键值对, separator表示遍历每个键值对后的分隔符openclose表示遍历开始前和结束后拼接的字符串

接下来,对filter下的键值对进行遍历,通过判断不同值的类型,执行相应操作。因为我这里之前引入了fastjson来对Map和List进行处理,因此判断数据类型的时候采用了val instanceof com.alibaba.fastjson.JSONObjectval instanceof com.alibaba.fastjson.JSONArray。如果是原始的Map和List可以直接用val instanceof java.utils.Listval instanceof java.utils.Map来进行判断类型。

对于Map类型的处理参考刚刚的方式。对于List数据类型的方法如下:

mybatis处理List数据类型的方法

利用foreach标签进行循环,其中colleaction参数表示待遍历的List数据集item表示List中的每个值,separator表示遍历每个值后的分隔符openclose表示遍历开始前和结束后拼接的字符串

接下来分别处理Integer和String的类型,对于Integer类型的数据判空时需要设置val != null

最后处理排序和分页的功能。根据sort指定的键进行排序,键可以指定为任意值。分页时判断传入的参数中是否有limit和skip参数,利用sql本身的语法实现分页。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是使用Spring BootMyBatis Plus实现多数据源的最简解决方案: 1.在pom.xml文件中添加MyBatis Plus和Druid的依赖: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.6</version> </dependency> ``` 2.在application.yml文件中配置数据源: ```yaml spring: datasource: master: url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver slave: url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver ``` 3.创建数据源配置类: ```java @Configuration @MapperScan(basePackages = "com.example.demo.mapper") public class DataSourceConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean public DynamicDataSource dynamicDataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("slaveDataSource") DataSource slaveDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DataSourceType.MASTER, masterDataSource); targetDataSources.put(DataSourceType.SLAVE, slaveDataSource); return new DynamicDataSource(masterDataSource, targetDataSources); } @Bean public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dynamicDataSource); sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml")); return sqlSessionFactory.getObject(); } @Bean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } } ``` 4.创建数据源类型枚举类: ```java public enum DataSourceType { MASTER, SLAVE } ``` 5.创建动态数据源类: ```java public class DynamicDataSource extends AbstractRoutingDataSource { private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<>(); public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) { super.setDefaultTargetDataSource(defaultTargetDataSource); super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } @Override protected Object determineCurrentLookupKey() { return contextHolder.get(); } public static void setDataSourceType(DataSourceType dataSourceType) { contextHolder.set(dataSourceType); } public static void clearDataSourceType() { contextHolder.remove(); } } ``` 6.创建数据源切换注解: ```java @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface DataSource { DataSourceType value() default DataSourceType.MASTER; } ``` 7.创建数据源切换切面: ```java @Aspect @Component public class DataSourceAspect { @Pointcut("@annotation(com.example.demo.annotation.DataSource)") public void dataSourcePointCut() { } @Before("dataSourcePointCut()") public void before(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); DataSource dataSource = signature.getMethod().getAnnotation(DataSource.class); if (dataSource != null) { DynamicDataSource.setDataSourceType(dataSource.value()); } } @After("dataSourcePointCut()") public void after(JoinPoint joinPoint) { DynamicDataSource.clearDataSourceType(); } } ``` 8.在Mapper接口中使用@DataSource注解指定数据源: ```java @DataSource(DataSourceType.MASTER) List<User> selectAllUsers(); @DataSource(DataSourceType.SLAVE) List<User> selectAllUsersFromSlave(); ``` 9.最后,启动Spring Boot应用程序并测试多数据源是否正常工作。 --相关问题--: 1. Spring Boot如何集成MyBatis Plus? 2. 如何在Spring Boot中配置

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

JermeryBesian

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

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

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

打赏作者

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

抵扣说明:

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

余额充值