脱离枯燥的CRUD,灵活使用Mybatis,根据mybatis动态的xml片段和接口规范动态生成代理类,轻松应付简单业务场景。

需求

需求是这样的,我们有一个数据服务平台的产品,用户先将数据源信息保存到平台上,一个数据源可以提供多个接口服务,而每个接口服务在数据库中存一个具有mybatis语法的sql片段。这样的话,对于一些简单的业务只需要编写好sql保存到数据库中然后提供一个接口文档就可以实现了。我们只需要对外提供一个http接口,http接口参数是接口服务ID和sql的参数。

保存在数据库中的xml片段大致如下:

select * from student as s left join score as sc on s.sno = sc.sno
<where>
	<if sname != null and sname != ''>
		s.sname = #{sname}
	</if>
</where>

思路

从数据库中获取到xml文本片段后,如果手动去解析mybatis的各种标签和其中的OGNL表达式的话,无疑是一件很累人的事情。所以换种思路,既然功能是按照mybatis的规范来做的,那么mybatis有没有现成的API提供给我们使用呢?当然是有的,示例如下:

实现

  1. 先定义一套查询规范,也就是Mapper接口

    
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author m
     */
    public interface CommonMapper {
        /**
         * 查询列表
         *
         * @param params
         * @return
         */
        List<Map<String, Object>> selectList(Map<String, Object> params);
    
        /**
         * 查询单个
         *
         * @param params
         * @return
         */
        Map<String, Object> selectOne(Map<String, Object> params);
    }
    
  2. 再定义一套业务规范,也就是Service接口

    
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author m
     */
    public interface CommonService {
    
        /**
         * 查询列表
         *
         * @param sql
         * @param params
         * @return
         */
        List<Map<String, Object>> selectList(String sql, Map<String, Object> params);
    
        /**
         * 查询单个
         *
         * @param params
         * @return
         */
        Map<String, Object> selectOne(Map<String, Object> params);
    }
    
    
  3. 实现业务规范,在这里我们只实现查询列表的功能,其他的也就类似了。这里是没有实现数据源的切换和分页功能的,可以查询一下mybatis怎么切换数据源、PageHelper怎么去适配多数据源下的分页功能。当然也可以在评论区留言或者给我私信的。

    import com.demo.common.mapper.CommonMapper;
    import com.demo.common.service.CommonService;
    import org.apache.ibatis.builder.xml.XMLMapperBuilder;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.io.ByteArrayInputStream;
    import java.io.InputStream;
    import java.nio.charset.StandardCharsets;
    import java.util.Collections;
    import java.util.List;
    import java.util.Map;
    
    @Service
    public class CommonServiceImpl implements CommonService {
        @Resource
        private SqlSessionFactory sqlSessionFactory;
    
        /**
         * 查询列表
         *
         * @param params
         * @return
         */
        @Override
        public List<Map<String, Object>> selectList(String sql, Map<String, Object> params) {
    
            String sqlXml = wrapSql2SelectListXml(sql);
            InputStream inputStream = new ByteArrayInputStream(sqlXml.getBytes(StandardCharsets.UTF_8));
    
            // 手动加载 XML 配置到 MyBatis
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(inputStream, sqlSessionFactory.getConfiguration(), "dynamic-mapper", sqlSessionFactory.getConfiguration().getSqlFragments());
            xmlMapperBuilder.parse();
            // 获取 SqlSession 并执行查询
            try (SqlSession session = sqlSessionFactory.openSession()) {
                CommonMapper mapper = session.getMapper(CommonMapper.class);
                return mapper.selectList(params);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return Collections.emptyList();
        }
    
        /**
         * 查询单个
         *
         * @param params
         * @return
         */
        @Override
        public Map<String, Object> selectOne(Map<String, Object> params) {
        	// TODO 待实现
            return Collections.emptyMap();
        }
    
        /**
         * 将sql封装为一个完整的xml,对应CommonMapper::selectList接口
         *
         * @param sql
         * @return
         */
        private String wrapSql2SelectListXml(String sql) {
            String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
                    + "<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">"
                    + "<mapper namespace='com.demo.common.mapper.CommonMapper'>"
                    + "  <select id='selectList' resultType='java.util.Map'>"
                    + 		sql
                    + "  </select>"
                    + "</mapper>";
            return xml;
        }
    
    }
    
    
  4. 测试,经过测试是OK的。

    
    import com.demo.common.service.CommonService;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    import java.util.List;
    import java.util.Map;
    
    @RestController
    @RequestMapping("test")
    public class TestController {
        @Resource
        private CommonService commonService;
    
        @RequestMapping("list")
        public Object test(@RequestParam Map<String, Object> params) {
            List<Map<String, Object>> maps = commonService.selectList("SELECT * FROM datagov.dg_alg WHERE alg_id = #{algId}", params);
            System.out.println(maps);
            return maps;
        }
    }
    
    

    在这里插入图片描述

总结

通过以上实现,不难发现,以上解决方案无非就是改变了Mybatis生成代理类的时机而已。在平常,Mybatis是通过扫描指定的xml目录和mapper接口,然后在容器启动时生成代理对象。而此时,我们是在方法执行的时候动态获取的xml并生成的动态的代理对象。两者使用起来是没有什么差别的。

当然以上只是平台一部分功能,像参数和结果集的提取,参数的校验,接口的鉴权、精细化控制、限流、负载均衡、熔断、幂等性,数据的分页处理、缓存等,在这里就不一一赘述了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/882560.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux 文件权限详解与管理

文章目录 前言一、文件权限概述1. 权限表示格式2. 权限组合值 二、查看文件权限三、修改文件所有者与所属组1. 使用 chown 修改文件所有者2. 使用 chgrp 修改文件所属组3. 添加所有者 四、修改文件权限1. 符号方式2. 八进制方式3. 实际修改 总结 前言 在 Linux 系统中&#xf…

香港科技大学广州|金融科技学域博士招生宣讲会——武汉大学、华中科技大学

&#x1f514;&#x1f514;&#x1f514;明日宣讲&#x1f514;&#x1f514;&#x1f514; &#x1f490;香港科技大学广州&#xff5c;金融科技学域博士招生宣讲会 &#x1f4cd;武汉大学专场 &#x1f559;时间&#xff1a;2024年9月24日&#xff08;星期二&#xff09;1…

Java项目实战II基于Java+Spring Boot+MySQL的洗衣店订单管理系统(开发文档+源码+数据库)

目录 一、前言 二、技术介绍 三、系统实现 四、论文参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者 一、前言 随着生活节奏的加快&#xff0c;现代人对便捷、高效服务的需求日益增长&#xff0c;洗衣店作为日常生…

11 - TCPClient实验

在上一个章节的UDP通信测试中&#xff0c;尽管通信的实现过程相对简洁&#xff0c;但出现了通信数据丢包的问题。因此&#xff0c;本章节将基于之前建立的WIFI网络连接&#xff0c;构建一个基础的TCPClient连接机制。我们利用网络调试助手工具来发送数据&#xff0c;测试网络通…

【图虫创意-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

力扣 困难 154.寻找旋转排序数组中的最小值 II

文章目录 题目介绍题解 题目介绍 题解 题源&#xff1a; 153.寻找旋转排序数组中的最小值 在此基础上&#xff0c;进行二分之前&#xff0c;单独处理一下左指针和最后一个数相同的情况就好了。 class Solution {public int findMin(int[] nums) {int left 0, right nums.le…

conda 虚拟环境安装GDAL

一. 背景 换了新电脑&#xff0c;要重新安装GDAL。从前是下了GDAL的.wheel文件用pip安装&#xff0c;但平时下轮子的网站现在都打不开&#xff0c;比如https://www.lfd.uci.edu/~gohlke/pythonlibs/#gdal&#xff0c;不晓得为什么。 后面看了这篇教程解决了问题&#xff08;h…

Codeforces Round 973 (Div. 2) - D题

传送门&#xff1a;Problem - D - Codeforces 题目大意&#xff1a; 思路&#xff1a; 尽量要 最大值变小&#xff0c;最小值变大 即求 最大值的最小 和 最小值的最大 -> 二分答案 AC代码&#xff1a; 代码有注释 #include<bits/stdc.h> using namespace std; #…

C++模拟实现list:list、list类的初始化和尾插、list的迭代器的基本实现、list的完整实现、测试、整个list类等的介绍

文章目录 前言一、list二、list类的初始化和尾插三、list的迭代器的基本实现四、list的完整实现五、测试六、整个list类总结 前言 C模拟实现list&#xff1a;list、list类的初始化和尾插、list的迭代器的基本实现、list的完整实现、测试、整个list类等的介绍 一、list list本…

计算机网络34——Windows内存管理

1、计算机体系结构 2、内存管理 分为连续分配管理和非连续分配管理 在块内存在的未使用空间叫内部碎片&#xff0c;在块外存在的未使用空间叫外部碎片 固定分区分配可能出现内部碎片&#xff0c;动态分区分配可能出现外部碎片 3、逻辑地址和实际地址的互相转换 4、缺页中断 …

k8s中,pod生命周期,初始化容器,容器探针,事件处理函数,理解其设计思路及作用

k8s中&#xff0c;为什么要设计pod 平台直接管理容器不是挺好的吗 为什么要以pod为单位进行管理&#xff0c; 然后把容器放在pod里面 那么有pod和没pod的区别是什么 也就是pod提供了什么作用 这个可以考虑从pod生命周期管理的角度去思考 如图&#xff0c;pod主容器在运行…

JAVA并发编程系列(10)Condition条件队列-并发协作者

一线大厂面试真题&#xff0c;模拟消费者-生产者场景。 同样今天的分享&#xff0c;我们不纸上谈兵&#xff0c;也不空谈八股文。以实际面经、工作实战经验进行开题&#xff0c;然后再剖析核心源码原理。 按常见面经要求&#xff0c;生产者生产完指定数量产品后&#xff0c;才能…

文档矫正算法:DocTr++

文档弯曲矫正&#xff08;Document Image Rectification&#xff09;的主要作用是在图像处理领域中&#xff0c;对由于拍摄、扫描或打印过程中产生的弯曲、扭曲文档进行校正&#xff0c;使其恢复为平整、易读的形态。 一. 论文和代码 论文地址&#xff1a;https://arxiv.org/…

LDRA Testbed(TBrun)软件单元测试_常见问题及处理

系列文章目录 LDRA Testbed软件静态分析_操作指南 LDRA Testbed软件静态分析_自动提取静态分析数据生成文档 LDRA Testbed软件静态分析_Jenkins持续集成&#xff08;自动静态分析并用邮件自动发送分析结果&#xff09; LDRA Testbed软件静态分析_软件质量度量 LDRA Testbed软件…

POI操作EXCEL增加下拉框

文章目录 POI操作EXCEL增加下拉框 POI操作EXCEL增加下拉框 有时候通过excel将数据批量导入到系统&#xff0c;而业务操作人员对于一些列不想手动输入&#xff0c;而是采用下拉框的方式来进行选择 采用隐藏sheet页的方式来进行操作 String sheetName "supplier_hidden_s…

Python记录

1.冒泡排序 时间复杂度O&#xff08;n^2) 选择、插入都是 def bubble(data, reverse):for i in range(len(data)-1):for j in range(len(data)-i-1):if data[j] > data[j1]:data[j], data[j1] data[j1], data[j]if reverse:data.reverse()return data 2.快速排序 时间…

基于深度学习的文本情感原因提取研究综述——论文阅读

前言 既然要学习情感分析&#xff0c;那么肯定还要了解情感原因对抽取的发展历程&#xff0c;所以我又搜了一篇研究综述&#xff0c;虽然是2023年发表的&#xff0c;但是里面提及到的历程仅停留到2022年。这篇综述发布在TASLP期刊&#xff0c;是音频、声学、语言信号处理的顶级…

【Verilog学习日常】—牛客网刷题—Verilog快速入门—VL21

根据状态转移表实现时序电路 描述 某同步时序电路转换表如下&#xff0c;请使用D触发器和必要的逻辑门实现此同步时序电路&#xff0c;用Verilog语言描述。 电路的接口如下图所示。 输入描述&#xff1a; input A , input clk , …

结构设计模式 -装饰器设计模式 - JAVA

装饰器设计模式 一. 介绍二. 代码示例2.1 抽象构件&#xff08;Component&#xff09;角色2.2 具体构件&#xff08;Concrete Component&#xff09;角色2.3 装饰&#xff08;Decorator&#xff09;角色2.4 具体装饰&#xff08;Concrete Decorator&#xff09;角色2.5 测试 结…

【HTML5】html5开篇基础(1)

1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 亲爱的朋友们&#x1f44b;&#x1f44b;&#xff0c;这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章&#xff0c;请别吝啬你的点赞❤️❤️和收藏&#x1f4d6;&#x1f4d6;。如果你对我的…