SSM框架学习(三、MyBatis实践:提高持久层数据处理效率)

目录

一、Mybatis简介

1.简介

2.持久层框架对比

3.快速入门(基于Mybatis3方式)

4.ibatis方式的实现和原理

5.ibatis与mybatis之间的关系

二、Mybatis基本使用 

1.向 sql 语句传参

(1)mybatis日志输出配置

(2)#{ key } 和 ${ key } 

 2.数据输入

(1)单个简单类型参数

(2)实体类类型参数 

(3)零散的简单类型数据

(4)Map类型参数

 3.数据输出

(1)单个简单类型

(2)返回实体类对象

(3)返回 Map 类型

(4)返回 List 类型

(5)返回主键值

Ⅰ. 自增长类型主键

Ⅱ. 非自增长类型主键 

 (6)实体类属性和数据库字段对应关系

三、Mybatis 多表映射

1.概念

(1)实体类设计方案

 (2)多表映射案例准备

2.对一映射

3.对多映射

4.多表映射优化

四、MyBatis动态语句

1. if 和 where标签 

2. set 标签

3. trim标签(了解)

4. choose/when/otherwise 标签

5. foreach标签

6. sql 片段提取

五、Mybatis 高级扩展

1. Mapper 批量映射优化

2. 插件和分页插件 PageHelper

(1)插件机制

(2)PageHelper 插件使用

 3. 逆向工程和MybatisX插件

(1)ORM思维介绍

(2)逆向工程

 (3)逆向工程插件 MyBatisX 使用


一、Mybatis简介

1.简介

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github。

2.持久层框架对比

JDBC:

        SQL 夹杂在Java代码中耦合度高,导致硬编码内伤

        维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见

        代码冗长,开发效率低

Hibernate 和 JPA:

        操作简便,开发效率高

        程序中的长难复杂 SQL 需要绕过框架

        内部自动生成的 SQL,不容易做特殊优化

        基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。

        反射操作太多,导致数据库性能下降

MyBatis:

        轻量级,性能出色

        SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据

        开发效率稍逊于 Hibernate,但是完全能够接收

开发效率:Hibernate>Mybatis>JDBC

运行效率:JDBC>Mybatis>Hibernate

3.快速入门(基于Mybatis3方式)

a.准备数据模型

CREATE DATABASE `mybatis-example`;USE `mybatis-example`;CREATE TABLE `t_emp`(emp_id INT AUTO_INCREMENT,emp_name CHAR(100),emp_salary DOUBLE(10,5),PRIMARY KEY(emp_id)
);INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("tom",200.33);
INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("jerry",666.66);
INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("andy",777.77);

b.项目搭建和准备

① 项目搭建

② 依赖导入

<dependencies><!-- mybatis依赖 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.11</version></dependency><!-- MySQL驱动 mybatis底层依赖jdbc驱动实现,本次不需要导入连接池,mybatis自带! --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.25</version></dependency><!--junit5测试--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.3.1</version></dependency>
</dependencies>

③ 实体类准备

public class Employee {private Integer empId;private String empName;private Double empSalary;public Integer getEmpId() {return empId;}public void setEmpId(Integer empId) {this.empId = empId;}public String getEmpName() {return empName;}public void setEmpName(String empName) {this.empName = empName;}public Double getEmpSalary() {return empSalary;}public void setEmpSalary(Double empSalary) {this.empSalary = empSalary;}
}

 c.准备Mapper接口和MapperXML文件

MyBatis 框架下,SQL语句编写位置发生改变,从原来的Java类,改成XML或者注解定义!

推荐在XML文件中编写SQL语句,让用户能更专注于 SQL 代码,不用关注其他的JDBC代码。

Mybatis 中的 Mapper 接口相当于以前的 Dao。但是区别在于,Mapper 仅仅只是建接口即可,我们不需要提供实现类,具体的SQL写到对应的Mapper文件。

① 定义mapper接口

package com.mihoyo.mapper;
import com.mihoyo.pojo.Employee;public interface EmployMapper {//根据id查询员工信息Employee queryById(Integer id);
}

② 定义mapper.xml 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- namespace = mapper 对应接口类的全限定名 -->
<mapper namespace="com.mihoyo.mapper.EmployeeMapper"><!-- 查询使用 select标签每个标签对应一个方法,是方法的实现--><select id="queryById" resultType="com.mihoyo.pojo.Employee"><!-- #{id}代表动态传入的参数,并且进行赋值! -->select emp_id empId,emp_name empName, emp_salary empSalary fromt_emp where emp_id = #{id}</select>
</mapper>

注意: 

① 四个一致:

        方法名和 SQL 的 id 一致

        方法返回值和 resultType 一致

        方法的参数和 SQL 的参数一致

        接口的全类名和映射配置文件的名称空间一致

mapper 接口不能方法重载!因为虽然 java 语法不会出现问题,但 mapper.xml 无法识别,它是根据方法名进行识别的。

③ mapper.xml 一般写在 resource 目录下,这样编译后 maven 会打包到类路径(target/classes)中。

d.准备MyBatis配置文件

mybatis框架配置文件: 数据库连接信息,性能配置,mapper.xml配置等。

习惯上命名为 mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合 Spring 之后,这个配置文件可以省略,所以大家操作时可以直接复制、粘贴。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- environments表示配置Mybatis的开发环境,可以配置多个环境。在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 --><environments default="development"><!-- environment表示配置Mybatis的一个具体的环境 --><environment id="development"><!-- Mybatis的内置的事务管理器 --><transactionManager type="JDBC"/><!-- 配置数据源 --><dataSource type="POOLED"><!-- 建立数据库连接的具体信息 --><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis-example"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><mappers><!-- Mapper注册:指定Mybatis映射文件的具体位置 --><!-- mapper标签:配置一个具体的Mapper映射文件 --><!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 --><!--    对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 --><mapper resource="mappers/EmployeeMapper.xml"/></mappers></configuration>

e.运行和测试

public class MybatisTest {//使用 mybatis 提供的api进行方法的调用@Testpublic void test_01() throws IOException {//1.读取外部配置文件(mybatis-config.xml)InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");//2.创建sqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);//3.根据创建sqlSessionFactory创建sqlSession(每次业务创建一个,用完就释放)SqlSession sqlSession = sqlSessionFactory.openSession();//4.获取接口的代理对象(通过代理技术),调用代理对象的方法,就会查找mapper接口的方法EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);Employee employee = mapper.queryById(1);System.out.println("employee = "+ employee);//5.提交事务(非查询语句)和释放资源//sqlSession.commit();sqlSession.close();}
}

说明:

SqlSession:代表Java 程序和数据库之间的会话。(HttpSession是Java程序和浏览器之间的会话)

SqlSessionFactory:是 “生产” SqlSession 的 “工厂”。

工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的相关代码封装到一个 “工厂类” 中,以后都使用这个工厂类来 “生产” 我们需要的对象。


SqlSession 和 HttpSession 区别:

        HttpSession:工作在Web服务器上,属于表述层。

                                代表浏览器和Web服务器之间的会话。

        SqlSession:不依赖Web服务器,属于持久化层。

                                代表Java程序和数据库之间的会话。

4.ibatis方式的实现和原理

a.准备数据模型

use `mybatis-example`;create table student(sid int primary key  auto_increment,sname varchar(20)
);INSERT INTO `student` (sid, sname) VALUES (1, "tom");
INSERT INTO `student` (sid, sname) VALUES (2, "jerry");

 b.准备实体类

public class Student {private Integer sid;private String sname;public Student() {}public Student(Integer sid, String sname) {this.sid = sid;this.sname = sname;}public Integer getSid() {return sid;}public void setSid(Integer sid) {this.sid = sid;}public String getSname() {return sname;}public void setSname(String sname) {this.sname = sname;}public String toString() {return "Student{sid = " + sid + ", sname = " + sname + "}";}
}

 c.定义mapper.xml (不用定义mapper接口)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- ibatis方式进行数据库操作:1.不用写接口2.直接创建mapper.xml文件,内部编写sql语句3.namespace 无要求,随意声明一个字符串即可4.内部通过增删改查标签声明sql语句
-->
<mapper namespace="xx.yy"><!--  id 也无任何要求,随意声明即可--><select id="kkk" resultType="com.mihoyo.pojo.Student"><!-- #{id}代表动态传入的参数,并且进行赋值! -->select * from student where sid = #{id}</select>
</mapper>

d.准备MyBatis配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- environments表示配置Mybatis的开发环境,可以配置多个环境。在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 --><environments default="development"><!-- environment表示配置Mybatis的一个具体的环境 --><environment id="development"><!-- Mybatis的内置的事务管理器 --><transactionManager type="JDBC"/><!-- 配置数据源 --><dataSource type="POOLED"><!-- 建立数据库连接的具体信息 --><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis-example"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><mappers><!-- Mapper注册:指定Mybatis映射文件的具体位置 --><!-- mapper标签:配置一个具体的Mapper映射文件 --><!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 --><!--    对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 --><mapper resource="mappers/StudentMapper.xml"/></mappers></configuration>

e.运行和测试

    @Testpublic void test_02() throws IOException {//1.读取外部配置文件(mybatis-config.xml)InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");//2.创建sqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);//3.根据创建sqlSessionFactory创建sqlSession(每次业务创建一个,用完就释放)SqlSession sqlSession = sqlSessionFactory.openSession();//4.使用sqlSession提供的增删改查方法进行数据库操作//参数1:sql标签对应的标识(id 或者 namespace.id)//参数2:执行sql语句传入的参数Student stu = sqlSession.selectOne("xx.yy.kkk", 1);System.out.println("Student = " + stu);//5.提交事务(非查询语句)和释放资源//sqlSession.commit();sqlSession.close();}

 注意:

ibatis 方式的缺点:

        a. sql 语句标签对应的字符串标识太过随意,容易出现错误

        b. 执行 sql 语句传入的参数只能传一个,多个参数需要整合成一个 Map 集合中

        c. 返回值类型需要自己指定,不会提示,默认是 Object 类型

② sqlSession 提供了增删改查方法,但该方法并不是帮我们生成 sql 语句,而是帮我们查找对应的 sql 语句标签,再交给 mybatis 执行。

5.ibatis与mybatis之间的关系

Mybatis 会先使用 jdk 动态代理技术,生成一个代理对象。

① 代理对象 根据 接口的全限定符 和 方法名,内部拼接成 "接口的全限定符.方法名",再去调用 ibatis 对应的方法,去查找对应的 sql 语句标签,进行执行。

所以 mapper.xml 会严格限定 namespace 和 id,因为拼接后要根据它们去查找对应的 sql 标签。

这样就确保了 查找 sql 语句标签的过程不会出现错误!

② 代理对象也会将传入的参数进行整合,然后给 ibatis 对应的方法传入整合后的参数。

这样也优化了参数传递!

所以:mybatis 底层依然调用 ibatis,只不过底层进行了封装,有一套固定的模式!

二、Mybatis基本使用 

1.向 sql 语句传参

(1)mybatis日志输出配置

mybatis配置文件设计标签和顶层结构如下:

我们可以在 mybatis 的配置文件使用 settings 标签设置,输出运过程SQL日志!

通过查看日志,我们可以判定#{} 和 ${}的输出效果!

settings 设置项:

logImpl

指定 MyBatis 所用日志的具体实现,未指定时将自动查找。

SLF4J

LOG4J(3.5.9 起废弃)

LOG4J2

JDK_LOGGING

COMMONS_LOGGING

STDOUT_LOGGING

NO_LOGGING

未设置

日志配置: 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><settings><!-- 开启了 mybatis 的日志输出功能,选择使用System进行控制台输出 --><setting name="logImpl" value="STDOUT_LOGGING"/></settings>...
</configuration>

(2)#{ key } 和 ${ key } 

#{ key }占位符 + 赋值
${ key }字符串拼接

 #{ key } 形式:

 ${ key } 形式:

细节:

① 推荐使用 #{ key } 的形式,可以防止 SQL注入

② 存在即合理!#{ key } 只能替代值的位置,对于列名,容器名,关键字,都无法替代。

     比如:select * from 表 where 动态列名 = 动态值

     动态列名只能使用 ${ columnName },动态值可以使用 #{ columnValue }

所以:动态值,使用 #{ key }

           动态列名,容器名,关键字,使用 ${ key }

 2.数据输入

这里数据输入具体是指上层方法(例如Service方法)调用 Mapper接口 时,数据传入的形式。 

简单类型:只包含一个值的数据类型(单值类型) 

        基本数据类型:int、byte、short、double、……

        基本数据类型的包装类型:Integer、Character、Double、……

        字符串类型:String

复杂类型:包含多个值的数据类型(多值类型)

        实体类类型:Employee、Department、……

        集合类型:List、Set、Map、……

        数组类型:int[]、String[]、……

        复合类型:List<Employee>、实体类中包含集合……

(1)单个简单类型参数

Mapper 接口中抽象方法的声明:

    //根据id删除员工信息int deleteById(Integer id);//根据工资查询员工信息List<Employee> queryBySalary(Double salary);

 SQL语句:

    <delete id="deleteById">delete from t_emp where emp_id = #{empId}</delete><select id="queryBySalary" resultType="com.mihoyo.pojo.Employee">select emp_id empId,emp_name empName,emp_salary empSalaryfrom t_emp where emp_salary= #{salary}</select>

单个简单类型参数,在#{} 中可以随意命名,但是没有必要。通常还是使用和接口方法参数同名

(2)实体类类型参数 

Mapper 接口中抽象方法的声明:

    //插入员工数据(实体对象)int insertEmp(Employee employee);

SQL语句:

    <insert id="insertEmp">insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary})</insert>

当传入的是一个实体对象,key = 属性名

原理:Mybatis 会根据 #{} 中传入的数据,加工成getXxx()方法通过反射在实体类对象中调用这个方法,从而获取到对应的数据。填充到 #{} 解析后的问号占位符这个位置。

(3)零散的简单类型数据

方案一:使用 mybatis 默认机制,形参从左到右依次对应:arg0 / param1, arg1 / param2 ...

Mapper接口中抽象方法的声明:

    //根据员工姓名和工资查询员工信息List<Employee> queryByNameAndSalary(String name,Double salary);

 SQL语句:

    <select id="queryByNameAndSalary" resultType="com.mihoyo.pojo.Employee">select emp_id empId,emp_name empName,emp_salary empSalaryfrom t_emp where emp_name = #{arg0} and emp_salary = ${arg1}</select>

或者

    <select id="queryByNameAndSalary" resultType="com.mihoyo.pojo.Employee">select emp_id empId,emp_name empName,emp_salary empSalaryfrom t_emp where emp_name = #{param1} and emp_salary = ${param2}</select>

方案二:使用 @Param 注解指定(推荐)

Mapper接口中抽象方法的声明:

    //根据员工姓名和工资查询员工信息List<Employee> queryByNameAndSalary(@Param("eName") String name, @Param("eSalary") Double salary);

 SQL语句:

    <select id="queryByNameAndSalary" resultType="com.mihoyo.pojo.Employee">select emp_id empId,emp_name empName,emp_salary empSalaryfrom t_emp where emp_name = #{eName} and emp_salary = ${eSalary}</select>

(4)Map类型参数

Mapper 接口中抽象方法的声明:

    //插入员工数据,传入一个map(name=员工名,salary=员工薪水)int insertEmpMap(Map data);

SQL语句:

    <insert id="insertEmpMap">insert into t_emp(emp_name,emp_salary) values(#{name},#{salary})</insert>

当传入的是 map 类型的参数时,key = map 中的 key

测试:

    @Testpublic void test_01() throws IOException {//1.读取外部配置文件InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");//2.创建sqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);//3.获取sqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();//4.获取代理mapper对象EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);Map<String, Object> paramMap = new HashMap<>();//存入键值对paramMap.put("name", "zhangsan");paramMap.put("salary", 123.45);int result = mapper.insertEmpMap(paramMap);System.out.println("result = " + result);//5.提交事务或释放资源sqlSession.close();}

 3.数据输出

数据输出总体上有两种形式:

        增删改操作:默认返回的是受影响行数,直接使用 int 或 long 类型接收即可

        查询操作:查询结果的返回值类型可能不确定

我们需要做的是,指定查询的输出数据类型,并且插入场景下,实现主键数据回显示。

(1)单个简单类型

Mapper 接口中的抽象方法:

    //dml语句(插入,修改,删除)-->返回受影响的行数int deleteById(Integer id);//根据员工的id查询员工姓名String queryNameById(Integer id);//根据员工的id查询员工工资Double querySalaryById(Integer id);

SQL语句:

    <delete id="deleteById">delete from t_emp where emp_id = #{id}</delete><select id="queryNameById" resultType="java.lang.String">select emp_name from t_emp where emp_id = #{id}</select><select id="querySalaryById" resultType="double">select emp_salary from t_emp where emp_id = #{id}</select>

select 标签,通过 resultType 指定查询返回值类型。

resultType 的取值有两种:类的全限定符类的别名

mybatis 给我们常用的 Java 数据类型,都提供了别名,如下:

映射的类型别名
byte_byte
char_char (since 3.5.10)
char_character (since 3.5.10)
long_long
short_short
int_int
int_integer
double_double
float_float
boolean_boolean
Stringstring
Bytebyte
Characterchar (since 3.5.10)
Charactercharacter (since 3.5.10)
Longlong
Shortshort
Integerint
Integerinteger
Doubledouble
Floatfloat
Booleanboolean
Datedate
BigDecimaldecimal
BigDecimalbigdecimal
BigIntegerbiginteger
Objectobject
Date[]date[]
BigDecimal[]decimal[]
BigDecimal[]bigdecimal[]
BigInteger[]biginteger[]
Object[]object[]
Mapmap
HashMaphashmap
Listlist
ArrayListarraylist
Collectioncollection
Iteratoriterator

不用死记,规则如下:

        基本数据类型:int   double  ->  _int   _double

        包装数据类型:Integer   Double  ->  int / integer  double

        集合容器类型:Map   List   HashMap  ->  map   list   hashmap(小写即可)


Test:如果返回的类型,java 没有提供相应的别名,怎么办?

可以自定义别名,或 使用类的全限定符。

扩展(给自定义类定义别名):

① 给单独的类定义别名:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 定义别名 --><typeAliases><typeAlias type="com.mihoyo.pojo.Employee" alias="employee"/></typeAliases>...
</configuration>

② 批量定义别名

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 批量定义别名 --><typeAliases><package name="com.mihoyo.pojo"/></typeAliases>...
</configuration>

批量将包下的类给于别名,别名就是首字母小写的类名(如 Employee --> employee)

批量定义别名后(前提条件),不想使用批量的别名,可以使用 @Alias 注解重新定义别名

@Alias("emp")
public class Employee {...
}

(2)返回实体类对象

Mapper 接口的抽象方法:

    //返回单个自定义实体类型Employee queryById(Integer id);

SQL语句:

    <select id="queryById" resultType="com.mihoyo.pojo.Employee"><!-- 给每一个字段设置一个别名,让别名和Java实体类中属性名一致 -->select emp_id empId,emp_name empName,emp_salary empSalaryfrom t_emp where emp_id = ${empId}</select>

返回实体类对象时,resultType = 实体类型即可

注意:

当返回实体类对象时,存在一个默认要求:列名和属性名要一 一映射,因为底层会根据映射对应的属性名存入对象。

所以,sql 语句中需要给每个数据库表字段起别名

但这种方式过于麻烦,我们可以增加全局配置自动识别对应关系:

<!-- 在全局范围内对Mybatis进行配置 -->
<settings><!-- 将mapUnderscoreToCamelCase属性配置为true,表示开启自动映射驼峰式命名规则规则要求数据库表字段命名方式:单词_单词规则要求Java实体类属性名命名方式:首字母小写的驼峰式命名--><setting name="mapUnderscoreToCamelCase" value="true"/></settings>

在 Mybatis 全局配置文件中,进行如上配置,select语句中可以不给字段设置别名。

可以自动将字段名(XXX_YYY)变为属性名(xxxYyy)。

(3)返回 Map 类型

SQL查询返回的各个字段综合起来并不和任何一个现有的实体类对应,没法封装到实体类对象中,就可以用map集合进行存储,key = 查询的列名,value = 查询到的值

Mapper 接口的抽象方法:

    //查询部门的最高工资和平均工资Map<String,Object> selectEmpNameAndMaxSalary();

SQL语句:

    <select id="selectEmpNameAndMaxSalary" resultType="map">SELECTemp_name 员工姓名,emp_salary 员工工资,(SELECT AVG(emp_salary) FROM t_emp) 部门平均工资FROM t_emp WHERE emp_salary=(SELECT MAX(emp_salary) FROM t_emp)</select>

测试:

    @Testpublic void test_01() throws IOException {//1.读取外部配置文件InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");//2.创建sqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);//3.获取sqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();//4.获取代理mapper对象EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);Map<String, Object> resultMap = mapper.selectEmpNameAndMaxSalary();Set<Map.Entry<String, Object>> entrySet = resultMap.entrySet();for (Map.Entry<String, Object> entry : entrySet) {String key = entry.getKey();Object value = entry.getValue();System.out.println(key + "=" + value);}//5.提交事务或释放资源sqlSession.close();}

(4)返回 List 类型

Mapper 接口中抽象方法:

    //查询工资高于传入值的员工姓名List<String> queryNameBySalary(Double salary);//查询所有员工信息List<Employee> queryAll();

SQL语句:

    <select id="queryNameBySalary" resultType="string">select emp_name from t_emp where emp_salary > #{ salary }</select><select id="queryAll" resultType="employee">select * from t_emp</select>

返回的是 List 集合类型,此时不需要任何特殊处理,在 resultType 属性中指定泛型类型即可。

原理:

Mybatis 底层调用的是 ibatis,查询共有两个方法:selectOne 和 selectList,而 SelectOne 底层也是调用 selectList,所以底层本质全是按照 List 集合进行查找的

(5)返回主键值

Ⅰ. 自增长类型主键

Mapper 接口中的抽象方法:

    //员工插入int insertEmp(Employee employee);

SQL语句:

    <!-- 自增主键回显:useGeneratedKeys="true" 使用数据库自增的主键keyColumn="emp_id"      主键的列名keyProperty="empId"     接收主键值的属性名--><insert id="insertEmp" useGeneratedKeys="true" keyColumn="emp_id" keyProperty="empId">insert into t_emp(emp_name,emp_salary) value(#{empName},#{empSalary})</insert>

测试:

    @Testpublic void test_02() throws IOException {//1.读取外部配置文件InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");//2.创建sqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);//3.获取sqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();//4.获取代理mapper对象EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);//准备实体对象Employee employee = new Employee();employee.setEmpName("zhangsan");employee.setEmpSalary(999.0);System.out.println("插入前,empId = " + employee.getEmpId());//插入mapper.insertEmp(employee);System.out.println("插入后,empId = " + employee.getEmpId());//5.提交事务或释放资源sqlSession.close();}

运行结果:

Ⅱ. 非自增长类型主键 

a.准备数据库

create table teacher(t_id varchar(64) primary key ,t_name varchar(20)
)

b. 准备实体类

public class Teacher {private String tId;private String tName;public Teacher() {}public Teacher(String tId, String tName) {this.tId = tId;this.tName = tName;}public String getTId() {return tId;}public void setTId(String tId) {this.tId = tId;}public String getTName() {return tName;}public void setTName(String tName) {this.tName = tName;}public String toString() {return "Teacher{tId = " + tId + ", tName = " + tName + "}";}
}

Mapper 接口中的抽象方法:

public interface TeacherMapper {//插入老师信息int insertTeacher(Teacher teacher);
}

SQL语句:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.mihoyo.mapper.TeacherMapper"><insert id="insertTeacher">insert into teacher(t_id, t_name) values (#{tId},#{tName})</insert></mapper>

测试:

    @Testpublic void test_03() throws IOException {//1.读取外部配置文件InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");//2.创建sqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);//3.获取sqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();//4.获取代理mapper对象TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);//准备实体对象Teacher teacher = new Teacher();teacher.setTName("zhangsan");//用UUID随机生产主键idString id= UUID.randomUUID().toString().replace("-","");teacher.setTId(id);//插入mapper.insertTeacher(teacher);//5.提交事务或释放资源sqlSession.close();}

上述数据库的主键是一个字符串类型的数据,每次插入新数据都需要书写一段代码来生成主键值,过于麻烦(需要自己维护主键)。

改进方案:Mybatis 可以帮助我们对主键进行维护,无需自己生成主键值。

    <insert id="insertTeacher"><!-- 插入前,先指定一段sql语句,生成一个主键值order="BEFORE | AFTER" 表示该sql语句是在插入之前还是插入之后执行resultType              返回值类型keyProperty="tId"       查询结果给哪个属性赋值--><selectKey order="BEFORE" resultType="string" keyProperty="tId">select replace(uuid(),'-','')</selectKey>insert into teacher(t_id, t_name) values (#{tId},#{tName})</insert>

此时,测试代码中无需再自行维护主键:

    @Testpublic void test_03() throws IOException {//1.读取外部配置文件InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");//2.创建sqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);//3.获取sqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();//4.获取代理mapper对象TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);//准备实体对象Teacher teacher = new Teacher();teacher.setTName("zhangsan");System.out.println("插入前,tId = " + teacher.getTId());//插入mapper.insertTeacher(teacher);System.out.println("插入后,tId = " + teacher.getTId());//5.提交事务或释放资源sqlSession.close();}

 运行结果:

 (6)实体类属性和数据库字段对应关系

方案一:别名对应

    <select id="queryById" resultType="com.mihoyo.pojo.Employee">select emp_id empId,emp_name empName,emp_salary empSalaryfrom t_emp where emp_id = ${empId}</select>

方案二:全局配置自动识别驼峰式命名规则

<!-- 使用settings对Mybatis全局进行设置 -->
<settings><!-- 将xxx_xxx这样的列名自动映射到xxXxx这样驼峰式命名的属性名 --><setting name="mapUnderscoreToCamelCase" value="true"/></settings>

SQL语句中可以不使用别名:

    <select id="queryById" resultType="com.mihoyo.pojo.Employee">select emp_id,emp_name,emp_salaryfrom t_emp where emp_id = ${empId}</select>

还可以直接使用:select * 

方案三:使用 resultMap 自定义映射

    <!-- 声明resultMap标签,自定义映射规则id标识:<select resultMap="标识"type: 返回值类型--><resultMap id="eMap" type="employee"><!--id:主键映射关系result:普通字段的映射关系column属性用于指定字段名;property属性用于指定Java实体类属性名--><id column="emp_id" property="empId"/><result column="emp_name" property="empName"/><result column="emp_salary" property="empSalary"/></resultMap><select id="queryById" resultMap="eMap">select emp_id,emp_name,emp_salary from t_emp where emp_id = ${empId}<!-- 或 select * from t_emp where emp_id = ${empId} --></select>

使用 resultMap 标签定义映射关系,再在后面的SQL语句中引用这个对应关系。

三、Mybatis 多表映射

1.概念

开发中更多的是多表查询需求,这种情况我们如何让进行处理?

我们的学习目标:

        ① 多表查询语句使用

        ② 多表结果承接实体类设计

        ③ 使用ResultMap完成多表结果映射

(1)实体类设计方案

数据库中,多表关系是双向查看的关系:一对一,一对多,多对多

而在 Java 实体类设计时,多表关系是单向查看的关系:

对一:一个订单对应一个客户

//客户实体
public class Customer {private Integer customerId;private String customerName;}//订单实体
public class Order {private Integer orderId;private String orderName;private Customer customer;// 体现的是对一的关系}  

实体类设计:对一关系下,类中只要包含单个对方对象类型属性即可。

对多:一个客户对应多个订单

//订单实体
public class Order {private Integer orderId;private String orderName;}//客户实体
public class Customer {private Integer customerId;private String customerName;private List<Order> orderList;// 体现的是对多的关系
}

实体类设计:对多关系下,类中只要包含对方类型集合属性即可!


注意:

① 只有真实发生多表查询时,才需要设计和修改实体类,否则不提前设计和修改实体类。

② 无论多少张表联查,实体类设计都是两两考虑。

③ 在查询映射的时候,只需要关注本次查询相关的属性!例如:查询订单和对应的客户,就不要关注客户中的订单集合。

 (2)多表映射案例准备

数据库:

CREATE TABLE `t_customer` (`customer_id` INT NOT NULL AUTO_INCREMENT, `customer_name` CHAR(100), PRIMARY KEY (`customer_id`) );CREATE TABLE `t_order` ( `order_id` INT NOT NULL AUTO_INCREMENT, `order_name` CHAR(100), `customer_id` INT, PRIMARY KEY (`order_id`) ); INSERT INTO `t_customer` (`customer_name`) VALUES ('c01');INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o1', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o2', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o3', '1'); 

注意:

实际开发时,一般在开发过程中,不给数据库表设置外键约束
原因是避免调试不方便。 一般是功能开发完成,再加外键约束检查是否有bug。


实体类设计:

@Data    //    lombok
public class Customer {private Integer customerId;private String customerName;private List<Order> orderList;// 体现的是对多的关系}  @Data
public class Order {private Integer orderId;private String orderName;private Integer customerId;//外键private Customer customer;// 体现的是对一的关系
}

2.对一映射

需求:根据ID查询订单,以及订单关联的用户的信息

a. OrderMapper接口

public interface OrderMapper {//根据订单id查询订单信息和订单所对应的客户Order queryOrderById(Integer id);//订单信息中包含客户
}

b. OrderMapper.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.mihoyo.mapper.OrderMapper"><resultMap id="orderMap" type="order"><!-- 第一层映射 order对象属性 --><id column="order_id" property="orderId"/><result column="order_name" property="orderName"/><result column="customer_id" property="customerId"/><!-- 第二层映射 customer对象属性association :给对一的对象属性赋值property:对象属性名javaType:对象类型--><association property="customer" javaType="customer"><id column="customer_id" property="customerId"/><result column="customer_name" property="customerName"/></association></resultMap><select id="queryOrderById" resultMap="orderMap">select * from t_order tor join t_customer tcron tor.customer_id = tcr.customer_idwhere tor.order_id = #{id}</select></mapper>

对应关系可以参考下图:

细节:

 resultType 只支持一层映射,而 resultMap 可以支持深层映射(可以多层嵌套 association)。

c. mybatis-config.xml 配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><settings><!-- 开启了 mybatis 的日志输出功能,选择使用System进行控制台输出 --><setting name="logImpl" value="STDOUT_LOGGING"/></settings><!-- 批量起别名 --><typeAliases><package name="com.mihoyo.pojo"/></typeAliases><!-- environments表示配置Mybatis的开发环境,可以配置多个环境(开发、测试、生产环境)。在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。--><environments default="development"><!-- environment表示配置Mybatis的一个具体的环境 --><environment id="development"><!-- Mybatis的内置的事务管理器(取值:JDBC | MANAGED )JDBC:自动开启事务MANAGED:不会自动开启事务--><transactionManager type="JDBC"/><!-- 配置数据源(取值:POOLED | UNPOOLED)POOLED:mybatis会提供一个连接池,帮我们维护(性能没有第三方专门的连接池好)UNPOOLED:每次都新建或者释放连接--><dataSource type="POOLED"><!-- 建立数据库连接的具体信息 --><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis-example"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><mappers><!-- Mapper注册:指定Mybatis映射文件的具体位置 --><!-- mapper标签:配置一个具体的Mapper映射文件 --><!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 --><!--    对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 --><mapper resource="mappers/OrderMapper.xml"/></mappers></configuration>

d. 测试

public class MybatisTest {private SqlSession sqlSession;@BeforeEach // 每次都测试方法之前,先走的初始化方法public void init() throws IOException {//1.读取外部配置文件InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");//2.创建sqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);//3.获取sqlSessionsqlSession = sqlSessionFactory.openSession();}@AfterEach  // 每次都测试方法之后,调用的结束方法public void clean(){//5.提交事务或释放资源sqlSession.close();}@Testpublic void testToOne() {//根据id查询订单和对应的客户OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);Order order = mapper.queryOrderById(1);System.out.println(order);System.out.println(order.getCustomer());}
}

3.对多映射

需求:查询所有客户和客户关联的订单信息

a. CustomerMapper 接口:

public interface CustomerMapper {//查询所有客户信息以及客户对应的订单信息List<Customer> queryCustomerAll();
}

b. CustomerMapper.xml 文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.mihoyo.mapper.CustomerMapper"><resultMap id="customerMap" type="customer"><id column="customer_id" property="customerId"/><result column="customer_name" property="customerName"/><!-- 给对多的集合对象属性赋值property:集合属性名ofType:集合的泛型类型--><collection property="orderList" ofType="Order"><id column="order_id" property="orderId"/><result column="order_name" property="orderName"/><result column="customer_id" property="customerId"/></collection></resultMap><select id="queryCustomerAll" resultMap="customerMap">select * from t_customer tcrjoin t_order toron tcr.customer_id=tor.customer_id</select>
</mapper>

对应关系可以参考下图:

c. Mybatis 全局注册Mapper文件 

<!-- 注册Mapper配置文件:告诉Mybatis我们的Mapper配置文件的位置 -->
<mappers><!-- 在mapper标签的resource属性中指定Mapper配置文件以“类路径根目录”为基准的相对路径 --><mapper resource="mappers/OrderMapper.xml"/><mapper resource="mappers/CustomerMapper.xml"/>
</mappers>

d. 测试:

    @Testpublic void testToMany(){CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);List<Customer> customers = mapper.queryCustomerAll();System.out.println("customers = " + customers);for (Customer customer : customers) {List<Order> orderList = customer.getOrderList();System.out.println("orderList = " + orderList);}}

总结:

关联关系配置项关键词所在配置文件和具体位置
对一association标签 / javaType属性 / property属性Mapper配置文件中的resultMap标签内
对多collection标签 / ofType属性 / property属性Mapper配置文件中的resultMap标签内

4.多表映射优化

setting属性属性含义可选值默认值
autoMappingBehavior

指定 MyBatis 应如何自动映射列到字段或属性。

NONE 表示关闭自动映射

PARTIAL 只会自动映射没有定义嵌套结果映射的字段(对于只有一层的 result 标签,可以自动映射)

FULL 会自动映射任何复杂的结果集(无论是否多少层,都自动映射)

NONE, PARTIAL,

FULL

PARTIAL

将 autoMappingBehavior 设置为 full,进行多表 resultMap 映射的时候,可以省略符合列和属性命名映射规则的 result 标签。

注意:

只有 列名=属性名,或者开启驼峰映射(XXX_YYY -> xxxYyy)的 reult 标签才能自动映射。


修改 mybati-sconfig.xml:

    <settings><!-- 将mapUnderscoreToCamelCase属性配置为true,表示开启自动映射驼峰式命名规则 --><setting name="mapUnderscoreToCamelCase" value="true"/><!--开启resultMap自动映射 --><setting name="autoMappingBehavior" value="FULL"/></settings>

此时,CustomerMapper.xml 中的自定义映射可省略为:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.mihoyo.mapper.CustomerMapper"><resultMap id="customerMap" type="customer"><id column="customer_id" property="customerId"/><collection property="orderList" ofType="Order"><id column="order_id" property="orderId"/></collection></resultMap><select id="queryCustomerAll" resultMap="customerMap">select * from t_customer tcrjoin t_order toron tcr.customer_id=tor.customer_id</select>
</mapper>

四、MyBatis动态语句

经常遇到很多按照很多查询条件进行查询的情况,比如智联招聘的职位搜索等。

其中经常出现很多条件不取值的情况,在后台应该如何完成最终的SQL语句呢? 

动态 SQL 是 MyBatis 的强大特性之一!

1. if 和 where标签 

需求:根据多个条件查询员工信息,但条件可能为空

 Mapper 接口中的抽象方法:

public interface EmployeeMapper {//根据员工的姓名和工资查询员工信息List<Employee> query(@Param("name") String name, @Param("salary") Double salary);
}

SQL语句:

    <select id="query" resultType="employee">select * from t_emp<where><if test="name!=null">emp_name = #{name}</if><if test="salary !=null and salary &gt; 0">and emp_salary = #{salary}</if></where></select>

细节:

① if 标签可以通过 test 属性进行判断,如果为 true,就将标签内的 sql 语句进行拼接。

     test 属性中的判断语句:key  比较符号  值(且:and    或:or)

     大于和小于可以直接写符号(< >),但不推荐,因为低版本的 mybatis 可能会识别成标签的开始或者结束符号。推荐使用实体符号(大于:&gt;        小于:&lt; )

② where 标签的作用:

        a.只要 where 内部有任何一个 if 满足,自动添加 where 关键字,否则不会添加 where 关键字

        b.自动去掉条件中多余的 and 和 or 关键字


测试:

    @Testpublic void test_01() {//根据id查询订单和对应的客户EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);List<Employee> list = mapper.query(null, 100d);System.out.println("list = " + list);}

2. set 标签

 Mapper 接口中的抽象方法:

    //根据员工id更新员工数据(要求name和salary不为空时才更新)int update(Employee employee);

 SQL语句:

    <update id="update">update t_emp<set><if test="empName!=null">emp_name = #{empName},</if><if test="empSalary !=null">emp_salary = #{empSalary}</if></set>where emp_id = #{empId}</update>

细节:

set 标签的作用:

        a. 自动添加 set 关键字

        b. 自动去掉多余的逗号(,)

注意:

update 方法中,必须保证至少一个 if 满足。因为如果都不满足,不管有没有 set 关键字,语法都是错的!

3. trim标签(了解)

使用 trim 标签控制条件部分两端是否包含某些字符

prefix属性指定要动态添加的前缀
suffix属性指定要动态添加的后缀
prefixOverrides属性指定要动态去掉的前缀,使用“|”分隔有可能的多个值
suffixOverrides属性指定要动态去掉的后缀,使用“|”分隔有可能的多个值

例如,前两个案例中的 SQL 语句可修改为:

    <select id="query" resultType="employee">select * from t_emp<trim prefix="where" prefixOverrides="and|or"><if test="name!=null">emp_name = #{name}</if><if test="salary !=null and salary &gt; 0">and emp_salary = #{salary}</if></trim></select><update id="update">update t_emp<trim prefix="set" suffixOverrides=","><if test="empName!=null">emp_name = #{empName},</if><if test="empSalary !=null">emp_salary = #{empSalary}</if></trim>where emp_id = #{empId}</update>

4. choose/when/otherwise 标签

在多个分支条件中,仅执行一个。

从上到下依次执行条件判断,遇到的第一个满足条件的分支会被采纳,被采纳分支后面的分支都将不被考虑。

如果所有的when分支都不满足,那么就执行otherwise分支。(类似于 switch-case-default)。


 Mapper 接口中的抽象方法:

    /*根据两个条件查询如果name不为空,根据name查询如果name为空,salary不为空,根据salary查询都为空,查询全部*/List<Employee> queryChoose(@Param("name") String name, @Param("salary") Double salary);

 SQL语句:

    <select id="queryChoose" resultType="employee">select * from t_emp where<choose><when test="name!=null">emp_name = #{name}</when><when test="salary !=null">and emp_salary = #{salary}</when><otherwise>1=1</otherwise></choose></select>

5. foreach标签

 Mapper 接口中的抽象方法:

    //根据id进行批量查询List<Employee> queryBatch(@Param("ids") List<Integer> ids);//批量插入int insertBatch(@Param("list") List<Employee> employeeList);//批量更新int updateBatch(@Param("list") List<Employee> employeeList);

 SQL语句:

    <select id="queryBatch" resultType="employee">select * from t_emp where emp_id in<!--collection属性:要遍历的集合item属性:遍历集合的过程中能得到每一个具体对象,在item属性中设置一个名字,将来通过这个名字引用遍历出来的对象separator属性:每个遍历项之间的分隔符open属性:遍历之前要添加的字符串close属性:遍历之后要追加的字符串index属性:这里起一个名字,便于后面引用遍历List集合,这里能够得到List集合的索引值遍历Map集合,这里能够得到Map集合的key--><foreach collection="ids" open="(" separator="," close=")" item="id">#{id}</foreach></select><insert id="insertBatch">insert into t_emp(emp_name,emp_salary) values<foreach collection="list" separator="," item="employee">(#{employee.empName},#{employee.empSalary})</foreach></insert><update id="updateBatch"><foreach collection="list" item="employee" separator=";">update t_emp set emp_name = #{employee.empName},emp_salary = #{employee.empSalary}where emp_id = #{employee.empId}</foreach></update>

注意:

① 上述批量查询和批量插入,本质上都是一条 sql 语句执行。

而批量更新,事实上是一次性执行多条 sql 语句,中间用分号隔开。

当一次性发送多条SQL语句让数据库执行,此时需要在数据库连接信息的URL地址中设置:

<property name="url" value="jdbc:mysql://localhost:3306/mybatis-example?allowMultiQueries=true"/>

 ② 关于 foreach 标签的 collection 属性:

如果没有给接口中 List 类型的参数使用 @Param 注解指定一个具体的名字,那么默认可以使用collection 或 list 或 arg0 来引用要遍历的集合。

但在实际开发中,为了避免隐晦的表达造成一定的误会,建议使用 @Param 注解明确声明变量的名称,然后 collection 属性使用注解指定的名称。

6. sql 片段提取

    <!-- 抽取重复的 sql 片段 --><sql id="selectSql">select * from t_emp</sql><select id="query" resultType="employee"><!--引用 sql 片段--><include refid="selectSql"/><where><if test="name!=null">emp_name = #{name}</if><if test="salary !=null and salary &gt; 0">and emp_salary = #{salary}</if></where></select>

五、Mybatis 高级扩展

1. Mapper 批量映射优化

Mapper 配置文件很多时,在全局配置文件中一个一个注册太麻烦,我们希望可以批量注册。

配置方式:

<mappers><!-- 指定 Mapper 接口 和 Mapper.xml 打包后所在的包 --><package name="com.mihoyo.mapper"/>
</mappers>

注意:

批量 mapper 配置文件指定,package = “Mapper 接口 和 Mapper.xml 打包后共同所在的包”

所以,必要满足 2 个要求:

        ① 要求 Mapper 接口 和 Mapper.xml 的命名必须相同

        ② 要求 Mapper 接口 和 Mapper.xml 的所在的包名必须相同(最终打包的位置相同)

                方案一:xml 文件也加入到接口所在包,并且 pom 文件中在 build 中配置 resources,来防止我们资源导出失败的问题

    <build><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>false</filtering></resource></resources></build>

                方案二:resource 文件夹也创建和 mapper 接口一样的文件夹结构

细节:resources 目录下创建多级目录,用 / 分割,而不是 . (即 com/mihoyo/mapper )。

2. 插件和分页插件 PageHelper

(1)插件机制

MyBatis 对插件进行了标准化的设计,并提供了一套可扩展的插件机制。

插件可以在用于语句执行过程中进行拦截,并允许通过自定义处理程序来拦截和修改 SQL 语句、映射语句的结果等。

具体来说,MyBatis 的插件机制包括以下三个组件:

Interceptor(拦截器):定义一个拦截方法 intercept,该方法在执行 SQL 语句、执行查询、查询结果的映射时会被调用。

Invocation(调用):实际上是对被拦截的方法的封装,封装了Object target、Method method 和 Object[] args 这三个字段。

InterceptorChain(拦截器链):对所有的拦截器进行管理,包括将所有的链接成一条链,并在执行 SQL 语句时按顺序调用。

(2)PageHelper 插件使用

PageHelper 是 MyBatis 中比较著名的分页插件,它提供了多种分页方式(例如 MySQL 和 Oracle 分页方式),支持多种数据库,并且使用非常简单。

a. pom.xml 引入依赖

<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.1.11</version>
</dependency>

b. mybatis-config.xml配置分页插件

    <!-- mybatis内部配置插件,进行sql语句拦截--><plugins><plugin interceptor="com.github.pagehelper.PageInterceptor"><!-- 分页插件对应的数据库类型 --><property name="helperDialect" value="mysql"/></plugin></plugins>

c. mapper接口 和 mapper.xml 

public interface EmployeeMapper {List<Employee> queryList();
}
<mapper namespace="com.mihoyo.mapper.EmployeeMapper"><select id="queryList" resultType="employee"><!-- 正常编写sql语句即可,不要使用 ; 结尾 -->select * from t_emp where emp_salary > 100</select>
</mapper>

注意:sql 语句末尾不用加 ; 

d. 分页插件使用

    //使用分页插件@Testpublic void test_01() {EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);//调用方法之前,先设置分页数据(当前是第几页,每页显示多少数据)PageHelper.startPage(2,2);List<Employee> list = mapper.queryList();//将查询数据封装到一个 PageInfo 的分页实体类中PageInfo<Employee> pageInfo = new PageInfo<>(list);//从 pageInfo 中获取分页的数据List<Employee> list1 = pageInfo.getList();//获取当前页的数据System.out.println("list1 = " + list1);int pages = pageInfo.getPages();//获取总页数System.out.println("pages = " + pages);long total = pageInfo.getTotal();//获取总条数System.out.println("total = " + total);int pageNum = pageInfo.getPageNum();//获取当前页数System.out.println("pageNum = " + pageNum);int pageSize = pageInfo.getPageSize();//获取每页容量System.out.println("pageSize = " + pageSize);//...}

注意:不能将两条查询装到一个分页区(PageInfo)。

运行结果:

 3. 逆向工程和MybatisX插件

(1)ORM思维介绍

ORM(Object-Relational Mapping,对象-关系映射)是一种将数据库和面向对象编程语言中的对象之间进行转换的技术。

它将对象和关系数据库的概念进行映射,最后我们就可以通过方法调用进行数据库操作。

最终,让我们可以使用面向对象思维进行数据库操作

ORM 框架的两种方式:

半自动 ORM :通常需要程序员手动编写 SQL 语句或者配置文件,将实体类和数据表进行映射,还需要手动将查询的结果集转换成实体对象。

全自动 ORM :将实体类和数据表进行自动映射,使用 API 进行数据库操作时,ORM 框架会自动执行 SQL 语句并将查询结果转换成实体对象,程序员无需再手动编写 SQL 语句和转换代码。

(2)逆向工程

MyBatis 的逆向工程是一种自动化生成持久层代码和映射文件的工具,它可以根据数据库表结构和设置的参数生成对应的实体类、Mapper.xml 文件、Mapper 接口等代码文件,简化了开发者手动生成的过程。逆向工程使开发者可以快速地构建起 DAO 层,并快速上手进行业务开发。

简单来说:逆向工程使得半自动 orm 框架,也能实现单表的 crud 自动生成,向全自动 orm 迈进!

注意:逆向工程只能生成单表 crud 的操作,多表查询依然需要我们自己编写! 

 (3)逆向工程插件 MyBatisX 使用

MyBatisX 是一个 MyBatis 的代码生成插件,可以通过简单的配置和操作快速生成 MyBatis Mapper、pojo 类和 Mapper.xml 文件。

步骤:

① 安装插件

② 使用 IntelliJ IDEA连接数据库

a. 连接数据库

 

 b. 填写信息

c. 展示库表

 

d. 逆向工程使用

 

③ 查看生成结果

 

注意:逆向工程插件 MybatisX 只能生成单表 crud 的操作,多表查询依然需要我们自己编写!

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

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

相关文章

存储课程学习笔记5_iouring的练习(io_uring,rust_echo_bench,fio)

我们知道&#xff0c;在处理大量高并发网络时&#xff0c;一般考虑并发&#xff0c;以及设计对应的方案&#xff08;比如select,poll,epoll&#xff09;等。 那么如果频繁进行文件或者磁盘的操作&#xff0c;如何考虑性能和并发&#xff0c;这里就可以考虑用到io_uring。 0&a…

RK3588镜像打包制作,替换文件系统

1.在开发板上安装async apt-get async 2.在另一台linux机器上执行命令拷贝文件系统 注意&#xff1a; 这里使用root权限或者账户 mkdir rootfs rsync -avx root192.168.1.3:/ rootfs 3.制作空镜像文件 先去开发板上验证自己的系统使用了多少空间&#xff0c;然后输入命令制…

rancker 图形化界面

rancker 图形化界面 图形化界面进行k8s集群的管理 rancher自带监控————普罗米修斯 #在master和两个node上都操作 [rootmaster01 opt]# rz -E rz waiting to receive. [rootmaster01 opt]# docker load -i rancher.tar ​ #在master上操作 [rootmaster01 opt]# docker pul…

828华为云征文|华为云Flexus X搭建借贷管理系统、二次开发借贷小程序 前端源码uniapp

在华为云828 B2B企业节的盛宴中&#xff0c;Flexus X实例以其卓越的算力性能和灵活的资源配置脱颖而出。对于追求极致性能、渴望在借贷管理、电商交易等场景中脱颖而出的您来说&#xff0c;Flexus X无疑是最佳拍档。搭载创新加速引擎&#xff0c;让您的自建MySQL、Redis、Nginx…

浙大数据结构:04-树6 Complete Binary Search Tree

这道题利用了完全二叉树的性质&#xff0c;我也参考了一些代码写的。 &#xff08;自己一开始写了别的方法&#xff0c;但一直过不了最后一个测试点&#xff0c;红温了&#xff09; 机翻&#xff1a; 1、条件准备 用vector存输入的数据&#xff0c;另一个数组存输出的结果&a…

实战外网配置——光猫桥接+路由器PPPoE拨号+防火墙外网链路健康检查+外网流量负载均衡

一、适用场景&#xff1a; 1、企业规模较大时&#xff0c;1条公网带宽流量可能不足&#xff0c;需要用到多条公网出口时。 2、企业有业务需要静态ip映射&#xff0c;但是因静态ip专线价格较高&#xff0c;所以需要拨号光纤承载较多的下行流量。 3、当公网出口有多条链路&#…

学习笔记 - 知识图谱的符号表示方法

学习笔记 - 知识图谱的符号表示方法 说明&#xff1a; 首次发表日期&#xff1a;2024-09-13个人阅读学习并摘录成笔记 知识表示的相关名词定义 以下内容摘录自 Knowledge Graphs Applied 2.3小节&#xff0c;然后AI翻译人工润色。 实体&#xff08;Entities&#xff09;—表…

Python | 练习作业 2

为学生登录系统新增搜索功能。 第二天作业的解题思路&#xff1a; # 1.创建一个空列表保存搜索结果 # 2.让用户输入要搜索的内容 # 3.遍历学生信息&#xff0c;检查学生的id name age gender score # 中的属性值 是否跟用户搜索的内容一致 # 4.如果有一致的属性 那么就将该学生…

TikTok运营需要的独立IP如何获取?

TikTok作为当下炙手可热的社交媒体平台&#xff0c;吸引了众多个人创作者和企业进驻。在进行TikTok运营时&#xff0c;许多经验丰富的用户都倾向于选择独立IP。那么&#xff0c;TikTok运营为什么需要独立IP&#xff1f;又该如何获取呢&#xff1f;本文将详细为您解答这些问题。…

vue2基础系列教程之v-model及面试高频问题

v-model是表单组件里面的核心知识点&#xff0c;这个指令给我们写表单业务带来了很大的方便。 元素标签上的 v-model 指令用于双向绑定数据,它是一个语法糖&#xff0c;可以用于代替 v-bind:value 和 input 例如&#xff1a;<input v-model"message" placeholder…

Springboot中mybatis的使用

一.创建Springboot项目并加载依赖 1.利用IDEA创建SpringBoot项目&#xff0c;并勾选必须依赖&#xff0c;步骤如下&#xff08;IDEA版本为2024版&#xff09; 注意&#xff1a; 1.首先更换镜像源&#xff0c;否则加载配置环境比较慢&#xff0c;网上搜阿里的镜像源就行。 2…

Python数据类型详解:这12个类型你都知道吗?

在Python中&#xff0c;数据类型是编程的基石&#xff0c;它们定义了可以操作的数据的种类。Python是一种动态类型语言&#xff0c;意味着你不需要显式地声明变量的类型&#xff1b;Python解释器会自动推断出变量所存储数据的类型。Python提供了多种内置数据类型&#xff0c;这…

c++类和对象(3):默认成员函数(下)

1.拷贝构造函数 如果⼀个构造函数的第⼀个参数是自身类类型的引用&#xff0c;且任何额外的参数都有默认值&#xff0c;则此构造函数也叫做拷贝构造函数&#xff0c;也就是说拷贝构造是⼀个特殊的构造函数。 c规定&#xff1a;类类型的传值传参必须用拷贝构造 1.1拷贝构造函数…

OpenAI 刚刚推出 o1 大模型!!突破LLM极限

北京时间 9 月 13 日午夜&#xff0c;OpenAI 正式发布了一系列全新的 AI 大模型&#xff0c;专门用于应对复杂问题。 这一新模型的出现代表了一个重要突破&#xff0c;其具备的复杂推理能力远远超过了以往用于科学、代码和数学等领域的通用模型&#xff0c;能够解决比之前更难的…

近期常见软件测试面试题

1、软件的生命周期&#xff1a; 又称为软件生命期、生存期&#xff0c;是指从形成开发软件概念起&#xff0c;所开发的软件使用以后&#xff0c;直到失去使用价值消亡为止的整个过程。 一般来说&#xff0c;整个生命周期包括&#xff1a;计划&#xff08;定义&#xff09;、开…

上汽大众:存储成本节约85%,查询性能提升5倍|OceanBase案例

近日&#xff0c;上汽大众汽车有限公司&#xff08;简称“上汽大众”&#xff09;的积分卡券等关键业务系统&#xff0c;已成功升级至 OB Cloud 云数据库。借助 OceanBase 原生分布式数据库的卓越性能与先进技术&#xff0c;实现了存储成本的大幅降低&#xff0c;高达85%&#…

初级软件测试面试题汇总

一、请描述如何划分缺陷与错误严重性和优先级别&#xff1f; 给软件缺陷与错误划分严重性和优先级的通用原则&#xff1a; &#xff08;1&#xff09;表示软件缺陷所造成的危害和恶劣程度。 &#xff08;2&#xff09;优先级表示修复缺陷的重要程度和次序。 严重性&#xf…

Python 课程6-Pandas 和 Matplotlib库

前言 在数据科学和数据分析领域&#xff0c;Pandas 和 Matplotlib 是两个最常用的 Python 库。Pandas 主要用于数据处理和分析&#xff0c;而 Matplotlib 则用于数据的可视化。它们的结合能够帮助我们快速、直观地展示数据的趋势和规律。在这篇详细的教程中&#xff0c;教程中将…

自动驾驶:LQR、ILQR和DDP原理、公式推导以及代码演示(六、ILQR正则化和line search)

&#xff08;六&#xff09;ILQR正则化和line search 1. ILQR正则化 在iLQR中&#xff0c;我们通常线性化系统动力学并对目标函数进行二阶近似。在反向传播步骤中&#xff0c;我们需要计算逆矩阵&#xff08;控制变量对目标函数的二阶导数矩阵&#xff09;&#xff0c;用以更…

驰域货车四路监控ts视频格式化恢复方法

不少大货车都使用了驰域货车监控&#xff0c;一般是至少装四路&#xff0c;前后左右&#xff0c;有的还会车顶加一路。驰域货车记录仪特殊的地方在于&#xff1a;其采用了一种上古时期的视频格式----TS视频流。 故障存储: 128G卡/fat32 故障现象: 客户提供的信息是格式化后…