MyBatis
2025年11月6日大约 6 分钟
MyBatis
- MyBatis是一个持久层框架,用于简化Java应用程序与数据库之间(JDBC)的交互。
MyBatis基本操作步骤
- 添加MyBatis依赖(如果使用Maven或Gradle)
- 创建数据库表
- 创建实体类
- 配置MyBatis(application中配置数据库)
spring:
datasource:
url: jdbc:mysql://localhost:3306/dbname
username: your_username
password: your_password- 编写Mybatis接口(Mapper)
@Mapper
public interface UserMapper {
@Select("SELECT * FROM user")
public List<User> getAllUsers();
}数据库连接池
- 数据库连接池是一个容器,负责分配和管理数据库连接,以提高应用程序的性能和资源利用率。
- 它允许应用程序重复使用现有的数据库连接,而不是每次需要访问数据库时都创建一个新的连接。
- 并且会释放超过最大空闲时间的连接,防止资源浪费。
提示
- 常用的数据库连接池有HikariCP(SpringBoot默认)、C3P0、Druid
- 如果想要实现自己的连接池,需要实现DataSource接口
增删改查
删除操作 Delete
@Delete("DELETE FROM user WHERE id = #{id}")
public void deleteById(Integer id);提示
如果需要知道影响的行数,可以将返回值 void 改为 Integer
即
@Delete("DELETE FROM user WHERE id = #{id}")
public Integer deleteById(Integer id);MyBatis占位符(#{}和${}的区别)
| 符号 | 说明 | 是否推荐 |
|---|---|---|
#{} | 预编译占位符,会将#{}替换为?,生成预编译SQL防止SQL注入 | ✅ |
${} | 拼接SQL,会将参数值直接拼接到SQL语句中,存在SQL注入风险 | ❌ |
新增操作 Insert
@Insert("INSERT INTO user (name, email) VALUES (#{name}, #{email})")
public void insertUser(User user);提示
根据上面例子可见,可以通过传入的对象(如User)的属性名来映射SQL语句中的列名
实现原理:MyBatis会根据传入的User对象的属性值,生成对应的SQL语句来更新数据库中的记录。
需要返回主键时(主键返回)
如果需要在插入数据后获取生成的主键,可以使用@Options注解来指定主键返回的配置
@Insert("INSERT INTO user (name, email) VALUES (#{name}, #{email})")
@Options(keyProperty = "id", useGeneratedKeys = true)
public void insertUser(User user);keyProperty:指定实体类中用于接收生成主键的属性名称useGeneratedKeys:设置为true表示使用JDBC的自动生成主键功能
修改操作 Update
@Update("UPDATE user SET name = #{name}, email = #{email} WHERE id = #{id}")
public void updateUser(User user);实现原理同上
查询操作 Select
@Select("SELECT * FROM user WHERE name = #{name} AND email = #{email}")
public User findByNameAndEmail(@Param("name") String name, @Param("email") String email);提示
- 如果方法参数有多个,需要使用
@Param注解来指定参数名称,以便在SQL语句中引用。 - 如果方法参数只有一个,可以省略
@Param注解,直接使用参数名称。
原因:Java编译后参数名称不会保留,MyBatis无法通过反射获取参数名称,从而导致无法正确映射参数值到SQL语句中。
此外 基于SpringBoot官方骨架构建的项目中,可以省略@Param注解。
数据封装问题
- 如果实体属性名和数据库列名一致,MyBatis会自动将查询结果封装到实体类对象中。
- 如果实体属性名和数据库列名不一致,则不能自动封装。
下面会介绍如何解决这个问题。
查询时的数据封装问题
- 解决方法一:开启驼峰命名自动映射
- 在
application.yml中添加如下配置
- 在
mybatis:
configuration:
map-underscore-to-camel-case: true- 解决方案二:使用SQL别名
@Select("SELECT id,create_time AS createTime,update_time AS updateTime FROM user WHERE id = #{id}")
public User findById(Integer id);- 解决方案三:使用
@Results和@Result注解进行映射封装column属性:指定数据库表中的列名称property属性:指定实体类中的属性名称
@Select("SELECT * FROM user WHERE id = #{id}")
@Results({
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
public User findById(Integer id);XML映射配置
- 除了使用注解方式编写SQL语句外,还可以使用XML文件来配置SQL映射。
配置步骤和规则(默认情况下)
- 在
resources目录下创建和mapper包对应的目录结构(如com/example/demo/mapper) - 在对应目录下创建XML文件,命名规则为
接口名 + Mapper(如UserMapper.xml)(1和2总结起来就是要同mapper包“同包同名”) - mapper标签的
namespace属性必须和Mapper接口的全限定类名一致 - 在XML文件中定义SQL语句和映射关系,sql语句的id必须和Mapper接口中的方法名一致,且返回值类型通过
resultType属性指定并保持一致
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
<select id="findByNameAndEmail" resultType="com.example.demo.entity.User">
SELECT * FROM user WHERE name = #{name} AND email = #{email}
</select>
</mapper>辅助配置
- 指定XML映射配置文件位置:需要在
application.yml中添加如下配置
mybatis:
mapper-locations: classpath:自定义的路径/*.xml动态SQL
随着用户的输入或外部条件变化而变化的SQL语句,称为动态SQL。
- MyBatis提供了一些标签来帮助我们构建动态SQL语句。
<if>标签
判断条件是否成立,使用test属性指定条件表达式
<select id="findUsers" resultType="com.example.demo.entity.User">
SELECT * FROM user
<where>
<if test="name != null">
AND name = #{name}
</if>
<if test="email != null">
AND email = #{email}
</if>
</where>
</select>注意
可以看到上面的代码,我将where替换为了<where>标签,因为直接使用where可能会导致SQL语法错误(如多余的AND)
<where>标签
用于动态生成WHERE子句,会自动处理多余的AND、OR,并且如果没有条件时不会生成WHERE子句。
<set>标签(用于update)
用于动态生成SET子句,会自动处理多余的逗号,并且如果没有更新字段时不会生成SET子句。
<update id="updateUser">
UPDATE user
<set>
<if test="name != null">
name = #{name},
</if>
<if test="email != null">
email = #{email},
</if>
</set>
WHERE id = #{id}
</update><foreach>标签
用于遍历集合(如List、数组等),生成动态的SQL片段
<select id="findUsersByIds" resultType="com.example.demo.entity.User">
SELECT * FROM user WHERE id IN
<foreach item="id" collection="ids" open="(" separator="," close=")">
#{id}
</foreach>
</select>collection属性:指定要遍历的集合名称item属性:指定当前遍历的元素名称separator属性:每一次遍历元素之间的分隔符
open属性:遍历开始前添加的内容close属性:变量结束后添加的内容
<sql>和<include>标签
为了提高代码复用性,可以使用<sql>标签定义可重用的SQL片段,然后通过<include>标签引入这些片段
<sql id="userColumns">
SELECT id, name, email, create_time, update_time FROM user
</sql>
<select id="findAllUsers" resultType="com.example.demo.entity.User">
<include refid="userColumns"/>
</select>