Mybatis概述
MyBatis 作为一款优秀的持久层框架,有着诸多显著的特点和优势:
一、发展历程与背景
MyBatis 最初是 Apache 的开源项目 iBatis。在 2010 年 6 月,项目从 Apache Software Foundation 迁移到了 Google Code,同时 iBatis 3.x 正式更名为 MyBatis。这一转变标志着其发展进入了新的阶段,吸引了众多开发者的关注和使用。
二、便捷的数据库操作
它避免了几乎所有的 JDBC 代码手动设置参数以及手动获取结果集的操作。将基本的 JDBC 常用接口进行封装,开发者只需对外提供操作指令,极大地提高了开发效率。例如,以往使用 JDBC 进行数据库操作时,需要手动编写大量重复的代码来设置参数、执行查询并处理结果集,而 MyBatis 则简化了这些繁琐的步骤,让开发者能够更加专注于业务逻辑的实现。
三、灵活的配置与映射
MyBatis 可以使用 XML 或注解来配置和映射。通过 XML 配置文件,可以清晰地定义 SQL 语句、参数映射以及结果集的映射关系。同时,注解方式也为开发者提供了更加简洁的配置选择,尤其适用于小型项目或简单的数据库操作场景。
它能够将数据库中的记录映射成 Java 的 POJO(普通的 Java 对象),实现了对象关系映射(ORM)。这种映射机制使得数据库中的数据能够以面向对象的方式进行操作和处理,方便了业务逻辑的编写和维护。
四、强大的功能特性
-
支持动态 SQL:动态 SQL 功能使得 MyBatis 能够根据不同的条件动态地生成 SQL 语句。在实际应用中,这对于处理复杂的查询条件非常有用。例如,根据用户输入的不同参数,动态地构建查询语句,提高了查询的灵活性和效率。
-
数据缓存:MyBatis 支持数据缓存,可以减少对数据库的访问次数,提高应用的性能。缓存可以分为一级缓存和二级缓存,开发者可以根据实际需求进行配置和使用。
五、丰富的资源与社区支持
MyBatis 中文官网(mybatis – MyBatis 3 | 简介)提供了详细的文档、教程和示例代码,方便开发者快速上手和深入学习。同时,MyBatis 拥有活跃的社区,开发者可以在社区中交流经验、解决问题,获取最新的技术动态和最佳实践。
MyBatis 环境搭建
1.创建学习与测试项目
2023版的idea选择普通的java项目,以前选择Maven项目
2.导入 MyBatis jar 包,mysql 数据库驱动包
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.2</version> </dependency>
3. 创建 MyBatis 全局配置文件
在resources文件下创建mybatis.xml文件
<?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> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="" /> <property name="url" value="" /> <property name="username" value="" /> <property name="password" value=""/> </dataSource> </environment> </environments> </configuration>
4 定义接口与模型类
确定接口方法
例如,定义一个用户接口,可能包含以下方法:
package com.example.mybatisdemo.dao; import com.example.mybatisdemo.entity.User; import java.util.List; public interface UserDao { // 查询所有用户 List<User> findAllUsers(); // 根据用户 ID 查询用户 User findUserById(int id); // 插入用户 int insertUser(User user); // 更新用户 int updateUser(User user); // 删除用户 int deleteUser(int id); } public class Admin { private int id; private String account; private String password; private String gender; }/*(可以在配置文件中为类配置别名 <!--为类配置别名--> <typeAliases> <!--<typeAlias type="com.ffyc.mybatispro.model.Admin" alias="Admin"></typeAlias>--> <package name="com.ffyc.mybatispro.model"/> </typeAliases>)*/
5 添加注解或配置映射文件
1.使用注解方式:
在接口方法上添加 @Select
、@Insert
、@Update
和 @Delete
等注解,指定对应的 SQL 语句。
例如:
package com.ffyc.mybatispro.dao; import com.ffyc.mybatispro.model.Admin; import org.apache.ibatis.annotations.Param; import java.util.List; public interface AdminDao { Admin findAdminById(int id);//要配置映射文件 Admin findAdminByaccount(String account); Admin login(@Param("acc") String account,@Param("pwd") int password); Admin login1(Admin admin); @Insert("insert into admin(name)values(#{name})")//这是注解 void insertAdmin(Admin admin); void deleteAdmin(int id); void updataAdmin(Admin admin); @Select("select id,name from admin") List<Admin> admins(); List<Admin> findAdmin(@Param("order") String order); }
2.使用 XML 映射文件方式:
创建一个与接口同名的 XML 映射文件,在resources文件中创建mappers文件,在其下创建 AdminMapper.xml
文件
在文件中定义 SQL 语句,并通过 <mapper>
标签将接口与映射文件关联起来。
例:
<?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"> <!-- sql映射文件 主要写sql实现 --> <mapper namespace="com.ffyc.mybatispro.dao.AdminDao"> <insert id="insertAdmin" parameterType="Admin" useGeneratedKeys="true" keyProperty="id" keyColumn="id"> insert into admin(account,password,gender) values(#{account},#{password},#{gender}) </insert> <update id="updataAdmin" parameterType="Admin"> update admin set account=#{account},password=#{password} where id=#{id} </update> <delete id="deleteAdmin" parameterType="int"> delete from admin where id = #{id} </delete> <select id="findAdminById" parameterType="int" resultType="com.ffyc.mybatispro.model.Admin"> select * from admin where id = #{id} </select> <!-- #{} 和${}区别 #{} 占位符,是经过预编译的,编译好 SQL 语句再取值,#方式能够防止 sql 注入 #{}:delete from admin where id=#{id} 结果: delete from admin where id = ? ${}会将将值以字符串形式拼接到 sql 语句, ${ }方式无法防止 Sql 注入 ${}: delete from admin where id=’${id}’ 结果: delete from admin where id=’1’ * 一般是#{ } 向 sql 传值使用, 而使用${ }向 sql 传列名 * 区别: ************************************************************************************* 底层实现不同: #{}采用预编译 防止 sql 注入 更安全 **** 一般用于想sql的列传值 ${} 采用字符串拼接,直接将值拼接到sql **** 一般用于想sql动态传递列名 例如:排序时,order by 后面的列名可改变 select后的列名也可以自由选择 --> <select id="findAdminByaccount" parameterType="string" resultType="com.ffyc.mybatispro.model.Admin"> select * from admin where account = #{account} </select> <select id="login" resultType="com.ffyc.mybatispro.model.Admin"> select * from admin where account = #{acc} and password = #{pwd} </select> <select id="login1" parameterType="Admin" resultType="com.ffyc.mybatispro.model.Admin"> select * from admin where account = #{account} and password = #{password} </select> <!-- 使用resultMap标签,对查询结果进行自定义映射 type 是返回结果的类型 --> <resultMap id="adminMap" type="com.ffyc.mybatispro.model.Admin"> <id column="adminid" property="id"></id> <result column="account" property="account"></result> </resultMap> <select id="findAdmin" resultMap="adminMap"> select id adminid,account from admin order by ${order} </select> </mapper>
要在mybatis.xml文件里配置映射
6.测试 MyBatis
安装插件:
在Plugins里:搜索并安装MybatisX
1、测试准备
1.读取配置文件:
使用 Resources.getResourceAsReader("mybatis-config.xml")
方法读取 MyBatis 的配置文件。这个配置文件通常包含数据库连接信息、映射文件路径等重要配置。
例如:
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
2.创建SqlSessionFactory
通过 SqlSessionFactoryBuilder
类的 build
方法,传入配置文件的 Reader
对象来创建 SqlSessionFactory
。SqlSessionFactory
是创建 SqlSession
的工厂,负责管理数据库连接等资源。
例如:
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
2、执行测试操作
1.创建SqlSession
使用 SqlSessionFactory
的 openSession
方法创建一个 SqlSession
。SqlSession
代表与数据库的一次会话,通过它可以执行 SQL 语句并获取结果。
例如:
SqlSession sqlSession = sessionFactory.openSession();
2.获得接口代理对象:
使用 sqlSession.getMapper(接口.class)
方法获得 MyBatis 为接口生成的代理对象。这个代理对象实现了接口中定义的方法,并将方法调用转换为对数据库的操作。
例如:
YourInterface mapper = sqlSession.getMapper(YourInterface.class);
3、API 接口说明
-
SqlSessionFactory
接口:用于创建
SqlSession
。由于创建SqlSessionFactory
的开销较大,在整个应用过程中通常只创建一次,不建议多次创建。它负责管理数据库连接等资源,确保高效地与数据库进行交互。 -
SqlSession
接口:代表与数据库的一次会话。它封装了对数据库操作的方法,如执行 SQL 语句、提交事务、回滚事务等。在与数据库会话完成后,应及时关闭
SqlSession
以释放资源,防止资源泄漏。
通过以上步骤,可以有效地测试 MyBatis 与数据库的交互,确保数据访问层的正确性和稳定性。同时,了解 SqlSessionFactory
和 SqlSession
的作用和使用方法,有助于更好地使用 MyBatis 进行开发。
4.实例:
package com.ffyc.mybatispro.test; import com.ffyc.mybatispro.dao.AdminDao; import com.ffyc.mybatispro.model.Admin; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.Reader; public class Test1 { public static void main(String[] args) throws IOException { //1.mybatis读取配置文件 Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml"); //创建SqlSessionFactory,负责创建SqlSession对象(连接数据库的会话对象,类似Connection) SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader); //3.创建SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //4.创建接口的代理对象 吧不能把 AdminDao adminDao = sqlSession.getMapper(AdminDao.class); //5.调用 Admin admin = adminDao.findAdminById(1);//让代理对象帮我们调用映射文件中榆次接口中同名称的方法 System.out.println(admin); //6.关闭会话对象 sqlSession.close(); } }
Mybatis-Dao 层面向接口开发
测试时所运用的
面向接口开发需要遵循以下规范:
1、 Mapper.xml 文件中的 namespace 与 mapper 接口的类路径相同.
2、 Mapper 接口方法名和 Mapper.xml 中定义的每个 statement 的 id 相同.
3、 Mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的parameterType 的类型相同.
4、 Mapper 接口方法的输出参数类型和 mapper.xml 中定义的每个 sql 的resultType 的类型相同.
Mybatis 日志
具体选择哪个日志实现由 MyBatis 的内置日志工厂确定。它会使用最先找到的。
Mybatis 内置的日志工厂提供日志功能,具体的日志实现有以下几种方式:
SLF4J|LOG4J|JDK_LOGGINGCOMMONS_LOGGING|STDOUT_LOGGING
配置日志(在mybatis.xml)
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
简单封装Mybatis的读取配置等语句
1.在自定义的项目文件下创建util包
2.创建工具类
public class MyBatisUtil { static SqlSessionFactory SessionFactory = null; static { Reader reader = null; try { //1.mybatis读取配置文件 reader = Resources.getResourceAsReader("mybatis.xml"); } catch (IOException e) { e.printStackTrace(); } //创建SqlSessionFactory,负责创建SqlSession对象(连接数据库的会话对象,类似Connection) //SessionFactory只需要创建一个创建后不需要销毁 SessionFactory = new SqlSessionFactoryBuilder().build(reader); } //获得SqlSession对象 public static SqlSession getSqlSession(){ return SessionFactory.openSession(); } }
3.创建测试类
参数传递
单个参数直接传递
Admin selectAdmins(int id);
多个参数使用@Param(“id”)绑定
Admin selectAdmins(@Param(“account”)String account,
@Param(“password”)String password);
如果传入一个复杂的对象,就需要使用 parameterType 参数进行类型定义,例如:
void insertAdmin(Admin admin);
增删改查
public interface UserMapper { // 插入用户 int insertUser(User user); // 删除用户 int deleteUserById(Integer id); // 更新用户信息 int updateUser(User user); // 查询单个用户 User getUserById(Integer id); // 查询所有用户 List<User> getAllUsers(); }
映射文件
<!-- 插入用户 --> <insert id="insertUser" parameterType="User"> insert into user (name, age) values (#{name}, #{age}) </insert> <!-- 删除用户 --> <delete id="deleteUserById" parameterType="int"> delete from user where id = #{id} </delete> <!-- 更新用户信息 --> <update id="updateUser" parameterType="User"> update user set name = #{name}, age = #{age} where id = #{id} </update> <!-- 查询单个用户 --> <select id="getUserById" parameterType="int" resultType="User"> select * from user where id = #{id} </select> <!-- 查询所有用户 --> <select id="getAllUsers" resultType="User"> select * from user </select>
测试
public class Main { public static void main(String[] args) { try { SqlSession sqlSession = MyBatisUtil.getSqlSession(); UserDao userDao = sqlSession.getMapper(UserDao.class);//生成接口的对象 // 插入用户 User newUser = new User(); newUser.setName("张三"); newUser.setAge(25); int insertedId = userMapper.insertUser(newUser); sqlSession.commit(); System.out.println("插入的用户 ID 为:" + insertedId); // 查询单个用户 User user = userMapper.getUserById(insertedId); System.out.println("查询到的用户:" + user); // 更新用户信息 user.setName("李四"); user.setAge(30); userMapper.updateUser(user); sqlSession.commit(); System.out.println("更新后的用户:" + userMapper.getUserById(insertedId)); // 查询所有用户 List<User> users = userMapper.getAllUsers(); System.out.println("所有用户:"); for (User u : users) { System.out.println(u); } // 删除用户 userMapper.deleteUserById(insertedId); sqlSession.commit(); System.out.println("删除用户成功"); // 关闭 SqlSession sqlSession.close(); } catch (IOException e) { e.printStackTrace(); } } }
对象映射
如果表中的类名与类中的属性名完全相同,mybatis会自动将查询结果封装到POJO对象中.
如果java中使用标准驼峰命名,数据库中使用下划线连接命名,可以开始全局设置实现自动转换
<setting name="mapUnderscoreToCamelCase" value="true"/><select id="findUserInfoById" parameterType="int"resultType="Admin"> select * from admin where id=#{id} </select>
重要性:
在 Java 应用程序中,尤其是在使用 MyBatis 这样的框架进行数据库操作时,对象映射是一个关键的环节。它允许将数据库中的数据行转换为 Java 对象,以便在应用程序中更方便地进行处理和操作。这样可以提高代码的可读性和可维护性,同时也使得数据的处理更加面向对象化。
代码分析:
-
<setting name="mapUnderscoreToCamelCase" value="true"/>
:-
这个设置告诉 MyBatis 在进行数据库查询结果映射到 Java 对象时,将数据库表中的下划线命名风格的字段自动转换为 Java 对象中的驼峰命名风格的属性。例如,数据库表中的 “user_name” 字段会被自动映射到 Java 对象中的 “userName” 属性。
-
这一设置在数据库命名规范与 Java 命名规范不一致时非常有用,可以避免手动进行字段名称的转换。
-
-
<select id="findUserInfoById" parameterType="int" resultType="Admin">
:-
id
属性指定了这个查询语句的唯一标识,在代码中可以通过这个标识来调用这个查询语句。 -
parameterType="int"
表示这个查询语句接收一个整数类型的参数,通常用于指定查询条件,比如通过用户 ID 进行查询。 -
resultType="Admin"
表示这个查询语句的结果将被封装成一个Admin
对象返回。MyBatis 会根据这个设置自动将查询结果映射到Admin
类的对象中,前提是数据库表的字段名和Admin
类的属性名能够正确对应,或者开启了上述的自动命名转换设置。
-
-
select * from admin where id=#{id}
:-
这是实际的 SQL 查询语句,从名为 “admin” 的表中查询所有字段的数据,条件是
id
等于传入的参数值。#{id}
是 MyBatis 的参数占位符,MyBatis 会在运行时将传入的参数值替换到这个位置。
-
注意:
1.确保数据库表中的字段类型与 Java 对象中的属性类型能够正确匹配。例如,如果数据库中的字段是整数类型,那么对应的 Java 属性也应该是整数类型(如int
、Integer
)。如果类型不匹配,可能会导致数据转换错误或者出现空指针异常等问题。
2.对于日期类型的字段,要确保在 MyBatis 的配置中正确设置日期类型的处理方式,或者在 Java 对象的属性上使用合适的日期类型注解(如@DateTimeFormat
等)来进行日期格式的转换。
3.如果查询结果包含复杂的数据结构,比如关联查询得到多个表的结果集,需要仔细考虑如何将这些结果映射到 Java 对象中。可能需要使用 MyBatis 的关联映射配置(如association
、collection
等)来正确地构建复杂的 Java 对象结构。
4.当开启自动命名转换(mapUnderscoreToCamelCase
设置为true
)时,要注意可能出现的命名冲突和歧义。例如,如果数据库表中有两个字段分别是 “user_name” 和 “user_id_name”,在转换为 Java 对象的属性时可能会产生冲突或者不明确的命名。
5.在 Java 对象中,如果属性名本身就包含下划线或者与数据库表的命名风格不一致,开启自动命名转换可能会导致意外的结果。在这种情况下,可以考虑使用 MyBatis 的@Results
、@Result
等注解来手动指定查询结果的映射关系。
#{} 和${}区别
1、语法和使用方式
-
#{}
:-
使用预编译的方式处理参数,会将参数值以占位符的形式添加到 SQL 语句中,然后在执行 SQL 语句时通过设置参数值来完成查询。
-
例如:
select * from user where id = #{id}
。在实际执行时,MyBatis 会将#{id}
替换为一个占位符(具体的占位符形式取决于数据库驱动),并通过预编译语句的参数设置方法来设置参数值。
-
-
${}
:-
直接将参数值拼接在 SQL 语句中,不进行预编译处理。
-
例如:
select * from ${tableName}
。这里的${tableName}
会直接被替换为传入的参数值,相当于在 SQL 语句中直接插入了一个字符串。
-
2、安全性
-
#{}
:-
由于使用预编译方式,能够有效防止 SQL 注入攻击。数据库会将预编译的 SQL 语句和参数值分开处理,即使参数值中包含特殊字符或恶意 SQL 片段,也不会被数据库解释为 SQL 命令。
-
例如,如果传入的参数值是
1' or '1'='1
,使用#{}
时,数据库会将其视为一个普通的字符串参数,而不会将其解释为 SQL 条件,从而保证了安全性。
-
-
${}
:-
直接拼接参数值到 SQL 语句中,存在 SQL 注入的风险。如果传入的参数值包含恶意 SQL 片段,可能会被数据库执行,从而导致安全问题。
-
例如,使用
${}
时,如果传入的参数值是user; drop table other_table; --
,数据库可能会执行这个恶意的 SQL 语句,造成严重的后果。
-
3、使用场景
-
#{}
:-
适用于大多数情况下的参数传递,尤其是在进行条件查询、插入、更新等操作时,能够保证安全性和性能。
-
例如,在根据用户输入的用户名和密码进行登录验证时,可以使用
#{username}
和#{password}
来传递参数,避免 SQL 注入攻击。
-
-
${}
:-
通常用于动态设置表名、列名或排序字段等情况,这些情况一般不涉及用户输入的参数,且需要直接在 SQL 语句中拼接字符串。
-
例如,在根据不同的业务需求动态切换查询的表名时,可以使用
${tableName}
来传递表名参数。但在这种情况下,需要确保表名参数是可信的,不会被恶意篡改。
-
resutlMap
1、resultMap
的作用和优势
-
灵活的映射配置:
-
resultMap
允许开发者自定义数据库查询结果与 Java 对象之间的映射关系。与直接使用resultType
指定一个 Java 类不同,resultMap
可以更加精细地控制每个数据库字段如何映射到 Java 对象的属性上。 -
例如,可以处理数据库字段名与 Java 属性名不匹配的情况,或者进行复杂的数据类型转换。
-
-
复杂映射场景:
-
当查询结果包含多个表的关联数据或者需要进行特殊的数据处理时,
resultMap
非常有用。可以通过定义嵌套的映射关系来构建复杂的 Java 对象结构。 -
比如,一个订单对象可能包含多个订单项对象,通过
resultMap
可以将关联查询的结果正确地映射到订单和订单项的对象层次结构中。
-
2、代码分析
-
<select id="findAdminInfoResultMap" resultMap="adminResultMap">
:-
id
属性指定了这个查询语句的唯一标识,方便在代码中调用这个查询语句。 -
resultMap="adminResultMap"
表明这个查询语句的结果将使用名为 “adminResultMap” 的resultMap
进行映射。这意味着 MyBatis 将根据这个resultMap
的定义来将查询结果转换为 Java 对象。
-
-
SELECT id, account, password FROM admin
:-
这是实际的 SQL 查询语句,从名为 “admin” 的表中选择 “id”、“account” 和 “password” 三个字段的数据。
-
3、使用resultMap
-
定义resultMap:
-
在 MyBatis 的配置文件中,可以使用
<resultMap>
标签来定义一个resultMap
。例如:
-
<resultMap id="adminResultMap" type="Admin"> <id property="id" column="id"/> <result property="account" column="account"/> <result property="password" column="password"/> </resultMap>
-
在这个例子中,
id
属性指定了resultMap
的唯一标识,type
属性指定了要映射的 Java 类(这里是Admin
类)。 -
内部的
<id>
和<result>
标签分别用于定义主键字段和普通字段的映射关系。property
属性指定 Java 对象的属性名,column
属性指定数据库表中的字段名。
-
使用resultMap:
-
在查询语句中,通过
resultMap
属性引用定义好的resultMap
。就像前面的代码示例中那样,<select id="findAdminInfoResultMap" resultMap="adminResultMap">
。
-
多表关联处理结果集
对resultMap
中association
和collection
元素的详细解释及在部门与员工一对多关系中的使用示例:
1、association
和collection
元素的作用
-
association
:用于处理一对一的关联关系。例如,一个学生有一个对应的班级信息,就可以使用association
来映射这种一对一的关系。 -
collection
:用于处理一对多的关联关系。在部门与员工的场景中,一个部门有多个员工,就可以使用collection
来映射这种一对多的关系。
2、部门与员工一对多关系的实现
1.数据库表结构假设:
-
部门表(department):包含部门 ID(department_id)、部门名称(department_name)等字段。
-
员工表(employee):包含员工 ID(employee_id)、部门 ID(department_id)、员工姓名(employee_name)等字段。
2.Java 实体类:
-
部门实体类(Department):
public class Department { private int departmentId; private String departmentName; private List<Employee> employees; // 构造方法、getter 和 setter 方法 }
-
员工实体类(Employee):
public class Employee { private int employeeId; private int departmentId; private String employeeName; // 构造方法、getter 和 setter 方法 }
3.MyBatis 的映射文件配置:
这个resultMap
将部门表和员工表的查询结果映射到Department
对象和包含Employee
对象的集合中。
<resultMap id="departmentEmployeeResultMap" type="Department"> <id property="departmentId" column="department_id"/> <result property="departmentName" column="department_name"/> <collection property="employees" ofType="Employee"> <id property="employeeId" column="employee_id"/> <result property="departmentId" column="department_id"/> <result property="employeeName" column="employee_name"/> </collection> </resultMap> 查询语句: <select id="findDepartmentWithEmployees" resultMap="departmentEmployeeResultMap"> SELECT d.department_id, d.department_name, e.employee_id, e.employee_name FROM department d LEFT JOIN employee e ON d.department_id = e.department_id; </select>
这个查询语句使用左连接将部门表和员工表关联起来,并选择需要的字段。查询结果将使用 “departmentEmployeeResultMap” 进行映射。
详细讲解:
resultMap
部分
-
<resultMap id="departmentEmployeeResultMap" type="Department">
:-
id
属性为这个结果映射起了一个唯一的名称 “departmentEmployeeResultMap”,方便在其他地方引用。 -
type
属性指定了这个结果映射要映射到的 Java 对象类型为 “Department”,即表示最终的查询结果将被封装成Department
对象。
-
-
<id property="departmentId" column="department_id"/>
:-
property
属性指定了在Department
对象中的属性名,这里表示将数据库查询结果中的 “department_id” 字段的值映射到Department
对象的 “departmentId” 属性上。 -
这个
<id>
标签用于标识结果映射中的唯一标识字段,在处理复杂的关联关系和缓存时非常重要。
-
-
<result property="departmentName" column="department_name"/>
:-
类似地,将数据库查询结果中的 “department_name” 字段的值映射到
Department
对象的 “departmentName” 属性上。
-
-
<collection property="employees" ofType="Employee">
:-
property
属性指定了在Department
对象中用于存储关联的员工对象的集合属性名。这里表示Department
对象有一个名为 “employees” 的属性,它是一个包含Employee
对象的集合。 -
ofType
属性指定了这个集合中元素的类型为 “Employee”,即表示集合中的元素是Employee
对象。
-
-
<id property="employeeId" column="employee_id"/>
和<result property="departmentId" column="department_id"/>
以及<result property="employeeName" column="employee_name"/>
:-
这三个标签用于定义集合中
Employee
对象的属性与数据库查询结果字段的映射关系。分别将数据库查询结果中的 “employee_id”、“department_id” 和 “employee_name” 字段的值映射到Employee
对象的 “employeeId”、“departmentId” 和 “employeeName” 属性上。
-
查询语句部分
-
<select id="findDepartmentWithEmployees" resultMap="departmentEmployeeResultMap">
:-
id
属性为这个查询语句起了一个唯一的名称 “findDepartmentWithEmployees”,方便在代码中调用这个查询语句。 -
resultMap="departmentEmployeeResultMap"
表示这个查询语句的结果将使用前面定义的 “departmentEmployeeResultMap” 结果映射进行封装。
-
-
SELECT d.department_id, d.department_name, e.employee_id, e.employee_name FROM department d LEFT JOIN employee e ON d.department_id = e.department_id;
:-
这是实际的 SQL 查询语句,使用左连接(LEFT JOIN)将 “department” 表(别名 “d”)和 “employee” 表(别名 “e”)连接起来。
-
选择了两个表中的特定字段:“department_id”、“department_name” 来自 “department” 表,“employee_id”、“employee_name” 来自 “employee” 表。
-
三、在 Java 代码中调用查询方法
import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.util.List; public class Main { public static void main(String[] args) { try { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); // 获取映射接口的实现 DepartmentMapper departmentMapper = sqlSession.getMapper(DepartmentMapper.class); // 调用查询方法 List<Department> departmentsWithEmployees = departmentMapper.findDepartmentWithEmployees(); // 输出结果 for (Department department : departmentsWithEmployees) { System.out.println("部门 ID:" + department.getDepartmentId() + ",部门名称:" + department.getDepartmentName()); for (Employee employee : department.getEmployees()) { System.out.println(" 员工 ID:" + employee.getEmployeeId() + ",员工姓名:" + employee.getEmployeeName()); } } sqlSession.close(); } catch (IOException e) { e.printStackTrace(); } } }
通过以上步骤,我们可以使用resultMap
中的collection
元素来处理部门与员工的一对多关联关系,将查询结果映射到 Java 对象中,方便在应用程序中进行操作和处理。
学生与课程的关系可以类比部门与员工的一对多关系来理解,但又不完全相同。在学生与课程的关系中,通常是多对多的关系。
一个学生可以选择多门课程,同时一门课程也可以被多个学生选择。
一、数据库表结构假设
-
学生表(student):包含学生 ID(student_id)、学生姓名(student_name)等字段。
-
课程表(course):包含课程 ID(course_id)、课程名称(course_name)等字段。
-
学生课程关联表(student_course):包含学生 ID 和课程 ID,用于建立学生与课程的多对多关系。
二、Java 实体类
-
学生实体类(Student):
public class Student { private int studentId; private String studentName; private List<Course> courses; // 构造方法、getter 和 setter 方法 }
-
课程实体类(Course):
public class Course { private int courseId; private String courseName; private List<Student> students; // 构造方法、getter 和 setter 方法 }
三、MyBatis 的映射文件配置
这个resultMap
将学生表、课程表和学生课程关联表的查询结果映射到Student
对象和包含Course
对象的集合中,同时在Course
对象中又包含了Student
对象的集合,以实现双向的多对多关系。
<resultMap id="studentCourseResultMap" type="Student"> <id property="studentId" column="student_id"/> <result property="studentName" column="student_name"/> <collection property="courses" ofType="Course"> <id property="courseId" column="course_id"/> <result property="courseName" column="course_name"/> <collection property="students" ofType="Student" autoMapping="true"/> </collection> </resultMap> 查询语句: <select id="findStudentsWithCourses" resultMap="studentCourseResultMap"> SELECT s.student_id, s.student_name, c.course_id, c.course_name FROM student s LEFT JOIN student_course sc ON s.student_id = sc.student_id LEFT JOIN course c ON sc.course_id = c.course_id; </select>
这个查询语句使用左连接将三个表关联起来,并选择需要的字段。查询结果将使用 “studentCourseResultMap” 进行映射。
四、在 Java 代码中调用查询方法
import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.util.List; public class Main { public static void main(String[] args) { try { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); // 获取映射接口的实现 StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); // 调用查询方法 List<Student> studentsWithCourses = studentMapper.findStudentsWithCourses(); // 输出结果 for (Student student : studentsWithCourses) { System.out.println("学生 ID:" + student.getStudentId() + ",学生姓名:" + student.getStudentName()); for (Course course : student.getCourses()) { System.out.println(" 课程 ID:" + course.getCourseId() + ",课程名称:" + course.getCourseName()); for (Student s : course.getStudents()) { System.out.println(" 选择该课程的学生 ID:" + s.getStudentId() + ",学生姓名:" + s.getStudentName()); } } } sqlSession.close(); } catch (IOException e) { e.printStackTrace(); } } }
嵌套查询
一、嵌套查询的概念和作用
嵌套查询是一种在 MyBatis 中处理多表关联的方式,它将一个复杂的多表关联查询拆分为多个单独的查询。这种方式在某些情况下可以提高查询的灵活性和性能,特别是当关联关系比较复杂或者需要对关联查询进行更多的控制时。
二、代码分析
-
<association property="dept" javaType="Dept" select="findDeptByID" column="dept_id">
-
property="dept"
:指定在主对象(假设这里是Emp
对象)中用于存储关联对象(Dept
对象)的属性名。 -
javaType="Dept"
:明确关联的对象类型为Dept
。 -
select="findDeptByID"
:指定用于查询关联对象的 Mapper Statement 的 ID。这意味着 MyBatis 会执行这个命名的查询语句来获取关联对象。 -
column="dept_id"
:在进行关联查询时,将主表中的 “dept_id” 列的值传入名为 “findDeptByID” 的查询语句中,作为查询条件。查询结果将被映射到主对象的 “dept” 属性中。
-
三、配置方法说明
-
collection
和association
的配置方法相同点:-
对于
collection
和association
元素,当需要进行嵌套查询时,都可以使用select
和column
属性来指定关联查询的方式。 -
select
属性用于指定关联查询的 Mapper Statement ID,column
属性用于指定在主表中用于关联查询的列名。
-
-
不同点及注意事项:
-
collection
用于处理一对多的关联关系,而association
用于处理一对一的关联关系。 -
在配置时,需要根据实际的关联关系选择合适的元素,并正确设置属性值。同时,要确保关联查询的结果能够正确地映射到主对象的相应属性中。
-
例如,如果有一个员工表(employee)和部门表(department),员工表中有一个部门 ID(dept_id)字段关联到部门表的主键。在员工对象中,需要关联查询部门信息,可以使用如下配置:
<resultMap id="employeeResultMap" type="Employee"> <!-- 员工表的字段映射 --> <id property="employeeId" column="employee_id"/> <result property="employeeName" column="employee_name"/> <!-- 关联查询部门信息 --> <association property="dept" javaType="Department" select="findDeptByID" column="dept_id"/> </resultMap> <select id="findDeptByID" parameterType="int" resultType="Department"> SELECT * FROM department WHERE dept_id = #{dept_id} </select>
这样,当查询员工信息时,MyBatis 会先查询员工表的数据,然后根据每个员工的部门 ID,执行 “findDeptByID” 查询语句来获取部门信息,并将部门信息映射到员工对象的 “dept” 属性中。
详细讲解:
resultMap
部分
-
<resultMap id="employeeResultMap" type="Employee">
:-
id
属性为这个结果映射定义了一个唯一的名称 “employeeResultMap”,方便在其他地方引用。 -
type
属性指定了这个结果映射要映射到的 Java 对象类型为 “Employee”,表示最终的查询结果将被封装成Employee
对象。
-
-
<id property="employeeId" column="employee_id"/>
:-
property
属性指定了在Employee
对象中的属性名,这里表示将数据库查询结果中的 “employee_id” 字段的值映射到Employee
对象的 “employeeId” 属性上。 -
这个
<id>
标签用于标识结果映射中的唯一标识字段,在处理复杂的关联关系和缓存时非常重要。
-
-
<result property="employeeName" column="employee_name"/>
:-
将数据库查询结果中的 “employee_name” 字段的值映射到
Employee
对象的 “employeeName” 属性上。
-
-
<association property="dept" javaType="Department" select="findDeptByID" column="dept_id"/>
:-
property="dept"
:指定在Employee
对象中用于存储关联的部门对象的属性名。这里表示Employee
对象有一个名为 “dept” 的属性,它是一个Department
对象。 -
javaType="Department"
:明确关联的对象类型为 “Department”,即表示这个属性存储的是一个部门对象。 -
select="findDeptByID"
:指定用于查询关联对象(部门)的 Mapper Statement 的 ID。MyBatis 会执行这个命名的查询语句来获取关联的部门对象。 -
column="dept_id"
:在进行关联查询时,将员工表中的 “dept_id” 列的值传入名为 “findDeptByID” 的查询语句中,作为查询条件。查询结果将被映射到Employee
对象的 “dept” 属性中。
-
查询语句部分:
-
<select id="findDeptByID" parameterType="int" resultType="Department">
:-
id
属性为这个查询语句定义了一个唯一的名称 “findDeptByID”,与resultMap
中的select
属性引用的名称相对应。 -
parameterType="int"
表示这个查询语句接收一个整数类型的参数,这里预期接收的是部门 ID。 -
resultType="Department"
表示这个查询语句的结果将被封装成Department
对象返回。
-
-
SELECT * FROM department WHERE dept_id = #{dept_id}
:-
这是实际的 SQL 查询语句,从 “department” 表中选择所有字段的数据,条件是部门 ID 等于传入的参数值。
#{dept_id}
是 MyBatis 的参数占位符,MyBatis 会在运行时将传入的参数值替换到这个位置。
-
注解方式
一、@Insert
-
作用:用于标注一个方法,表示该方法执行一个插入 SQL 语句。
-
示例:
@Insert("INSERT INTO employee (employee_name, department_id) VALUES (#{employeeName}, #{departmentId})") int insertEmployee(Employee employee);
这个注解会将传入的Employee
对象的属性值插入到数据库的 “employee” 表中。
二、@Select
-
作用:标注方法执行查询 SQL 语句。
-
示例:
@Select("SELECT * FROM employee WHERE employee_id = #{employeeId}") Employee findEmployeeById(int employeeId);
该方法根据传入的员工 ID 查询员工信息。
三、@Update
-
作用:用于标注执行更新 SQL 语句的方法。
-
示例:
@Update("UPDATE employee SET employee_name = #{employeeName}, department_id = #{departmentId} WHERE employee_id = #{employeeId}") int updateEmployee(Employee employee);
更新员工表中指定员工 ID 的记录。
四、@Delete
-
作用:标注执行删除 SQL 语句的方法。
-
示例:
@Delete("DELETE FROM employee WHERE employee_id = #{employeeId}") int deleteEmployeeById(int employeeId);
删除指定员工 ID 的记录。
五、@Param
-
作用:当方法有多个参数时,用于给参数命名,以便在 SQL 语句中引用。
-
示例:
@Select("SELECT * FROM employee WHERE employee_name = #{name} AND department_id = #{departmentId}") Employee findEmployeeByNameAndDepartment(@Param("name") String employeeName, @Param("departmentId") int departmentId);
这里给两个参数分别命名为 “name” 和 “departmentId”,在 SQL 语句中可以通过这些名称引用参数。
六、@Results 和 @Result
-
作用:用于自定义查询结果的映射关系。
-
示例:
@Select("SELECT e.employee_id, e.employee_name, d.department_id, d.department_name " + "FROM employee e " + "JOIN department d ON e.department_id = d.department_id " + "WHERE e.employee_id = #{employeeId}") @Results({ @Result(property = "employeeId", column = "employee_id"), @Result(property = "employeeName", column = "employee_name"), @Result(property = "dept", javaType = Department.class, column = "department_id", one = @One(select = "findDepartmentById")) }) Employee findEmployeeWithDepartmentById(int employeeId);
这里通过@Results
和@Result
注解自定义了查询结果到Employee
对象的映射关系,并且使用@One
注解关联查询部门信息。
Mybatis 动态 SQL
MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力。 如果你有使用JDBC 或其他 相似框架的经验,你就明白条件地串联 SQL 字符串在一起是多么的痛苦,确保不能忘了空格或在列表的最后省略逗号。动态 SQL 可以彻底处理这种痛苦。
一、动态 SQL 的作用和优势
-
灵活性:动态 SQL 可以根据不同的输入参数和业务逻辑生成不同的 SQL 语句,使代码更加灵活,能够适应各种复杂的查询需求。
-
减少代码重复:避免了为不同的查询条件编写大量重复的 SQL 语句,提高了代码的可维护性。
-
提高性能:可以根据具体情况只生成必要的 SQL 片段,减少数据库的负担,提高查询性能。
二、常用的动态 SQL 标签
1.<if>:根据条件判断是否包含某个 SQL 片段。
-
示例:
<select id="findUsersByConditions" resultType="User"> SELECT * FROM user WHERE 1=1 <if test="username!= null"> AND username = #{username} </if> <if test="age!= null"> AND age = #{age} </if> </select>
(在使用动态 SQL 时,如 MyBatis 等框架中,通常需要根据不同的条件拼接 SQL 片段。如果没有一个初始的条件,那么在第一个条件判断为真时,拼接条件语句会比较麻烦,因为需要考虑如何添加WHERE关键字。
-
例:在使用
<if>
标签进行条件判断时,如果没有WHERE 1=1
,那么第一个条件的拼接可能需要额外的逻辑判断。而有了WHERE 1=1
,后续的条件可以直接使用AND
或OR
进行拼接,无需考虑第一个条件的特殊情况。)
在这个例子中,如果传入的参数username
不为空,则会在 SQL 语句中添加AND username = #{username}
条件;如果age
不为空,则添加AND age = #{age}
条件。
2.<choose>、<when>、<otherwise>:类似于 Java 中的switch语句,根据不同的条件选择不同的 SQL 片段。
-
示例:
<select id="findUsersByAgeRange" resultType="User"> SELECT * FROM user WHERE 1=1 <choose> <when test="minAge!= null and maxAge!= null"> AND age BETWEEN #{minAge} AND #{maxAge} </when> <when test="minAge!= null"> AND age >= #{minAge} </when> <when test="maxAge!= null"> AND age <= #{maxAge} </when> <otherwise> -- 如果没有传入任何年龄条件,这里可以添加默认的查询条件或者不添加任何条件 </otherwise> </choose> </select>
3.<foreach>:用于循环遍历集合参数,生成多个 SQL 片段。
-
示例:
<select id="findUsersByIds" resultType="User"> SELECT * FROM user WHERE user_id IN <foreach item="id" collection="ids" open="(" separator="," close=")"> #{id} </foreach> </select>
这个查询语句可以根据传入的ids
集合参数,动态生成IN
条件的 SQL 片段。
4.<trim>、<where>、<set>:用于对 SQL 语句进行修剪和添加特定的关键字。
-
<where>
:会自动去除条件语句前面多余的AND
或OR
。 -
<set>
:用于动态生成UPDATE
语句中的SET
子句,会自动去除末尾多余的逗号。 -
<trim>
:可以更灵活地控制 SQL 片段的前后缀和去除多余的关键字。 -
示例:
<update id="updateUser" parameterType="User"> UPDATE user <set> <if test="username!= null"> username = #{username}, </if> <if test="age!= null"> age = #{age}, </if> </set> WHERE user_id = #{userId} </update>
在这个更新语句中,<set>
标签会根据条件动态生成SET
子句,并且去除末尾多余的逗号。
三、在项目中的应用场景
-
复杂的查询条件:当查询条件很多且不确定时,使用动态 SQL 可以根据用户输入的条件动态生成查询语句。
-
批量操作:如批量插入、批量更新或批量删除,可以使用
<foreach>
标签遍历集合参数,生成相应的 SQL 语句。 -
动态更新:在更新数据时,只更新有变化的字段,使用动态 SQL 可以根据传入的参数动态生成
SET
子句。
特殊符号处理
一、特殊符号处理的必要性
在 MyBatis 的 XML 文件中,如果直接使用一些特殊符号如<
、>
、"
、&
、<>
等,会导致 MyBatis 解析 XML 文件时出错。这是因为这些符号在 XML 中有特定的语法含义,直接使用会被 XML 解析器误解。例如,<
和>
在 XML 中用于标记标签的开始和结束,&
用于表示实体引用的开始等。
二、转义字符的使用
-
转义字符列表:
-
<
转义为<
。 -
>
转义为>
。 -
"
转义为"
。 -
'
转义为'
。 -
&
转义为&
。
使用转义字符可以确保特殊符号在 XML 文件中被正确识别为普通文本内容,而不是被 XML 解析器错误地解释为 XML 语法的一部分。
-
-
示例: 如果在 SQL 条件中需要使用小于号
<
,可以写成<
。例如:
<if test="age > 18"> AND some_condition < #{value} </if>
三、<![CDATA[]]>
的使用
1.作用: <![CDATA[ ]]>
是 XML 中的一个特殊语法结构,用于包裹一段内容,告诉 XML 解析器不要解析其中的内容。在 MyBatis 的 XML 文件中,可以使用<![CDATA[ ]]>
来包裹包含特殊符号的 SQL 片段,以避免特殊符号被错误解析。
2.示例:
<if test="id!= null"> AND <![CDATA[ id <> #{id} ]]> </if>
在这个例子中,id <> #{id}
这个 SQL 条件被包裹在<![CDATA[ ]]>
中,确保其中的<>
符号不会被 XML 解析器错误处理。
3.注意事项: 如文档中提到的,使用<![CDATA[ ]]>
时,要尽量缩小其范围,只把包含特殊符号的语句放在其中。这是因为如果将过多的内容放在<![CDATA[ ]]>
中,可能会导致一些 MyBatis 的动态 SQL 标签(如<if>
、<where>
、<choose>
、<trim>
等)无法被正确解析。这会影响 MyBatis 动态生成 SQL 语句的功能。
缓存
为什么使用缓存
缓存(cache)的作用是为了减去数据库的压力,提高查询性能。缓存实现的原理是从数据库中查询出来的对象在使用完后不要销毁,而是存储在内存(缓存)中,当再次需要获取该对象时,直接从内存(缓存)中直接获取,不再向数据库执行 select 语句,从而减少了对数据库的查询次数,因此提高了数据库的性能。
Mybatis 有一级缓存和二级缓存。
一级缓存的作用域是同一个 SqlSession,在同一个 sqlSession 中两次执行相同的 sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个 sqlSession 结束后该 sqlSession 中的一级缓存也就不存在了。
Mybatis 默认开启一级缓存。
二级缓存是多个 SqlSession 共享的,其作用域是同一个 namespace,不同的sqlSession 两次执行相同 namespace 下的 sql 语句且向 sql 中传递参数也相同即最终执行相同的 sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。
Mybatis 默认没有开启二级缓存需要在 setting 全局参数中配置开启二级缓存。
一级缓存
-
作用范围:
-
一级缓存是 SqlSession 级别的缓存,也称为本地缓存。它的作用范围仅限于一个 SqlSession 内部的查询操作。
-
当一个 SqlSession 执行查询后,结果会被缓存起来。如果在同一个 SqlSession 中再次执行相同的查询,MyBatis 会首先从一级缓存中获取结果,而不会再次执行 SQL 查询,从而提高查询性能。
-
-
生命周期:
-
一级缓存的生命周期与 SqlSession 一致。当 SqlSession 关闭、提交或回滚事务时,一级缓存中的数据会被清空。
-
-
自动触发缓存:
-
只要是在同一个 SqlSession 中执行相同的查询条件,MyBatis 会自动触发一级缓存。无需进行额外的配置。
-
二级缓存
-
作用范围:
-
二级缓存是 Mapper 级别的缓存,它可以在多个 SqlSession 之间共享。不同的 SqlSession 可以从二级缓存中获取相同查询的结果,从而减少数据库的访问次数,提高性能。
-
-
开启方式:
-
需要在 MyBatis 的配置文件(通常是
mybatis.xml
)中进行配置才能开启二级缓存。例如:
-
<configuration> <settings> <setting name="cacheEnabled" value="true"/> </settings> <!-- 其他配置 --> </configuration>
-
同时,在 Mapper XML 文件中,需要对相应的 Mapper 进行二级缓存的配置,通过添加
<cache/>
标签来开启特定 Mapper 的二级缓存:
<mapper namespace="com.example.mapper.UserMapper"> <cache/> <!-- 其他查询语句 --> </mapper>
-
缓存的更新:
-
当对数据库中的数据进行插入、更新或删除操作时,MyBatis 会自动刷新相关的二级缓存,以确保缓存中的数据与数据库中的数据保持一致。
-
-
缓存的存储:
-
二级缓存可以使用不同的缓存实现,如 Ehcache、Redis 等。通过配置 MyBatis 的缓存插件,可以将二级缓存存储在外部的缓存服务器中,提高缓存的性能和可扩展性。
-
两者的区别
-
作用范围:
-
一级缓存是 SqlSession 级别,仅限于单个 SqlSession 内部。
-
二级缓存是 Mapper 级别,可以在多个 SqlSession 之间共享。
-
-
生命周期:
-
一级缓存与 SqlSession 生命周期一致。
-
二级缓存的生命周期相对较长,只要没有进行数据更新操作或者手动清空缓存,缓存中的数据就会一直存在。
-
-
配置方式:
-
一级缓存无需配置,自动生效。
-
二级缓存需要在 MyBatis 配置文件和 Mapper XML 文件中进行配置才能开启。
-
MyBatis架构
MyBatis 是一个优秀的持久层框架,其架构主要包括以下几个核心组件:
一、SQL Mapper
-
作用:
-
SQL Mapper 是 MyBatis 的核心组件之一,它负责将 SQL 语句与 Java 方法进行映射。通过在 Mapper XML 文件或使用注解的方式,定义 SQL 语句以及输入参数和输出结果的映射关系。
-
例如,定义一个查询用户信息的方法,可以在 Mapper XML 文件中使用
<select>
标签定义 SQL 语句,并指定结果映射到的 Java 对象类型。
-
-
配置方式:
-
Mapper XML 文件:使用 XML 语法,通过
<mapper>
标签定义一个 Mapper 接口的实现。在<select>
、<insert>
、<update>
、<delete>
等标签中定义具体的 SQL 语句和参数映射。 -
注解方式:在 Mapper 接口的方法上使用 MyBatis 提供的注解,如
@Select
、@Insert
、@Update
、@Delete
等,直接定义 SQL 语句。
-
二、SQL 执行器(SQL Executor)
-
作用:
-
SQL 执行器负责执行 SQL 语句,并与数据库进行交互。它根据 SQL Mapper 定义的 SQL 语句和参数,将 SQL 语句发送到数据库执行,并将结果返回给调用者。
-
SQL 执行器还负责处理事务管理、连接管理等任务,确保数据库操作的正确性和可靠性。
-
-
执行过程:
-
当调用 Mapper 接口的方法时,MyBatis 会根据方法的签名和配置找到对应的 SQL 语句。
-
SQL 执行器将 SQL 语句和参数传递给数据库驱动,执行 SQL 查询或更新操作。
-
数据库返回结果集后,SQL 执行器将结果集映射到 Java 对象中,根据结果映射的配置进行属性赋值和类型转换。
-
最后,将映射后的 Java 对象返回给调用者。
-
三、数据源(DataSource)
-
作用:
-
数据源负责提供数据库连接。MyBatis 可以使用多种数据源实现,如 JDBC 数据源、连接池数据源等。
-
数据源的配置包括数据库驱动类、数据库连接 URL、用户名、密码等信息。通过配置数据源,MyBatis 可以获取数据库连接,并在需要时进行连接的创建、管理和释放。
-
-
连接池管理:
-
在实际应用中,通常会使用连接池来管理数据库连接,以提高性能和资源利用率。MyBatis 可以集成第三方连接池,如 DBCP、C3P0 等,也可以使用 MyBatis 自带的连接池实现。
-
连接池负责维护一组数据库连接,当需要执行数据库操作时,从连接池中获取一个可用的连接,执行操作后将连接归还到连接池中,以便下次使用。
-
四、结果映射器(Result Mapper)
-
作用:
-
结果映射器负责将数据库查询结果映射到 Java 对象中。当 SQL 执行器从数据库获取结果集后,结果映射器根据配置的结果映射规则,将结果集中的每一行数据转换为一个 Java 对象,并进行属性赋值和类型转换。
-
结果映射器可以处理简单的结果映射,如将数据库表的字段映射到 Java 对象的属性上,也可以处理复杂的结果映射,如多表关联查询、嵌套结果集等。
-
-
映射方式:
-
通过在 Mapper XML 文件中使用
<resultMap>
标签定义结果映射规则,可以指定数据库字段与 Java 对象属性的对应关系、类型转换、嵌套结果集的映射等。 -
也可以使用 MyBatis 的注解方式,在 Java 对象的属性上使用
@Results
、@Result
等注解定义结果映射规则。
-
五、缓存(Cache)
-
作用:
-
MyBatis 提供了一级缓存和二级缓存机制,用于提高数据库查询的性能。缓存可以减少数据库的访问次数,将查询结果缓存起来,以便下次相同的查询可以直接从缓存中获取结果,而无需再次执行数据库查询。
-
-
一级缓存:
-
一级缓存是 SqlSession 级别的缓存,也称为本地缓存。它的作用范围仅限于一个 SqlSession 内部的查询操作。当一个 SqlSession 执行查询后,结果会被缓存起来。如果在同一个 SqlSession 中再次执行相同的查询,MyBatis 会首先从一级缓存中获取结果,而不会再次执行 SQL 查询。
-
-
二级缓存:
-
二级缓存是 Mapper 级别的缓存,可以在多个 SqlSession 之间共享。不同的 SqlSession 可以从二级缓存中获取相同查询的结果,从而减少数据库的访问次数,提高性能。二级缓存需要在 MyBatis 的配置文件和 Mapper XML 文件中进行配置才能开启。
-
六、插件(Plugin)
-
作用:
-
MyBatis 允许开发人员通过插件机制对框架的核心功能进行扩展和增强。插件可以拦截 MyBatis 的 SQL 执行过程,在 SQL 执行前后进行一些额外的处理,如性能监控、日志记录、SQL 优化等。
-
-
实现方式:
-
开发人员可以实现 MyBatis 提供的
Interceptor
接口,编写插件逻辑。在 MyBatis 的配置文件中,通过<plugins>
标签注册插件,指定插件的实现类。
-