原生Java使用Mybatis操作数据库接口注解形式,与SpringBoot类似且无需管理SqlSession连接的工具类

news2024/11/17 13:35:02

Hi I’m Shendi



https://sdpro.top/blog/html/article/1044.html



需求描述

用 SpringBoot 整合 Mybatis 使用久了,再编写没有Spring但需要操作数据库的程序时就会想着使用接口+注解的形式,这样效率比较高和简单

Spring 中只需要编写好接口映射,然后在需要使用的地方用 @Autowirted 注解来自动装载,即可直接使用

没有使用Spring时,使用Mybatis需要用 SqlSessionFactory,然后拿到 SqlSession,再处理操作,操作完再把 SqlSession关闭,非常麻烦。

我原以为 SqlSession.getMapper 拿到接口实例可以一直使用,但是发现关闭连接后,实例就不能再使用了,会报错,于是换了种方法实现需求


下面就介绍下Mybatis的使用过程(接口+注解形式,不列举xml形式的了)



依赖引入

需要两个jar包,一个是jdbc驱动,例如mysql就使用mysql-connector-java.jar,可以从网上找到

还用一个是mybatis的jar包,直接去mybatis的github就可以下载

使用Maven的与SpringBoot引入依赖类似,随便百度下就可以找到引入的依赖,需要引入jdbc和mybatis



Mybatis配置文件编写

格式是xml的,可以参考Mybatis的官方文档,是中文的,文件命名可随意,一般为config.xml或者mybatis-config.xml

Mybatis XML配置文件官方文档


配置文件想放哪放哪,但是一般放到src目录下,因为需要找到这个配置文件,Mybatis提供了一个类

MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,使得从类路径或其它位置加载资源文件更加容易。

只要能让程序找到这个文件就可以了(Mybatis提供的Resources类感觉不太好用,只有放到src目录下才能被找到,放到src下的子目录都找不到了…)



我这里把我的配置贴出来了,可以看着更改

<?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>
	<!-- 这里的default指向的是使用的是哪个配置(id) 可以有多个 environment 指向多个数据库 -->
	<environments default="devMySql">
		<environment id="devMySql">
			<!-- 基于jdbc -->
			<transactionManager type="JDBC" />
			<!-- 数据源 配置一些数据库配置 type="POOLED" 使用MyBatis自带的连接池 -->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.cj.jdbc.Driver" />
                <!-- 数据库 -->
				<property name="url" value="jdbc:mysql://localhost:3306/数据库?serverTimezone=UTC&amp;allowMultiQueries=true" />
				<property name="username" value="数据库用户名" />
				<property name="password" value="数据库密码" />
			</dataSource>
		</environment>
	</environments>
	<!-- 使用注解的方式 -->
	<mappers>
		<mapper class='shendi.TestMapper'/>
	</mappers>
</configuration>


从 XML 中构建 SqlSessionFactory

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。

SqlSessionFactory包含上面配置文件的信息,并且后续用到的SqlSession就是从这个类实例拿到的

可以理解为是连接池,SqlSession是连接


构建方法如下(官方示例)

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

上面的resource代表了配置文件的路径(经过实际尝试表示出错)

可以把配置文件放到src文件夹下,然后resource直接为配置文件的文件名



编写Mapper接口

对于Sql语句有三种方式,一种是xml的方式,一种是直接接口+注解的方式,还有一种是接口+xml的方式

对我来说好用的当然是直接接口+注解的方式了,简单高效


你只需要编写一个接口,然后使用注解标注在函数上,就可以让函数代表对应的sql操作了,常用的增删改查注解

  • @Insert
  • @Delete
  • @Update
  • @Select

这里直接贴出代码了,毕竟非常非常非常简单,例如查询用户根据id

public interface TestMapper {
    @Select("SELECT * FROM user WHERE id=${id} LIMIT 1")
    HashMap<String,String> userById(int id);
}

上面只是举个例子,还可以使用Bean的方式来获取返回值,而不是HashMap的方式

甚至还可以 if 判断,for循环,递归查询(@Results)等

具体可以去查阅其他文章,本文重点不在这里



需要注意的是,Java在编译过程中,会把参数名改为 arg0,arg1这种,丢失掉原来名称,而Mybatis是通过反射来找参数的,有两种解决办法

1、给参数加 @Param 注解指定名称

@Select("SELECT * FROM user WHERE id=${id} LIMIT 1")
HashMap<String,String> userById(@Param("id") int id);

2、编译时指定parameter参数

javac -parameters 这样编译,但咱当然不可能直接用命令行,一般都是用IDE,例如Eclipse,IDEA这些

  • IDEA
    • 依次点击 Preferences - Build,Execution,Deployment - Compiler - java Compiler
    • 在Additional command line parameters 输入 -parameters
  • Eclipse
    • Window - prefenrences - java - Compiler
    • 勾选 Strore infomation about method parameters(usable via reflection)

本人更偏向第二种



配置映射

有了 Mapper 就需要配置了,在最开始的配置文件编写的那部分有

<!-- 使用注解的方式 -->
<mappers>
    <mapper class='shendi.TestMapper'/>
</mappers>

其中 class 代表类路径,TestMapper为映射类,多个就配置多条mapper就好了


我在官网还看到一种,可以将整个包的接口都作为映射类的,但是我使用报错

<mappers>
	<package name="shendi"/>
</mappers>


使用

配置,SQL,工厂,都ok了,最后一步就是使用了,因为太简单,这里直接上代码了

// 拿到工厂
String resource = "config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(inputStream);

// 通过工厂获取SqlSession(连接)
SqlSession ss = ssf.openSession();
// 通过SqlSession获取接口映射实例
TestMapper tm = ss.getMapper(TestMapper.class);
// 直接使用接口就可以了
HashMap<String,String> user = tm.userById(1);
// 最后关闭SqlSession连接
ss.close();

当SqlSession关闭后,getMapper拿到的接口实例就不能在使用了,否则直接报错,所以不能作为类成员这样存起来,这样有点不合理,每次都要拿到SqlSession,使用getMapper,用完关闭。

对于用惯了SpringBoot的我来说太麻烦了,SpringBoot的接口Mapper实例只有一个,也不需要关心拿到连接和关闭连接,于是我就封装了个工具类



工具类(无需关心连接,使用更加简单)

使用动态代理实现,核心代码如下图

在这里插入图片描述


先展示如何使用的,像之前获取用户信息只需要一行代码了

HashMap<String,String> user = MapperUtil.TEST.userById(1);


工具类完整代码如下

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

/**
 * 所有的映射对象.
 * <br>
 * @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
 */
public class MapperUtil {

	public static final SqlSessionFactory SSF;
	
    // 映射类,全局放出去供调用
	public static final TestMapper TEST;
	
	static {
		SqlSessionFactory ssf = null;
        // 这个地方是找配置文件,用的自己写的,可以根据自己需求改掉
		try (FileInputStream fi = new FileInputStream(PathFactory.getPath(PathFactory.PROJECT, "/files/mybatis/config.xml"))) {
			ssf = new SqlSessionFactoryBuilder().build(fi);
		} catch (IOException e) {
			e.printStackTrace();
            // 日志,根据自己需求改掉
			Log.printErr("SqlSessionFactory创建失败,程序终止: %s", e.getMessage());
			System.exit(0);
		}
		SSF = ssf;
		ssf = null;
		
        // 拿到接口实例,后面多一个接口就照着多加一行就可以了
		TEST = getMapper(TestMapper.class);
	}
	
    // 动态代理,将接口实例的每个函数都代理了,执行前拿到SqlSession,执行完关闭SqlSession
	private static <T> T getMapper(Class<T> clazz) {
		@SuppressWarnings("unchecked")
		T obj = (T) Proxy.newProxyInstance(MapperUtil.class.getClassLoader(), new Class[] {clazz}, new InvocationHandler() {
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				try (SqlSession ss = SSF.openSession()) {
					T mapper = ss.getMapper(clazz);
					
					Object result = method.invoke(mapper, args);
					
					return result;
				}
			}
		});
		
		return obj;
	}
	
}


补充(重要)

Mybatis的增删改是需要手动调用 SqlSession.commit 才会生效的(事务),所以上面工具类需要稍微改改

如果不commit,不会报错,但是数据库没有数据。

改成如下

private static <T> T getMapper(Class<T> clazz) {
    @SuppressWarnings("unchecked")
    T obj = (T) Proxy.newProxyInstance(MapperUtil.class.getClassLoader(), new Class[] {clazz}, new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            SqlSession ss = SSF.openSession();
            try {
                T mapper = ss.getMapper(clazz);

                Object result = method.invoke(mapper, args);

                return result;
            } finally {
                ss.commit();
                ss.close();
            }
        }
    });

    return obj;
}

具体在哪commit可以自行封装



END

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

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

相关文章

字节跳动正式开源分布式训练调度框架 Primus

动手点关注 干货不迷路 项目地址&#xff1a;https://github.com/bytedance/primus 随着机器学习的发展&#xff0c;模型及训练模型所需的数据量越来越大&#xff0c;也都趋向于通过分布式训练实现。而算法工程师通常需要对这些分布式框架涉及到的底层文件存储和调度系统有较深…

剑指 Offer 52. 两个链表的第一个公共节点 / LeetCode 160. 相交链表(双指针 / 哈希集合)

题目&#xff1a; 链接&#xff1a;剑指 Offer 52. 两个链表的第一个公共节点&#xff1b;LeetCode 160. 相交链表 难度&#xff1a;简单 输入两个链表&#xff0c;找出它们的第一个公共节点。 如下面的两个链表&#xff1a; 在节点 c1 开始相交。 示例 1&#xff1a; 输入…

Spring MVC Bean加载控制

回顾一下我们一般写的项目包括那些包吧&#xff1a; config目录存入的是配置类,写过的配置类有: ServletContainersInitConfigSpringConfigSpringMvcConfigJdbcConfigMybatisConfig controller目录存放的是SpringMVC的controller类service目录存放的是service接口和实现类dao目…

Doo Prime 德璞资本:股指期货交易如何管理好个人情绪

在股指期货交易中&#xff0c;我们可以感觉到心态随着交易的成败而变化。有时心态对交易影响不大&#xff0c;但有时影响很大&#xff0c;一个好的心态&#xff0c;能够应对各种变化&#xff0c;各种损益和市场判断的正确和错误&#xff0c;不会对心态产生很大的影响&#xff0…

ArcGIS中的土地利用变化分析详解

本篇主要是针对矢量数据的分析。 一、不同时期的土地利用矢量数据&#xff0c;如何分析其图形及属性变化&#xff1f; 土地利用图&#xff08;左图为1993年&#xff0c;右图为2003年&#xff09; 思路如下&#xff1a; 可以先对2个图层进行Union操作&#xff0c;然后在结果中…

【三十天精通Vue 3】第十四天 Vue 3 的单元测试详解

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: 三十天精通 Vue 3 文章目录 引言一、为什么要进行单元测试1.1 单元测试的概念1.2 单元测试的优…

Javase学习文档------面象对象再探

再续前缘面向对象 书接上回构造器 在Java中&#xff0c;可以通过在空参构造方法中使用 this() 关键字来调用类中其它的构造方法。 使用 this() 关键字来调用其它构造方法时&#xff0c;需要注意以下几点&#xff1a;1.this() 必须是构造方法的第一条语句&#xff1b; 2.一个构…

经典文献阅读之--NORLAB-ICP(重力约束ICP)

0. 简介 最近几年IPC相关的文章也出了不少&#xff0c;最近作者有看到了一篇比较有意思的ICP论文—《Gravity-constrained point cloud registration》&#xff0c;这篇论文将传统的ICP考虑了重力因素&#xff0c;高频率的IMU数据弥补了低频的传感器数据。除此之外&#xff0c…

4K分辨率搭配光学变焦功能,极米H6成旗舰家用投影首选

近几年&#xff0c;我国投影机市场产品竞争日趋激烈&#xff0c;以极米为代表的国产品牌迅速崛起并逐步超越国际品牌成为中国投影机市场的领跑者。虽然目前国产投影仪品牌比较多&#xff0c;但其中极米科技旗下的产品最受消费者青睐。IDC数据显示&#xff0c;2022年上半年&…

easyexcel导出中自定义合并单元格,通过重写AbstractRowWriteHandler

针对 阿里的easyexcel 自定义处理 任意单元格合并 官方给出的合并单元格 只给出固定规律的单元格合并,当然官方也指出可以自定义合并单元格的策略,我们跟进LoopMergeStrategy 这个合并策略的实例类,发现里面继承了AbstractRowWriteHandler,官方示例代码如下 /*** 合并单元格…

揭秘!Chrome 调试的11+隐藏技巧,让你在开发中如虎添翼!

前言 chrome 浏览器作为前端童鞋的老婆&#xff0c;相信你一定不陌生。调页面、写BUG、画样式、看php片少了它整个世界都不香了。 不信&#xff1f;一起来看看我们的老婆有多厉害… 1、一键重新发起请求 在与后端接口联调或排查线上 BUG 时&#xff0c;你是不是也经常听到他…

Python中的主函数

在Python代码中&#xff0c;我们常常看到主函数是以if __name__ __main__开头的&#xff0c;比如 它的原理是什么呢&#xff1f; 首先要知道&#xff0c;__name__是内置变量&#xff0c;用于表示当前模块的名字。在一个模块中运行以下语句&#xff0c;你会发现输出的是__main…

CSS:横向导航栏

横向导航栏&#xff08;盗版导航栏&#xff0c;B站仿写。&#xff09; 原视频链接 <html><head><title>demo</title><style>*{margin: 0;padding: 0;list-style: none;text-decoration: none;}body{display: flex;justify-content: center;a…

模型蒸馏与压缩简单介绍

目录 一、概述 二、DistilBERT模型介绍 2.1 基本结构 2.2 知识蒸馏方法 一、概述 预训练语言模型虽然在众多自然语言任务中取得了很好的效果&#xff0c;但通常这类模型的参数量较大&#xff0c;很难满足实际应用中的时间和空间需求。 下图给出了常见预训练语言模型参数量的…

JDK11 下载与安装、环境配置(全网最详情,值得收藏)

目录 一、下载JDK11 二、安装JDK11 三、配置环境变量 四、验证环境配置是否成功 五、答疑&#xff0c;为什么不配置 CLASSPATH 什么是JDK JDK是 Java 语言的软件开发工具包&#xff0c;主要用于移动设备、嵌入式设备上的java应用程序。JDK是整个java开发的核心&#xff0…

【算法训练(day3)】快速排序模版选择及不同版本快排对比

目录 一.划分区间的选取 二.代码实现lomuto版本快速排序 三.hoare版本快速排序 四.竞赛模板的选取 五.竞赛模板的代码实现 一.划分区间的选取 目前市面上常用的有两种划分区间&#xff0c;一种是hoare划分另一种是Lomuto划分。常见快速排序实现模版比如挖坑法和经典快速排…

第三章(2):深入理解NTLK库基本使用方法

第三章&#xff08;2&#xff09;&#xff1a;深入理解NTLK库基本使用方法 本节主要介绍了NLTK库的基本使用方法&#xff0c;其中对NLTK的安装与配置进行了介绍。随后&#xff0c;对文本处理中常用的分词、句子分割和词性标注这三个任务进行了详细讲解。 如果感觉有用&#xff…

《商用密码应用与安全性评估》第一章密码基础知识1.6密钥交换协议

密码协议是指两个或者两个以上参与者使用密码算法时&#xff0c;为了达到加密保护或安全认证目的而约定的交互规则。 密钥交换协议 公钥密码出现之前&#xff0c;密钥交换很不方便&#xff0c;公钥密码可以在不安全信道上进行交换&#xff0c;交换的密码协议是为了协商会话密钥…

实现开机动画和自定义Toolbar的高级写法

需求是自定义一个Toolbar和全屏展示一个第一次激活App的开机动画 1自定义Toolbar的使用 1仍然是先将工程的theme.xml中设置成NoActionBar <resources xmlns:tools"http://schemas.android.com/tools"><!-- Base application theme. --><style name&…

Oracle11g全新讲解之PLSQL编程

一、PLSQL编程 是过程语言(Procedural Language)与结构化查询语言(SQL)结合而成的编程语言.通过增加变量、控制语句&#xff0c;使我们可以写一些逻辑更加复杂的数据库操作. 语法结构 declare--声明变量 变量名称 v_ 开头&#xff0c;规范 begin--执行具体的语句--异常处理 …