绝对干货,超全的 MyBatis 动态代理原理讲解!

点击关注公众号,实用技术文章及时了解绝对干货,超全的 MyBatis 动态代理原理讲解!


1.Mybatis简介

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

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

2.使用步骤

1.引入依赖

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

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.5</version>
</dependency>
<dependency>
    <groupId>MySQL</groupId>
    <artifactId>mysql-connector-Java</artifactId>
    <version>5.1.49</version>
</dependency>

2.配置文件

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

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
 
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <!-- 数据库连接方式 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost/user" />
                <property name="username" value="root" />
                <property name="password" value="123456" />
            </dataSource>
        </environment>
    </environments>
 
    <!-- 注册表映射文件 -->
    <mappers>
        <mapper resource="mybatis/User.xml"/>
    </mappers>
 
</configuration>

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<User> queryUser();
}

定义映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

 
<mapper namespace="com.xiongxin.mybatis.mapper.UserMapper">
 
    <select id="queryUser" resultType="com.xiongxin.mybatis.entity.User">
        select * from tbl_user
    </select>
 
</mapper>

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<User> users = userMapper.queryUser();
        System.out.println(JSON.toJSONString(users));
    }
 
}
---------------------------------
..consule print..
[{"password":"password","username":"xiongxin"}]

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

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

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

3.原理解析

绝对干货,超全的 MyBatis 动态代理原理讲解!

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

绝对干货,超全的 MyBatis 动态代理原理讲解!

使用了JDK动态代理的功能

绝对干货,超全的 MyBatis 动态代理原理讲解!

代理类执行方法调用

绝对干货,超全的 MyBatis 动态代理原理讲解!

方法调用中执行MethodInvoker

绝对干货,超全的 MyBatis 动态代理原理讲解!

最终执行execue方法。

绝对干货,超全的 MyBatis 动态代理原理讲解!

获取返回结果Result

4.手撕框架

前置知识:

绝对干货,超全的 MyBatis 动态代理原理讲解!

源码:

<dependencies>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.74</version>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.4.199</version>
    </dependency>
</dependencies>
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 <T>
     * @return
     */

    public static <T> getMapper(Class<T> 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 <T>
     */

    public interface ResultMap<T{
        convert(ResultSet resultSet) throws Exception;
    }
 
 
    /**
     * 创建连接并执行
     *
     * @param sql
     * @param resultMap
     * @param <T>
     * @return
     * @throws Exception
     */

    public static <T> List<T> queryList(String sql, ResultMap<T> resultMap) throws Exception {
        //jdbc数据库
        Connection conn = getConnH2();
        //3.通过数据库的连接操作数据库,实现增删改查(使用Statement类)
        Statement st = conn.createStatement();
        ResultSet rs = st.executeQuery(sql);
        List<T> 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 <T>
     * @return
     * @throws Exception
     */

    public static <T> List<T> queryList(String sql, Class<T> returnType) throws Exception {
        List<T> 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(01).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(01).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(11NOT NULL AUTO_INCREMENT,
  username varchar(255DEFAULT NULL,
  password varchar(255DEFAULT NULL,
  PRIMARY KEY (id)
);
 
insert into user(id,username,passwordvalues(1,'xiongxina','123456');
insert into user(id,username,passwordvalues(2,'xiongxinb','123456');
insert into user(id,username,passwordvalues(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<UserEntity> 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<UserEntity> userEntities = userMapper.queryUser();
        for (UserEntity userEntity : userEntities) {
            System.out.println(userEntity);
        }
    }
}

来源:blog.csdn.net/tsxiong123/article/

details/119057825

推荐

Java面试题宝典

技术内卷群,一起来学习!!

绝对干货,超全的 MyBatis 动态代理原理讲解!

PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。“在看”支持我们吧!

原文始发于微信公众号(Java知音):绝对干货,超全的 MyBatis 动态代理原理讲解!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/61641.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!