Mybatis操作数据库的两种方式:Mapper代理模式

1.Mapper代理模式的特点

程序员没有写接口的子实现——直接获取数据库的数据

因为Mybatis定义了一套规则,对方法进行了实现,程序员只要遵循这套方法就可以直接使用

2.如何实现Mapper代理模式

步骤:

1.创建一个dao接口,在接口中写增删改查的方法

2.创建一个子清单文件,且子清单文件中的命名空间必须namespace="包名.接口名"

3.在子清单文件中:select\insert\update\delete 节点id为接口中的方法名称

        方法的参数对应parameterType,返回值对应resultType

4.在总清单文件引入子清单文件

5.在需要的地方:接口类型 jdk动态代理对象=sqlSqssion.getMapper(接口类型.class)

       返回类型 返回类型对象 jdk动态代理对象.调用目标方法();

程序员没有写接口子实现,就能获得数据库数据

configruation.xml:

 设置类别名,方便书写:

    <typeAliases><!--设置别名--><typeAlias type="org.example.entity.User" alias="User"/></typeAliases>

注意添加子清单文件 

 <mapper resource="mapper/userMapper.xml"/>

增删改实现 

xxMapper.java

    public int addUser(User user);public int deleteUser(Integer id);public int updateUser(User user);

userMapper.xml

<mapper namespace="org.example.dao.UserMapper"><!--插入用户--><insert id="addUser"parameterType="User">insert into t_user(user_name,user_password,address)values(#{name},#{password},#{address});</insert><!--删除用户--><delete id="deleteUser"parameterType="java.lang.Integer">delete from t_userwhere id=#{id};</delete><!--根据id更新用户--><update id="updateUser"parameterType="org.example.entity.User">update t_user setuser_name=#{name},user_password=#{password},address=#{address}whereid=#{id}</update>
</mapper>

单元测试:

@Testpublic void addUser() {//假数据User user=new User();user.setName("add");user.setPassword("654321");user.setAddress("测试用例|mapper.addUser()");Integer rowAffect=0;SqlSession sqlSession=null;try{sqlSession= MybatisUtil.getSession();//接口类型, jdk动态代理对象=sqlSession.getMapper();UserMapper userMapper=sqlSession.getMapper(UserMapper.class);rowAffect= userMapper.addUser(user);sqlSession.commit();}catch (Exception e){e.printStackTrace();sqlSession.rollback();}finally {if(sqlSession!=null){sqlSession.close();}}System.out.println(rowAffect);}@Testpublic void deleteUser() {Integer rowAffect=0;SqlSession sqlSession=null;try{sqlSession= MybatisUtil.getSession();//接口类型, jdk动态代理对象=sqlSession.getMapper();UserMapper userMapper=sqlSession.getMapper(UserMapper.class);rowAffect= userMapper.deleteUser(9);sqlSession.commit();}catch (Exception e){e.printStackTrace();sqlSession.rollback();}finally {if(sqlSession!=null){sqlSession.close();}}System.out.println(rowAffect);}@Testpublic void updateUser() {//假数据User user=new User();user.setId(9);user.setName("update");user.setPassword("654321");user.setAddress("测试用例|mapper.updateUser()");Integer rowAffect=0;SqlSession sqlSession=null;try{sqlSession= MybatisUtil.getSession();//接口类型, jdk动态代理对象=sqlSession.getMapper();UserMapper userMapper=sqlSession.getMapper(UserMapper.class);rowAffect= userMapper.updateUser(user);sqlSession.commit();}catch (Exception e){e.printStackTrace();sqlSession.rollback();}finally {if(sqlSession!=null){sqlSession.close();}}System.out.println(rowAffect);}

 效果展示:

addUser

updateUser 

 deleteUser

总结:

mybatis 的 mapper代理模式和原生api相比,可以不写接口的具体实现类。

mapper模式是纯接口调用,因为有接口的存在,可以使用jdk动态代理为其动态生成实现类。

jdk生成的实现类和接口之间是实现和被实现的关系,

jdk生成的实现类是实现类,也是代理类,其创建动态代理对象,通过代理对象.目标方法的方式,即invacationHandler调用invoke(),来进行实现。

查询:根据id获取用户信息

xxMapper.java

  public User findUserById(Integer id);

userMapper.xml

    <!--根据id获得用户信息--><select id="findUserById"resultType="org.example.entity.User"parameterType="java.lang.Integer">selectid,user_name as name,user_password as password,addressfrom t_userwhere id = #{id}</select>

单元测试:

    @Testpublic void findUserById() {User user=null;SqlSession sqlSession=null;try{sqlSession= MybatisUtil.getSession();//接口类型, jdk动态代理对象=sqlSession.getMapper();UserMapper userMapper=sqlSession.getMapper(UserMapper.class);user= userMapper.findUserById(1);sqlSession.commit();}catch (Exception e){e.printStackTrace();sqlSession.rollback();}finally {if(sqlSession!=null){sqlSession.close();}}System.out.println(user);}

结果展示:

查询:根查所有用户List

xxMapper.java

    //返回list结构public List<User> findAllUser1();public List<Map<String,Object>> findAllUser2();

userMapper.xml

<!--查所有返回list<User>--><select id="findAllUser1"resultType="User">selectid,user_name as name,user_password as password,addressfrom t_user</select><!--查所有返回list<Map>--><select id="findAllUser2"resultType="java.util.Map">selectid,user_name,user_password,addressfrom t_user</select>

单元测试:

@Testpublic void findAllUser1() {List<User> userList=new ArrayList<User>();SqlSession sqlSession=null;try{sqlSession= MybatisUtil.getSession();//接口类型, jdk动态代理对象=sqlSession.getMapper();UserMapper userMapper=sqlSession.getMapper(UserMapper.class);userList= userMapper.findAllUser1();sqlSession.commit();}catch (Exception e){e.printStackTrace();sqlSession.rollback();}finally {if(sqlSession!=null){sqlSession.close();}}for(User user:userList){System.out.println(user);}}@Testpublic void findAllUser2() {List<Map<String,Object>> userList=new ArrayList<Map<String, Object>>();SqlSession sqlSession=null;try{sqlSession= MybatisUtil.getSession();//接口类型, jdk动态代理对象=sqlSession.getMapper();UserMapper userMapper=sqlSession.getMapper(UserMapper.class);userList= userMapper.findAllUser2();sqlSession.commit();}catch (Exception e){e.printStackTrace();sqlSession.rollback();}finally {if(sqlSession!=null){sqlSession.close();}}for(Map<String,Object> user:userList){System.out.println(user);}}

结果展示:

 

查询:根查所有用户Map

xxMapper.java

    //返回map结构@MapKey("id")public Map<Integer,User> findAllUser3();@MapKey("id")public Map<Integer,Map<String,Object>> findAllUser4();

userMapper.xml

    <!--查所有返回Map<User>--><select id="findAllUser3"resultType="User">selectid,user_name as name,user_password as password,addressfrom t_user</select><!--查所有返回Map<Map>--><select id="findAllUser4"resultType="java.util.Map">selectid,user_name,user_password,addressfrom t_user</select>

单元测试:

    @Testpublic void findAllUser3() {Map<Integer,User> userMap= new HashMap<Integer, User>();SqlSession sqlSession=null;try{sqlSession= MybatisUtil.getSession();//接口类型, jdk动态代理对象=sqlSession.getMapper();UserMapper userMapper=sqlSession.getMapper(UserMapper.class);userMap= userMapper.findAllUser3();sqlSession.commit();}catch (Exception e){e.printStackTrace();sqlSession.rollback();}finally {if(sqlSession!=null){sqlSession.close();}}for(Integer key:userMap.keySet()){System.out.println(key+" "+userMap.get(key));}}@Testpublic void findAllUser4() {Map<Integer,Map<String,Object>> userMap= new HashMap<Integer, Map<String,Object>>();SqlSession sqlSession=null;try{sqlSession= MybatisUtil.getSession();//接口类型, jdk动态代理对象=sqlSession.getMapper();UserMapper userMapper=sqlSession.getMapper(UserMapper.class);userMap= userMapper.findAllUser4();sqlSession.commit();}catch (Exception e){e.printStackTrace();sqlSession.rollback();}finally {if(sqlSession!=null){sqlSession.close();}}for(Integer key:userMap.keySet()){System.out.println(key+" "+userMap.get(key));}}

结果展示:

3.JDKProxy模拟Mapper代理实现

Mybatis的Mapper代理模式,可以通过接口使用jdk的代理机制实现自动代理。(jdk的动态代理是基于接口的,而此处的xxMapper接口即为被代理的对象接口)

        本质上xxMapper并没有实现类,jdk的动态代理也没有对所谓的老方法进行代理。

程序员不需要写接口的具体实现,只要根据规则定义好接口,即可使用相关功能。

我们使用JDKProxy类模拟Mybatis实现getMapper()的方法,理解其中的内部实现。

package org.example.proxy;import org.example.proxy.handler.MapperHandler;import java.lang.reflect.Proxy;public class JDKProxy {/*** 根据接口获取代理对象* @param clazz* @return*/public static Object getMapper(Class clazz){Object proxyObject=null;/*** 参数一: 类加载器* 参数二: 接口数组* 参数三: InvocationHandler 接口的回调*/proxyObject= Proxy.newProxyInstance(clazz.getClassLoader(),new Class[]{clazz},new MapperHandler());return proxyObject;}}

这里虽然使用了动态代理,但是并不是动态代理的常规用法,这里只有代理对象的接口,且该接口没有任何实现。动态代理在这里的目的并不是调用老方法做切面设计,而是获取方法信息:参数列表、返回值等,进行判断,根据方法选择调用mybatis的原生api。所以Mapper代理模式底层调用的还是Mybatis的原生api,  其只是通过动态代理实现了一个默认实现版本,来较少程序员的代码量。

关于JDK的第三个参数 InvocationHandler 接口的回调,我们构建实现类MapperHandler,其实现InvocationHandler接口。具体来说就是:

package org.example.proxy.handler;import org.example.entity.User;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class MapperHandler implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object returnValue=null;System.out.println("由Mybatis根据若干信息选择执行对应的api");/*** mybatis 根据method能得到目标方法的返回值,方法名,参数类型* if判断返回的数据为一个值,则调用mybatis的原生api  selectOne方法* if判断返回的数据为多个值时,看返回类型是否为List,则调用mybatis的原生 selectList()方法* if判断返回的数据为多个值时,看返回类型是否为Map,则调用mybatis的原生 selectMap()方法* if判断为insert/delete/update时,调用原生api  insert()  delete()  update()*///eg:执行selectOne()User user=new User();user.setId(1);user.setName("aaa");user.setPassword("123456");user.setAddress("诺亚方舟");returnValue=user;return returnValue;}
}

 使用单元测试来对方法进行测试:

package org.example.proxy;import org.example.dao.UserMapper;
import org.example.entity.User;
import org.junit.Test;import static org.junit.Assert.*;public class JDKProxyTest {@Testpublic void getMapper() {//接口类型 jdk动态代理对象=sqlSession.getMapper(接口类型.class);UserMapper userMapper=(UserMapper) JDKProxy.getMapper(UserMapper.class);User user=userMapper.findUserById(1);System.out.println(user);}
}

值得注意的是,getMapper中我们并没有写具体的实现逻辑,因为测试的是findUserById()方法,我们在getMapper()中返回了一个假数据,用来做测试。而Mybatis则根据逻辑写了具体的实现。 

4.Mybatis 中getMapper的实现

重点:模拟getMapper的实现

public Object execute(SqlSession sqlSession, Object[] args) {Object param;Object result;switch (this.command.getType()) {case INSERT:param = this.method.convertArgsToSqlCommandParam(args);result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));break;case UPDATE:param = this.method.convertArgsToSqlCommandParam(args);result = this.rowCountResult(sqlSession.update(this.command.getName(), param));break;case DELETE:param = this.method.convertArgsToSqlCommandParam(args);result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));break;case SELECT:if (this.method.returnsVoid() && this.method.hasResultHandler()) {this.executeWithResultHandler(sqlSession, args);result = null;} else if (this.method.returnsMany()) {result = this.executeForMany(sqlSession, args);} else if (this.method.returnsMap()) {result = this.executeForMap(sqlSession, args);} else if (this.method.returnsCursor()) {result = this.executeForCursor(sqlSession, args);} else {param = this.method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(this.command.getName(), param);}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + this.command.getName());}if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");} else {return result;}}

基本流程:getMapper()接口——>创建Proxy对象——>execute()实现具体实现   

特别的关于select()方法

其中method.hasResultHandler()来实现自定义的方法处理

还可以匹配不同的返回类型,其中游标模式Cursor不怎么用了

5.总结

接口定义:userDao 和UserMapper一样的写法不同,用来定义方法接口

        其可以自己写实现类也可以通过mapper() 方式来操作数据

总清单文件:

        数据库配置

        子清单文件

                模板统一

                原生写法的子清单文件 namespace和增删改查节点信息任意配置

                mapper写法的子清单 namespace="包名.接口"

                        接口方法名和节点id一致

                        接口方法参数和节点parameterType一样

                        接口的返回值跟节点resultType一样

mybatis自己的api解析xml来构建sqlSessionFactory,用于生产SqlSession

使用sqlSession来做增删改查

mapper接口方法底层还是原生api  api结合jdk动态代理

原生api

  • insert() delete() update()
  • selectOne()  selectList() selectMap()
  • select() 自定义返回的数据结构(策略+回调)
  • 返回结果类型丰富

6.设计模式:

代理模式:MapperProxy实现jdk动态代理

7.补充

什么是面向接口编程?

如select面向接口、jdbc面向接口和spring面向接口都有相关的面向对象接口编程,其目的是实现解耦操作

  • 面向接口编程是一种编程范式,它强调的是在设计软件应用时,应该先定义接口,然后再实现接口。这种方式有很多优点,包括提高代码的可读性、可维护性和可扩展性,以及降低代码之间的耦合度。
  • 接口是一种契约,它定义了一组方法,这些方法应该在实现接口的类中实现。接口本身并不包含任何实现细节,它只是定义了一种规范,规定了实现接口的类应该做什么,而不是怎么做。

摘自:《17.Spring 面向接口编程 - 知乎》

select()也是面向接口编程提供select接口但没有实现,其具体的实现在handler中去处理。实现了接口和实现的分离。


 

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

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

相关文章

java项目之英语知识应用网站源码(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的英语知识应用网站。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 英语知识应用网站的主要…

【免费】AME最新Adobe Media Encoder电脑软件安装包2024-2018支持WIN和MAC

Adobe MediaEncoder「Me」2024是一款功能强大的转码和媒体处理软件&#xff0c;它不仅能轻松应对各种媒体文件的编码和导出需求&#xff0c;还支持多种视频格式和分辨率&#xff0c;让你的视频处理变得更加高效。此外&#xff0c;该软件界面简洁明了&#xff0c;操作简便&#…

【一步一步了解Java系列】:了解Java与C语言的运算符的“大同小异”

看到这句话的时候证明&#xff1a;此刻你我都在努力~ 加油陌生人~ 个人主页&#xff1a; Gu Gu Study ​​ 专栏&#xff1a;一步一步了解Java 喜欢的一句话&#xff1a; 常常会回顾努力的自己&#xff0c;所以要为自己的努…

【Element-UI快速入门】

文章目录 **Element-UI快速入门****一、Element-UI简介****二、安装Element-UI****三、引入Element-UI****四、使用Element-UI组件****五、自定义Element-UI组件样式****六、Element-UI布局组件****七、Element-UI表单组件****八、插槽&#xff08;Slots&#xff09;和主题定制…

【数据结构】排序(一)—— 希尔排序(思路演进版)

目录 一、常见的排序算法分类 二、常见排序算法的实现 2.1插入排序 2.1.1基本思想 2.1.2直接插入排序 思路 step1.单趟控制 step2.总体控制 代码实现 测试 特性总结 2.1.3 希尔排序( 缩小增量排序 ) 基本思想 思路演进 &#x1f308;1.代码实现单组排序&#…

端午节线上活动方案怎么写?

一年一端午&#xff0c;一岁一安康。 如果您想组织端午活动&#xff0c;却不知道如何安排&#xff0c;可以看看何策网&#xff0c;有很多案例参考&#xff0c;仿造模板修改即可。 下面分享一个线上端午节活动策划方案&#xff0c;希望能帮到你&#xff01; 端午节作为祭祖祈…

mysql集群NDBcluster引擎在写入数据时报错 (1114, “The table ‘ads‘ is full“)

问题描述&#xff1a;mysql集群在写入数据时&#xff0c;出现上述报错 问题原因&#xff1a;表数据已满&#xff0c;一般是在集群的管理节点设置里面datamemory的值太小&#xff0c;当数据量超过该值时就会出现该问题 解决方案&#xff1a; 修改集群管理节点的config.ini里面…

ICode国际青少年编程竞赛- Python-4级训练场-嵌套for循环练习2

ICode国际青少年编程竞赛- Python-4级训练场-嵌套for循环练习2 1、 for i in range(3):Dev.turnRight()for j in range(3):Dev.step(-3)Dev.turnRight()Dev.step(4-2*i)2、 for i in range(6):for j in range(2):Dev.step(2 2 * i)if i > 3: Dev.step(i - 2)Dev.turnRi…

C++小程序:同一路由器下两台计算机间简单通信(2/2)——客户端

客户端的程序结构前半部分与服务器端基本相同&#xff0c;后半部分也相对简单。相关函数的解释可以参考前文服务器端的内容。有关客户端的内容除个别地方外&#xff0c;就不再做长篇大论的解释。强调一点&#xff0c;如果将此程序移到其它电脑上运行&#xff0c;编译需要releas…

Ciphey无法安装的解决办法

安装过程纯属自己实践&#xff0c;满满干货 困扰我几天的问题终于解决了 我看着教程在window上安装 python3.8/python3.9/python3.10无论如何都安装不上&#xff0c; 在win10虚拟机仍然安装不上 可能是我电脑环境问题 解决办法&#xff1a; 在kali中安装&#xff0c;但是…

第13节 第二种shellcode编写实战(2)

在第二种shellcode编写实战(1)的基础上&#xff0c;新增加一个CAPI类&#xff0c;将所有用到的函数都在这个类中做动态调用的处理&#xff0c;这样使得整个shellcode功能结构更加清晰。 1. 新建类CAPI&#xff08;即api.h和api.cpp两个文件&#xff09;&#xff1a; api.h&…

flutter自定义日期选择器按日、按月、自定义开始、结束时间

导入包flutter_datetime_picker: 1.5.0 封装 import package:atui/jade/utils/JadeColors.dart; import package:flutter/cupertino.dart; import package:flutter/material.dart; import package:flutter_datetime_picker/flutter_datetime_picker.dart; import package:flut…

景源畅信电商:经营抖店需要电脑吗?

经营抖店是否需要电脑?这个问题看似简单&#xff0c;实则关乎着商家的运营效率和成本投入。在当前数字化、网络化的商业环境中&#xff0c;电脑已经成为了不可或缺的工具。那么&#xff0c;经营抖店究竟是否需要电脑呢?答案是肯定的。 一、高效处理订单 电脑能够高效地处理大…

如何在湖师大官网找到考研真题

今天学弟问我怎么找真题&#xff0c;我必须告诉他怎么找湖师大的真题&#xff0c;身为考研学子&#xff0c;这是必须要知道滴&#xff0c;尤其是自命题&#xff0c;是吧&#xff0c;话不多说&#xff0c;言归正传&#xff0c;我们开始吧&#xff01; 1 打开湖师大官网 什么&a…

树莓派nmap扫描

debian系统安装nmap&#xff1a; sudo apt install nmap安装nmap完成后&#xff0c;输入 ip route 来查看当前Wi-Fi路由器的ip地址。 第一行的default via后显示的便是网关地址&#xff0c;也就是路由器地址。 获取到路由器ip地址后&#xff0c;在终端中输入&#xff1a; …

一站式HMI软件开发套件eStation,让开发更简单高效

4月份举办的北京国际车展上全球首发车117辆&#xff0c;新能源车型278个&#xff0c;越来越多的车厂通过差异化和改善UI/UE体验&#xff0c;来获取更多用户的青睐。为快速响应差异化竞争需求&#xff0c;智能座舱HMI市场遇到以下挑战&#xff1a; 如何兼容不同项目开发人员编程…

力扣例题(用栈实现队列)

目录 链接. - 力扣&#xff08;LeetCode&#xff09; 描述 思路 push pop peek empty 代码 链接. - 力扣&#xff08;LeetCode&#xff09; 描述 思路 push 例如我们将10个元素放入栈中&#xff0c;假设最左边为栈顶&#xff0c;最右侧为栈底 则为10,9,8,7,6,5,4,3,…

嵌入式 - GPIO编程简介

An Introduction to GPIO Programming By Jeff Tranter Wednesday, June 12, 2019 编者按&#xff1a;本 2019 年博客系列是 ICS 最受欢迎的系列之一&#xff0c;现已更新&#xff08;2022 年 12 月&#xff09;&#xff0c;以确保内容仍然准确、相关和有用。 本博客是 Integr…

图片转word如何转换?

要将图片转换为Word文档&#xff0c;你可以使用以下方法之一&#xff1a; 以上这些方法都可以帮助你将图片中的文本转换为可编辑的Word文档&#xff0c;你可以根据自己的喜好和需求选择其中一种方法来操作。 使用OCR软件或在线工具&#xff1a;有许多OCR&#xff08;Optical Ch…

电子资源|基于SSM+vue的电子资源管理系统(源码+数据库+文档)​

电子资源管理系统 目录 基于SSMvue的电子资源管理系统 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2管理员功能模块 5.2.1管理员功能模块 5.2.2用户功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&am…