首页 > 其他分享 >手写MyBatis

手写MyBatis

时间:2024-07-09 17:58:01浏览次数:14  
标签:String parameters public SQLException sql MyBatis 手写 class

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

源码路径

标签:String,parameters,public,SQLException,sql,MyBatis,手写,class
From: https://www.cnblogs.com/booleandev/p/18292486/handwritten-mybatis-zcib9x

相关文章

  • Mybatis -过滤查询
    一、过滤查询过滤查询实则是根据用户传入的条件进行数据的筛选查询,最终返回结果给用户。1、需求及效果图例如:在商品列表中,用户可以根据自己的需要,按照商品的名称和价格范围对商品进行查询。2、原理分析以上条件设计,后台需执行的 SQL会有如下几种: 需求1:查询所有货品......
  • MyBatisPlus的Mapper.xml入参List执行in函数
    使用情景这个是开发过程中比较常见的情景,入参一个list,在Mapper.xml里面执行sql的in函数,今天来记录下这个问题,希望可以给大家一点帮助启发。Mapper文件解决方案xml文件<selectid="get"resultType="com.vo.tVo">SELECTnameFROMus......
  • Pytorch实现基于MNIST的手写数字识别
    本文目的在于训练一个模型,使其能对手写的数字图片进行分类识别,并不断优化使其准确度尽可能地提高一、数据预处理(1)运行时所需库importnumpyasnpimporttorchimporttorchvisionfromtorchimportnnfromtorch.utils.dataimportDataLoaderfromtorchvision......
  • 快速上手:前后端分离开发(Vue+Element+Spring Boot+MyBatis+MySQL)
    文章目录前言项目简介环境准备第一步:初始化前端项目登录页面任务管理页面第二步:初始化后端项目数据库配置数据库表结构实体类和Mapper服务层和控制器第三步:连接前后端总结......
  • 使用Mybatis框架操作数据库
    --------------idea中创建springboot项目引入Mybatis框架-----------------1、新建空项目2.创建模块3.选择springboot版本,添加mybatisframework框架和Mysqldriver驱动 4.删除多余文件 5.选择父工程中选择spring-boot版本6.选择依赖版本号(1)mybatis的起步依赖......
  • 【MyBatis-Plus】 代码生成器使用指南——快速上手最好用的代码生成器!
    MyBatis-Plus代码生成器使用指南1.简介2.环境准备3.项目结构4.引入依赖5.编写代码生成器配置类6.配置解释6.1全局配置6.2数据源配置6.3包配置6.4模板配置6.5策略配置7.运行代码生成器8.生成的代码结构9.总结1.简介MyBatis-Plus是一个MyBatis......
  • 手写简单模拟mvc
    目录结构: 两个注解类:@Controller:packagecom.heaboy.annotation;importjava.lang.annotation.*;/***注解没有功能只是简单标记*.RUNTIME运行时还能看到*.CLASS类里面还有,构建对象久没来了,这个说明是给类加载器的*.SOURCE表示这个注解能存活到......
  • mybatis缓存
    MyBatis提供了两级缓存机制:一级缓存(本地缓存)和二级缓存(全局缓存)。这两级缓存可以显著提高数据查询的效率,减少数据库访问的次数。下面介绍MyBatis的一级缓存和二级缓存的原理、配置和使用方法。题外话:......
  • MybatisX插件使用
    什么是MybatisX插件:MybatisX是idea的一个插件,可以方便地生成MyBatis的映射文件和对应的Java代码。MybatisX的优点:提高开发效率,提高开发效率更方便进行数据库操作怎么使用MybatisX:创建一个springboot项目,在pom.xml文件中引入mybatis-plus依赖建立MySQL连接输入......
  • 使用mybatis切片实现数据权限控制
     数据权限控制需要对查询出的数据进行筛选,对业务入侵最少的方式就是利用mybatis或者数据库连接池的切片对已有业务的sql进行修改。切片逻辑完成后,仅需要在业务中加入少量标记代码,就可以实现对数据权限的控制。这种修改方式,对老业务的逻辑没有入侵或只有少量入侵,基本不影响老业......