当前位置: 首页 > news >正文

【JavaEE】(17) MyBatis 基础

一、什么是 MyBatis

        之前我们学习了用 JDBC 操作数据库,但流程复杂且重复:

  • 创建数据库连接池,获取数据库连接。(配置数据库连接的 url、用户名、密码)
  • 创建带占位符的 SQL 语句。(写 SQL 语句)
  • 参数绑定,替换占位符。(设置参数值)
  • 执行 SQL 语句返回结果集。
  • 处理结果集。(SQL 字段映射为 Java 对象)
  • 释放资源。

        以上流程中,只有括号中的内容需要个性化设置,其余的操作都是重复的。MyBatis 就是基于 JDBC 封装的用于简化程序与数据库交互的框架(持久层框架)。

二、快速入门

1、添加依赖

注意:Spring Boot 3.X(2.X) 对应 MyBatis 3.X(2.X)

2、建立用户表和实体类

        SQL 字段命名标准:全小写(Windows 不区分大小写,Linux 区分,全小写不会出错)、蛇形,如 delete_flag(便于后续按照规律自动把 SQL 字段映射为 Java 属性)。

        Java 属性命名标准:小驼峰

-- 创建数据库
DROP DATABASE IF EXISTS mybatis_test;CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;-- 使用数据数据
USE mybatis_test;-- 创建表[用户表]
DROP TABLE IF EXISTS user_info;
CREATE TABLE `user_info` (`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,`username` VARCHAR ( 127 ) NOT NULL,`password` VARCHAR ( 127 ) NOT NULL,`age` TINYINT ( 4 ) NOT NULL,`gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1-男 2-女 0-默认',`phone` VARCHAR ( 15 ) DEFAULT NULL,`delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now() ON UPDATE now(),PRIMARY KEY ( `id` ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4; -- 添加用户信息
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );
package com.edu.mybatisdemo.demos;import lombok.Data;import java.util.Date;@Data
public class UserInfo {private Integer id;private String username;private String password;private Integer age;private Integer gender;private String phone;private Integer deleteFlag;private Date createTime;private Date updateTime;
}

3、数据库连接配置

        用的时候直接 copy:如果密码是数字,需要用双引号引起来。

spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=trueusername: rootpassword: "123456"driver-class-name: com.mysql.jdbc.Driver

4、持久层实现

  • MyBatis 持久层接口,规范命名 xxxMapper
  • @Mapper 注解由 MyBatis 引入,用于将 bean 交给 Spring 管理。
  • @Select 表示查询的实现(我们设计成抽象方法,由注解实现方法)。增删改操作同理。
package com.edu.mybatisdemo.demos.mapper;import com.edu.mybatisdemo.demos.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper
public interface UserInfoMapper {// 查询所有用户信息@Select("select * from user_info")public List<UserInfo> selectAll();
}

5、单元测试

        写 Controller、Service 提供接口,然后利用浏览器或 postman 进行某个功能的测试很麻烦,我们直接根据某个功能方法写单元测试。

  • @SpringBootTest:加载 Spring 环境,包括 IoC 容器。
  • @Test:表示可执行的方法。如果写 main 方法测试,则只能有一个测试方法。用 @Test 则可以有多个。

        SQL 字段与 Java 类属性名不一样的,没有绑定成功:

        自动添加测试类:在需要测试的类中操作。

package com.edu.mybatisdemo.demos.mapper;import com.edu.mybatisdemo.demos.model.UserInfo;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@SpringBootTest
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@BeforeEachvoid setUp() {System.out.println("Before each test");}@AfterEachvoid tearDown() {System.out.println("After each test");}@Testvoid selectAll() {List<UserInfo> userInfoList = userInfoMapper.selectAll();System.out.println(userInfoList);}
}

三、基础操作(注解方式)

1、打印日志

mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2、查

2.1、字段绑定

        SQL 字段命名与 Java 类(返回类型)属性命名不匹配,绑定不上:

(1)方法1

        在 SQL 语句中使用 as 重命名字段,与 Java 属性名匹配:

(2)方法2(@Result)

        在 Java 中使用 @Results、@Result 注解绑定字段和属性:

(3)方法3(推荐)

        配置:自动根据 SQL 字段的小写、蛇形命名,转换为 Java 属性小驼峰命名。

mybatis:configuration:map-underscore-to-camel-case: true # 配置驼峰⾃动转换

2.2、参数传递

  • 如果只有一个参数, #{} 里的属性名可以和参数名不一致,但建议一致。
  • 如果有多个参数, #{} 里的属性名要么和参数名一致,要么按 param1、param2 的顺序命名(不建议,因为如果再加一个参数,顺序又要重新改)。
  • 总之,建议 #{} 里的属性名和参数名一致
  • 可以用 @Param 参数重命名。如果重命名的参数是对象,那么 #{} 里按 对象参数名.属性 引用。
  • 如果命名一模一样,还出错,加 @Param 重命名

3、增(@Option)

  • 返回的是插入成功的条数。
  • 如果想插入后立即获取 id(自增项),使用 @Options。 比如创建订单后立即获取订单 id。

4、删

5、改

四、基础操作(XML 方式)

1、配置文件

        配置 xml 文件所在路径:

  • classpath 表示在 resources 文件下。
  • mapper/**Mapper.xml 表示 mapper 文件下所有以 Mapper.xml 结尾的文件,可以自定义。
mybatis:mapper-locations: classpath:mapper/**Mapper.xml

2、快速入门

(1)定义 mapper 接口:

(2)添加 xml 实现方法:

        固定格式:

  • namespace 表示要实现哪个接口(全限定名)。
 <?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.example.demo.mapper.UserInfoMapper"></mapper>

        加入 MyBatisX 插件,自动生成部分 xml 代码:方法名最好以功能命名,如 select,便于自动生成代码。

(3)单元测试

3、查

        SQL 字段绑定到返回值类型的属性上:

  • 驼峰命名自动转换。
  • 起别名。
  • 指定映射关系。

        参数传递规则跟注解方式一样。

4、增、删、改

        其它的跟注解方式差不多,只不过换成了在 xml 标签里面写 SQL 语句。

5、多表查询

        准备文章表:

-- 创建文章表
DROP TABLE IF EXISTS article_info;CREATE TABLE article_info (id INT PRIMARY KEY auto_increment,title VARCHAR ( 100 ) NOT NULL,content TEXT NOT NULL,uid INT NOT NULL,delete_flag TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',create_time DATETIME DEFAULT now(),update_time DATETIME DEFAULT now() 
) DEFAULT charset 'utf8mb4';-- 插入测试数据
INSERT INTO article_info ( title, content, uid ) VALUES ( 'Java', 'Java正文', 1 );

        文章表的实体类:

package com.edu.mybatisdemo.demos.model;import lombok.Data;import java.util.Date;@Data
public class ArticleInfo {private Integer id;private String title;private String content;private Integer uid;private Integer deleteFlag;private Date createTime;private Date updateTime;// 用户部分信息private String username;private Integer age;
}

        多表查询语句:

select ta.*, tu.username, tu.age from article_info ta
left join user_info tu on ta.uid = tu.id where ta.id = 1;

        但工作中不会用多表查询,因为太慢了(要做笛卡尔积),通常会拆开:

select * from article_info where id = 1; 
从结果集中取出 uid,再根据 uid 在用户表中查询 username 和 age。

        对于一次程序与数据库的交互,不能超过 100ms。对于大数据量的多表查询,非常慢,并且很容易导致其它程序无法对数据库进行访问。拆成多条的话,多线程提高效率。

五、#{} 和 ${}

1、预编译 SQL 和即时 SQL

  • #{}预编译 SQL,输入的参数并没有拼接到 SQL 语句中,而是用 ? 占位。编译时,它对于字符串类型的参数,会自动根据类型添加上 ' '
  • ${} 是即时 SQL,输入的参数会直接拼接到 SQL 语句中。它对于字符串类型的参数,需要手动添加上 ' '

        预编译 SQL:

        即时 SQL:直接拼接,对于字符串类型参数,要手动加引号。

2、两者的区别

(1)预编译 SQL 性能更高

        SQL 执行流程:

  • 语法解析:校验语法对不对。
  • 语法优化。
  • 语法编译。
  • 语法执行。

        对于即时 SQL,会直接拼接好 SQL 语句后执行上述流程。如果对于多条仅参数不同 SQL 语句,会逐步执行上述所有流程(每条 SQL 语句都不同)。

        对于预编译 SQL,只有同样的一条 SQL 语句,那么语法解析、优化、编译都是一样的,因此会缓存编译结果。对于不同的参数,在执行前替换占位符即可。

(2)预编译 SQL 更安全(防止 SQL 注入)

        SQL 注入:通过设计输入参数,更改预先定义好的 SQL 语句,来攻击服务器。

        如,输入参数:' or 1='1

        而预编译 SQL 不会被攻击:

(3)预编译 SQL 无法实现排序功能

        我们有时希望可以根据参数选择升序、降序排序。但预编译 SQL 会自动根据参数类型加上引号,导致 SQL 语句出错。但即时 SQL 是直接拼接,可以设计为不手动加引号,SQL 语句正确。

        但是即时 SQL 不安全,为了避免 SQL 注入,我们需要校验输入参数是否在设计范围之内

(4)直接使用预编译 SQL 无法实现 like 查询

        同样,预编译无法根据输入参数,来进行模糊查询。

        但为了防止 SQL 注入,使用 mysql 的内置函数 concat() 来处理:

六、数据库连接池

        为了避免重复创建、销毁数据库连接,提高效率,程序启动时就创建一个数据库连接池,里面存放了多个连接对象。需要时就从里面取连接,用完了就放回去。

        常见的数据库连接池:Hikari, Druid。

        Hikari 是 SpringBoot 默认使用的:

        如果想换成其它的,比如 Druid,要在 POM 配置依赖:

// Spring Boot 3.X 
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-3-starter</artifactId><version>1.2.21</version></dependency>// Spring Boot 2.X
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.17</version></dependency>

七、MySQL 企业规范

  • 字段名:小写字母或数字。避免数字开头、数字在两个下划线中间。数据库字段名修改代价大。
  • 表必备三字段:id、创建时间、修改时间(需要时保证有记录可查)。
  • 避免用 *(网络开销大),就算全部字段都要用也建议一个个写全(修改字段后,容易发现不了 ResultMap 没有跟着修改的问题)。
http://www.dtcms.com/a/340749.html

相关文章:

  • 【JavaEE】多线程(线程安全问题)
  • k8sday12数据存储(1/2)
  • 【表的操作】
  • 开源大模型如何选择?GPT-OSS综合评估
  • HTML--pre标签的作用
  • 决策树1.2
  • Flink学习
  • 数据安全事件分级
  • 嵌入式的各个要点总结(不断更新)
  • Building Systems with the ChatGPT API 使用 ChatGPT API 搭建系统(第二章学习笔记及总结)
  • idea maven 设置代理
  • SSM从入门到实战:2.1 MyBatis框架概述与环境搭建
  • 【STM32】HAL库中的实现(六):DAC (数模转换)
  • 调用海康威视AI开放平台接口实现人体关键点检测
  • Java毕业设计选题推荐 |基于SpringBoot+Vue的知识产权管理系统设计与实现
  • langchain-ds的报告生成提示词
  • 如何低比特量化算法的工程实战与落地优化
  • 从零开始的云计算生活——第四十七天,细水长流,kubernetes模块之ingress资源对象
  • 开源 AR 眼镜怎么选?OpenGlass ,OSSG,cheApR 分析推荐
  • 无需驱动!单文件实现键盘按键禁用的技术方案
  • 通用物联网接口调用完整解决方案2
  • Ubuntu_22.04安装文档
  • k8s--Discuz论坛lnmp平台部署
  • 软件可视化与前端、后端技术开发的关系
  • WPF MVVM进阶系列教程(四、ViewModel通信)
  • std::map 的插入元素方式
  • 下拉组件Tag支持自定义背景颜色,图片组支持设置刷新频率,DataEase开源BI工具v2.10.12 LTS版本发布
  • iOS 应用上架常见问题与解决方案,多工具组合的实战经验
  • 深入解析RAGFlow六阶段架构
  • iOS 应用迭代与上架节奏管理 从测试包到正式发布的全流程实践