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

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

文章目录

  • 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 mapperPUBLIC "-//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 的 sqlSessionSqlSession 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 {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String sql = method.getAnnotation(QueryList.class).value();Class<?> returnType = method.getReturnType();//返回类型为Listif (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.rhkb.cn/news/205243.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Golang中rune和Byte,字符和字符串有什么不一样

Rune和Byte&#xff0c;字符和字符串有什么不一样 String Go语言中&#xff0c; string 就是只读的采用 utf8 编码的字节切片(slice) 因此用 len 函数获取到的长度并不是字符个数&#xff0c;而是字节个数。 for循环遍历输出的也是各个字节。 Rune rune 是 int32 …

在微服务架构中的数据一致性

当从传统的单体应用架构转移到微服务架构时&#xff0c;特别是涉及数据一致性时&#xff0c;数据一致性是微服务架构中最困难的部分。传统的单体应用中&#xff0c;一个共享的关系型数据库负责处理数据一致性。在微服务架构中&#xff0c;如果使用“每个服务一个数据库”的模式…

Ros报错:The Plugin for class ‘jsk_rviz_plugin/Plotter2D‘ failed to load

一般出现这种情况&#xff0c;是提醒Ros缺少某种库&#xff1a; 图中显示的错误是说明少了jsk_rviz_plugins库&#xff0c;他是一个提供原始rviz插件的包。 解决办法是安装相应的库与插件&#xff1a; #根据自己ROS的版本选择相应的指令 # ubuntu20.04:noetic sudo apt-get i…

幼教智能时代精英论坛北京举行

中国日报11月29日电 近日&#xff0c;智能时代赢之道—2023幼教智能时代精英论坛在北京泰山饭店成功举办&#xff0c;来自全国各地的近百位幼儿园园长、幼教老师集聚一堂&#xff0c;探索智能时代幼教的智慧化解决方案。 伴随“教育数字化战略行动”的深入开展&#xff0c;智慧…

Linux高级IO

文章目录 一.IO的基本概念二.钓鱼五人组三.五种IO模型四.高级IO重要概念1.同步通信 VS 异步通信2.阻塞 VS 非阻塞 五.其他高级IO六.阻塞IO七.非阻塞IO 一.IO的基本概念 什么是IO&#xff1f; I/O&#xff08;input/output&#xff09;也就是输入和输出&#xff0c;在著名的冯诺…

python爱心代码高级

在Python中&#xff0c;我们可以使用matplotlib库来创建一个更高级的爱心图形。以下是一个示例&#xff1a; import matplotlib.pyplot as pltimport numpy as npx np.linspace(-2, 2, 1000)y1 np.sqrt(1-(abs(x)-1)**2)y2 -3*np.sqrt(1-(abs(x)/2)**0.5)fig, ax plt.subp…

树与二叉树堆:链式二叉树的实现

目录 链式二叉树的实现&#xff1a; 前提须知&#xff1a; 前序&#xff1a; 中序&#xff1a; 后序&#xff1a; 链式二叉树的构建&#xff1a; 定义结构体&#xff1a; 初始化&#xff1a; 构建左右子树的指针指向&#xff1a; 前序遍历的实现&#xff1a; 中序…

Android String.xml 设置加粗字体/修改字体颜色/动态设置修改文案

之前经常使用Spannable 这次主要在String.xml使用&#xff1a;<![CDATA[和]]> 效果&#xff1a; <resources><string name"str_bianse"><![CDATA[变色 <font color"#ff0000">曲项向天歌</font> 白毛浮绿水]]></st…

Leetcode—266.回文排列【简单】Plus

2023每日刷题&#xff08;四十&#xff09; Leetcode—266.回文排列 C语言实现代码 char chara[26] {0};int calculate(char *arr) {int nums 0;for(int i 0; i < 26; i) {nums arr[i];}return nums; }bool canPermutePalindrome(char* s) {int len strlen(s);for(in…

InstructDiffusion-多种视觉任务统一框架

论文:《InstructDiffusion: A Generalist Modeling Interface for Vision Tasks》 github&#xff1a;https://github.com/cientgu/InstructDiffusion InstructPix2Pix&#xff1a;参考 文章目录 摘要引言算法视觉任务统一引导训练集重构统一框架 实验训练集关键点检测分割图像…

Vue3的项目创建到启动

Vue3的项目创建 检查node版本创建 npm init vuelatest 安装依赖 项目启动 启动成功

OpenAI再次与Sam Altman谈判;ChatGPT Voice正式上线

11月22日&#xff0c;金融时报消息&#xff0c;OpenAI迫于超过700名员工联名信的压力&#xff0c;再次启动了与Sam Altman的谈判&#xff0c;希望他回归董事会。 在Sam确定加入微软后&#xff0c;OpenAI超700名员工签署了一封联名信&#xff0c;要求Sam和Greg Brockman&#x…

Spring---对象的存储和读取

文章目录 Spring对象的存储创建Bean对象将Bean对象存储到spring中添加配置文件存储Bean对象 Spring对象的读取得到Spring上下文对象从Spring中取出Bean对象使用Bean对象 Spring对象的存储 创建Bean对象 Bean对象其实就是一个普通的Java对象。我们按照创建Java对象的方式来创建…

【云原生】什么是 Kubernetes ?

什么是 Kubernetes &#xff1f; Kubernetes 是一个开源容器编排平台&#xff0c;管理着一系列的 主机 或者 服务器&#xff0c;它们被称作是 节点&#xff08;Node&#xff09;。 每一个节点运行了若干个相互独立的 Pod。 Pod 是 Kubernetes 中可以部署的 最小执行单元&#x…

USB驱动开发基础

USB标准 USB1.0&#xff0c; 1996&#xff0c;低速1.5Mbps和高速12Mbps&#xff0c;USB1.1 iMac G3&#xff0c;Type A和Type B接口USB 2.0 2000&#xff0c; 480Mpbs&#xff0c;Type A/B/C接口、Micro A/BUSB 3.0 5Gbps, 随着USB 3.2命名规定&#xff0c;现在也叫USB 3.2 Ge…

Linux网络——数据链路层

目录 一.认识以太网 二.以太网帧格式 三.认识MAC地址 四.认识MTU 五.以太局域网的通信原理 六.其他重要协议 1.DNS协议 2.域名简介 3.ICMP协议 4.NAT技术 5.NAT技术的缺陷 6.NAT和代理服务器 一.认识以太网 "以太网" 不是一种具体的网络, 而是一种技术标…

docker (简介、dcoker详细安装步骤、容器常用命令)一站打包- day01

一、 为什么出现 Docker是基于Go语言实现的云开源项目。 Docker的主要目标是“Build&#xff0c;Ship and Run Any App,Anywhere”&#xff0c;也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理&#xff0c;使用户的APP&#xff08;可以是一个WEB应用或数据库应…

数据库的重要你了解多少?如何保障数据库的安全?

随着信息技术的快速发展&#xff0c;数据库已经成为企业、组织以及个人日常生活中不可或缺的一部分。然而&#xff0c;随着数据库的广泛应用&#xff0c;其安全性问题也日益凸显。数据库的安全性主要包括数据的完整性、保密性和可用性。本文将探讨数据库安全性的重要性、以及如…

常使用的定时任务

常使用的定时任务 一、 linux自带的定时任务 1、crontab 有这样一个需求&#xff1a;我们使用Java写一个工具jar包在系统空闲的时候去采集已经部署在Linux系统上的项目的一 些数据&#xff0c;可以使用 linux 系统的 crontab。 运行crontab -e&#xff0c;可以编辑定时器&…

C语言——深入理解指针(3)

目录 1. 字符指针 2. 数组指针 2.1 数组指针变量 2.2 数组指针变量的初始化 3.二维数组传参&#xff08;本质&#xff09; 4. 函数指针 4.1 函数指针变量的创建 4.2 函数指针的使用 4.3 typedef 5. 函数指针数组 6. 转移表&#xff08;函数指针数组的使用&#xff…