MyBatis映射文件常用元素详解与示例
引言
MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis的配置文件和映射文件分离,映射文件用于定义SQL语句和结果映射。本文将介绍MyBatis映射文件中常用的元素及其示例用法。
一、基础CRUD元素
1. <mapper>
作用:定义映射文件的根元素,指定命名空间。
属性:namespace
(对应Mapper接口的全限定名)。
<mapper namespace="com.example.UserMapper">
2. <select>
作用:定义查询操作。
关键属性:
id
:方法名parameterType
:参数类型resultType
/resultMap
:返回类型
示例:查询用户
<!--
查询用户信息
参数:用户ID(整数类型)
返回:一个User对象
说明:WHERE id = #{id}:根据id条件进行查询,#{id}是MyBatis的占位符,用于动态传参。
用法:在Mapper接口中定义对应的方法,例如:
User getUserById(int id);
-->
<select id="getUserById" parameterType="int" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
3. <insert>
作用:insert
元素用于定义插入语句,用于向数据库中插入数据。
属性:useGeneratedKeys
(是否使用自增主键),keyProperty
(主键属性名)。
示例:插入用户并返回自增ID
<!--
插入用户信息到数据库
参数:User对象(包含name和age属性)
功能:将用户信息插入到users表中
说明:
1. useGeneratedKeys="true":表示启用数据库自动生成主键(通常是自增ID)。
2. keyProperty="id":指定生成的主键值将赋值到User对象的id属性中。
3. 通过#{name}和#{age}动态获取User对象的name和age属性值。
用法:在Mapper接口中定义对应的方法,例如:
int insertUser(User user);
调用后,user对象的id属性会被自动填充为数据库生成的主键值。
-->
<insert id="insertUser" parameterType="User"
useGeneratedKeys="true" keyProperty="id">
INSERT INTO users (name, age) VALUES (#{name}, #{age})
</insert>
4. <update>
与 <delete>
作用:更新和删除操作。
示例:更新用户
<!--
更新用户信息
参数:User对象(包含id、name和age属性)
功能:根据用户ID更新用户的名字和年龄
说明:
1. parameterType="User":表示传入的参数是一个User对象。
2. #{name}、#{age}、#{id}:MyBatis占位符,用于从User对象中动态获取对应的属性值。
3. UPDATE users SET name=#{name}, age=#{age} WHERE id=#{id}:更新users表中指定ID的用户记录。
用法:在Mapper接口中定义对应的方法,例如:
int updateUser(User user);
调用时传入一个User对象即可。
-->
<update id="updateUser" parameterType="User">
UPDATE users SET name=#{name}, age=#{age} WHERE id=#{id}
</update>
示例:删除用户
<!--
删除用户信息
参数:用户ID(整数类型)
功能:根据用户ID删除用户记录
说明:
1. parameterType="int":表示传入的参数是一个整数类型的用户ID。
2. #{id}:MyBatis占位符,用于动态获取传入的用户ID值。
3. DELETE FROM users WHERE id=#{id}:从users表中删除指定ID的用户记录。
用法:在Mapper接口中定义对应的方法,例如:
int deleteUser(int id);
调用时传入用户ID即可。
-->
<delete id="deleteUser" parameterType="int">
DELETE FROM users WHERE id = #{id}
</delete>
二、结果映射 <resultMap>
拓展阅读:MyBatis映射文件 <resultMap>
元素详解与示例
1. 基础字段映射
用途:解决数据库列名与Java属性名不一致问题。
示例:
<!--
用户结果映射
功能:将数据库查询结果映射到User对象
说明:
1. id="UserResult":标识这个结果映射的唯一名称。
2. type="User":指定目标Java对象类型为User。
3. <id property="userId" column="user_id">:定义User对象的userId属性与数据库表的user_id列对应。
4. <result property="userName" column="user_name">:定义User对象的userName属性与数据库表的user_name列对应。
-->
<resultMap id="UserResult" type="User">
<id property="userId" column="user_id" />
<result property="userName" column="user_name" />
</resultMap>
<!--
查询用户信息
功能:从users表中查询user_id和user_name列,并将结果映射到User对象
说明:
1. id="getUser":标识这个查询操作的唯一名称。
2. resultMap="UserResult":指定使用UserResult结果映射来处理查询结果。
3. SELECT user_id, user_name FROM users:从users表中查询user_id和user_name两列。
用法:在Mapper接口中定义对应的方法,例如:
List<User> getUser();
返回一个包含所有用户信息的User对象列表。
-->
<select id="getUser" resultMap="UserResult">
SELECT user_id, user_name FROM users
</select>
2. 关联关系映射
<association>
:一对一关联<collection>
:一对多关联
示例:用户与订单的一对多关系
<!--
用户与订单结果映射
功能:将用户和其关联的订单信息映射到User对象
说明:
1. id="UserWithOrders":标识这个结果映射的唯一名称。
2. type="User":指定目标Java对象类型为User。
3. <id property="id" column="id">:定义User对象的id属性与数据库表的id列对应,作为主键。
4. <collection property="orders" ofType="Order">:定义User对象的orders属性为集合类型,存储Order对象。
a. property="orders":User类中的订单集合属性。
b. ofType="Order":集合中的元素类型为Order。
c. <id property="orderId" column="order_id">:定义Order对象的orderId属性与数据库表的order_id列对应,作为主键。
d. <result property="orderNo" column="order_no">:定义Order对象的orderNo属性与数据库表的order_no列对应。
5. 通过这个结果映射,可以将用户的订单信息一并查询出来,并映射为嵌套的Java对象结构。
-->
<resultMap id="UserWithOrders" type="User">
<id property="id" column="id"/>
<collection property="orders" ofType="Order">
<id property="orderId" column="order_id"/>
<result property="orderNo" column="order_no"/>
</collection>
</resultMap>
<!--
查询用户及其订单信息
功能:从users和orders表中查询用户及订单数据,并通过UserWithOrders结果映射转换为Java对象
说明:
1. id="getUserWithOrders":标识这个查询操作的唯一名称。
2. resultMap="UserWithOrders":指定使用UserWithOrders结果映射来处理查询结果。
3. SELECT u.*, o.order_id, o.order_no FROM users u LEFT JOIN orders o ON u.id = o.user_id:
a. 查询users表的所有列(u.*)和orders表的order_id、order_no列。
b. 使用LEFT JOIN将users表和orders表连接,连接条件为用户ID(u.id = o.user_id)。
c. 查询结果将包含用户的基本信息和其关联的订单信息。
用法:在Mapper接口中定义对应的方法,例如:
List<User> getUserWithOrders();
返回一个包含用户及其订单信息的User对象列表。
-->
<select id="getUserWithOrders" resultMap="UserWithOrders">
SELECT u.*, o.order_id, o.order_no
FROM users u LEFT JOIN orders o ON u.id = o.user_id
</select>
三、动态SQL元素
1. <if>
用途:条件判断。
示例:动态过滤用户
<!--
动态条件查询用户信息
功能:根据传入的条件动态查询用户信息,支持组合条件过滤
参数:parameterType="map" 表示传入的参数是一个Map对象,可以包含多个条件
说明:
1. SELECT * FROM users:从users表中查询所有列的数据。
2. <where>:标识查询条件的开始,MyBatis会自动处理WHERE关键字的拼接。
3. <if> 标签用于动态判断条件是否满足:
- test="name != null":如果传入的name参数不为空,则拼接name = #{name}作为查询条件。
- test="age != null":如果传入的age参数不为空,则拼接age = #{age}作为查询条件。
4. 这个查询的灵活性在于可以根据传入的参数动态生成查询条件,如果多个条件同时存在,MyBatis会自动将它们用AND连接。
用法:在Mapper接口中定义对应的方法,例如:
List<User> findUsers(Map<String, Object> params);
参数params可以包含"name"和/或"age"键,也可以都不包含。
如果参数不包含这些键,查询将没有条件限制(即查询所有用户)。
注意:
1. 如果需要查询默认条件下的数据,建议为每个参数设置默认值或在Mapper方法中提供默认参数。
2. 如果需要更复杂的条件组合(如OR条件),可以使用<choose>、<when>等动态SQL标签。
3. 传递的参数类型为Map,便于动态传递多个查询条件。
4. #{name}和#{age}是MyBatis的占位符,用于动态获取Map中的值。
-->
<select id="findUsers" parameterType="map" resultType="User">
SELECT * FROM users
<where>
<if test="name != null">AND name = #{name}</if>
<if test="age != null">AND age = #{age}</if>
</where>
</select>
2. <choose>
, <when>
, <otherwise>
用途:多条件分支(类似Java的switch-case
)。
示例:
<!--
动态条件查询用户信息
功能:根据传入的条件动态查询用户信息
参数:parameterType="map" 表示传入的参数是一个Map对象,可以包含多个条件
说明:
1. SELECT * FROM users:从users表中查询所有列的数据。
2. <where>:定义查询条件的开始,MyBatis会自动处理WHERE关键字的拼接。
3. <choose>:类似于Java中的switch语句,用于条件选择。
4. <when test="name != null">name = #{name}:当传入的name不为空时,拼接name = #{name}作为查询条件。
5. <when test="age != null">age = #{age}:当传入的age不为空时,拼接age = #{age}作为查询条件。
6. <otherwise>status = 1:当所有<when>条件都不满足时,拼接status = 1作为默认查询条件。
用法:在Mapper接口中定义对应的方法,例如:
List<User> selectByCondition(Map<String, Object> params);
参数params可以包含"name"或"age"键,也可以都不包含。
返回一个包含符合条件的User对象列表。
注意:
1. 如果传入多个条件,MyBatis会自动将它们拼接成AND连接的条件。
2. 如果不需要默认条件,可以移除<otherwise>标签。
3. 传递的参数类型为Map,便于动态传递查询条件。
4. #{name}和#{age}是MyBatis的占位符,用于动态获取Map中的值。
5. 如果需要更复杂的查询条件,可以结合使用<if>、<where>等动态SQL标签。
-->
<select id="selectByCondition" parameterType="map" resultType="User">
SELECT * FROM users
<where>
<choose>
<when test="name != null">name = #{name}</when>
<when test="age != null">age = #{age}</when>
<otherwise>status = 1</otherwise>
</choose>
</where>
</select>
3. <foreach>
用途:遍历集合,常用于IN查询或批量操作。
示例:批量插入用户
<!--
批量插入用户信息
功能:将多个用户对象批量插入到users表中
参数:parameterType="list"指定传入的参数是一个用户对象列表
说明:
1. INSERT INTO users (name, age) VALUES
定义插入语句的目标表和字段,这里是name和age。
2. <foreach item="user" collection="list" separator=",">
- 标识这是一个批量操作。
- collection="list"表示传入的参数是一个列表。
- item="user"表示列表中的每个元素。
- separator=","用于分隔每个插入的值。
3. (#{user.name}, #{user.age})
表示从每个用户对象中获取name和age属性值,并插入到数据库中。
4. 此方法适用于需要批量插入多个用户记录的场景,可以显著提高插入效率。
使用方法:
在Mapper接口中定义对应的方法,例如:
int batchInsert(List<User> users);
调用时传入一个User对象列表即可。
注意:
1. 如果传入的用户列表为空,MyBatis将不会执行任何插入操作。
2. 如果用户的数据库不支持批量插入,可能会导致插入失败或者性能问题。
3. 如果需要插入多个字段,可以在values中添加更多的 #{user.xxx} 占位符。
-->
<insert id="batchInsert" parameterType="list">
INSERT INTO users (name, age) VALUES
<foreach item="user" collection="list" separator=",">
(#{user.name}, #{user.age})
</foreach>
</insert>
四、复用SQL片段
1. <sql>
与 <include>
用途:定义可重用的SQL片段。
示例:
<!--
定义SQL片段
功能:定义一个可重用的SQL片段,包含用户表的列名
说明:
1. id="userColumns":定义这个SQL片段的唯一名称,用于在其他地方引用。
2. 这个片段包含用户表的主要列名:id, name, age。
3. 通过使用SQL片段,可以避免在多个查询中重复编写相同的列名。
4. 如果需要按条件查询或动态列过滤,可以在此基础上进行扩展。
-->
<sql id="userColumns">id, name, age</sql>
<!--
查询所有用户信息
功能:从 users 表中查询所有用户信息,并返回 User 对象列表
说明:
1. id="getAllUsers":标识这个查询操作的唯一名称。
2. resultType="User":指定查询结果的类型为 User 对象。
3. <include refid="userColumns"/>:引用之前定义的SQL片段,避免重复编写列名。
4. 这个查询会从 users 表中选择 id, name, age 列的所有数据。
用法:在Mapper接口中定义对应的方法,例如:
List<User> getAllUsers();
返回一个包含所有用户信息的User对象列表。
建议:
1. 如果需要查询特定条件下的用户,可以在SQL中添加 WHERE 子句。
2. 如果需要避免列名和别名冲突,可以在SQL片段中使用别名,例如:
<sql id="userColumns">id AS user_id, name AS user_name, age AS user_age</sql>
3. 对于查询结果的列数较多的情况,建议使用 resultMap 映射字段,以确保列名与对象属性正确对应。
-->
<select id="getAllUsers" resultType="User">
SELECT <include refid="userColumns"/> FROM users
</select>
五、参数处理
1. #{}
与 ${}
#{}
:预编译参数(防SQL注入)${}
:直接替换(慎用,如动态表名)
示例:动态排序
<!--
动态排序查询用户信息
功能:从 users 表中查询所有用户信息,并根据传入的 orderBy 参数进行排序
参数:orderBy(字符串类型)
注意:
1. ${orderBy} 是 MyBatis 的动态 SQL 特性,用于将传入的参数直接拼接到 SQL 语句中,而不是通过预编译参数。
2. 使用 ${} 时需要特别小心,因为它会直接将参数插入到 SQL 语句中,可能导致 SQL 注入漏洞。
3. 如果 `orderBy` 参数未经过充分验证,可能会被恶意构造的 SQL 语句利用,对数据库造成危害。
建议:
1. 在应用程序中,确保 `orderBy` 参数只能来源于受信任的来源,如预定义的有效排序字段。
2. 对于不可信的输入,应使用白名单机制对 `orderBy` 参数进行验证,只允许指定的有效字段。
3. 如果必须使用动态排序,可以考虑以下更安全的实现方式:
- 使用枚举起有效的排序字段,并在代码中进行验证。
- 或者,将 `orderBy` 参数替换为一个完整的 SQL 片段,但同样需要进行严格的验证和转义。
-->
<select id="getUsersOrdered" resultType="User">
SELECT * FROM users ORDER BY ${orderBy}
</select>
六、总结
拓展阅读:MyBatis映射文件 resultMap 元素详解与示例
MyBatis官方文档(mybatis.org)