了解mybatis源码手写mybatis
一:mybatis概述
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
二:手写mybatis
实现思路::
1创建SqlSessionFactory实例
2:实例化过程中,加载配置文件创建configuration对象
3:通过factory创建SqlSeesion
---------------------初始化阶段完成--------------------------
4:通过SqlSeesion获取mapper接口动态代理
5:动态代理回调sqlsession中某查询方法
---------------------代理阶段完成--------------------------
6:SqlSession将查询方法转发给Executor
7:Executor基于jdbc访问数据库获取数据
8:Executor通过反射将数据转化成POJP并返回给SqlSession
9:将数据返回给调用者
手写源码展示:
2.1、初始化阶段
将db.properties,以及*mapper.xml读取到内存中,主要通过SqlSessionFactory实现
/*** 初始化加载配置信息映射* 生成sqlsession* @param: mybatis03* @description:* @author: tangl* @create: 2019-05-13 12:07*/ public class SqlSessionFactory {public final Configuration conf = new Configuration();public SqlSessionFactory(){loadDbInfo();loadMappersInfo();//加载 }//mappers.xml文件保存路径public static final String MAPPER_CONFIG_LOCATION="mappers";//数据库连接配置文件路径public static final String DB_CONFIG_FILE="db.properties";private void loadDbInfo(){InputStream inputStream = SqlSessionFactory.class.getClassLoader().getResourceAsStream(DB_CONFIG_FILE);Properties p = new Properties();try {p.load(inputStream);} catch (IOException e) {e.printStackTrace();}conf.setJdbcDriver(p.get("jdbc.driver").toString());conf.setJdbcUrl(p.get("jdbc.url").toString());conf.setJdbcUsername(p.get("jdbc.username").toString());conf.setJdbcPassword(p.get("jdbc.password").toString());}//读取xml并解析成mapprivate void loadMappersInfo(){URL resources =null;resources = SqlSessionFactory.class.getClassLoader().getResource(MAPPER_CONFIG_LOCATION);File mappers = new File(resources.getFile());if(mappers.isDirectory()){File[] listFiles = mappers.listFiles();for(File file:listFiles){loadMapperInfo(file);}}}//加载指定*.xml文件private void loadMapperInfo(File file){SAXReader reader = new SAXReader();Document document=null;try {document = reader.read(file);} catch (DocumentException e) {e.printStackTrace();}Element root = document.getRootElement();//获取命名空间String namespace = root.attribute("namespace").getData().toString();//select子元素List<Element> selects = root.elements("select");//遍历所有查询语句for( Element element:selects){MappedStatement mappedStatement = new MappedStatement();String id = element.attribute("id").getData().toString();String resultType = element.attribute("resultType").getData().toString();String sql = element.getData().toString();String sourceId = namespace+"."+id; //map的key是命名空间+id mappedStatement.setNamespace(namespace);mappedStatement.setSourceId(sourceId);mappedStatement.setResultType(resultType);mappedStatement.setSql(sql);conf.getMappedStatements().put(sourceId,mappedStatement);}}public SqlSession openSession(){return new DefuaultSqlSession(this.conf);} }
SqlSessionFactory
/*** 数据库配置文件映射* *mapper.xml映射map* @param: mybatis03* @description:* @author: tangl* @create: 2019-05-13 12:10*/ public class Configuration {private String jdbcDriver;//数据库驱动private String jdbcUrl;//数据库url地址private String jdbcUsername;//数据库用户名private String jdbcPassword;//数据库密码private Map<String, MappedStatement> mappedStatements = new HashMap<>();//mapper.xml解析成对象public String getJdbcDriver() {return jdbcDriver;}public void setJdbcDriver(String jdbcDriver) {this.jdbcDriver = jdbcDriver;}public String getJdbcUrl() {return jdbcUrl;}public void setJdbcUrl(String jdbcUrl) {this.jdbcUrl = jdbcUrl;}public String getJdbcUsername() {return jdbcUsername;}public void setJdbcUsername(String jdbcUsername) {this.jdbcUsername = jdbcUsername;}public String getJdbcPassword() {return jdbcPassword;}public void setJdbcPassword(String jdbcPassword) {this.jdbcPassword = jdbcPassword;}public Map<String, MappedStatement> getMappedStatements() {return mappedStatements;}public void setMappedStatements(Map<String, MappedStatement> mappedStatements) {this.mappedStatements = mappedStatements;} }
Configuration
/*** mapper.xml映射文件* @param: mybatis03* @description:* @author: tangl* @create: 2019-05-13 12:13*/ public class MappedStatement {private String namespace;//命名空间private String sourceId;//方法id名称private String resultType;//返回数据类型private String sql;//sql语句public String getNamespace() {return namespace;}public void setNamespace(String namespace) {this.namespace = namespace;}public String getSourceId() {return sourceId;}public void setSourceId(String sourceId) {this.sourceId = sourceId;}public String getResultType() {return resultType;}public void setResultType(String resultType) {this.resultType = resultType;}public String getSql() {return sql;}public void setSql(String sql) {this.sql = sql;} }
MappedStatement
2.2、代理阶段
主要通过DefuaultSqlSession实现
/*** 1: 对外提供访问的api* 2:对内请求转发给Executor实现* @param: mybatis03* @description:* @author: tangl* @create: 2019-05-13 14:51*/ public interface SqlSession {<T> T selectOne(String statement,Object parameter) throws Exception;<E> List<E> selectList(String statement, Object parameter);<T> T getMapper(Class<T> type); }
SqlSession
/*** 1: 对外提供访问的api* 2:对内请求转发给Executor实现* 3: 通过动态代理,面向接口编程* @param: mybatis03* @description:* @author: tangl* @create: 2019-05-13 14:51*/ public class DefuaultSqlSession implements SqlSession {private final Configuration conf;private Executor executor;public DefuaultSqlSession(Configuration conf){super();this.conf=conf;executor = new DefaultExcutor(conf);}@Overridepublic <T> T selectOne(String statement, Object parameter) throws Exception {MappedStatement ms = conf.getMappedStatements().get(statement);List<Object> selectList = executor.query(ms,parameter);if(null==selectList || selectList.size()==0){return null;}if(selectList.size()==1){return (T)selectList.get(0);}else{throw new Exception();}}@Overridepublic <E> List<E> selectList(String statement, Object parameter) {MappedStatement ms = conf.getMappedStatements().get(statement);return executor.query(ms,parameter);}@Overridepublic <T> T getMapper(Class<T> type) {return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type},new MapperProxy(this));}public class MapperProxy implements InvocationHandler{private SqlSession session;public MapperProxy(SqlSession session){super();this.session = session;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Class<?> returnType = method.getReturnType();System.out.println("returnType="+returnType.toString());if(Collection.class.isAssignableFrom(returnType)){//返回参数是不是集合return session.selectList(method.getDeclaringClass().getName()+"."+method.getName(),null==args?null:args[0]);}else{return session.selectOne(method.getDeclaringClass().getName()+"."+method.getName(),null==args?null:args[0]);}}} }
DefuaultSqlSession
2.3、数据库操作返回数据并做映射处理阶段
主要通过Executor实现
/*** @param: mybatis03* @description: 封装对数据库操作,以及对返回数据的封装* @author: tangl* @create: 2019-05-13 15:25*/ public interface Executor {<E> List<E> query(MappedStatement mappedStatement, Object args); }
Executor
/*** @param: mybatis03* @description: 封装对数据库操作,以及对返回数据的封装* @author: tangl* @create: 2019-05-13 15:25*/ public class DefaultExcutor implements Executor {private Configuration conf;public DefaultExcutor(Configuration conf){super();this.conf=conf;}@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter) {List<E> ret = new ArrayList<>();try {Class.forName(conf.getJdbcDriver());} catch (ClassNotFoundException e) {e.printStackTrace();}Connection connection=null;PreparedStatement preparedStatement =null;ResultSet resultSet=null;try {connection = DriverManager.getConnection(conf.getJdbcUrl(),conf.getJdbcUsername(),conf.getJdbcPassword());preparedStatement = connection.prepareStatement(ms.getSql());//处理sql语句中的占位符 parameterize(preparedStatement,parameter);resultSet = preparedStatement.executeQuery();//通过发射技术,填充到list handlerResultSet(resultSet,ret,ms.getResultType());} catch (SQLException e) {e.printStackTrace();}finally {try {resultSet.close();preparedStatement.close();connection.close();} catch (SQLException e) {e.printStackTrace();}}return ret;}public void parameterize(PreparedStatement preparedStatement,Object parameter) throws SQLException {if(parameter instanceof String){preparedStatement.setString(1,(String)parameter);}else if(parameter instanceof Long){preparedStatement.setLong(1,(Long)parameter);}else if(parameter instanceof Integer){preparedStatement.setInt(1,(int)parameter);}}public <E> void handlerResultSet(ResultSet resultSet,List<E> ret,String className){Class<E> clazz = null;try {clazz = (Class<E>)Class.forName(className);} catch (ClassNotFoundException e) {e.printStackTrace();}try {while(resultSet.next()){//通过反射实例化对象Object entity = clazz.newInstance();//使用反射工具将resultSet中的数据填充到entity中//id,name,sex,age // Integer id = resultSet.getInt(1); // String name = resultSet.getString(2); // String sex = resultSet.getString(3); // Integer age = resultSet.getInt(4); // // Integer id02 = resultSet.getInt("id"); // String name02 = resultSet.getString("name"); // String sex02 = resultSet.getString("id"); // Integer age02 = resultSet.getInt("age");// ReflectionUtils.// 获取实体类的所有属性,返回Field数组Field[] fields = clazz.getDeclaredFields();for(Field field:fields){field.setAccessible(true);String fname = field.getName();Type type = field.getGenericType();System.out.println("fname="+fname+" type="+type.toString());if(type.toString().equals("class java.lang.String")){String column = resultSet.getString(fname);field.set(entity,column);}else if(type.toString().equals("class java.lang.Integer")){Integer column = resultSet.getInt(fname);field.set(entity,column);}}ret.add((E)entity);}} catch (SQLException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}} }
DefaultExcutor
三:测试
/*** @param: mybatis03* @description:手写mybatis测试* @author: tangl* @create: 2019-05-13 14:57*/ public class TestMybatis {public static void main(String[] args) throws ClassNotFoundException {//第一阶段,加载配置信息放入内存SqlSessionFactory factory = new SqlSessionFactory();SqlSession sqlSession = factory.openSession();System.out.println(sqlSession);//第二阶段 封装ibatis编程模式,使用mapper接口开发的初始化工作TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);//第三阶段TUser tUser = mapper.getUserById(1);System.out.println(tUser);List<TUser> allList = mapper.getUserAllList();if(null!=allList && allList.size()>0){for (TUser tUser02:allList){System.out.println(tUser02);}}} }
View Code
四:项目补充
1:pom.xml文件
<dependencies>
<!-- 解析xml文件--> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version></dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency></dependencies> 2:db.properties
jdbc.driver=com.mysql.cj.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&verifyServerCertificate=false&useSSL=false&serverTimezone=UTCjdbc.username=rootjdbc.password=123456
五:项目说明
这仅仅是参考源码完成mybatis的核心功能,mybatis还有一些其它辅助功能,如:数据库连接池等。
转载于:https://www.cnblogs.com/tanglinsheng/p/10861539.html
了解mybatis源码手写mybatis相关推荐
- 【MyBatis源码解析】MyBatis一二级缓存
MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级),尤其是对于一些相 ...
- MyBatis源码-深入理解MyBatis Executor的设计思想
文章目录 Pre JDBC的执行过程 JDBC Demo JDBC Statement 接口 MyBatis执行过程 四大组件 组件之间的关系 Executor 执行器组件 架构总览 接口继承关系 P ...
- Mybatis源码解析之Mybatis初始化过程
一.搭建一个简单的Mybatis工程 为了了解Mybatis的初始化过程,这里需要搭建一个简单的Mybatis工程操作数据库,工程结构如下: 一个UserBean.java private int i ...
- Mybatis 源码解析(六) Mybatis方言支持
背景 现实中,我们经常会遇到使用oracle.mysql的客户,那么我们一般就需要工程同时支持这两种数据库.所以我们需要mybatis对方言进行支持,可以根据不同的数据源执行不同的sql语句. 实现 ...
- Pytorch二维卷积 conv2d 使用/源码/手写实现conv2d/手写向量内积实现conv2d/转置卷积实现——学习笔记
这里是一份学习笔记- 学习视频点这里
- MyBatis源码-解读Executor的三个实现类之SimpleExecutor(简单执行器)
文章目录 Pre Executor 执行器 接口继承关系 SimpleExecutor(简单执行器) 入门小demo 实例化SimpleExecutor doQuery方法 Pre MyBatis源码 ...
- Mybatis源码分析: MapperMethod功能讲解
canmengqian </div><!--end: blogTitle 博客的标题和副标题 --> <div id="navigator"> ...
- MyBatis源码分析(一)MyBatis整体架构分析
文章目录 系列文章索引 一.为什么要用MyBatis 1.原始JDBC的痛点 2.Hibernate 和 JPA 3.MyBatis的特点 4.MyBatis整体架构 5.MyBatis主要组件及其相 ...
- MyBatis源码-解读Executor的三个实现类之BatchExecutor(批处理执行器)
文章目录 Pre Executor 执行器 接口继承关系 BatchExecutor(重用执行器) 入门小demo 源码 BatchExecutor VS ReuseExecutor Pre MyBa ...
最新文章
- 腾讯大佬用了12小时讲完的Python,整整400集,拿走不谢!
- 经典C语言程序100例之七一
- 使用powermock 测试static 方法,jacoco统计覆盖率问题
- oracle与mysql的区别总结(一)
- django 设置登录成功后的页面
- 【福利】PyTorch中文版官方教程来啦(附下载)
- Mac OS使用技巧之四:修改打开不同格式视频的默认播放器
- 会议主视觉_揭秘!2018杭州云栖大会主视觉设计——光锥之内皆命运
- 智芯传感ZXP0电容式大气压力传感器 拓展多领域创新应用
- 微信好友排行榜 最简教程 二
- Simulink仿真计算中保留特定位数的小数
- C语言编程>第二十三周 ③ 下列给定程序中,函数fun的功能是:利用插入排序法对字符串中的字符按从小到大的顺序进行排序。插入法的基本算法是:先对字符串中的头两个元素进行排序;然后把第三字符插入
- 激光雷达还是摄影测量?两者数据融合如何提高点云质量
- 打开桌面计算机投屏到扩展屏,将Win10电脑屏幕内容投屏到小米电视的操作方法...
- 10年后,从新开始...
- .netframewor金山卫士推送微软8月补丁 IE所有版本存在高危漏洞
- 红黑树的性质以及时间复杂度证明
- 安装Carthage
- 耐克人脸识别_人脸识别启动,全系9个车型随意选,买它立马有1.2万现金拿
- 杰理蓝牙芯片AC6950的使用-按键功能设置