为什么需要持久层框架?

首先我们先看看使用原生jdbc存在的问题?

public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
// 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 通过驱动管理类获取数据库链接
connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?
characterEncoding=utf-8", "root", "root");
// 定义sql语句?表示占位符
String sql = "select * from user where username = ?";
// 获取预处理statement
preparedStatement = connection.prepareStatement(sql);
// 设置参数,第⼀个参数为sql语句中参数的序号(从1开始),第⼆个参数为设置的参数值
preparedStatement.setString(1, "tom");
// 向数据库发出sql执⾏查询,查询出结果集
resultSet = preparedStatement.executeQuery();
// 遍历查询结果集
while (resultSet.next()) {
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
// 封装User
user.setId(id);
user.setUsername(username);
}
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放资源
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
} }
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
} }

可以看出原始jdbc存在的问题如下:

  1. 数据库连接、创建、释放频繁造成资源浪,影响系统性能
  2. sql语句卸载代码里,不易维护,也不好复用
  3. 使用preparedStatement向占位符传参存在硬编码,如果where条件变了,需要改sql
  4. 对结果集解析存在硬编码,加字段需要改sql并且改解析的代码,如果能将数据库查出的记录封装成pojo对象解析会比较方便

解决方法如下:

  1. 使用数据库连接池初始化连接资源
  2. 将sql语句抽取到xml配置文件中
  3. 使用反射等技术,自动将实体与表进行属性与字段的自动映射

自定义持久层框架

本质就是对jdbc的一个封装和功能的增强,大概需要这几个步骤

  1. 创建配置文件

    • sqlMapConfig.xml :数据库配置信息
    • xxMapper.xml:sql语句信息
  2. 创建两个bean(容器对象):存放的就是对配置文件解析出来的内容
    • Configuration(核心配置类):存放sqlMapConfig.xml解析出来的内容
    • MappedStatement(映射配置类):存放xxMapper.xml解析出来的内容
  3. 创建类SqlSessionFactoryBuilder
    • 使用dom4j解析配置文件,将内容封装到容器对象中
    • 创建SqlSessionFactory对象,生产SqlSession
  4. 创建SqlSessionFactory接口及实现类DefaultSqlSessionFactory。通过方法openSession生产SqlSession
  5. 创建接口SqlSession接口及实现类DefaultSqlSession。定义query、update、save接口
  6. 创建Executor接口及实现类SimpleExecutor,执行具体的jdbc代码操作。

开发完成后,第一阶段我们的dao层的实现是这样的:

public class UserDaoImpl implements UserDao {

    @Override
public List<User> queryAll() throws IllegalAccessException, IntrospectionException, InstantiationException, SQLException, InvocationTargetException, NoSuchFieldException, DocumentException, PropertyVetoException, ClassNotFoundException {
SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
SqlSessionFactory builder = sqlSessionFactoryBuilder.builder("sqlMapConfig.xml");
SqlSession sqlSession = builder.openSession();
List<User> objects = sqlSession.queryAll("User.selectList");
System.out.println(objects);
return objects;
}
}

可以看出还有两个明显需要改进的地方:

  • 每个dao层的实现都需要写大段重复代码,userDaoImpl写一次,ProductDaoImpl写一次
  • 每次调用queryAll都需要传入参数statementid

那么可以怎么解决呢?

  • SqlSessionFactory可以做成单例,方便调取。Dao层的实现类重复编码可以通过动态代理实现,动态生成Dao的实现类,Dao层只写接口就行了。
  • statementid可以通过约定名称对应的方式替代

代码如下:

原dao层实现:

public class UserDaoImpl implements UserDao {

    @Override
public List<User> queryAll() throws IllegalAccessException, IntrospectionException, InstantiationException, SQLException, InvocationTargetException, NoSuchFieldException, DocumentException, PropertyVetoException, ClassNotFoundException {
SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
SqlSessionFactory builder = sqlSessionFactoryBuilder.builder("sqlMapConfig.xml");
SqlSession sqlSession = builder.openSession();
List<User> objects = sqlSession.queryAll("User.selectList");
System.out.println(objects);
return objects;
}
}

动态代理:

  public <T> T getMapper(Class mapperClass) {
Object instance = Proxy.newProxyInstance(mapperClass.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("queryAll")) {
String className = method.getDeclaringClass().getName();
String statementid = className + "." + method.getName();
List<User> objects = queryAll(statementid);
return objects;
}
return null;
}
});
return (T) instance;
}

简单的封装就完成了。

总体项目地址:https://gitee.com/mmcLine/simple-mybatis

半天撸一个简易版mybatis的更多相关文章

  1. 基于 getter 和 setter 撸一个简易的MVVM

    Angular 和 Vue 在对Angular的学习中,了解到AngularJS 的两个主要缺点: 对于每一次界面时间,Ajax 或者 timeout,都会进行一个脏检查,而每一次脏检查又会在内部循环 ...

  2. .NET Core的文件系统[5]:扩展文件系统构建一个简易版“云盘”

    FileProvider构建了一个抽象文件系统,作为它的两个具体实现,PhysicalFileProvider和EmbeddedFileProvider则分别为我们构建了一个物理文件系统和程序集内嵌文 ...

  3. 依赖注入[5]: 创建一个简易版的DI框架[下篇]

    为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在<依赖注入[4]: 创建一个简易版的DI框架[上篇]> ...

  4. 依赖注入[4]: 创建一个简易版的DI框架[上篇]

    本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章(<控制反转>.<基于IoC的设计模式>和< 依赖注入模式>)从纯理论的角度 ...

  5. .NET CORE学习笔记系列(2)——依赖注入[4]: 创建一个简易版的DI框架[上篇]

    原文https://www.cnblogs.com/artech/p/net-core-di-04.html 本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章从 ...

  6. 手动实现一个简易版SpringMvc

    版权声明:本篇博客大部分代码引用于公众号:java团长,我只是在作者基础上稍微修改一些内容,内容仅供学习与参考 前言:目前mvc框架经过大浪淘沙,由最初的struts1到struts2,到目前的主流框 ...

  7. 如何实现一个简易版的 Spring - 如何实现 Setter 注入

    前言 之前在 上篇 提到过会实现一个简易版的 IoC 和 AOP,今天它终于来了...相信对于使用 Java 开发语言的朋友们都使用过或者听说过 Spring 这个开发框架,绝大部分的企业级开发中都离 ...

  8. 如何实现一个简易版的 Spring - 如何实现 Constructor 注入

    前言 本文是「如何实现一个简易版的 Spring」系列的第二篇,在 第一篇 介绍了如何实现一个基于 XML 的简单 Setter 注入,这篇来看看要如何去实现一个简单的 Constructor 注入功 ...

  9. 如何实现一个简易版的 Spring - 如何实现 @Component 注解

    前言 前面两篇文章(如何实现一个简易版的 Spring - 如何实现 Setter 注入.如何实现一个简易版的 Spring - 如何实现 Constructor 注入)介绍的都是基于 XML 配置文 ...

随机推荐

  1. vue js 手写 正则判断 手机号码 和 密码

    const phoneOrEmails = /^1[3|4|5|6|7|8|9][0-9]\d{8}$/             if(this.ruleForms.phoneOrEmail  ==  ...

  2. Mybatis-基本学习(下)

    四,MAP的使用--超常用 思考:多表连接查询怎么做?---MAP的好处!---返回List

  3. python模块--collections(容器数据类型)

    Counter类(dict的子类, 计数器) 方法 返回值类型 说明 __init__ Counter 传入可迭代对象, 会对对象中的值进行计数, 值为键, 计数为值 .elements() 迭代器 ...

  4. FastAPI 学习之路(一)fastapi--高性能web开发框架

    fastapi是高性能的web框架.他的主要特点是:- 快速编码- 减少人为bug- 直观- 简易- 具有交互式文档 - 高性能 - 基于API的开放标准 支持python 3.6版本. 安装 pip ...

  5. freeswitch的网关配置

    vim  /usr/local/freeswitch/conf/sip_profiles/external/weihu1.xml 1 <!-- 点对点式 --> 2 <!-- 3 & ...

  6. RocketMQ详解(四)核心设计原理

    专题目录 RocketMQ详解(一)原理概览 RocketMQ详解(二)安装使用详解 RocketMQ详解(三)启动运行原理 RocketMQ详解(四)核心设计原理 RocketMQ详解(五)总结提高 ...

  7. 机器学习——集成学习(Bagging、Boosting、Stacking)

    1 前言 集成学习的思想是将若干个学习器(分类器&回归器)组合之后产生一个新学习器.弱分类器(weak learner)指那些分类准确率只稍微好于随机猜测的分类器(errorrate < ...

  8. 图论---最小生成树----普利姆(Prim)算法

    普利姆(Prim)算法 1. 最小生成树(又名:最小权重生成树) 概念:将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树.最小生成树属于一种树形结构(树形结构是一 ...

  9. UVA 506 System Dependencies(模拟 烂题)

    https://vjudge.net/problem/UVA-506 题目是给出了五种指令,DEPEND.INSTALL.REMOVE.LIST.END,操作的格式及功能如下: DEPEND item ...

  10. 30天自制操作系统(二)汇编语言学习和Makefile入门

    我们继续学习操作系统的相关内容. ; hello-os ; TAB=4 ORG 0x7c00 ; このプログラムがどこに読み込まれるのか ; 以下は標準的なFAT12フォーマットフロッピーディスクのた ...