手写spring+springmvc+mybatis框架篇【Mybatis】

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 手写spring+springmvc+mybatis框架篇【Mybatis】

整合Mybatis是本项目中的一个难点。
实现功能:
1 动态绑定用户输入参数
2 Mybatis的resultType动态绑定返回实体类。
3 在spring中的接口注入
4 xml版本的mapper注入。
关于Mybatis的优秀文章给大家推荐两个:
1、手写简化版mybatis
https://my.oschina.net/liughDevelop/blog/1631006
2、Mybatis源码解读-设计模式总结
http://www.crazyant.net/2022.html
手写板大致思路如下:
这里的Myconfiguration和我的JDBCUtils类似。
实现思路:
首先用XmlBuilderMapper类读取mapper.xml(我是在initBean中指定要读取的配置文件,并没有写成在xml中配置动态的读取xml。)文件,获取MapperInfo对象保存信息。
用户用MysqlSession的getMapper方法,返回一个代理对象,用这个代理对象来执行MySqlSession定义的selectOne方法来查询,Mybaits中的SqlSession中定义了大量的方法,我这里简化只有一个selectOne方法。
然后这个方法调用executor执行器来解析sql,传入参数,执行数据库操作。最后返回结果。将返回结果封装成对象。
动态代理一般有两种,一种是jdk一种是cglib动态代理,本项目采用是jdk动态代理实现。
XmlBuilderMapper.class
package spring.mybatis;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import spring.constants.Constants;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**

  • @ClassName XmlBuilder
  • @Description
  • @Data 2018/7/8
  • @Author xiao liang

*/
@Slf4j
public class XmlBuilderMapper {
public List buildMapper(String xmlMapperPath){

   //实例化mapperInfo的链表,每一条sql对应一个MapperInfo对象
   List<MapperInfo> mapperInfoList = new ArrayList<>();
   MapperInfo mapperInfo = new MapperInfo();
   // 创建saxReader对象
   SAXReader reader = new SAXReader();
   // 通过read方法读取一个文件 转换成Document对象
   Document document = null;
   String pathName = Constants.PATH + xmlMapperPath;
   try {
       document = reader.read(new File(pathName));
   } catch (DocumentException e) {
       log.error("文件没有找到,{}", pathName);
   }
   //获取根节点元素
   Element node = document.getRootElement();
   mapperInfo.setInterfaceName(node.attributeValue("namespace"));
   //获取所有的bean
   List<Element> elementsList = node.elements();
   for (Element element :
           elementsList) {
       if ("select".equals(element.getName())){
           mapperInfo.setMethodName(element.attributeValue("id"));
           mapperInfo.setResultClassName(element.attributeValue("resultType"));
           mapperInfo.setSqlContent(element.getText());
       }
       mapperInfoList.add(mapperInfo);
   }
   return mapperInfoList;

}
}
然后介绍一下MapperInfo对象
package spring.mybatis;
import lombok.Data;
/**

  • @ClassName MapperInfo
  • @Description 用来封装读取mapper.xml文件后的信息
  • @Data 2018/7/8
  • @Author xiao liang

*/
@Data
public class MapperInfo {
//namespace命名空间
private String interfaceName;
//sql内容
private String sqlContent;
//对应的方法名
private String methodName;
//返回值的class名
private String resultClassName;
}
JDBCUtils工具类我在开篇就介绍了,也没什么新内容,不贴在这里了
其实关键点就是动态代理和执行器
MySqlSession(动态代理部分)
package spring.mybatis;
import java.lang.reflect.Proxy;
/**

  • @ClassName MySqlSession
  • @Description
  • @Data 2018/7/8
  • @Author xiao liang

*/
public class MySqlSession {
public T selectOne(MapperInfo mapperInfo ,Object[] paremeters){

   MyExecutor myexecutor = new MyExecutor();
   return myexecutor.query(mapperInfo,paremeters);

}

public T getMapper(Class<?> aClass,String mybatisXmlName){

   return (T) Proxy.newProxyInstance(aClass.getClassLoader(),new Class[]{aClass},new MyMapperProxy(this,mybatisXmlName));

}
}
MyMapperProxy(jdk动态代理的实现) 代理之后执行的还是sqlSession中的方法
package spring.mybatis;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;
/**

  • @ClassName MyMapperProxy
  • @Description
  • @Data 2018/7/8
  • @Author xiao liang

*/
public class MyMapperProxy implements InvocationHandler {
private MySqlSession mySqlSession;
private String mybatisXmlName;
//mybatisXmlName传入的要读取的xml文件名
public MyMapperProxy(MySqlSession mySqlSession , String mybatisXmlName){

  this.mySqlSession = mySqlSession;
  this.mybatisXmlName = mybatisXmlName;

}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

   XmlBuilderMapper xmlBuilderMapper = new XmlBuilderMapper();
   List<MapperInfo> mapperInfoList = xmlBuilderMapper.buildMapper(mybatisXmlName);
   //如果存在sql,开始执行方法
   if (mapperInfoList != null && mapperInfoList.size() != 0){
       for (MapperInfo mapperInfo :
               mapperInfoList) {
           if (!method.getDeclaringClass().getName().equals(mapperInfo.getInterfaceName())){
               return null;
           }
           if (method.getName().equals(mapperInfo.getMethodName())){
               //其实最后执行的mySqlSession中的方法,args是用户传递的参数数组
               return mySqlSession.selectOne(mapperInfo,args);
           }
       }
   }
   return null;

}
}
返回到MySqlSession后,就轮到执行器MyExcutor出场了。
MyExcutor:也是一个难点,先说一下程序的逻辑。读取到mapperinfo对象,获得定义的sql,返回类的名称等信息。
用正则解析sql,根据#{},判断sql中有几处?,然后将用户传递的参数按照顺序依次写入到sql的?中。
最后读取结果集,用set方法注入到返回的对象中,这样就实现了返回时候的实体类绑定了。
package spring.mybatis;
import lombok.extern.slf4j.Slf4j;
import spring.Utils.GetMethodName;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**

  • @ClassName MyExecutor
  • @Description 执行器
  • @Data 2018/7/8
  • @Author xiao liang

*/
@Slf4j
public class MyExecutor {
public T query(MapperInfo mapperInfo, Object[] paremeters) {

   //获取mapper.xml文件中的sql语句
   String preSql = mapperInfo.getSqlContent();
   //正则匹配规则,匹配有几个#{}
   String rgex = "#\\{.*?}";
   String sql = null;
   String resultClassName = mapperInfo.getResultClassName();
   Class<?> aClass = null;
   Field[] fields = null;
   Method[] methods = null;
   Object object = null;
   Pattern pattern = Pattern.compile(rgex);
   Matcher m = pattern.matcher(preSql);
   Connection connection = null;
   ResultSet rs = null;
   PreparedStatement preparedStatement = null;
   //Preparement注入参数的个数
   int orderPre = 0;
   //每匹配一次,加一
   while (m.find()) {
       orderPre++;
   }
   //匹配完成之后,将#{}用?代替,preparement执行sql
   sql = m.replaceAll("?");
   try {
       aClass = Class.forName(resultClassName);
       fields = aClass.getDeclaredFields();
   } catch (ClassNotFoundException e) {
       e.printStackTrace();
   }
   try {
       JDBCUtils jdbcUtils = new JDBCUtils();
        connection = jdbcUtils.getConnection();
       preparedStatement = connection.prepareStatement(sql);
       //将用户传递过来的参数按照顺序依次赋值给prepareStatement的sql
       for (int i = 1; i <= orderPre; i++) {
           preparedStatement.setObject(i, paremeters[i - 1]);
       }
       rs = preparedStatement.executeQuery();
       object = aClass.newInstance();
       while (rs.next()) {
           int i = 1;
           for (Field field :
                   fields) {
               //遍历每个属性,然后将结果集中的数据用set方法注入到返回的对象中
               String setMethodNameByField = GetMethodName.getSetMethodNameByField(field.getName());
               Method method2 = aClass.getMethod(setMethodNameByField, field.getType());
               if (field.getType().getSimpleName().equals("String")) {
                   method2.invoke(object, rs.getString(i));
               } else if (field.getType().getSimpleName().equals("Integer")) {
                   method2.invoke(object, rs.getInt(i));
               }
               i++;
           }
       }
       return (T) object;
   } catch (SQLException e) {
       log.error("sql语句异常,请检查sql{}", sql);
       e.printStackTrace();
   } catch (InstantiationException e) {
       e.printStackTrace();
   } catch (IllegalAccessException e) {
       e.printStackTrace();
   } catch (InvocationTargetException e) {
       e.printStackTrace();
   } catch (NoSuchMethodException e) {
       e.printStackTrace();
   }
   finally {
       JDBCUtils.colseResource(connection,preparedStatement,rs);
   }
   return null;

}
}
我将此项目上传到了github,需要的童鞋可以自行下载。
https://github.com/836219171/MySSM

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
8天前
|
Java 数据库连接 数据库
spring复习05,spring整合mybatis,声明式事务
这篇文章详细介绍了如何在Spring框架中整合MyBatis以及如何配置声明式事务。主要内容包括:在Maven项目中添加依赖、创建实体类和Mapper接口、配置MyBatis核心配置文件和映射文件、配置数据源、创建sqlSessionFactory和sqlSessionTemplate、实现Mapper接口、配置声明式事务以及测试使用。此外,还解释了声明式事务的传播行为、隔离级别、只读提示和事务超时期间等概念。
spring复习05,spring整合mybatis,声明式事务
|
11天前
|
Java 数据库连接 数据库
SpringBoot 整合jdbc和mybatis
本文详细介绍了如何在SpringBoot项目中整合JDBC与MyBatis,并提供了具体的配置步骤和示例代码。首先,通过创建用户实体类和数据库表来准备基础环境;接着,配置Maven依赖、数据库连接及属性;最后,分别展示了JDBC与MyBatis的集成方法及其基本操作,包括增删查改等功能的实现。适合初学者快速入门。
SpringBoot 整合jdbc和mybatis
|
11天前
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
557 8
|
8天前
|
XML Java 关系型数据库
springboot 集成 mybatis-plus 代码生成器
本文介绍了如何在Spring Boot项目中集成MyBatis-Plus代码生成器,包括导入相关依赖坐标、配置快速代码生成器以及自定义代码生成器模板的步骤和代码示例,旨在提高开发效率,快速生成Entity、Mapper、Mapper XML、Service、Controller等代码。
springboot 集成 mybatis-plus 代码生成器
|
8天前
|
SQL XML Java
springboot整合mybatis-plus及mybatis-plus分页插件的使用
这篇文章介绍了如何在Spring Boot项目中整合MyBatis-Plus及其分页插件,包括依赖引入、配置文件编写、SQL表创建、Mapper层、Service层、Controller层的创建,以及分页插件的使用和数据展示HTML页面的编写。
springboot整合mybatis-plus及mybatis-plus分页插件的使用
|
8天前
|
XML Java 数据库连接
springboot中整合mybatis及简单使用
这篇文章介绍了如何在Spring Boot项目中整合MyBatis,包括依赖引入、配置数据源、创建测试表、编写Mapper接口和XML文件、以及创建Service和Controller层的步骤。
springboot中整合mybatis及简单使用
|
8天前
|
XML 缓存 前端开发
springMVC02,restful风格,请求转发和重定向
文章介绍了RESTful风格的基本概念和特点,并展示了如何使用SpringMVC实现RESTful风格的请求处理。同时,文章还讨论了SpringMVC中的请求转发和重定向的实现方式,并通过具体代码示例进行了说明。
springMVC02,restful风格,请求转发和重定向
|
9天前
|
XML 前端开发 Java
控制spring框架注解介绍
控制spring框架注解介绍
|
9天前
|
存储 NoSQL Java
Spring Session框架
Spring Session 是一个用于在分布式环境中管理会话的框架,旨在解决传统基于 Servlet 容器的会话管理在集群和云环境中的局限性。它通过将用户会话数据存储在外部介质(如数据库或 Redis)中,实现了会话数据的跨服务器共享,提高了应用的可扩展性和性能。Spring Session 提供了无缝集成 Spring 框架的 API,支持会话过期策略、并发控制等功能,使开发者能够轻松实现高可用的会话管理。
Spring Session框架
|
前端开发 druid Java
SpringBoot 整合 MyBatis
文本是基于MVC前后端分离模式的一个SpringBoot整合MyBatis的项目,不过没有用到前端页面,使用了更方便的Apifox请求工具。SpringBoot+MyBatis使用起来更方便,更舒服。掌握SpingBoot整合MyBatis,要比Spring整合简单的多,少了很多繁琐的配置。......
210 0
SpringBoot 整合 MyBatis
下一篇
无影云桌面