自学内容网 自学内容网

脱离枯燥的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并生成的动态的代理对象。两者使用起来是没有什么差别的。

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


原文地址:https://blog.csdn.net/muls1/article/details/142434709

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!