绝对肝货,超全的 MyBatis 动态代理原理讲解。

1.MyBatis简介

MyBatis是一个ORM工具,封装了JDBC的操作,简化业务编程;

Mybatis在web工程中,与Spring集成,提供业务读写数据库的能力。

2.使用步骤

1.引入依赖

采用Maven包依赖管理,mybatis-3.5.5版本;同时需要数据库连接驱动


    org.mybatis
    mybatis
    3.5.5


    mysql
    mysql-connector-java
    5.1.49

2.配置文件

配置文件配置数据库连接源,及映射文件。



 

    
        
            
            
            
                
                
                
                
            
        
    
 
    
    
        
    
 

3.接口定义

定义实体

package com.xiongxin.mybatis.entity;
 
public class User {
 
    private String username;
    private String password;
  ...getter&&setter
}

接口定义

package com.xiongxin.mybatis.mapper;
import com.xiongxin.mybatis.entity.User;
import java.util.List;
public interface UserMapper {
    List queryUser();
}

定义映射文件



 

 
    
        select * from tbl_user
    
 

4.加载执行

package com.xiongxin.mybatis;
 
import com.alibaba.fastjson.JSON;
import com.xiongxin.mybatis.entity.User;
import com.xiongxin.mybatis.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 
import java.io.IOException;
import java.io.Reader;
import java.util.List;
 
public class TestMain {
 
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        //加载 mybatis 的配置文件(它也加载关联的映射文件)
        Reader reader = Resources.getResourceAsReader(resource);
        //构建 sqlSession 的工厂
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        //创建能执行映射文件中 sql 的 sqlSession
        SqlSession session = sessionFactory.openSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        List users = userMapper.queryUser();
        System.out.println(JSON.toJSONString(users));
    }
 
}
---------------------------------
..consule print..
[{"password":"password","username":"xiongxin"}]

到这里,这个Mybatis的使用环节结束。

整个实现过程中,我们并未编写Mapper的实现类,框架是如何在无实现类的场景下实现接口方法返回的呢?

这里就不得不说到接口的动态代理方法了。

3.原理解析

绝对肝货,超全的 MyBatis 动态代理原理讲解。_第1张图片

SqlSession接口的实现中,获取Mapper的代理实现类

绝对肝货,超全的 MyBatis 动态代理原理讲解。_第2张图片

使用了JDK动态代理的功能

绝对肝货,超全的 MyBatis 动态代理原理讲解。_第3张图片

代理类执行方法调用

绝对肝货,超全的 MyBatis 动态代理原理讲解。_第4张图片

方法调用中执行MethodInvoker

绝对肝货,超全的 MyBatis 动态代理原理讲解。_第5张图片

最终执行execue方法。

绝对肝货,超全的 MyBatis 动态代理原理讲解。_第6张图片

获取返回结果Result

4.手撕框架

前置知识:

绝对肝货,超全的 MyBatis 动态代理原理讲解。_第7张图片

 

源码:


    
        com.alibaba
        fastjson
        1.2.74
    
    
        com.h2database
        h2
        1.4.199
    

package com.dbutil.session;
 
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
 
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
/**
 * @author xiongxin
 */
public class SqlSession {
 
    public static Connection getConnH2() throws Exception {
        String url = "jdbc:h2:mem:db_h2;MODE=MYSQL;INIT=RUNSCRIPT FROM './src/main/resources/schema.sql'";
        String user = "root";
        String password = "123456";
        //1.加载驱动程序
        Class.forName("org.h2.Driver");
        //2.获得数据库链接
        Connection conn = DriverManager.getConnection(url, user, password);
        return conn;
    }
 
 
 
    /**
     * 自定义注解
     */
    @Target({TYPE, FIELD, METHOD})
    @Retention(RUNTIME)
    public @interface QueryList {
        public String value();
    }
 
    /**
     * 动态代理
     *
     * @param mapperInterface
     * @param 
     * @return
     */
    public static  T getMapper(Class mapperInterface) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, new MapperInvocationHandler());
    }
 
    /**
     * 代理类方法
     */
    public static class MapperInvocationHandler implements InvocationHandler {
 
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String sql = method.getAnnotation(QueryList.class).value();
            Class returnType = method.getReturnType();
            //返回类型为List
            if (returnType == List.class) {
                Type genericReturnType = method.getGenericReturnType();
                String typeName = genericReturnType.getTypeName();
                String replace = typeName.replace("java.util.List<", "").replace(">", "");
                //获取泛型类型
                Class forName = Class.forName(replace);
                return SqlSession.queryList(sql, forName);
            }
            return null;
        }
    }
 
    /**
     * 结果集转换
     *
     * @param 
     */
    public interface ResultMap {
        T convert(ResultSet resultSet) throws Exception;
    }
 
 
    /**
     * 创建连接并执行
     *
     * @param sql
     * @param resultMap
     * @param 
     * @return
     * @throws Exception
     */
    public static  List queryList(String sql, ResultMap resultMap) throws Exception {
        //jdbc数据库
        Connection conn = getConnH2();
        //3.通过数据库的连接操作数据库,实现增删改查(使用Statement类)
        Statement st = conn.createStatement();
        ResultSet rs = st.executeQuery(sql);
        List list = new ArrayList<>();
        //4.处理数据库的返回结果(使用ResultSet类)
        while (rs.next()) {
            T convert = resultMap.convert(rs);
            list.add(convert);
        }
        //关闭资源
        rs.close();
        st.close();
        conn.close();
        return list;
    }
 
    /**
     * 查询数据集
     *
     * @param sql
     * @param returnType
     * @param 
     * @return
     * @throws Exception
     */
    public static  List queryList(String sql, Class returnType) throws Exception {
        List list = SqlSession.queryList(sql, rs -> {
            T obj = returnType.newInstance();
            Field[] declaredFields = returnType.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                Class type = declaredField.getType();
                //类型为String时的处理方法
                if (type == String.class) {
                    String value = rs.getString(declaredField.getName());
                    String fieldName = declaredField.getName();
                    Method method = returnType.getDeclaredMethod(
                            "set".concat(fieldName.substring(0, 1).toUpperCase().concat(fieldName.substring(1))),
                            String.class);
                    method.invoke(obj, value);
                }
                if (type == Long.class) {
                    Long value = rs.getLong(declaredField.getName());
                    String fieldName = declaredField.getName();
                    Method method = returnType.getDeclaredMethod(
                            "set".concat(fieldName.substring(0, 1).toUpperCase().concat(fieldName.substring(1))),
                            Long.class);
                    method.invoke(obj, value);
                }
                //其他类型处理方法
            }
            return obj;
        });
        return list;
    }
}

schema.sql文件

drop table if exists user;
CREATE TABLE user
(
  id       int(11) NOT NULL AUTO_INCREMENT,
  username varchar(255) DEFAULT NULL,
  password varchar(255) DEFAULT NULL,
  PRIMARY KEY (id)
);
 
insert into user(id,username,password) values(1,'xiongxina','123456');
insert into user(id,username,password) values(2,'xiongxinb','123456');
insert into user(id,username,password) values(3,'xiongxinc','123456');

mapper定义

package com.dbutil.mapper;
 
import com.dbutil.entity.UserEntity;
import com.dbutil.session.SqlSession;
 
import java.util.List;
 
public interface UserMapper {
 
    @SqlSession.QueryList("select * from user")
    List queryUser();
}

使用:

package com.dbutil;
 
import com.dbutil.entity.UserEntity;
import com.dbutil.mapper.UserMapper;
import com.dbutil.session.SqlSession;
 
import java.util.List;
 
public class UserService {
 
    public static void main(String[] args) throws Exception {
        UserMapper userMapper = SqlSession.getMapper(UserMapper.class);
        List userEntities = userMapper.queryUser();
        for (UserEntity userEntity : userEntities) {
            System.out.println(userEntity);
        }
    }
}

你可能感兴趣的:(java,mybatis,java,spring)