手写 MyBatis

本贴最后更新于 2087 天前,其中的信息可能已经事过景迁

手写 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 { @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 动态代理设计模式,总之,收获很大!!

源码路径

  • 注解
    10 引用 • 22 回帖
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3194 引用 • 8214 回帖
  • 反射
    20 引用 • 29 回帖

相关帖子

1 回帖

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • nobt

    核心 InvocationHandler