MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的工作。本文将深入探讨 MyBatis 中的增删改查操作,重点讲解静态与动态 SQL 语句的拼接,并分析 SQL 注入问题及其防范措施。
1. MyBatis 基础配置
在开始之前,我们需要配置 MyBatis 的基本环境。以下是一个简单的 pom.xml
配置文件,包含了 MyBatis 的核心依赖和 MySQL 驱动依赖:
<dependencies><!-- MyBatis 核心包 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.5</version></dependency><!-- MySQL 驱动包 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><!-- 单元测试 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.10</version></dependency><!-- 日志 --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency>
</dependencies>
2. 静态 SQL 语句
静态 SQL 语句是指在编写 SQL 时,SQL 语句的结构和内容是固定的,不会根据条件的变化而变化。以下是一个简单的静态 SQL 查询示例:
<!--id:方法名--><!--resultType:定义数据的返回-->
<!-- <select id="findAll" resultType="com.qcby.entity.User">-->
<!-- select *from user;-->
<!-- </select>--><!-- <select id="findById" resultType="com.qcby.entity.User" parameterType="java.lang.Integer">-->
<!-- select * from user where id=#{id}-->
<!-- </select>--><!-- <select id="selectByUserName" resultType="com.qcby.entity.User" parameterType="java.lang.String">-->
<!-- select *from user where username=#{username}-->
<!-- </select>--><!-- <insert id="insert" parameterType="com.qcby.entity.User">-->
<!-- insert into user (username,birthday,sex,address) value (#{username},#{birthday},#{sex},#{address});-->
<!-- </insert>--><!-- <update id="update" parameterType="com.qcby.entity.User">-->
<!-- update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}-->
<!-- where id=#{id}-->
<!-- </update>--><!-- <delete id="delete" parameterType="java.lang.Integer">-->
<!-- delete from user where id=#{id}-->
<!-- </delete>-->
<!-- <select id="likeByName" resultType="com.qcby.entity.User" parameterType="java.lang.String">-->
<!-- select * from user where username like '%${value}%';-->
<!-- </select>-->
<!-- <select id="likeByName1" resultType="com.qcby.entity.User" parameterType="java.lang.String">-->
<!-- select * from user where username like #{username};-->
<!-- </select>-->
在这个例子中,findAll
方法会返回 user
表中的所有记录。静态 SQL 语句适用于简单的查询场景,但在复杂的业务逻辑中,静态 SQL 往往无法满足需求。
3. 动态 SQL 语句
动态 SQL 是 MyBatis 的强大特性之一,它允许我们根据不同的条件动态生成 SQL 语句。MyBatis 提供了多种标签来实现动态 SQL,如 <if>
、<choose>
、<when>
、<otherwise>
、<trim>
、<where>
、<set>
和 <foreach>
。
3.1 <if>
标签
<if>
标签用于根据条件判断是否包含某段 SQL 语句。以下是一个使用 <if>
标签的动态 SQL 示例:
<!--动态sql:能够在不同的条件下拼接出不同的sql语句--><!--where 标签的功能:能够去掉where 后边的 and 或 or--><select id="selectUser" parameterType="com.qcby.entity.User" resultType="com.qcby.entity.User">select * from user<where><if test="username!=null and username!=''">username=#{username}</if><if test="birthday!=null">and birthday=#{birthday}</if><if test="sex!=null and sex!=''">and sex=#{sex}</if><if test="address!=null and address!=''">and address=#{address}</if></where></select>
在这个例子中,selectUser
方法会根据传入的 User
对象的 username
和 sex
属性动态生成 SQL 语句。如果 username
或 sex
为空,则不会包含对应的条件。
3.2 <choose>
、<when>
和 <otherwise>
标签
<choose>
标签类似于 Java 中的 switch
语句,它可以根据不同的条件选择不同的 SQL 片段。以下是一个使用 <choose>
标签的示例:
<select id="selectUserByChoose" resultType="com.qcby.entity.User" parameterType="com.qcby.entity.User">SELECT * FROM user<where><choose><when test="username != null and username != ''">username = #{username}</when><when test="sex != null and sex != ''">sex = #{sex}</when><otherwise>id = #{id}</otherwise></choose></where>
</select>
在这个例子中,selectUserByChoose
方法会根据 username
、sex
和 id
的不同值动态生成 SQL 语句。
3.3 <foreach>
标签
<foreach>
标签用于遍历集合或数组,并生成相应的 SQL 语句。以下是一个使用 <foreach>
标签的批量删除示例:
<!--foreach循环--><!--批量删除--><!-- 批量删除的sql语句:delete from user where id in (6,7,8); --><delete id="deleteMoreByArray" >delete from user where id in<foreach collection="ids" item="id" separator="," open="(" close=")">#{id}</foreach></delete><!-- collection:当前要循环的数组或者集合 --><!-- item: 我们指定要循环的数组的每一个元素 --><!-- separator:每一个元素应该用什么来做分割 --><!-- open:当前循环是以什么开始 --><!-- close:当前循环是以什么结束 --><!--批量添加--><!--insert into user(username,birthday,sex,address) values (#{user.username},#{user.birthday},#{user.sex},#{user.address}), (#{user.username},#{user.birthday},#{user.sex},#{user.address}), (#{user.username},#{user.birthday},#{user.sex},#{user.address}), (#{user.username},#{user.birthday},#{user.sex},#{user.address}) --><update id="insertMoreByList" parameterType="com.qcby.entity.User">insert into user(username,birthday,sex,address) values<foreach collection="users" item="user" separator=",">(#{user.username},#{user.birthday},#{user.sex},#{user.address})</foreach></update>
在这个例子中,deleteMoreByArray
方法会根据传入的 ids
数组动态生成批量删除的 SQL 语句。
4. SQL 注入问题及防范
SQL 注入是一种常见的安全漏洞,攻击者可以通过在输入中插入恶意 SQL 代码来操纵数据库查询。MyBatis 通过使用 #{}
占位符来防止 SQL 注入。
4.1 #{}
与 ${}
的区别
-
#{}
:MyBatis 会使用预编译语句(PreparedStatement)来处理参数,参数会被安全地转义,从而防止 SQL 注入。 -
${}
:MyBatis 会直接将参数拼接到 SQL 语句中,存在 SQL 注入的风险。
以下是一个使用 #{}
的示例:
<select id="findById" resultType="com.qcby.entity.User" parameterType="java.lang.Integer">SELECT * FROM user WHERE id = #{id}
</select>
在这个例子中,#{}
会确保 id
参数被安全地处理,防止 SQL 注入。
4.2 防范 SQL 注入的最佳实践
-
始终使用
#{}
:在大多数情况下,应使用#{}
来处理参数,避免使用${}
。 -
避免动态拼接 SQL:尽量避免在 SQL 语句中动态拼接用户输入的内容。
-
使用 MyBatis 的动态 SQL 标签:通过使用
<if>
、<choose>
等标签,可以安全地构建动态 SQL 语句。
5. 总结
MyBatis 提供了强大的动态 SQL 功能,使得我们可以根据不同的条件灵活地生成 SQL 语句。同时,MyBatis 通过 #{}
占位符有效地防止了 SQL 注入问题。在实际开发中,我们应充分利用 MyBatis 的动态 SQL 特性,并遵循最佳实践来确保应用的安全性。
通过本文,你应该对 MyBatis 的增删改查操作、动态 SQL 语句拼接以及 SQL 注入问题有了更深入的理解。希望这些内容能帮助你在实际项目中更好地使用 MyBatis。
参考文献:
-
MyBatis 官方文档
-
SQL 注入攻击与防御