mybatis参数有list和实体类_手写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

手写板大致思路如下:

97ca288ef82b234fabed96fda4c7d96d.png

https://my.oschina.net/liughDevelop/blog/1631006

这里的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*/@Slf4jpublic class XmlBuilderMapper { public List buildMapper(String xmlMapperPath){ //实例化mapperInfo的链表,每一条sql对应一个MapperInfo对象 List 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 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*/@Datapublic 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 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*/@Slf4jpublic 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; }}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值