当前位置: 首页 > news >正文

手写MyBatis

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 {@Overridepublic 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 {@Testpublic void addUser() {UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);userMapper.addUser("boolean-","123456");log.info("---------->");}@Testpublic void findUsers() {UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);List<User> list = userMapper.findUsers();log.info("---------->list={}", list);}@Testpublic void getUser() {UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);User user = userMapper.getUser(2);log.info("---------->user={}", user);}@Testpublic void updateUser() {UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);Integer result = userMapper.updateUser("鄢剑涛update", "yjt123", 1);log.info("count={}", result);}@Testpublic void deleteUser() {UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);Integer count = userMapper.deleteUser(1);log.info("count={}", count);}
}

9 总结

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

源码路径

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 20W+喜爱的Pathview网页版 | 整合表达谱数据KEGG通路可视化
  • 大模型备案全网最详细流程说明【附附件】
  • SpringBootV12和mybatis全部知识点
  • pointnet2_ops_lib/.安装报错解决方案
  • 【fscan】Windows环境下的fscan安装与使用指南
  • html——VSCode的使用
  • 枚举对象序列化规则(将Java枚举转换为JSON字符串的步骤)
  • C#字符串操作:判断一个字符串是否存在于另一个字符串按特定字符分割后的子字符串中的几种方法
  • linux查看目录下的文件夹命令,find 查找某个目录,但是不包括这个目录本身?
  • 为什么需要重写equals和如何重写equals
  • 数据中台设计书及建设指南(中台及大数据解决技术方案)
  • 每天10个vue面试题(一)
  • 全能型CAE/CFD建模工具SimLab 详解Part1: Geomtry,轻松集成力学、电磁学、疲劳优化等功能
  • MySQL 创建数据库
  • Java面试八股之MySQL中的MVCC是什么,作用是什么?
  • Docker入门(二) - Dockerfile
  • EventListener原理
  • node和express搭建代理服务器(源码)
  • PHP变量
  • php的插入排序,通过双层for循环
  • Redis 懒删除(lazy free)简史
  • redis学习笔记(三):列表、集合、有序集合
  • SpiderData 2019年2月13日 DApp数据排行榜
  • windows-nginx-https-本地配置
  • 爱情 北京女病人
  • 多线程事务回滚
  • 如何使用 JavaScript 解析 URL
  • 微信小程序开发问题汇总
  • 小程序 setData 学问多
  • 以太坊客户端Geth命令参数详解
  • 智能合约开发环境搭建及Hello World合约
  • (2009.11版)《网络管理员考试 考前冲刺预测卷及考点解析》复习重点
  • (C++)八皇后问题
  • (创新)基于VMD-CNN-BiLSTM的电力负荷预测—代码+数据
  • (第27天)Oracle 数据泵转换分区表
  • (二十一)devops持续集成开发——使用jenkins的Docker Pipeline插件完成docker项目的pipeline流水线发布
  • (附源码)spring boot儿童教育管理系统 毕业设计 281442
  • (力扣)1314.矩阵区域和
  • (十七)Flink 容错机制
  • (实战篇)如何缓存数据
  • (转)C#开发微信门户及应用(1)--开始使用微信接口
  • (转)Oracle存储过程编写经验和优化措施
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • (转)德国人的记事本
  • (转载)OpenStack Hacker养成指南
  • .net 4.0 A potentially dangerous Request.Form value was detected from the client 的解决方案
  • .NET CORE 2.0发布后没有 VIEWS视图页面文件
  • .net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
  • .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
  • .Net 应用中使用dot trace进行性能诊断
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .NET单元测试使用AutoFixture按需填充的方法总结
  • .NET开源纪元:穿越封闭的迷雾,拥抱开放的星辰
  • .net通过类组装数据转换为json并且传递给对方接口
  • .net通用权限框架B/S (三)--MODEL层(2)