mybatis面试
Mybatis缓存
MyBatis的一级缓存和二级缓存是用于提高查询效率的两种不同的缓存机制,它们的主要区别在于作用范围、存储位置以及生命周期等方面。
一级缓存(Local Cache 或 Session 级别缓存)
- 作用范围:一级缓存是SqlSession级别的缓存,仅在当前SqlSession的生命周期内有效。
- 存储位置:存储在SqlSession的内存中。
- 特点:
- 自动管理:MyBatis自动维护一级缓存,无需手动配置。
- 生命周期:与SqlSession的生命周期一致,SqlSession关闭后,一级缓存失效。
- 作用:减少对数据库的访问次数,提高查询效率。
- 访问顺序:
- 首先访问本地缓存。
- 如果本地缓存没有,则从数据库中获取数据,并将结果放入缓存中。
二级缓存(Mapper 级别缓存)
- 作用范围:二级缓存是Mapper级别的缓存,作用于整个应用级别,多个SqlSession可以共享二级缓存。
- 存储位置:存储在Cache接口的实现类中,可以是内存缓存、文件缓存或其他存储方式。
- 特点:
- 需要手动配置:需要在Mapper XML文件中配置标签开启二级缓存。
- 生命周期:与应用的生命周期一致,应用关闭后,二级缓存失效。
- 作用:进一步减少对数据库的访问次数,提高查询效率,适用于多用户共享的场景。
- 访问顺序:
- 在commit的时候,把二级缓存中的数据刷新到正在代理的一级缓存中。
- 然后直接访问一级缓存中的数据。
- 如果一级缓存中没有,则访问数据库获取数据,并更新缓存。
当使用MyBatis时,默认情况下会启用一级缓存。而二级缓存则默认是关闭的,如果需要使用,必须通过配置来开启,并且实体类需要实现Serializable接口以便支持序列化。此外,对于缓存数据的更新机制,无论是哪一级缓存,在执行了C/U/D(创建/更新/删除)操作之后,都会导致相关select查询的缓存被清空。
springboot中如何使用mybatis
在 Spring Boot 中使用 MyBatis 是一种非常常见的开发方式,它结合了 Spring Boot 的自动化配置和 MyBatis 的灵活性,能够快速实现数据库操作。以下是详细的步骤和关键点:
占位符#{}和${}
在MyBatis中,#{} 和${}是两种不同的参数占位符,它们用于SQL语句中传递参数值,但它们的工作方式和使用场景有所不同。
#{}
- 作用:
#{}被用来创建预编译语句(PreparedStatement)中的参数占位符。MyBatis会将你在#{}中指定的属性名与传递给语句的参数对象的属性进行匹配,并设置相应的参数值。 - 安全性:由于它使用的是预编译技术,因此可以有效防止SQL注入攻击。这是因为实际的参数值会被当作Java字符串或数字等数据类型处理,而不是作为SQL语句的一部分直接拼接进去。
- 适用场景:适用于所有需要传递参数的地方,特别是当参数是用户输入或其他不可信来源时。
- 示例:
这里的SELECT * FROM users WHERE id = #{userId}#{userId}将会被替换为一个对应的参数值,比如123,并且这个值会被正确地转义以避免SQL注入问题。
${}
- 作用:
${}用于执行SQL字符串插值。这意味着它会直接将你提供的变量值插入到生成的SQL语句中。这种方式不会对插入的值进行任何处理或转义。 - 安全性:因为它是直接将值嵌入到SQL语句中,所以存在SQL注入的风险。因此,在使用
${}时必须非常小心,确保插入的值是安全的或者已经经过了严格的验证和清理。 - 适用场景:主要用于动态表名、列名或者其他SQL结构部分,这些地方不能使用预编译语句的占位符。
- 示例:
在这里SELECT * FROM ${tableName} WHERE 1=1${tableName}将会被直接替换为具体的表名,如users,而不会有额外的转义处理。
总的来说,推荐尽可能使用#{}来保证安全性,减少SQL注入的风险。只有在确实需要的时候,才谨慎地使用${}。例如,当你需要构建动态查询语句且涉及到数据库对象名称(如表名或列名)时,可能不得不使用${}。不过,在这种情况下,请确保对传入的值进行了充分的验证和清理。
ResultMap和ResultType的区别
ResponseMap 和 ResponseType 这两个术语并不是标准的MyBatis概念,但它们听起来像是在讨论处理返回类型时可能涉及的概念。基于常见的编程实践和框架使用,我可以推测你可能是在问关于MyBatis中的结果映射(Result Map)与返回类型(Result Type)的区别。如果是这样的话,以下是对这两个概念的解释:
ResultMap(结果映射)
- 定义:
ResultMap是 MyBatis 中用于描述数据库查询结果如何映射到 Java 对象的一个复杂机制。它允许您手动定义从列到Java对象属性的详细映射规则。 - 用途:当数据库表结构和实体类不完全对应,或者需要进行复杂的转换时(例如处理多表联合查询的结果),可以使用
ResultMap来精确地控制结果集到对象的映射过程。 - 优点:
- 提供了更高的灵活性和控制力。
- 支持一对一、一对多等复杂关系映射。
- 示例:
<resultMap id="userResultMap" type="User"> <id property="id" column="user_id"/> <result property="username" column="user_name"/> <!-- 更多映射规则 --> </resultMap> <select id="selectUser" resultMap="userResultMap"> SELECT user_id, uname, pwd FROM users WHERE user_id = #{id} </select>
ResultType(返回类型)
- 定义:
ResultType是一个更简单直接的方式,用来指定查询结果应该映射成哪种类型的Java对象。在这种情况下,MyBatis会尝试自动将每一行的数据按名称匹配的原则映射到相应的Java对象属性上。 - 用途:适用于简单的查询场景,其中数据库列名与Java对象属性名相同或可以通过驼峰命名法自动匹配。
- 优点:
- 简单易用,不需要额外配置。
- 当列名与属性名相匹配时,能够快速实现数据映射。
- 示例:
<select id="selectUsers" resultType="User"> SELECT user_id, user_name FROM users </select>
总结来说,如果你的应用场景相对简单,并且数据库字段名可以直接或通过简单的命名约定映射到Java对象的属性上,那么使用ResultType会更加方便快捷。然而,对于更复杂的映射需求,如不同名的属性映射、关联对象映射等,则应选择ResultMap以获得更大的灵活性和控制力。
使用
1. 添加依赖
在 pom.xml 文件中添加必要的依赖项:
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- MyBatis Spring Boot Starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Spring Boot JDBC Starter(可选,用于数据库连接池) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- Lombok(可选,用于简化实体类代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
2. 配置数据库连接信息
在 application.properties 或 application.yml 文件中配置数据库连接信息。
application.properties 示例
# 数据库连接配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=your_password
# MyBatis 配置
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.configuration.map-underscore-to-camel-case=true
application.yml 示例
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis?useSSL=false&serverTimezone=UTC
username: root
password: your_password
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
3. 创建实体类
创建一个与数据库表对应的实体类。
package com.example.model;
import lombok.Data;
@Data
public class User {
private Integer id;
private String name;
private Integer age;
private Integer gender;
private String phone;
}
4. 创建 Mapper 接口
创建一个接口,并使用 @Mapper 注解将其标记为 MyBatis 的 Mapper。
package com.example.mapper;
import com.example.model.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface UserMapper {
// 查询所有用户
@Select("SELECT * FROM user")
List<User> findAll();
// 根据 ID 查询用户
@Select("SELECT * FROM user WHERE id = #{id}")
User findById(Integer id);
}
或者,你也可以将 SQL 写在 XML 文件中。
5. 编写 Mapper XML 文件(可选)
如果你不想使用注解,可以将 SQL 语句写在 XML 文件中。
在 src/main/resources/mapper 目录下创建 UserMapper.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.example.mapper.UserMapper">
<!-- 查询所有用户 -->
<select id="findAll" resultType="com.example.model.User">
SELECT * FROM user
</select>
<!-- 根据 ID 查询用户 -->
<select id="findById" parameterType="int" resultType="com.example.model.User">
SELECT * FROM user WHERE id = #{id}
</select>
</mapper>
6. 开启 Mapper 扫描(可选)
如果项目中有多个 Mapper 接口,可以在主类上使用 @MapperScan 注解来扫描 Mapper 接口所在的包。
package com.example;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.mapper") // 指定 Mapper 接口所在的包
public class MyBatisApplication {
public static void main(String[] args) {
SpringApplication.run(MyBatisApplication.class, args);
}
}
7. 测试 Mapper 方法
在 src/test/java 下编写测试类,验证 Mapper 是否正常工作。
package com.example;
import com.example.mapper.UserMapper;
import com.example.model.User;
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
public class MyBatisTests {
@Autowired
private UserMapper userMapper;
@Test
public void testFindAll() {
List<User> users = userMapper.findAll();
users.forEach(System.out::println);
}
@Test
public void testFindById() {
User user = userMapper.findById(1);
System.out.println(user);
}
}
8. 使用事务管理
Spring Boot 默认集成了事务管理功能,你可以通过 @Transactional 注解来管理事务。
package com.example.service;
import com.example.mapper.UserMapper;
import com.example.model.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
private final UserMapper userMapper;
public UserService(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Transactional
public void addUser(User user) {
userMapper.insert(user);
// 如果抛出异常,事务会回滚
if (user.getName().equals("test")) {
throw new RuntimeException("模拟异常");
}
}
}
9. 常见问题
1. Mapper 接口未被扫描到
- 确保在主类上添加了
@MapperScan或者在每个 Mapper 接口上添加了@Mapper注解。 - 确保 Mapper 接口的包路径正确。
2. SQL 映射文件未生效
- 确保
mybatis.mapper-locations配置正确。 - 确保 XML 文件中的
namespace与 Mapper 接口的全限定名一致。
3. 数据库字段与实体类属性不匹配
- 在
application.properties中启用驼峰命名映射:mybatis.configuration.map-underscore-to-camel-case=true。
主键返回
在使用 MyBatis 进行数据库操作时,插入数据后获取主键是一个常见的需求。例如,当你插入一条记录时,数据库会自动生成主键(如 AUTO_INCREMENT),你需要将这个生成的主键返回给应用程序。
MyBatis 提供了多种方式来实现主键返回,以下是详细的实现方法和示例:
1. 使用 useGeneratedKeys 和 keyProperty
适用场景
- 数据库支持自增主键(如 MySQL 的
AUTO_INCREMENT)。 - 插入数据后需要获取生成的主键值。
配置方式
在 <insert> 标签中设置以下属性:
useGeneratedKeys="true":启用主键返回功能。keyProperty="id":指定主键字段对应的实体类属性。
Mapper XML 示例
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (name, age, gender, phone)
VALUES (#{name}, #{age}, #{gender}, #{phone})
</insert>
Java 实体类
@Data
public class User {
private Integer id; // 主键
private String name;
private Integer age;
private Integer gender;
private String phone;
}
测试代码
@Test
public void testInsertAndGetId() {
User user = new User();
user.setName("张三");
user.setAge(25);
user.setGender(1);
user.setPhone("12345678901");
userMapper.insertUser(user);
System.out.println("插入后的主键:" + user.getId());
}
2. 使用 <selectKey>
适用场景
- 数据库不支持自增主键(如 Oracle 的序列)。
- 需要在插入前或插入后获取主键值。
配置方式
通过 <selectKey> 标签显式指定主键生成逻辑。
示例 1:MySQL 自增主键
<insert id="insertUser">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO user (name, age, gender, phone)
VALUES (#{name}, #{age}, #{gender}, #{phone})
</insert>
示例 2:Oracle 序列
<insert id="insertUser">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
SELECT user_seq.NEXTVAL FROM dual
</selectKey>
INSERT INTO user (id, name, age, gender, phone)
VALUES (#{id}, #{name}, #{age}, #{gender}, #{phone})
</insert>
3. 使用注解方式
如果你使用的是 MyBatis 注解,也可以通过 @Options 注解实现主键返回。
示例代码
@Mapper
public interface UserMapper {
@Insert("INSERT INTO user (name, age, gender, phone) VALUES (#{name}, #{age}, #{gender}, #{phone})")
@Options(useGeneratedKeys = true, keyProperty = "id") // 指定主键返回
int insertUser(User user);
}
4. 返回主键的工作原理
-
自增主键:
- 数据库在插入记录时会自动生成主键(如 MySQL 的
AUTO_INCREMENT)。 - MyBatis 通过 JDBC 的
getGeneratedKeys()方法获取生成的主键值。
- 数据库在插入记录时会自动生成主键(如 MySQL 的
-
序列主键:
- 在插入前通过查询序列(如 Oracle 的
NEXTVAL)获取主键值。 - 将该值作为参数插入到数据库中。
- 在插入前通过查询序列(如 Oracle 的
-
手动设置主键:
- 如果主键由应用程序生成(如 UUID),可以在插入前手动设置主键值。
5. 注意事项
-
主键冲突:
- 确保插入的数据不会违反主键约束(如重复插入相同的主键值)。
-
事务管理:
- 如果插入操作在一个事务中,确保事务提交后再获取主键值。
-
多主键支持:
- 如果表有多个主键,可以使用
keyProperty="id,anotherKey"来同时返回多个主键。
- 如果表有多个主键,可以使用
-
性能优化:
- 对于批量插入,可以结合
useGeneratedKeys或<selectKey>使用,但需要注意性能问题。
- 对于批量插入,可以结合
总结
MyBatis 提供了多种方式来实现插入后返回主键的功能,主要分为以下两种:
- 自增主键:通过
useGeneratedKeys和keyProperty属性。 - 非自增主键:通过
<selectKey>标签或手动设置主键。
mybatis 缓存
MyBatis 提供了两种类型的缓存来提高查询效率:一级缓存和二级缓存。
一级缓存
- 默认开启,是SqlSession级别的缓存。在操作数据库时,数据会被先从数据库中取出放入到SqlSession的一级缓存中,在该SqlSession没有被关闭的情况下,再次读取相同的数据时会直接从缓存中获取;当执行了更新、插入、删除等操作后,SqlSession会清空缓存。
二级缓存
-
是Mapper级别的缓存,默认情况下是关闭的,需要手动开启。二级缓存可以跨SqlSession使用,即不同的SqlSession对象访问同一个Mapper下的sql语句,都可以共享二级缓存的数据。二级缓存是建立在一级缓存之上的,作用域比一级缓存大。
开启二级缓存的方法是在MyBatis配置文件中加入
<setting name="cacheEnabled" value="true"/>,然后在对应的Mapper XML文件中添加<cache/>标签即可开启当前Mapper下的二级缓存。
缓存回收策略
- 基于最近最少使用的回收算法(LRU - Least Recently Used)
- 基于最不经常使用的回收算法(LFU - Least Frequently Used)
- 基于时间的回收算法(TTL - Time To Live)
你可以通过设置cache元素的属性来选择适合你应用的回收策略:
<cache eviction="FIFO" size="512" readOnly="true"/>
在这个例子中,eviction指定了回收策略为先进先出(FIFO),size定义了缓存的最大条目数,readOnly表示缓存是否为只读。
注意事项
- 使用缓存时要注意事务隔离的问题,避免脏读问题的发生。
- 当表中的数据有变化时,要确保缓存能够及时更新或失效,以免读取到过期的数据。
MyBatis延迟加载
MyBatis 的延迟加载(Lazy Loading)是一种优化技术,用于在需要时才加载关联数据,而不是在查询主对象时立即加载所有关联数据。延迟加载可以有效减少数据库查询次数,提高性能,特别是在处理复杂对象关系时。
延迟加载的原理
延迟加载的核心思想是:只有在真正访问某个属性或关联数据时,才会触发对应的 SQL 查询。如果没有访问到这些数据,就不会执行额外的查询。
在 MyBatis 中,延迟加载主要通过动态代理实现。当查询主对象时,MyBatis 会返回一个代理对象,而不会立即加载关联数据。只有当调用代理对象的相关方法时,才会触发实际的 SQL 查询。
延迟加载的配置
要启用延迟加载,需要进行以下配置:
1. 全局配置
在 mybatis-config.xml 文件中,设置全局延迟加载相关的参数:
<settings>
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 关闭积极加载(即默认不加载关联数据) -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
lazyLoadingEnabled: 是否开启延迟加载,默认值为false。aggressiveLazyLoading: 是否启用积极加载(即是否在加载主对象时自动加载所有关联数据)。如果设置为true,则延迟加载无效;建议设置为false。
2. Mapper 配置
在映射文件中,使用 association 或 collection 标签来定义关联对象,并指定 fetchType 属性为 lazy:
<resultMap id="userMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!-- 定义延迟加载的关联对象 -->
<association property="address" javaType="Address" fetchType="lazy">
<id property="id" column="address_id"/>
<result property="city" column="city"/>
</association>
</resultMap>
<select id="selectUserWithAddress" resultMap="userMap">
SELECT u.id, u.name, a.id AS address_id, a.city
FROM users u
LEFT JOIN addresses a ON u.address_id = a.id
WHERE u.id = #{id}
</select>
fetchType: 指定加载策略,取值为eager(立即加载)或lazy(延迟加载)。
延迟加载的工作流程
-
查询主对象:
- 当执行查询主对象的 SQL 时,MyBatis 只会加载主对象的数据,而不会加载关联对象。
- 如果关联对象被定义为延迟加载,MyBatis 会返回一个代理对象。
-
访问关联对象:
- 当代码中第一次访问关联对象(例如调用
getAddress()方法)时,MyBatis 会触发对应的 SQL 查询,加载关联数据。
- 当代码中第一次访问关联对象(例如调用
-
缓存机制:
- 一旦关联数据被加载,MyBatis 会将其缓存起来,避免重复查询。
示例代码
数据库表结构
假设我们有两张表:
users: 用户表,包含用户的基本信息。addresses: 地址表,包含用户的地址信息。
Java 实体类
public class User {
private Integer id;
private String name;
private Address address; // 关联的地址对象
// Getters and Setters
}
public class Address {
private Integer id;
private String city;
// Getters and Setters
}
Mapper XML 配置
<resultMap id="userMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<association property="address" javaType="Address" fetchType="lazy">
<id property="id" column="address_id"/>
<result property="city" column="city"/>
</association>
</resultMap>
<select id="selectUserWithAddress" resultMap="userMap">
SELECT u.id, u.name, a.id AS address_id, a.city
FROM users u
LEFT JOIN addresses a ON u.address_id = a.id
WHERE u.id = #{id}
</select>
测试代码
public static void main(String[] args) {
SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory().openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 查询用户(此时不会加载地址)
User user = userMapper.selectUserWithAddress(1);
System.out.println("User Name: " + user.getName());
// 访问地址时才会触发加载
Address address = user.getAddress();
System.out.println("City: " + address.getCity());
sqlSession.close();
}
运行结果:
- 第一次打印用户名称时,只会执行一条 SQL 查询(查询用户表)。
- 当访问
getAddress()方法时,才会执行第二条 SQL 查询(查询地址表)。
注意事项
-
事务隔离与延迟加载:
- 延迟加载依赖于
SqlSession的生命周期。如果在延迟加载之前关闭了SqlSession,会导致延迟加载失败。 - 解决方案:可以在业务层管理事务,确保
SqlSession在延迟加载完成后再关闭。
- 延迟加载依赖于
-
N+1 查询问题:
- 如果主对象的数量较多,且每个主对象都需要触发延迟加载,可能会导致大量的 SQL 查询(即 N+1 查询问题)。
- 解决方案:可以通过批量加载(Batch Loading)或预加载(Eager Loading)来优化。
-
代理对象限制:
- 延迟加载的对象是代理对象,不能直接序列化或传递给远程服务。
- 如果需要传递延迟加载的对象,可以先触发加载,或者将数据转换为普通对象。
-
调试困难:
- 延迟加载的 SQL 查询会在运行时动态触发,可能增加调试难度。
- 可以通过日志记录 SQL 查询,观察延迟加载的行为。
总结
MyBatis 的延迟加载是一种强大的优化工具,特别适合处理复杂的对象关系和大数据量场景。但在使用时需要注意事务管理、性能优化以及潜在的问题(如 N+1 查询)。合理地结合延迟加载和立即加载,才能充分发挥 MyBatis 的性能优势。
预编译
预编译(Prepared Statements)是一种用于数据库查询的技术,它能够提高性能并且有助于防止SQL注入攻击。MyBatis作为一种持久层框架,充分利用了预编译语句来优化数据库交互过程。
预编译的优点
-
提高性能:当执行多次相似的SQL语句时,数据库可以重用已经解析和编译好的SQL模板,只需替换参数值即可,这大大减少了重复分析、编译SQL语句的时间开销。
-
防止SQL注入:由于预编译语句将SQL逻辑与数据分离,因此即使用户输入恶意代码,也会被视为普通参数而非可执行代码,从而有效避免SQL注入风险。
在MyBatis中使用预编译
在MyBatis中,默认情况下,所有的<select>, <insert>, <update>, 和 <delete>标签都支持预编译。你只需要通过占位符#{}来指定参数的位置,MyBatis就会自动为你的SQL语句使用预编译技术。
例如:
<select id="findUser" parameterType="int" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
在这个例子中,#{id}是一个占位符,MyBatis会将其替换为传入的实际参数值,并确保这条SQL语句被预编译。
使用注意事项
-
不要直接拼接SQL字符串:避免使用
${}这样的表达式来构建SQL语句,因为这种方式不会进行预编译,容易导致SQL注入漏洞。<!-- 不推荐 --> <select id="findUserByName" parameterType="string" resultType="User"> SELECT * FROM users WHERE name = '${name}' </select>推荐的方式是使用
#{}:<!-- 推荐 --> <select id="findUserByName" parameterType="string" resultType="User"> SELECT * FROM users WHERE name = #{name} </select> -
批量操作时的考虑:对于批量插入等场景,虽然MyBatis也支持预编译,但需要注意不同数据库对批量操作的支持程度不同,可能需要根据实际情况调整配置或采用其他策略。
动态SQL
动态SQL是MyBatis框架中一个非常强大的功能,它允许你根据不同的条件构建灵活的SQL语句。这对于处理复杂的查询逻辑尤其有用,比如根据不同条件选择不同的查询字段、连接条件或是排序规则等。MyBatis提供了几种标签来帮助开发者实现这一目标。
常用的动态SQL标签
-
:用于单条件判断。
<select id="findActiveBlogWithTitleLike" resultType="Blog"> SELECT * FROM BLOG WHERE state = 'ACTIVE' <if test="title != null"> AND title like #{title} </if> </select>这个例子展示了如何使用
<if>标签来添加一个可选的查询条件。 -
, , :类似Java中的switch语句,适用于多分支选择。
<select id="findActiveBlog" resultType="Blog"> SELECT * FROM BLOG WHERE state = 'ACTIVE' <choose> <when test="title != null"> AND title like #{title} </when> <when test="author != null and author.name != null"> AND author_name like #{author.name} </when> <otherwise> AND featured = 1 </otherwise> </choose> </select> -
:自动处理
WHERE关键字,并且会智能地去掉多余的AND或OR。<select id="findActiveBlog" resultType="Blog"> SELECT * FROM BLOG <where> <if test="state != null"> state = #{state} </if> <if test="title != null"> AND title like #{title} </if> </where> </select> -
:用于更新语句,可以智能地处理
SET关键字,并去除最后一个逗号。<update id="updateAuthorIfNecessary"> update AUTHOR <set> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email},</if> <if test="bio != null">bio=#{bio}</if> </set> where id=#{id} </update> -
:用于遍历集合,常用于IN子句。
<select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P WHERE ID in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
动态SQL的优势
- 灵活性:可以根据传入的参数动态生成SQL语句,减少硬编码SQL的数量。
- 维护性:将SQL逻辑集中管理,便于后期维护和修改。
- 性能优化:通过合理的条件判断,避免不必要的全表扫描或其他低效操作。
更多推荐



所有评论(0)