1. 前言

本篇博客,将使用JDK动态代理、注解、反射等技术,编写一个最简单的MyBatis,可基本实现对象的增删查改

2. 注解的定义

2.1 Delete注解

/**
* @ClassName Delete
* @Descriiption 删除注解
* @Author yanjiantao
* @Date 2019/6/27 11:03
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Delete {
public String value();
}

2.2 Insert注解

/**
* @ClassName Delete
* @Descriiption 保存注解
* @Author yanjiantao
* @Date 2019/6/27 11:03
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Insert {
public String value();
}

2.3 Select注解

/**
* @ClassName Delete
* @Descriiption 查询注解
* @Author yanjiantao
* @Date 2019/6/27 11:03
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {
public String value();
}

2.4 Update注解

/**
* @ClassName Delete
* @Descriiption 更新注解
* @Author yanjiantao
* @Date 2019/6/27 11:03
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Update {
public String value();
}

3. jdk动态代理

3.1 方法代理类

/**
* @ClassName MethodProxy
* @Descriiption 方法代理
* @Author yanjiantao
* @Date 2019/6/27 11:11
**/
public class MethodProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return DaoOperatorHandler.handle(method,args);
}
}

该类实现JDK的InvocationHandler方法,并且实验invoke方法,即可实现JDK的动态代理

3.2 动态代理工厂类

/**
* @ClassName MethodProxyFactory
* @Descriiption 代理工厂类
* @Author yanjiantao
* @Date 2019/6/28 15:40
**/
public class MethodProxyFactory { @SuppressWarnings("unchecked")
public static <T> T getBean(Class<T> clazz) {
final MethodProxy methodProxy = new MethodProxy();
return (T) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[]{clazz},
methodProxy
);
}
}

该工厂的方法主要是得到Mapper的实例,并且把Mapper交给JDK进行动态代理

4. 数据库操作

4.1 数据库操作处理类

/**
* @ClassName DaoOperatorHandler
* @Descriiption 数据库操作处理器
* @Author yanjiantao
* @Date 2019/6/27 11:39
**/
public class DaoOperatorHandler { public static Object handle(Method method, Object[] parameters) throws SQLException, ClassNotFoundException {
String sql = null; // 插入
if (method.isAnnotationPresent(Insert.class)) {
sql = checkSql(method.getAnnotation(Insert.class).value(), Insert.class.getSimpleName());
insert(sql, parameters);
// 更新
}else if (method.isAnnotationPresent(Update.class)) {
sql = checkSql(method.getAnnotation(Update.class).value(), Update.class.getSimpleName());
return update(sql, parameters);
// 查询
}else if (method.isAnnotationPresent(Select.class)) {
sql = checkSql(method.getAnnotation(Select.class).value(), Select.class.getSimpleName());
Class returnType = method.getReturnType();
if (List.class.isAssignableFrom(returnType)) {
return selectMany(sql, parameters);
}else {
return selectMany(sql, parameters).get(0);
} }else if (method.isAnnotationPresent(Delete.class)) {
sql = checkSql(method.getAnnotation(Delete.class).value(), Delete.class.getSimpleName());
return update(sql, parameters);
}
System.out.println(sql);
return null;
} /**
* 插入
* @param sql sql
* @param parameters 参数
* @throws SQLException SQLException
* @throws ClassNotFoundException ClassNotFoundException
*/
private static void insert(String sql, Object[] parameters) throws SQLException, ClassNotFoundException {
Connection connection = JDBCUtils.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);
for (int i = 0; i < parameters.length; i++) {
statement.setObject(i+1, (String) parameters[i]);
}
statement.execute();
connection.close();
} /**
* 插入
* @param sql sql
* @param parameters 参数
* @throws SQLException SQLException
* @throws ClassNotFoundException ClassNotFoundException
*/
private static Integer update(String sql, Object[] parameters) throws SQLException, ClassNotFoundException {
Connection connection = JDBCUtils.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);
for (int i = 0; i < parameters.length; i++) {
statement.setObject(i+1, parameters[i]);
}
int result = statement.executeUpdate();
connection.close();
return result;
} /**
* 插入
* @param sql sql
* @param parameters 参数
* @return List<T>
* @throws SQLException SQLException
* @throws ClassNotFoundException ClassNotFoundException
*/
private static <T> List<T> selectMany(String sql, Object[] parameters) throws SQLException, ClassNotFoundException {
Connection connection = JDBCUtils.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);
for (int i = 0; parameters != null && i < parameters.length; i++) {
statement.setObject(i+1, parameters[i]);
}
ResultSet resultSet = statement.executeQuery();
List<T> result = new ResultToMapper<T>().mapToObject(resultSet,User.class);
return result;
} /**
* 检查sql
* @param sql sql
* @param type type
* @return the sql
* @throws SQLException SQLException
*/
private static String checkSql(String sql, String type) throws SQLException {
String sqlType = sql.split(" ")[0];
if (!sqlType.equalsIgnoreCase(type)) {
throw new SQLException("SQL语句错误");
}
return sql;
} }

该类主要是根据被代理类是否包含相关注解,根据注解的类型,进行增删查改的操作,最后,再将增删查改后的处理结果,使用反射映射到实体类上

5 实体类

5.1用户实体类

/**
* @ClassName User
* @Descriiption 用户实体类
* @Author yanjiantao
* @Date 2019/6/28 15:24
**/
@Data
public class User {
private Integer id;
private String username;
private String password;
}

6 工具类

6.1 JDBCUtils

/**
* @ClassName JDBCUtils
* @Descriiption jdbc连接工具类
* @Author yanjiantao
* @Date 2019/6/28 16:24
**/
public class JDBCUtils {
public static Connection getConnection() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
String username = "root";
String password = "root123456"; return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&serverTimezone=GMT%2B8", username, password);
} }

6.2 ResultToMapper

/**
* @ClassName ResultToMapper
* @Descriiption mysql查询结果转换为实体bean
* @Author yanjiantao
* @Date 2019/6/28 17:35
**/
public class ResultToMapper<T> { public List<T> mapToObject(ResultSet resultSet, Class<?> clazz) {
if (resultSet == null) {
return null;
} List<T> result = null;
try {
while (resultSet.next()) {
T bean = (T) clazz.newInstance();
ResultSetMetaData metaData = resultSet.getMetaData();
for (int i = 0; i < metaData.getColumnCount(); i++) {
String columnName = metaData.getColumnName(i + 1);
Object columnValue = resultSet.getObject(i + 1);
Field field = clazz.getDeclaredField(columnName);
if (field != null && columnValue != null) {
field.setAccessible(true);
field.set(bean,columnValue);
}
}
if (result == null) {
result = new ArrayList<>();
}
result.add(bean);
}
} catch (SQLException | InstantiationException | IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
if (result == null) {
return Collections.emptyList();
} return result;
} }

该类主要是将mysql查询的结果,通过反射,映射到实体类上

7 Mapper

public interface UserMapper {
@Insert("insert into user (username,password) values (?,?)")
public void addUser(String name, String password); @Select("select * from user")
public List<User> findUsers(); @Select("select * from user where id = ?")
public User getUser(Integer id); @Update("update user set username = ? , password=? where id=?")
public Integer updateUser(String name, String password, Integer id); @Delete("delete from user where id=?")
public Integer deleteUser(Integer id);
}

8 测试类


@Slf4j
public class UserMapperTest { @Test
public void addUser() {
UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);
userMapper.addUser("boolean-","123456");
log.info("---------->");
} @Test
public void findUsers() {
UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);
List<User> list = userMapper.findUsers();
log.info("---------->list={}", list);
} @Test
public void getUser() {
UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);
User user = userMapper.getUser(2);
log.info("---------->user={}", user);
} @Test
public void updateUser() {
UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);
Integer result = userMapper.updateUser("鄢剑涛update", "yjt123", 1);
log.info("count={}", result);
} @Test
public void deleteUser() {
UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);
Integer count = userMapper.deleteUser(1);
log.info("count={}", count);
}
}

9 总结

这次的编写简单的mybatis,让我对java基础有了进一步的了解,明白了反射、注解的厉害之处,也了解了JDK动态代理设计模式,总之,收获很大!!

源码路径

手写MyBatis的更多相关文章

  1. 要想精通Mybatis?从手写Mybatis框架开始吧!

    1.Mybatis组成 动态SQL Config配置 Mapper配置 2.核心源码分析 Configuration源码解析 SqlSessionFactory源码解析 SqlSession源码解析 ...

  2. 手写MyBatis ORM框架实践

    一.实现手写Mybatis三个难点 1.接口既然不能被实例化?那么我们是怎么实现能够调用的? 2.参数如何和sql绑定 3.返回结果 下面是Mybatis接口 二.Demo实现 1.创建Maven工程 ...

  3. 手写mybatis框架笔记

    MyBatis 手写MyBatis流程 架构流程图 封装数据 封装到Configuration中 1.封装全局配置文件,包含数据库连接信息和mappers信息 2.封装*mapper.xml映射文件 ...

  4. 手写MyBatis流程

    MyBatis 手写MyBatis流程 架构流程图 封装数据 封装到Configuration中 1.封装全局配置文件,包含数据库连接信息和mappers信息 2.封装*mapper.xml映射文件 ...

  5. 手写mybatis框架-增加缓存&事务功能

    前言 在学习mybatis源码之余,自己完成了一个简单的ORM框架.已完成基本SQL的执行和对象关系映射.本周在此基础上,又加入了缓存和事务功能.所有代码都没有copy,如果也对此感兴趣,请赏个Sta ...

  6. 带码农《手写Mybatis》进度3:实现映射器的注册和使用

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获!

  7. 框架源码系列十二:Mybatis源码之手写Mybatis

    一.需求分析 1.Mybatis是什么? 一个半自动化的orm框架(Object Relation Mapping). 2.Mybatis完成什么工作? 在面向对象编程中,我们操作的都是对象,Myba ...

  8. 从零搭建Spring Boot脚手架(4):手写Mybatis通用Mapper

    1. 前言 今天继续搭建我们的kono Spring Boot脚手架,上一文把国内最流行的ORM框架Mybatis也集成了进去.但是很多时候我们希望有一些开箱即用的通用Mapper来简化我们的开发.我 ...

  9. 手写mybatis框架

    前言 很久没有更新mybatis的源码解析了,因为最近在将自己所理解的mybatis思想转为实践. 在学习mybatis的源码过程中,根据mybatis的思想自己构建了一个ORM框架 .整个代码都是自 ...

  10. Mybatis执行流程学习之手写mybatis雏形

    Mybatis是目前开发中最常用的一款基于ORM思想的半自动持久层框架,平时我们都仅仅停留在使用阶段,对mybatis是怎样运行的并不清楚,今天抽空找到一些资料自学了一波,自己写了一个mybatis的 ...

随机推荐

  1. Seata源码—4.全局事务拦截与开启事务处理

    大纲 1.Seata Server的启动入口的源码 2.Seata Server的网络服务器启动的源码 3.全局事务拦截器的核心变量 4.全局事务拦截器的初始化源码 5.全局事务拦截器的AOP切面拦截 ...

  2. 操作系统:苹果的M1芯片因何而快?

    本届来看看操作系统下面的硬件层面,重点研究一下 CPU 的原理和它的加速套路. CPU的原理初探 从操作系统的位置来看,它除了能够向上封装,为软件调用提供 API(也就是系统调用),向下又对硬件资源进 ...

  3. 网络编程:使用poll单线程处理所有I/O事件

    事件驱动模型 事件驱动的好处:占用资源少,效率高,可扩展性强,是支持高性能高并发的不二之选. 事件驱动模型也叫作反应堆模型(reactor),或者是Event loop模型,该模型的核心有两点: 1. ...

  4. codeup之沙漏图形

    Description 问题:输入n,输出正倒n层星号三角形.首行顶格,星号间有一空格,效果见样例 输入样例: 3 输出样例: * * * * * * * * * * * 数据规模 1<= n ...

  5. Partition to K Equal Sum Subsets——LeetCode进阶路

    原题链接https://leetcode.com/problems/partition-to-k-equal-sum-subsets/ 题目描述 Given an array of integers ...

  6. @FeignClient注解配置局部超时时间、OkHttp长连接和SocketTimeoutException异常解决办法

    问题描述:open feign配置OKhttp调用远程API,连续调用次数较少时,一切正常,次数非常多时(例如,连续请求600次)就抛出java.net.SocketTimeoutException: ...

  7. java返回数据库中层级结构数据的treeListMap写法,以动态菜单为例

    说明 1.treeListMap写法,与纯算法版本比起来,treeListMap版的缺点是会查询多次数据库,而纯算法只会查询一次数据库 2.里面有不同角色分配不同的菜单相关代码,注意复制粘贴. 3.可 ...

  8. 「Note」您想来点数据结构吗?

    大分块系列 最初分块 \(\color{black}{P4119}\) 考虑数列分块+值域分块 数列分块需要维护: \(nid_{i,j}\) \(fid_i\) \(f_i\) 块 \(i\) 中数 ...

  9. python基础—数字,字符串练习题

    1.如有以下变量 n1=5,请使用 int 的提供的方法,得到该变量最少可以用多少个二进制位表示? n1=5 r=n1.bit_lenght() #当前数字的二进制,至少用n位表示.bit_lengh ...

  10. java把mysql的数据同步到prometheus

    1.mysql的数据  2.java代码  建立指标Collector类,指标类必须继承Collector import cn.hutool.extra.spring.SpringUtil; impo ...