准备工作
-
相关maven依赖
LombokSpring WebMyBatis FrameworkMySQL Driver -
application.properties 配置数据源
spring.datasource.url=jdbc:mysql://localhost:3306/myapp_db
spring.datasource.username=xxx
spring.datasource.password=xxx
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# resources目录下新建mapper目录
mybatis.mapper-locations=classpath:mapper/**.xml
# 数据库表列明 中横线转 表实体bean对应的小驼峰
mybatis.configuration.map-underscore-to-camel-case=true
# 开启sql日志
logging.level.org.example.mybatis.mapper=debug
- idea安装
mybatisx插件, 按键自动生成Mapper接口对应的xml实现 - @Mapper 增删改查接口 或者 @MapperScan扫描
@MapperScan({"org.example.mybatisgenerate.mapper"})
@Configuration
public class MybatisConfig {
}
取值
#{}安全的参数预编译
${}是直接的字符串拼接。
| 特性 | #{} (井号占位符) | ${} (美元占位符) |
|---|---|---|
| 处理方式 | 预编译(PreparedStatement) ,参数化查询 | 字符串拼接(Statement) ,直接替换 |
| 安全性 | 高,有效防止SQL注入 | 低,存在SQL注入风险 |
| 参数类型 | 任意类型,自动进行类型转换和转义 | 通常为字符串,原样替换,不转义 |
| 使用场景 | 几乎所有的值传递(WHERE条件值、INSERT值等) | 动态SQL片段(表名、列名、ORDER BY等) |
取入参值
- 当只有一个参数时,直接放变量名(xml中的变量名可以随便命名) 或 bean中属性, 这时可以不用
@Param;若用了@Param,xml中的变量名必须和@Param内的命名保持一致,不能直接取bean中属性,需要@Param内的命名.属性名
@Mapper
public interface UserMapper {
User getUserById(String id);
void insertUser(User user);
}
<select id="getUserById" resultType="org.example.mybatis.entity.User">
select * from users where id = #{id}
</select>
<!-- username email 为 User 中的属性 -->
<insert id="insertUser">
insert into users(username, email)values (#{username}, #{email})
</insert>
- 多个参数用
@Param
void updateUser(@Param("id") String id, @Param("us") User user);
<update id="updateUser">
update users
set username = #{us.username},
email = #{us.email}
where id = #{id}
</update>
最佳实践:为了统一化取参,即使一个参数也使用@Param
返回值处理
返回普通数据 resultType
- 返回基本类型、String、Long、普通javeBean都只需要在
resultType中声明返回值的全限定类名 - mybatis.configuration.map-underscore-to-camel-case=true
开启驼峰映射:表中查询结果集中a_column列会自动被映射为bean中的aColumn属性
自定义映射结果集 resultMap
当表中结果和javabean属性不能映射(字段名不对应,即使开启驼峰映射也不行),这时需要使用自定义映射
<!-- id自定义的结果映射唯一名 type-接受结果bean的全类名 -->
<resultMap id="UserRm" type="org.example.mybatis.entity.User">
<!-- id标签-主键 result标签-其他普通列 column-结果表的列名 property-bean的属性名 -->
<id column="id" property="id"/>
<result column="user_phone" property="phone"/>
</resultMap>
<select id="getUserByIdRm" resultMap="UserRm">
select * from users;
</select>
一对一,当遇到某个属性的类型是另一个javaBean,resultMap里面需使用association。
@Data
public class Order {
private String id;
private String productName;
private User user;
}
Order getOrderWithUserById(@Param("id") String id);
<resultMap id="OrderRm" type="org.example.mybatis.entity.Order">
<id column="id" property="id"/>
<result column="product_name" property="productName"/>
<!-- 一对一,当遇到某个属性类是另一个javabean,需使用association。-->
<association property="user" javaType="org.example.mybatis.entity.User">
<id column="user_id" property="id"/>
<result column="user_phone" property="phone"/>
<result column="username" property="username"/>
<result column="email" property="email"/>
</association>
</resultMap>
<select id="getOrderWithUserById" resultMap="OrderRm">
select orders.*, username, email, user_phone from orders left join users on orders.user_id = users.id
where orders.id = #{id}
</select>
一对多,当遇到某个属性的类型是另一个javaBean的集合,resultMap里面需使用collection。
@Data
public class User {
private Integer id;
private String username;
private String email;
private Date createdAt;
private String phone;
private List<Order> orderList;
public User(String username, String email) {
this.username = username;
this.email = email;
}
}
User getUserAllOrders(String id);
<resultMap id="getUserAllOrdersRm" type="org.example.mybatis.entity.User">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="email" property="email"/>
<result column="user_phone" property="phone"/>
<collection property="orderList" ofType="org.example.mybatis.entity.Order">
<id column="orderId" property="id"/>
<result column="product_name" property="productName"/>
</collection>
</resultMap>
<select id="getUserAllOrders" resultMap="getUserAllOrdersRm">
select users.*, orders.product_name, orders.id orderId
from users
left join orders on users.id = orders.user_id
where users.id = #{id}; </select>
动态标签
-
<if>标签作用:根据条件判断是否包含某段SQL。
属性:test(OGNL表达式,判断条件)
<!-- 根据条件查询用户列表 -->
<select id="selectUsers" resultType="User">
SELECT * FROM sys_user
WHERE 1=1
<!-- 如果username参数不为null且不为空字符串,则添加条件 -->
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="email != null and email != ''">
AND email = #{email}
</if>
<if test="status != null">
AND status = #{status}
</if>
</select>
-
<where>标签作用:自动处理WHERE关键字,并移除开头的AND/OR。
<select id="selectUsersSmart" resultType="User">
SELECT * FROM sys_user
<where>
<if test="username != null and username != ''">
username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="email != null and email != ''">
AND email = #{email}
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
</select>
-
<foreach>标签:遍历集合作用:遍历集合(List、Array、Map),常用于IN查询或批量操作。
核心属性:
| 属性 | 作用 | 示例值 |
|---|---|---|
collection | 要遍历的集合参数名 | idList、array、list |
item | 遍历时的每个元素别名 | id、item |
index | 遍历的索引(可选) | index |
open | 整个循环开始时的字符串 | ( |
close | 整个循环结束时的字符串 | ) |
separator | 每次循环间的分隔符 | , |
<!-- 根据多个ID查询用户 (IN查询) -->
<select id="selectUsersByIds" resultType="User">
SELECT * FROM sys_user
WHERE id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!-- 批量插入用户 -->
<insert id="batchInsert">
INSERT INTO sys_user (username, email, age) VALUES
<foreach collection="userList" item="user" separator=",">
(#{user.username}, #{user.email}, #{user.age})
</foreach>
</insert>
-
<set>标签作用:动态生成SET子句,自动去掉末尾的逗号。
<update id="updateUserSelective">
UPDATE sys_user
<set>
<if test="username != null and username != ''">
username = #{username},
</if>
<if test="email != null">
email = #{email},
</if>
<if test="age != null">
age = #{age},
</if>
<if test="status != null">
status = #{status}
</if>
</set>
WHERE id = #{id}
</update>
-
<sql>和<include>用于SQL片段复用的利器。它们能有效消除冗余,提升代码的可维护性。[抽取字段列表 + 抽取复杂查询条件]
<sql id="base_user_column">
id, username, user_phone as phone, email
</sql>
<sql id="base_user_query_condition">
<where>
<if test="user.username != null and user.username != '' ">
username LIKE CONCAT('%', #{user.username}, '%')
</if>
<if test="user.phone != null and user.phone != ''">
and user_phone = #{user.phone}
</if>
<if test="user.email != null and user.email != ''">
and email = #{user.email}
</if>
</where>
</sql>
<select id="filterUsers" resultType="org.example.mybatis.entity.User">
select
<include refid="base_user_column"/>
from users
<include refid="base_user_query_condition"/>
</select>
pagehelper 分页插件
引入分页插件依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>6.1.0</version>
</dependency>
配置拦截器插件
@Configuration
public class MyBatisConfig {
@Bean
PageInterceptor pageInterceptor(){
return new PageInterceptor();
}
}
使用分页[对于查询语句自动开启分页limit]
PageHelper.startPage(2, 3);
User user = new User();
List<User> users = userMapper.filterUsers(user);
log.info("users:{}", users);
PageInfo<User> userPageInfo = new PageInfo<>(users);
log.info("userPageInfo:{}", userPageInfo);
log.info("total:{}", userPageInfo.getTotal());
log.info("pageNum:{}", userPageInfo.getPageNum());
List<User> list = userPageInfo.getList();
可以看到sql变成
SELECT count(0) FROM users;
select id, username, user_phone as phone, email from users LIMIT ?, ?