一个优秀的持久层(Persistence Layer)框架,支持自定义 SQL、存储过程及高级映射。它主要用于 Java 应用程序中简化对数据库的操作。
为了演示 MyBatis 的基本用法,以下是一个简单的 "Hello World" 示例,展示如何进行数据库操作。
Dao 接口与 XML 配置文件:
Dao
接口(Data Access Object)都有一个对应的 XML 实现文件。自动生成的代理对象:
XML 文件配置:
namespace
: 必须与 Dao 接口的全类名一致,以便 MyBatis 能够将接口方法与 SQL 语句关联。select
、insert
、update
、delete
标签:用于定义相应的 SQL 操作。id
: 必须与 Dao 接口的方法名一致,便于映射。Dao 接口 (UserMapper.java
):
public interface UserMapper {
// 根据用户ID查询用户
User getUserById(int id);
// 插入新用户
void insertUser(User user);
}
XML 配置文件 (UserMapper.xml
):
<mapper namespace="com.example.mapper.UserMapper">
<!-- 查询用户 -->
<select id="getUserById" resultType="com.example.model.User">
SELECT * FROM users WHERE id = #{id}
</select>
<!-- 插入新用户 -->
<insert id="insertUser">
INSERT INTO users (name, age) VALUES (#{name}, #{age})
</insert>
</mapper>
MyBatis 提供了丰富的 CRUD 操作(增删改查)功能,通过 Mapper 接口和 XML 文件中的 CRUD 标签来实现。
在接口中定义方法,例如增删改查方法。
public interface UserMapper {
// 查询用户
User getUserById(int id);
// 插入用户
void insertUser(User user);
// 更新用户
void updateUser(User user);
// 删除用户
void deleteUser(int id);
}
在 XML 文件中使用 select
、insert
、update
和 delete
标签分别表示对应的 SQL 操作。
<mapper namespace="com.example.mapper.UserMapper">
<select id="getUserById" resultType="com.example.model.User">
SELECT * FROM users WHERE id = #{id}
</select>
<insert id="insertUser">
INSERT INTO users (name, age) VALUES (#{name}, #{age})
</insert>
<update id="updateUser">
UPDATE users SET name = #{name}, age = #{age} WHERE id = #{id}
</update>
<delete id="deleteUser">
DELETE FROM users WHERE id = #{id}
</delete>
</mapper>
如果数据库中的某些表具有自增 ID(auto-increment ID),MyBatis 也可以进行相关配置。
在插入操作中,配置 useGeneratedKeys
和 keyProperty
来自动获取生成的主键。
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users (name, age) VALUES (#{name}, #{age})
</insert>
#{}
和 ${}
的区别MyBatis 提供了两种方式来传递参数:#{}
和 ${}
,它们的区别在于底层使用的方式和安全性。
#{}
: 使用 PreparedStatement
方式,SQL 预编译后设置参数,避免了 SQL 注入风险。推荐优先使用。${}
: 使用 Statement
方式,直接拼接参数,有可能造成 SQL 注入攻击。应尽量避免使用。安全的参数传递(推荐):
<select id="getUserByName" resultType="com.example.model.User">
SELECT * FROM users WHERE name = #{name}
</select>
存在安全风险的参数传递:
<select id="getUserByNameUnsafe" resultType="com.example.model.User">
SELECT * FROM users WHERE name = ${name}
</select>
@Param
注解指定参数名称。List<User> getUsersByAgeAndGender(@Param("age") int age, @Param("gender") String gender);
List<User> getUsersByIds(List<Integer> ids);
在 MyBatis 中,不同类型的返回值有不同的处理方式
常见的返回值类型包括普通类型、对象类型、List
、Map
,以及通过自定义 ResultMap
进行复杂的结果映射。
普通类型和对象类型:
MyBatis 支持直接返回数据库中查询的单个字段结果(如 int
、String
等)。对于对象类型,查询结果会直接映射为 Java 对象类型,通常是实体类。
返回一个用户的姓名(String
类型):
<select id="getUserNameById" resultType="String">
SELECT name FROM users WHERE id = #{id}
</select>
返回一个用户对象(User
类型):
<select id="getUserById" resultType="com.example.model.User">
SELECT * FROM users WHERE id = #{id}
</select>
List
类型:返回一个用户的列表:
<select id="getAllUsers" resultType="com.example.model.User">
SELECT * FROM users
</select>
Map
类型:MyBatis 可以将查询结果封装为 Map
,常见的使用场景是需要将数据库中的数据映射为键值对。
示例:返回 Map
<select id="getUserMap" resultType="map">
SELECT id, name, age FROM users
</select>
可以使用 @MapKey
注解将 Map
的键指定为某个字段:
@MapKey("id")
Map<Integer, User> getUserMap();
当数据库字段和 Java 对象的属性名称不一致,或查询结果需要映射到多个对象时,可以使用 ResultMap
进行自定义结果映射。
示例:定义 ResultMap
<resultMap id="userResultMap" type="com.example.model.User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<result property="age" column="user_age"/>
</resultMap>
<select id="getUserById" resultMap="userResultMap">
SELECT user_id, user_name, user_age FROM users WHERE user_id = #{id}
</select>
MyBatis 通过 ResultMap
的 association
和 collection
标签来实现一对一和一对多的关联查询。
可以在查询过程中动态加载关联对象的数据,实现更加灵活的结果映射。
在开始关联查询之前,需要复习数据库中的关联关系(如一对一、一对多)并确保项目中正确配置 MyBatis 环境。
示例:环境配置
数据库中的用户表(User
)和订单表(Order
)之间存在一对多的关系。配置数据库连接信息和 Mapper 配置。
association
标签)一对一关系表示每个实体仅与另一个实体相关联。例如,每个用户(User
)只有一个订单(Order
)。在 MyBatis 中,可以使用 association
标签来处理一对一关系。
association
标签使用方法原生查询方法
通过 SQL 的 JOIN
语句将两个表的数据连接起来,实现一对一关系的查询:
<select id="getUserWithOrder" resultType="com.example.model.User">
SELECT u.user_id, u.user_name, o.order_id, o.order_date
FROM users u
JOIN orders o ON u.user_id = o.user_id
WHERE u.user_id = #{id}
</select>
在上述例子中,JOIN
语句将用户表 (users
) 和订单表 (orders
) 连接起来,并查询出用户及其对应的订单信息。
分步查询方法
在分步查询中,主查询和关联查询是分开的,通过 association
标签实现关联对象的按需加载。
association
标签配置
<resultMap id="userResultMap" type="com.example.model.User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<association property="order" javaType="com.example.model.Order"
select="com.example.mapper.OrderMapper.getOrderById" column="user_id"/>
</resultMap>
配置解释:
property="order"
:指定关联的 Java 对象属性名为 order
。javaType="com.example.model.Order"
:指定关联对象的类型为 Order
。select="com.example.mapper.OrderMapper.getOrderById"
:指定分步查询的方法 getOrderById
。column="user_id"
:指定作为参数传递的字段。定义分步查询方法
在 OrderMapper.xml
中:
<select id="getOrderById" resultType="com.example.model.Order">
SELECT order_id, order_date
FROM orders
WHERE user_id = #{user_id}
</select>
主查询使用分步查询:
<select id="getUserById" resultMap="userResultMap">
SELECT user_id, user_name
FROM users
WHERE user_id = #{id}
</select>
collection
标签)一对多关系表示一个实体可以与多个其他实体相关联。例如,一个用户(User
)可以有多个订单(Order
)。在 MyBatis 中,可以使用 collection
标签来处理一对多关系。
collection
标签使用方法原生查询方法
在原生 SQL 中,通过 JOIN
语句查询一个用户及其所有订单:
<select id="getUserWithOrders" resultType="com.example.model.User">
SELECT u.user_id, u.user_name, o.order_id, o.order_date
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE u.user_id = #{id}
</select>
这里使用 LEFT JOIN
可以将用户和其所有订单数据查询出来。
分步查询方法
在分步查询中,通过 collection
标签动态加载关联对象的集合。
collection
标签配置
<resultMap id="userResultMap" type="com.example.model.User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<collection property="orders" ofType="com.example.model.Order"
select="com.example.mapper.OrderMapper.getOrdersByUserId" column="user_id"/>
</resultMap>
配置解释:
property="orders"
:指定关联的 Java 集合属性名为 orders
。ofType="com.example.model.Order"
:指定集合中每个元素的类型为 Order
。select="com.example.mapper.OrderMapper.getOrdersByUserId"
:指定分步查询的方法 getOrdersByUserId
。column="user_id"
:指定用于分步查询的参数列。定义分步查询方法
在 OrderMapper.xml
中:
<select id="getOrdersByUserId" resultType="com.example.model.Order">
SELECT order_id, order_date
FROM orders
WHERE user_id = #{user_id}
</select>
主查询使用分步查询:
<select id="getUserById" resultMap="userResultMap">
SELECT user_id, user_name
FROM users
WHERE user_id = #{id}
</select>
if
标签if
标签用于在 SQL 语句中加入条件逻辑。根据指定的条件表达式,MyBatis 会判断是否将其包含的 SQL 片段放入最终的 SQL 中。
<select id="findUserByCondition" resultType="User">
SELECT * FROM users
<where>
<if test="username != null">
AND username = #{username}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
where
标签where
标签用于自动处理 SQL 语句中的条件部分。如果条件为空,它不会添加 WHERE
关键字,同时会去掉多余的 AND
或 OR
。
<select id="findUserByCondition" resultType="User">
SELECT * FROM users
<where>
<if test="username != null">
username = #{username}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
where
标签会自动去掉前置的 AND
或 OR
,并且仅在有条件时添加 WHERE
关键字。
set
标签set
标签用于生成 UPDATE
语句中的 SET
子句。它自动去掉尾部的逗号(,
)。
<update id="updateUser" parameterType="User">
UPDATE users
<set>
<if test="username != null">username = #{username},</if>
<if test="age != null">age = #{age},</if>
</set>
WHERE id = #{id}
</update>
set
标签会自动去掉 SET
子句中最后的逗号。
trim
标签trim
标签是一个更通用的标签,允许自定义前缀和后缀,通常用来替代 where
标签。
<select id="findUserByCondition" resultType="User">
SELECT * FROM users
<trim prefix="WHERE" prefixOverrides="AND | OR">
<if test="username != null">
AND username = #{username}
</if>
<if test="age != null">
AND age = #{age}
</if>
</trim>
</select>
trim
标签使用 prefix
属性指定前缀,用 prefixOverrides
去掉指定的多余前缀。
trim
标签同样可以用于 UPDATE
语句中的 SET
子句,用来替代 set
标签。
<update id="updateUser" parameterType="User">
UPDATE users
<trim prefix="SET" suffixOverrides=",">
<if test="username != null">username = #{username},</if>
<if test="age != null">age = #{age},</if>
</trim>
WHERE id = #{id}
</update>
trim
标签通过 suffixOverrides
属性去掉末尾的逗号。
choose
、when
、otherwise
标签用于实现类似 switch-case
的逻辑选择。
<select id="findUserByStatus" resultType="User">
SELECT * FROM users
<where>
<choose>
<when test="status == 'active'">
status = 'ACTIVE'
</when>
<when test="status == 'inactive'">
status = 'INACTIVE'
</when>
<otherwise>
status = 'UNKNOWN'
</otherwise>
</choose>
</where>
</select>
choose
标签确保只有一个 when
条件成立,如果没有匹配项,使用 otherwise
子句。
foreach
标签foreach
标签用于处理集合类型的参数(如 List
、Set
),通常用于 IN
查询。
<select id="findUsersByIds" resultType="User">
SELECT * FROM users WHERE id IN
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
foreach
标签生成一组逗号分隔的值,常用于批量操作。
foreach
标签也可以用于批量插入操作。
<insert id="insertUsers" parameterType="list">
INSERT INTO users (username, age) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.username}, #{user.age})
</foreach>
</insert>
foreach
标签生成多条 VALUES
子句,实现批量插入。
foreach
标签还可以用于批量更新操作。
<update id="updateUsers" parameterType="list">
<foreach collection="list" item="user" separator=";">
UPDATE users
<set>
username = #{user.username},
age = #{user.age}
</set>
WHERE id = #{user.id}
</foreach>
</update>
每次迭代生成一条 UPDATE
语句,用于批量更新数据。
在 MyBatis 中,批量操作默认会在一个事务中执行,只有在所有操作都成功时才会提交,否则会回滚。需要配置 ExecutorType.BATCH
才能启用批处理模式。
<sql>
标签<sql>
标签用于抽取可复用的 SQL 片段,<include>
标签用于引用这些片段。
<sql id="userColumns">id, username, age</sql>
<select id="findAllUsers" resultType="User">
SELECT <include refid="userColumns" /> FROM users
</select>
sql
标签定义一个片段,include
标签引用这个片段。
© 著作权归作者所有
本文由 趣代码Blog 创作,采用 知识共享署名4.0 国际许可协议进行许可,本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。