精选文章 手写mybatis框架(一)通过动态代理简单实现查询功能

手写mybatis框架(一)通过动态代理简单实现查询功能

作者:一键传鑫 时间: 2021-02-05 09:43:15
一键传鑫 2021-02-05 09:43:15
【摘要】git地址:点我 
在手写mybatis简化版框架先了解一下mybatis框架的执行流程。 
一、Mybatis框架执行流程 
 
1.配置文件有两种,一个为主配置文件,一个为映射文件。 
主配置文件:配置了jdbc等环境信息。 
映射文件:配置了接口对应的sql语句映射。 
这两个配置文件会被封装到Configuration中。 
2.通过mybatis配置文件得到SqlSessionFa...

git地址:点我

在手写mybatis简化版框架先了解一下mybatis框架的执行流程。

一、Mybatis框架执行流程

手写mybatis框架(一)通过动态代理简单实现查询功能1

1.配置文件有两种,一个为主配置文件,一个为映射文件。

主配置文件:配置了jdbc等环境信息。

映射文件:配置了接口对应的sql语句映射。

这两个配置文件会被封装到Configuration中。

2.通过mybatis配置文件得到SqlSessionFactory。

3.通过SqlSessionFactory得到SqlSession,一个sqlSession相当于一个request请求。

4.SqlSession调用Executor执行器来操作数据库。

5.解析入参,封装成statement,执行,映射结果返回。

将mybatis主要流程分析了一下,思路明确了,那么设计一下简化版的mybatis框架来完成一个查询。

二、简化版ybatis

同上面过程,毕竟是简化版,就不使用sqlsession工厂了,跳过这一步。

即:1.读取xml->2.调用sqlsession->3.调用executor->4.解析参数执行并返回映射结果

先上结果图:

手写mybatis框架(一)通过动态代理简单实现查询功能2

项目结构图:maven项目

手写mybatis框架(一)通过动态代理简单实现查询功能3

1.连接数据库


 com.mysql.jdbc.Driver jdbc:mysql://localhost:3306/test root root

内容为jdbc连接信息,哦忘了,先把pom文件展示一下,两个jar包:mysql连接和dom4j解析

  dom4j dom4j 1.6.1   mysql mysql-connector-java 5.1.29 

接下来是通过代码将该xml文件解析出来。

//启动应用程序类加载器 private static ClassLoader classLoader = ClassLoader.getSystemClassLoader(); /* 解析xml文件 */ public Element parseXML(String resource) { try { //返回用于读取指定资源的输入流。 InputStream resourceAsStream = classLoader.getResourceAsStream(resource); //使用dom4j方式解析 SAXReader saxReader = new SAXReader(); //使用SAX从给定流中读取文件 Document document = saxReader.read(resourceAsStream); //获得文件的根节点 Element rootElement = document.getRootElement(); return rootElement; } catch (DocumentException e) { throw new RuntimeException("解析文件失败:"+resource); } } /* 解析主配置xml文件节点 */ public Map parseNode(Element element){ //判断根目录名称 if (!element.getName().equals("dataSource")) { throw new RuntimeException("主配置文件根名称必须是dataSource"); } Map map = new HashMap(); map.put("driverClassName", null); map.put("url", null); map.put("username", null); map.put("password", null); //读取property属性内容 for (Object obj :element.elements("property")){ Element ele = (Element) obj; String name = ele.attributeValue("name"); String value =ele.getText(); if (name==null||value==""){ throw new RuntimeException("格式错误,正确格式为 xxx"); } if (name.equals("driverClassName")){ map.put("driverClassName",value); }else if(name.equals("url")){ map.put("url",value); }else if (name.equals("username")){ map.put("username",value); }else if(name.equals("password")){ map.put("password",value); }else { throw new RuntimeException("不能识别的名称:"+name); } } return map; }
  public Connection build(String resource){ Element element = parseXML(resource); Map jdbcMap =parseNode(element); try { Class.forName(jdbcMap.get("driverClassName")); } catch (ClassNotFoundException e) { throw new RuntimeException("驱动类未找到!"); } Connection connection=null; try { connection = DriverManager.getConnection(jdbcMap.get("url"),jdbcMap.get("username"),jdbcMap.get("password")); } catch (SQLException e) { throw new RuntimeException("jdbc连接错误!"); } return connection; }
private Connection getConnection(){ Connection connection=myParse.build("mybatis-config.xml"); return connection; }

 从代码上看,该简化的ybatis主配置文件必须名为mybatis-config.xml

可以自己写一个测试类,看是否报错,没报错则进行下一步。

2.sqlSession代理

sqlSession肯定不会自己去执行,因为不能写死所以使用动态代理来使代理类去实现具体方法。

public class MySqlSession { private MyExcutor excutor = new MyExcutorImpl();   //待会实现 private MyParse parse=new MyParse(); public  T selectObject(Mapping mapping, String param){ return  excutor.query(mapping,param); } public  T getMapper(Class cls){  //待会实现 return (T) Proxy.newProxyInstance(cls.getClassLoader(),new Class[]{cls},new MySqlSessionProxy(parse,this)); }

}

然后先写一点代理类,把mapper映射文件解析了。

/*
sqlSession代理类
 */
public class MySqlSessionProxy implements InvocationHandler { private MyParse parse; private MySqlSession sqlSession ; private String PATH="mapper/"; public MySqlSessionProxy(MyParse parse,MySqlSession sqlSession){ this.parse=parse; this.sqlSession=sqlSession; } public Object invoke(Object proxy, Method method, Object[] args) { String name =method.getDeclaringClass().getName(); String mapperName=name.substring(name.lastIndexOf(".")+1); MappingBean mappingBean=parse.parseMapper(parse.parseXML(PATH+mapperName+".xml")); return null; }
}

从这里可以看到该简化的ybatis的映射文件必须和接口名保持一致,并且在mapper文件夹里。

mapper.xml文件:


 

parseMapper()方法:

 /* 解析mapper映射xml文件 */ public MappingBean parseMapper(Element element){ //判断根目录名称 if (!element.getName().equals("mapper")) { throw new RuntimeException("mapper映射文件根名称必须是mapper"); } MappingBean mappingBean=new MappingBean(); String namespace=element.attributeValue("namespace"); if (namespace==null){ throw new RuntimeException("mapper映射文件namespace不存在"); } mappingBean.setInterfaceName(namespace); List mappingList = new ArrayList(); Iterator it=element.elementIterator(); while (it.hasNext()){ Element ele=(Element) it.next(); Mapping mapping =new Mapping(); String funcName =ele.attributeValue("id"); if (funcName==null){ throw new RuntimeException("mapper映射文件中id不存在"); } String sqlType = ele.getName(); String paramType = ele.attributeValue("parameterType"); String resultType=ele.attributeValue("resultType"); String sql=ele.getText().trim(); mapping.setFuncName(funcName); mapping.setSqlType(sqlType); mapping.setParamType(paramType); mapping.setSql(sql); Object object=null; try { object=Class.forName(resultType).newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } mapping.setResultType(object); mappingList.add(mapping); } mappingBean.setMappingList(mappingList); return mappingBean; }

这里就完成了对mapper映射文件的解析,接下来到下一步,进入executor执行。

3.executor执行

在写executor之前先创建两个实体类,一个是对应接口实体类,一个是xml文件sql实体类。

sql实体类:

public class Mapping { /* sql语句 */ private String sql; /* sql语句参数类型 */ private String sqlType; /* 入参类型 */ private String paramType; /* 返回参数类型 */ private Object resultType; /* 方法名 */ private String funcName;
//getter、setter省略

}

接口实体类:

public class MappingBean { /* 接口名 */ private String interfaceName; /* 接口名下所有方法 */ private List mappingList;
//getter、setter省略
}

补齐代理类:

 public Object invoke(Object proxy, Method method, Object[] args) { String name =method.getDeclaringClass().getName(); String mapperName=name.substring(name.lastIndexOf(".")+1); MappingBean mappingBean=parse.parseMapper(parse.parseXML(PATH+mapperName+".xml")); if (mappingBean!=null&&(mappingBean.getMappingList()!=null&&mappingBean.getMappingList().size()>0)){ for (Mapping mapping :mappingBean.getMappingList()){ //进入查询逻辑 if (mapping.getSqlType().equals("select")){ if (mapping.getFuncName().equals(method.getName())){ System.out.println("执行查询方法:"+mapping.getSql()); System.out.println("参数:"+args[0]); return sqlSession.selectObject(mapping,String.valueOf(args[0])); } } } } return null; }

通过sqlSession.selectObject调用到了executor方法,通过这里看到该简化的ybatis传参只接收一个,并且是字符串。

executor方法:这里通过反射将结果转换成对象,但只做了整型和字符串的转换。

public class MyExcutorImpl implements MyExcutor { private MyParse myParse = new MyParse(); public  T query(Mapping mapping, Object param) { Connection connection=getConnection(); PreparedStatement preparedStatement=null; ResultSet resultSet=null; Object obj=null; List list=new ArrayList(); try { preparedStatement=connection.prepareStatement(mapping.getSql()); preparedStatement.setString(1,param.toString()); if (mapping.getResultType()==null){ throw new RuntimeException("返回的映射结果不能为空!"); } resultSet = preparedStatement.executeQuery(); int row = 0; ResultSetMetaData rd = resultSet.getMetaData(); while (resultSet.next()){ obj=resultToObject(resultSet,mapping.getResultType()); row++; list.add(obj); } System.out.println("记录行数:"+row); } catch (SQLException e) { e.printStackTrace(); } return (T)list; } private Connection getConnection(){ Connection connection=myParse.build("mybatis-config.xml"); return connection; } /* * 把得到的一列数据存入到一个对象中
	 */ @SuppressWarnings("unchecked") private  T resultToObject(ResultSet rs,Object object) { Object obj=null; try { Class  cls=object.getClass(); /* 这里为什么要通过class再new一个对象,因为如果不new一个新的对象,每次返回的都是形参上的object, 而这个object都是同一个,会导致list列表后面覆盖前面值。 */ obj=cls.newInstance(); //获取结果集元数据(获取此 ResultSet 对象的列的编号、类型和属性。) ResultSetMetaData rd=rs.getMetaData(); for (int i = 0; i < rd.getColumnCount(); i++) { //获取列名 String columnName=rd.getColumnLabel(i+1); //组合方法名 String methodName="set"+columnName.substring(0, 1).toUpperCase()+columnName.substring(1); //获取列类型 int columnType=rd.getColumnType(i+1); Method method=null; switch(columnType) { case java.sql.Types.VARCHAR: case java.sql.Types.CHAR: method=cls.getMethod(methodName, String.class); if(method!=null) { method.invoke(obj, rs.getString(columnName)); } break; case java.sql.Types.INTEGER: method=cls.getMethod(methodName, Integer.class); if(method!=null) { method.invoke(obj, rs.getInt(columnName)); } break; default: break; } } }  catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } return (T) obj; }

}

4.测试

将该简化版的ybatis打成jar包。

手写mybatis框架(一)通过动态代理简单实现查询功能4

将导出的jar包引入到本地仓库。

命令:根据自己实际路径和名称修改。

mvn install:install-file -DgroupId=com.my -DartifactId=ybatis -Dversion=1.0.0 -Dpackaging=jar -Dfile=C:\Users\chuan\Desktop\glzxCode\ybatis\target\ybatis-1.0-SNAPSHOT.jar

新建一个maven项目。

添加pom文件:引入jar包,因为打包并没有将引入的jar打进去,所以要重新引用。

  mysql mysql-connector-java 5.1.29   dom4j dom4j 1.6.1   com.my ybatis 1.0.0 

实体类:

public class User implements Serializable { private Integer id; private String username; private String password;
//getter、setter省略
}

接口:

public interface UserMapper { public List getUserById(String id);
}

映射文件:


 

测试类:

public class TestMybatis { public static void main(String[] args) { MySqlSession sqlsession=new MySqlSession(); UserMapper mapper = sqlsession.getMapper(UserMapper.class); List user = mapper.getUserById("1"); System.out.println(user); }
}

结果:

执行查询方法:SELECT * FROM user WHERE id = ?
参数:1
记录行数:2
[User{id='1', username='aaa', password='bbb'}, User{id='1', username='c', password='c'}]

到这里整个过程就已经结束了,目前只有查询功能,可能后期会增加其他功能以及缓存等。有问题欢迎指正,谢谢。

勿删,copyright占位
分享文章到微博
分享文章到朋友圈

上一篇:C++提供了四个转换运算符:const_cast,static_cast,reinterpret_cast

下一篇:SqlServer2008实例43日期函数

您可能感兴趣

  • 用ASP实现论坛的UBB功能

    前几日我曾经阅读到一篇技术文章讲述如何使用VBScript中的正则表达式对象来实现论坛里面的UBB功能。VBScript提供的正则表达式对象功能非常强大,但是只有5。5以上的脚本引擎才可以很好的支持正则表达式对象,为了获得版本是5。5的脚本引擎,你需要单独安装这个脚本引擎,或者安装IE5.5,也就是说对服务器的要求比较苛刻,那就没有其他的方法实现UBB功能了吗?当然不是了,现在就跟随我一同来...

  • 用delphi实现冰河的远程屏幕操作功能

    分为服务端和客户端两个部分,虽然不是一个完整的delphi工程,但是我们关心的其中有用的代码,对吧?下面是服务端unit ServerDlg;interfaceusesWindows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,ExtCtrls, StdCtrls, WinSock, ScktComp, M...

  • JavaScript + PHP 应用二:网页设计中树形菜单的动态实现

    树形菜单,熟悉Windows程序管理器的读者一定不会陌生。单击项目左侧的+号,项目展开;再次单击,项目收缩。读过很多篇有关树形菜单的JavaScript实现方法,原理很简单,都是利用Style中display属性的控制。笔者本文的重点并不侧重于此,倒是想谈一谈如何实现每一次从数据库中提取数据并动态更新树形菜单。  树形菜单主要用来导航。网站有很多栏目,每个栏目下有很多子栏目,栏目经常变动,如...

  • 用CTI实现与Web交谈

    用CTI实现与Web交谈 世界上现在有十亿的电话终端,另外,有超过2亿的移动电话已经销售到世界上。而就人的自身习惯来看,通过言谈的交流,利用听和说是人们更愿意接受的交流和获取信息的方式。 移动通信技术与数据通信的结合,提供给人们随处接入网络的可能,但是只有WAP才是我们构建移动商务的唯一平台吗?CTI技术的发展给我们提供了一条新的途径。 CTI技术的进步 经过努力文本语音转换器(TTS...

  • 一个简单聊天室的两种实现 (fcntl 和 select)

    深入UNIX编程之一一个简单聊天室的两种实现 (fcntl 和 select) 作者:不详 eDOC工作组 在互联网相当普及的今天,在互联网上聊天对很多“网虫”来说已经是家常便饭了。聊天室程序可以说是网上最简单的多点通信程序。聊天室的实现方法有很多,但都是利用所谓的“多用户空间”来对信息进行交换,具有典型的多路I/O的架构。一个简单的聊天室, 从程序员的观点来看就是在多个I/O端点之间实现多...

  • PHP对象编程实现3D饼图

    作者:phpstudent //公用函数 //把角度转换为弧度 function deg2Arc($degrees) { return($degrees * (pi()/180.0)); } //RGB function getRGB($color){ $R=($color>>16) & 0xff; $G=($color>>8) & 0xff; $B=($color) & 0xff; ret...

  • ASP实现多语言支持

    ASP实现多语言支持纲要 :   让我们设想你使用 Active Server Pages 设计了一个成功的站点,而你的客户纷纷要求将此站点国际化以提供多种语言版本。这时候你该怎么办?请看本文。 正文:   一、介绍   让我们设想你使用 Active Server Pages 设计了一个成功的站点,而你的客户纷纷要求将此站点国际化以提供多种语言版本。该问题的关键在于,你必须使用某种方法实现...

  • 用VC 实现图象渐显和渐隐

    作者: 周长发摘 要 图象的渐显/渐隐被广泛运用与图象处理和多媒提娱乐软件。本文基于Windows的调色板动画和时间码技术设计了通用的图象渐显和渐隐算法,并实现了其Visual C++程序编码。关键词 渐显、渐隐、调色板、调色板动画、时间码图象的渐显/渐隐是十分重要的图象效果,广泛运用于图象处理和多媒提娱乐软件。渐显/渐隐算法设计的最大困难是速度控制,包括定时和快速改变图象中各象素的颜色。如...

CSDN

CSDN

中国开发者社区CSDN (Chinese Software Developer Network) 创立于1999年,致力为中国开发者提供知识传播、在线学习、职业发展等全生命周期服务。

华为云40多款云服务产品0元试用活动

免费套餐,马上领取!
手写mybatis框架(一)通过动态代理简单实现查询功能介绍:华为云为您免费提供手写mybatis框架(一)通过动态代理简单实现查询功能在博客、论坛、帮助中心等栏目的相关文章,同时还可以通过 站内搜索 查询更多手写mybatis框架(一)通过动态代理简单实现查询功能的相关内容。| 移动地址: 手写mybatis框架(一)通过动态代理简单实现查询功能 | 写博客