面试题:说一下MyBatis动态代理原理?

news2024/9/26 18:59:48

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 1.MyBatis简介
  • 2.使用步骤
    • 2.1、引入依赖
    • 2.2、配置文件
    • 2.3、接口定义
    • 2.4、加载执行
  • 3.原理解析


1.MyBatis简介

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

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

2.使用步骤

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.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>

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<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>

2.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.原理解析

图片

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

图片

使用了JDK动态代理的功能

图片

代理类执行方法调用

图片

方法调用中执行MethodInvoker

图片

最终执行execue方法。

图片

获取返回结果Result

4.手撕框架
前置知识:

图片

源码:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1262773.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

k8s中pod的hostport端口突然无法访问故障处理

故障背景&#xff1a; 租户告知生产环境的sftp突然无法访问了&#xff0c;登录环境查看sftp服务运行都是正常的&#xff0c;访问sftp的hostport端口确实不通。 故障处理过程 既然访问不通那就先给服务做个全面检查&#xff0c;看看哪里出了问题&#xff0c;看下sftp日志&#…

Java学习路线第二篇:Java Web

这篇则分享Java学习路线第二part&#xff1a;Java Web 恭喜你已经成功追到第二章节啦&#xff0c;要被自己的努力感动到了吧&#xff0c;而这节将承担起学完Java web 的使命&#xff0c;本使命为单向契约&#xff0c;你可选择YES或者选择YES。 HTMLCSSJavaScript(JS) 【动…

Linux—进程状态、僵尸进程、孤独进程、优先级

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、进程状态二、僵尸进程、孤儿进程1、Z(zombie)-僵尸进程2、僵尸进程危害3、孤儿进程 三、进…

DHCP协议及实验omnipeek抓包工具分析 IPv4协议

一 抓包命令 adb shell tcpdump -i wlan0 -w /data/tcpdump.pcap 抓包后截图如下 二 DHCP是什么 2.1 DHCP定义 DHCP( Dynamic Host Configuration Protocol, 动态主机配置协议)定义: 存在于应用层(OSI) 前身是BOOTP(Bootstrap Protocol)协议 是一个使用UDP(User …

【沁恒蓝牙mesh】程序烧录 硬件电路设计与原理

本文基于沁恒CH58X 单片机的程序烧录硬件电路设计原理解释 【沁恒蓝牙mesh】目录 1. 程序烧录方式2. USB 烧录过程描述3. 硬件设计原理图3.1 官方开发板原理图3.2 自开发设计原理图 4. 电容在上电瞬间为什么相当于短路&#xff1f; &#x1f4cb; 个人简介 &#x1f496; 作者简…

关于AM5系列微机保护装置在某产业园配电工程中的应用-安科瑞 蒋静

1 摘要 目前&#xff0c;微机保护装置广泛应用于电力系统中&#xff0c;该类装置能够合理监测电力系统的运行状况&#xff0c;并实时记录电力系统出现故障的位置及性质&#xff0c;从而为故障的快速处理提供合理的参考信息。本文介绍的AM5系列微机保护装置&#xff0c;可以针对…

DC电源模块的散热措施

BOSHIDA DC电源模块的散热措施 DC电源模块的散热措施可以分为以下几种&#xff1a; 1. 增加散热器&#xff1a;在DC电源模块的电路板上增加散热片或散热器&#xff0c;通过增加散热面积和散热能力来提高散热效果。 2. 增加风扇&#xff1a;在散热器的基础上增加风扇&#xff…

竞赛选题 题目:基于机器视觉的图像矫正 (以车牌识别为例) - 图像畸变校正

文章目录 0 简介1 思路简介1.1 车牌定位1.2 畸变校正 2 代码实现2.1 车牌定位2.1.1 通过颜色特征选定可疑区域2.1.2 寻找车牌外围轮廓2.1.3 车牌区域定位 2.2 畸变校正2.2.1 畸变后车牌顶点定位2.2.2 校正 7 最后 0 简介 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享…

修改分区序列号的简单方法!

“我使用的是自己组装的电脑&#xff0c;安装了正版Win10操作系统。但奇怪的是&#xff0c;这台电脑看起来完全正常&#xff0c;但是每次启动时的分区序列号却总是不同。我现在要使用的软件需要依赖分区序列号进行注册&#xff0c;所以这个问题不解决我就没法使用软件。系统是正…

提升企业网络安全的得力助手——EventLog Analyzer网络日志管理

在当今数字化时代&#xff0c;企业的网络安全问题变得尤为重要。为了更好地应对日益增多的威胁和安全漏洞&#xff0c;企业需要一种高效的网络日志管理工具&#xff0c;EventLog Analyzer便是其中一款卓越的解决方案。 EventLog Analyzer EventLog Analyzer是一款综合性的网络…

户外园林气象环境RTU采集主机监测的具体使用

户外园林是人们休闲、娱乐和放松心情的场所&#xff0c;良好的气象环境对于提供舒适的户外体验至关重要。为了有效监测和管理园林的气象环境&#xff0c;户外园林气象环境RTU&#xff08;Remote Terminal Unit&#xff09;采集主机应运而生。本文将详细介绍户外园林气象环境RTU…

ELK高级搜索,深度详解ElasticStack技术栈-上篇

前言 1、黑马视频地址&#xff1a;java中级教程-ELK高级搜索&#xff0c;深度详解ElasticStack技术栈 2、本内容仅用于个人学习笔记&#xff0c;如有侵扰&#xff0c;联系删除 1. 课程简介 1.1 课程内容 ELK是包含但不限于Elasticsearch&#xff08;简称es&#xff09;、Lo…

【领域驱动设计 学习目标及大纲】从CRUD到架构设计

从2018年至今&#xff0c;已工作了5年有余&#xff0c;回望这5年的工作历程&#xff0c;虽然一直在学习、一直在积累&#xff0c;但其实都在术的层面上停留&#xff0c;也就是具体的技术点。这5年多的时间里其实也不是没有窥道的想法&#xff1a; 一次是2018年刚工作的时候&am…

软件提示找不到“vcruntime140.dll丢失的五个解决方法”(有效方法)

“vcruntime140.dll丢失的五个解决方法”。在我们的日常生活和工作中&#xff0c;有时候会遇到一些电脑问题&#xff0c;而vcruntime140.dll丢失就是其中之一。那么&#xff0c;什么是vcruntime140.dll文件呢&#xff1f;它为什么会丢失&#xff1f;又该如何解决这个问题呢&…

【产品设计】SaaS平台产品架构设计

产品架构是基于业务架构的&#xff0c;那么做产品架构前&#xff0c;需要对业务架构有哪些清晰的了解呢&#xff1f; 当我们去搜索“架构”&#xff0c;可以得到很多的架构图片&#xff0c;比如组织架构、业务架构、数据架构、技术架构、安全架构、产品架构、部署架构等。 什么…

计算4人队形的最可能分布

2 2 2 1 2 2 2 2 2 1 2 2 2 2 2 1 2 2 3 3 3 x 3 3 2 2 2 1 2 2 2 2 2 1 2 2 在6*6的平面上2个点随机分布&#xff0c;有3种分布方式&#xff0c;2a1&#xff0c;2a2&#xff0c;2a3&#xff0c;占比为1&#xff1a;5&#xff1a;1. 3 3 …

附录11-math.h的常见方法

stdlib.h是做数学计算的头文件 目录 1 数学知识 1.1 弧度值/π 角度值/180 1.2 双曲函数 2 math.h 2.1 反余弦值 acos() 2.2 反正弦值 asin() 2.3 反正切值 atan() 2.4 两个数的反正切值 atan2() 2.5 向上取整 ceil() 2.6 余弦值 cos() 2.7 双曲余弦 c…

Python能否成为大型游戏开发的利器?

你是否曾想过&#xff0c;Python这个备受欢迎的编程语言是否能够胜任大型游戏开发的重任&#xff1f;Python以其简洁、易学的特点而著称&#xff0c;但在游戏世界中&#xff0c;性能和效率常常是关键。小编将带你深入探讨Python在大型游戏开发中的潜力&#xff0c;一探究竟&…

python 爬虫之 爬取网站信息并保存到文件

文章目录 前期准备探索该网页的HTML码的特点开始编写代码存入文件总的程序文件存储效果 前期准备 随便找个网站进行爬取&#xff0c;这里我选择的是(一个卖书的网站&#xff09; https://www.bookschina.com/24hour/62700000/ 我的目的是爬取这个网站的这个页面的书籍的名称以…

vue3中toRef创建一个ref对象

为源响应式对象上的某个属性创建一个 ref对象, 二者内部操作的是同一个数据值, 更新时二者是同步的 区别ref: 拷贝了一份新的数据值单独操作, 更新时相互不影响 应用: 当要将 某个prop 的 ref 传递给复合函数时&#xff0c;toRef 很有用 父组件代码: <template><…