JDBC的救赎之道——MyBatis(静态SQL)

目录

一:MyBatis是干什么的

二:MyBatis使用

1.工程搭建

2.注解

1.查

2.@Param

3.增

4.@Options

5.删和改

6.无法赋值问题

原因

解决方法

3.xml

1工程搭建修改

2.查找简单演示

3.返回自增id

4.xml中无法赋值问题

[三:#与区别](#与区别)

[#相比于的优点](#相比于的优点)

1.更快

2.更安全

$相比于#的优点

1:排序场景

2:like查询

四:总结


一:MyBatis是干什么的

兄弟们,今天来讲一讲MyBatis啊,也是好久没讲过一个这么优秀的,开源的,简化JDBC开发的,持久层框架了。想必各位彦祖们之前是接触过JDBC的,没接触过也没关系啊,我之前正好写过一篇Java程序如何连接数据库的后端代码怎么连接数据库-CSDN博客,如果你点进去看看会发现,MD操作个数据库这么麻烦,还得自己配置数据库连接源,自己构造Sql,甚至还得自己开启和释放资源,然后程序员们一看这不行,这啥都让我干,你怎么不让我直接手搓一个操作系统,网络,硬件,框架,来给你开发呢?

但我们总不能直接把编译器卸载了,直接回村里种地啊,那我们得找个优秀的,开源的,简化JDBC开发的,持久层框架啊。这不就找到MyBatis了,那场面堪比昊京认母,就差叫妈了


简单来说 MyBatis 是更简单完成程序和数据库交互的框架,也就是更简单的操作和读取数据库⼯具
,至于真的有吹的那么NB没有,下面我们直接使用一下这个框架感觉感觉就ok了。

二:MyBatis使用

1.工程搭建

引入依赖

XML 复制代码
<!--Mybatis 依赖包-->
 <dependency>
 <groupId>org.mybatis.spring.boot</groupId>
 <artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version>
 </dependency>
 <!--mysql驱动包-->
<dependency>
 <groupId>com.mysql</groupId>
 <artifactId>mysql-connector-j</artifactId>
 <scope>runtime</scope>
</dependency>

表结构与实体类

数据库连接

注意:
如果使⽤ MySQL 是 5.x 之前的使⽤的是"com.mysql.jdbc.Driver",如果是⼤于 5.x 使⽤的
是"com.mysql.cj.jdbc.Driver".

TypeScript 复制代码
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/database?characterEncoding=utf8&useSSL=false
    username: #用户名
    password: #密码
    driver-class-name: com.mysql.cj.jdbc.Driver

创建接口 并添加@Mapper注解

这个Mapper注解是 MyBatis 框架中的注解,用于标识接口为 MyBatis 的映射器接口。MyBatis 会自动为该接口生成实现类。

2.注解

1.查

用注解的方式操作数据库非常简单,如下代码所示,其中#{}是用来获取参数用的,其中{}里的值,要和方法参数里的值保持一致,不然MyBatis会映射不到。

java 复制代码
@Mapper
public interface UserInfoMapper {

 @Select("select * from userinfo where Id = #{id}")//将参数映射到sql语句中
     UserInfo selectById(Integer id);//一个参数查找数据

}

2.@Param

一个参数的时候如果对不上#{}中的参数没事,因为就那一个,但是两个参数的时候就必须要将名字一 一对应了,可以使用@Param注解来取别名,如下代码所示

java 复制代码
 @Select("select * from userinfo where id = #{id} and username = #{name}")
    UserInfo selectByName(@Param("id") Integer userid, @Param("name") String username);
//一个参数名字对不上无所谓,但两个参数时就必须要对上名字了

3.增

插入操作,也和直接写SQL差不多,不多赘述

java 复制代码
 @Insert("insert into userinfo(username,password,age,gender,phone)" +
            " values(#{userInfo.userName},#{userInfo.passWord},#{userInfo.age},#{userInfo.gender},#{userInfo.phone})")//#后面是java里的属性,语句里是sql里的属性
    Integer insert(@Param("userInfo") UserInfo userInfo);//如果使用Param进行修改,需要在属性前面加上类

4.@Options

insert语句默认返回的是受到影响的行数,但是有些情况下,比如在订单系统中,我们想要在插入后立刻获取新插入的id我们就需要使用@Optons注解来实现了

用法很简单就是直接加注解就行了

useGenerateKeys: MyBatis使用JDBC的getGeneratedKeys方法来获取数据库内部生成的主键,默认是false,这里我们设置为true表示让他获取

keyProperty :当上面MyBatis使用getGeneratedKeys方法获取到主键后,就会赋值到keyProperty指定的对象属性上,下面代码意思就是让得到的主键值设置在userInfo的id属性上

java 复制代码
 @Options(useGeneratedKeys = true, keyProperty = "id") 
@Insert("insert into userinfo(username,password,age,gender,phone)" +
            " values(#{userInfo.userName},#{userInfo.passWord},#{userInfo.age},#{userInfo.gender},#{userInfo.phone})")//#后面是java里的属性,语句里是sql里的属性
    Integer insert(@Param("userInfo") UserInfo userInfo);//如果使用Param进行修改,需要在属性前面加上类

5.删和改

其实都大同小异,下面我直接给两个示例就不解释了

删除示例

java 复制代码
@Delete("delete from userinfo where id = #{id}")
void delete(Integer id);//根据id删除

更新示例

java 复制代码
@Update("update userinfo set username=#{username} where id=#{id}")
void update(UserInfo userInfo);//更改名字和id

6.无法赋值问题

原因

我们应该知道,MyBatis只有Java对象属性和数据库字段⼀模⼀样时, 才会进⾏赋值
但是通常数据库列使⽤蛇形命名法进⾏命名(如user_name), ⽽ Java 属性⼀般遵循驼峰命名法约定(如userName)
但是MyBatis在进行结果映射的时候却发现这俩名字不一样,虽然它在映 射结果的时候会忽略大小写 ,但是这多了一个下划线也不好搞啊那该怎么办呢?其实有 三 根救命毫毛

解决方法

6.1起别名

我们只需要将数据库的列名起个别名,让它和实体类的对象保持一致就ok了。

下面我们将字段delete_flag ,create_time, update_time分别起了和实体类属性一样的名字deleteFlag, createTime,updateTime,这样MyBatis就不会不知道该怎么映射了

java 复制代码
@Select("select id, username, `password`, age, gender, phone, delete_flag as 
deleteFlag, " +
 "create_time as createTime, update_time as updateTime from userinfo")
 public List<UserInfo> queryAllUser();

4.2. 结果映射
其实跟刚刚的起别名差不多,就是用@Results注解,来讲每个字段和实体类对象的属性绑定起来,这样就相当于直接告诉MyBatis该怎么映射,但是可以看到下面的代码写起来是很麻烦的

java 复制代码
@Select("select id, username, `password`, age, gender, phone, delete_flag, 
create_time, update_time from userinfo")
@Results({
 @Result(column = "delete_flag",property = "deleteFlag"),
 @Result(column = "create_time",property = "createTime"),
 @Result(column = "update_time",property = "updateTime")
})
List<UserInfo> queryAllUser()

4.3. 开启驼峰命名(最简单)
就是直接修改配置文件加一个map-underscore-to-camel-case: true,意思是开启自动的驼峰转换
我们直接翻译一下,映射-下划线-驼峰:true,意思很清除就是将蛇形命名法和驼峰命名法自动映射
但是这种方式就对命名规范提出很高的要求,所以可以想到,严格遵守命名规范是可以给我们省去很多不必要的麻烦的

TypeScript 复制代码
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/database?characterEncoding=utf8&useSSL=false
    username: #用户名
    password: #密码
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  configuration: 
     map-underscore-to-camel-case: true #配置驼峰自动转换

3.xml

除了用注解,MyBaits还支持使用 xml方式开发的,当然在静态SQL这里xml可能优势不明显,但在动态SQL开发中xml绝对是要比注解使用方便的

1工程搭建修改

配置修改

添加mapper-location,指定xml文件的位置

TypeScript 复制代码
#数据库连接池配置
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/database?characterEncoding=utf8&useSSL=false
    username: #用户名
    password: #密码
    driver-class-name: com.mysql.cj.jdbc.Driver
#Mybatis配置
mybatis:

  configuration:
    map-underscore-to-camel-case: true #配置驼峰自动转换
  # 配置 mybatis xml 的⽂件路径,在 resources/mapper 创建所有表的 xml ⽂件
  mapper-locations: classpath:mapper/**Mapper.xml

添加Mapper接口,并添加@Mapper注解

添加xml文件,注意要与配置文件中配置的路径相对应

在xml文件中添加的固定格式

其中namespace 是指这个xml文件和哪个Mapper接口对应,后面跟的是Mapper接口的全限定类名

XML 复制代码
<?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.hf.demo.Mapper.UserInfoXmlMapper">

</mapper>

2.查找简单演示

查找全部user信息演示,也是很简单,就只是加一个select标签

XML 复制代码
<?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.hf.demo.Mapper.UserInfoXmlMapper">
<select id="queryAllUser" resultType="com.hf.demo.model.UserInfo" >
        select * from userinfo
</select>
</mapper>

id:是对应的Mapper接口中的方法

resultType:对应的是返回的实体类类型

xml与Mapper接口对应关系

3.返回自增id

与注解相同,都是需要配置useGenerateKeyskeyProperty,但是xml中只需要直接在insert标签中添加属性就ok了,不需要再借助@Options注解,如下图

XML 复制代码
 <insert id="insert" useGeneratedKeys="true" keyProperty="id">
          insert into userinfo(username,password,age,gender,phone) values (#{userInfo.userName},#{userInfo.passWord},#{userInfo.age},#{userInfo.gender},#{userInfo.phone})
        </insert>

4.xml中无法赋值问题

上面我们在注解中已经讲过,由于实体类属性和数据库字段名字不相同导致的映射关系无法匹配问题

我们在xml中同样可以用1取别名,2添加映射关系,3:开启驼峰自动转换

其中1和3和注解中做法类似,我们主要看一下xml中的映射关系是怎么添加的

如下代码中,我们使用resultMap标签,将需要辅助映射的字段,一 一对应起来,并给这个映射结果起个名字叫BaseMap

当select标签使用的时候,就不需要使用resultType来指定返回类型,而是直接使用resultMap,让它指定一个结果映射就行了

XML 复制代码
<?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.hf.demo.Mapper.UserInfoXmlMapper">
    <resultMap id="BaseMap" type="com.hf.demo.model.UserInfo">
        <id column="id" property="id"></id>
        <result column="delete_flag" property="deleteFlag"></result>
        <result property="createTime" column="create_time"></result>
        <result property="updateTime" column="update_time"></result>
    </resultMap>
<select id="queryAllUser" resultMap="BaseMap" >
        select * from userinfo
</select>
</mapper>

各个标签对应的意思示意图如下

三:#与$区别

我们直接看一组例子,这是两个相同的查询语句,唯一不同的就是用来接收参数的符号不一样

一个是**#** 一个是**$,**我们直接看运行结果

可以看到

# 是我们输入的参数没有在后面直接拼接,而是使用?进行占位,这种SQL我们称之为预编译SQL

$ 是将我们输入的参数直接拼接在后面,这种SQL我们称之为即时SQL

我们将参数换成String类型再看结果

可以看到#接收的参数一切正常,但是$接收的参数就会发生报错

因为在SQL语句中字符串类型是需要加引号(' ')的,**#接收参数的时候,如果是String类型会自动加上(' '),但是$**类型却不会自动加上引号,导致报错

#相比于$的优点

1.更快

即时SQL在每次运行时都要经历三个步骤
1.解析语法和语义, 校验SQL语句是否正确
2. 优化SQL语句, 制定执⾏计划
3. 执⾏并返回结果
如果一个SQL语句被反复调用但是只是有,where条件不同,set子句不同等....改变不是很大的情况,如果每次调用都经历上述三个步骤,就会让程序的性能很低。
但是预编译SQL,编译⼀次之后会 将编译后的SQL语句缓存起来 ,后⾯再次执⾏这条语句时,不会再次编译 (只是输⼊的参数不同), 省去了解析优化等过程,让效率大大提高

2.更安全

预编译SQL可以防止SQL注入攻击(SQL语句与用户传入的参数分离)

在预编译 SQL 中,SQL 语句先被发送到数据库服务器进行编译,此时数据库会对 SQL 语句的语法进行检查和解析,并为其生成执行计划。

然后,将用户输入作为参数单独传递给已经编译好的语句。这样,用户输入就不会被当作 SQL 语句的一部分进行解析,从而避免了攻击者通过在输入中插入恶意 SQL 代码来改变语句的逻辑

$相比于#的优点

那么就没有一点优点吗,那肯定不是的,既然存在就肯定有使用的地方

1:排序场景

比如在排序场景中,我们只能使用$不能够使用#,因为当使用#的时候,#会自动给加上引号,但是排序的SQL中是不允许出现引号的

java 复制代码
@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +
 "from userinfo order by id ${sort} ")
List<UserInfo> queryAllUserBySort(String sort);

2:like查询

同样的因为外面的%缘故所以必须加引号,但是#又会添加引号,会导致like查询有两个引号,导致报错,可以替换为$来解决。

java 复制代码
@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +
 "from userinfo where username like '%#{key}%' ")
List<UserInfo> queryAllUserByLike(String key);

虽然用了$符号可以解决报错,但依然有SQL注入的风险,所以我们更加倾向于用MySQL的concat()函数来解决

concat函数的作用是将多个字符串,串联成一个字符串,这样就解决了必须加引号的问题了

' %' + ' key ' + '%' --->concat处理后 ---> '%key%'

代码如下

java 复制代码
@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +
 "from userinfo where username like concat('%',#{key},'%')")
List<UserInfo> queryAllUserByLike(String key);

四:总结

你又问我我写了什么,来我告诉你

1.MyBatis大大简化JDBC开发,只需要关注SQL, 映射关系和实体类,知道了吧

2.MyBatis用@Param给实体类起名字,@Options返回自动生成的主键知道了吧

3.MyBatis因为什么没办法赋值,以及三种解决方法知道了吧

4.MyBatis有注解和xml两种开发方式知道了吧

5.#和$的区别知道了吧

相关推荐
刘大猫.4 分钟前
java导入excel更新设备经纬度度数或者度分秒
java·excel·导入excel·经纬度·度分秒·经纬度 度数·经纬度 度分秒
程序猿chen11 分钟前
《Java八股文の文艺复兴》第十篇:量子永生架构——对象池的混沌边缘
java·后端·面试·架构·跳槽·量子计算·改行学it
blog_jenny32 分钟前
Android 14 、15动态申请读写权限实现 (Java)
android·java·gitee
用户5669994370341 分钟前
一篇文章弄懂Lambda 表达式
java
凌冰_43 分钟前
Java Collections 类中常用方法使用
java·开发语言
我是坑货1 小时前
maven的项目管理和构建生命周期
java·log4j·maven
遥不可及~~斌1 小时前
基于Redis实现短信防轰炸的Java解决方案
java·数据库·redis
喵喵帕斯2 小时前
MySQL索引优化-索引可见性
后端·sql·mysql
AronTing2 小时前
300%性能提升!CompletableFuture异步编排四大核心模式与避坑指南
java·后端·安全
java奋斗者2 小时前
基于Java的人脸识别在线考试系统(jsp+springboot+mysql8.x)
java·开发语言·spring boot