面试题:说一下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.hqwc.cn/news/234070.html

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

相关文章

机器学习入门(第四天)——朴素贝叶斯

知识树 Knowledge tree P(y|x)&#xff0c;P给定x的条件下&#xff0c;y的概率。如&#xff1a;P(y我招女孩子喜欢的概率|我是学生) 一个小故事 A story 女朋友和妈妈掉河里&#xff0c;路人拿出3颗豆&#xff0c;两颗红豆1颗绿豆。如果我抽中红豆救女朋友&#xff0c;抽中绿…

安防监控系统的工作原理是什么?具体包含哪些组成部分?

关于安防监控系统&#xff0c;大家熟知的就是监控系统平台&#xff0c;其实不然&#xff0c;智能视频安防监控系统涵盖的内容非常多&#xff0c;今天小编就和大家一起来探讨一下。 安防监控视频系统主要分为以下7大类&#xff1a; 1、 摄像头采集图像 安防监控系统通常使用摄…

python 交互模式和命令行模式的问题

python 模式的冲突 unexpected character after line continuation character 理论上 ide里&#xff0c;输入 python 文件路径\文件.py 就可以执行 但是有时候却报错 unexpected character after line continuation character 出现上述错误的原因是没有退出解释器&#x…

采购业务中的组织概述

目录 一、采购和库存管理中组织单位的概览二、企业的组织结构三、采购中组织结构3.1采购组织3.2采购组 一、采购和库存管理中组织单位的概览 1、 客户端&#xff1a;在SAP ERP系统中&#xff0c;客户端通过三位数字定义&#xff0c;并代表这独立的数据记录和独立的业务流程。客…

Vue3 刷新后,pinia存储的数据丢失怎么解决

这个问题有两种解决办法&#xff1a; 一是使用pinia的持久化存储一是使用vue的依赖注入 刷新后&#xff0c;通过pinia存储的vue store数据丢失&#xff0c;实际上是因为Vue原组件卸载、新组件重新挂载导致的&#xff0c;vue store是挂载在组件上的&#xff0c;当刷新导致组件…

算法通关村第十八关青铜挑战——透析回溯的模板

大家好&#xff0c;我是怒码少年小码。 回溯是最重要的算法思想之一&#xff0c;主要解决一些暴力枚举也搞不定的问题&#xff08;组合、子集、分割、排列、棋盘等等&#xff09;。性能并不高&#xff0c;但是哪些暴力枚举都无法ko的问题能解出来就可以了&#x1f923;。 这一…

实战Flask+BootstrapTable后端传javascript脚本给前端实现多行编辑(ajax方式)

相信看到此文的朋友们一定会感到庆幸,总之我是用了两天死磕,才得到如下结果,且行且珍惜,祝好各位! 话不多说,有图有源码 1.看图 2.前端实现页面 <!DOCTYPE html> {% from "common/_macro.html" import static %} <html> <meta charset"utf-8&…

台灯到底对眼睛好不好?推荐高品质的护眼台灯

其实只要我们挑选一盏专业的台灯&#xff0c;并且正确的使用&#xff0c;那么台灯对眼睛是有很大的好处的&#xff01;如今夜间工作、学习已然成为了再常见不过的事情&#xff0c;在夜间最大的痛点就是光照不足&#xff0c;如果单靠室内灯是远远不足的&#xff0c;而且光线的分…

超融合可以“既要又要还要”吗?青云云易捷给出新选择

科技云报道原创。 超融合作为一种云时代的IT基础架构&#xff0c;诞生已有十余年&#xff0c;如今已是一种非常成熟且主流的应用。 多年的技术发展和市场需求的快速增长&#xff0c;让超融合成了一个非常“卷”的市场&#xff0c;云服务商、HCI创业公司、综合IT供应商&#x…

Java 数据结构篇-用链表、数组实现栈

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 栈的说明 2.0 用链表来实现栈 2.1 实现栈 - 入栈方法&#xff08;push&#xff09; 2.2 实现栈 - 出栈&#xff08;pop&#xff09; 2.3 实现栈 - 查看栈顶元素…

【JavaWeb】会话过滤器监听器

会话&过滤器&监听器 文章目录 会话&过滤器&监听器一、会话1.1 Cookie1.2 Session1.3 三大域对象 二、过滤器三、监听器3.1 application域监听器3.2 session域监听器3.3 request域监听器3.4 session域的两个特殊监听器3.4.1 session绑定监听器3.4.2 钝化活化监听…

MSUSB30模拟开关可Pin to Pin兼容FSUSB30/SGM7222

MSUSB30/MSUSB30N 是一款高速、低功耗双刀双掷 USB 模拟开关芯片&#xff0c;其工作电压范围是1.8V 至5.5V。可Pin to Pin兼容FSUSB30/SGM7222。其具有低码间偏移、高通道噪声隔离度、宽带宽的特性。 MSUSB30/MSUSB30N 主要应用范围包括&#xff1a;具有 USB2.0 接口的手持设备…