当前位置: 首页 > news >正文

MyBatis实现SQL

MyBatis实现SQL

  • MyBatis实现传统SQL
    • resultType和parameterType
    • SQL注入
  • MyBatis实现动态SQL
    • 什么是动态sql
    • 动态sql的使用

MyBatis实现传统SQL

UserTest.java

import com.qcby.dao.UserDao;
import com.qcby.entity.User;import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Date;
import java.util.List;public class UserTest {private InputStream in = null;private SqlSession session = null;private UserDao userDao = null;@Before  //前置通知, 在方法执行之前执行public void init() throws IOException {//加载主配置文件,目的是为了构建SqlSessionFactory对象in = Resources.getResourceAsStream("SqlMapConfig.xml");//创建SqlSessionFactory对象SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);//通过SqlSessionFactory工厂对象创建SqlSesssion对象session = factory.openSession();//通过Session创建UserDao接口代理对象userDao = session.getMapper(UserDao.class);}@After  //@After: 后置通知, 在方法执行之后执行 。public void destory() throws IOException {//释放资源session.close();in.close();}@Testpublic void findAll(){List<User> users = userDao.findAll();for (User user : users) {System.out.println(user.toString());}}@Testpublic void findById(){User user = userDao.findById(1);System.out.println(user.toString());}@Testpublic void findByName(){List<User> users = userDao.findByName("小");for (User user : users) {System.out.println(user.toString());}}@Testpublic void findByUsername(){List<User> users = userDao.findByUsername("熊");for (User user : users) {System.out.println(user.toString());}}@Testpublic void add(){User user = new User();user.setUsername("小王");user.setSex("男");user.setAddress("北京");user.setBirthday(new Date());int count = userDao.add(user);session.commit();   //事务提交System.out.println(count);}@Testpublic void update(){User user = new User();user.setId(1);user.setUsername("小黑");user.setSex("男");user.setAddress("上海");int count = userDao.update(user);session.commit();}@Testpublic void delete(){int count = userDao.delete(1);session.commit();}@Testpublic void insertGetId(){User user = new User();user.setUsername("小李");userDao.insertGetId(user);session.commit();System.out.println(user.getId());}
]

UserDao.java

package com.qcby.dao;import com.qcby.entity.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;public interface UserDao {List<User> findAll();User findById(Integer id);List<User> findByName(String username);List<User> findByUsername(String username);int add(User user);int update(User user);int delete(Integer id);int insertGetId(User user);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qcby.dao.UserDao"><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="findByName" resultType="com.qcby.entity.User"parameterType="java.lang.String">select * from user where username like '%${value}%';</select><select id="findByUsername" resultType="com.qcby.entity.User"parameterType="java.lang.String">select * from user where username like concat('%',#{username},'%');</select><insert id="add" parameterType="com.qcby.entity.User">insert into user(username,birthday,sex,address) values(#{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><!--返回主键:主键需要设置自动递增--><insert id="insertGetId" parameterType="com.qcby.entity.User"><selectKey keyProperty="id" resultType="int" order="AFTER">SELECT LAST_INSERT_ID()</selectKey>insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address});</insert></mapper>

关键细节讲解:

resultType和parameterType

<select> 标签的 resultTypeparameterType 本质是 MyBatis 框架利用 Java 反射机制 实现动态数据绑定的过程

  1. 针对 resultType="com.qcby.entity.User" 的反射作用时当 SQL 执行完成并返回结果集 ResultSet 后,MyBatis 需要将结果集中的字段值映射到 User 实体类的对象中:首先通过反射获取 User 类的 Class 对象(Class.forName(“com.qcby.entity.User”)),从而知晓这个类的结构,然后通过反射创建 User 类的实例(clazz.newInstance() 或通过构造方法),根据结果集中的列名找到 User 类中与数据库列名匹配的属性,反射查找 User 类中对应的 setter 方法,调用这些方法或直接访问属性,将结果集中的值设置到对象中。
  2. 针对 parameterType="java.lang.Integer" 的反射作用是当传递参数给 SQL 时,MyBatis 需要解析参数的类型和值:当传递参数给 SQL 时,MyBatis 需要解析参数的类型和值, MyBatis 会通过反射(Class.forName(“java.lang.Integer”))获取 Integer 类的 Class 对象,从而确定参数的具体类型是 Integer,反射获取参数的值,最终将值填充到 SQL 的 #{id} 占位符中。

MyBatis 中的反射机制,本质是利用 Java 反射的动态性,运行时获取类信息、操作类成员,实现了框架对任意实体类和参数类型的通用支持,无需硬编码适配特定类,而是通过反射动态处理各种类型,这也是 MyBatis 灵活性的重要体现。

SQL注入

正常情况下,程序会将用户输入作为数据传递给数据库;但如果程序没有对用户输入做严格过滤,用户输入就可能被当作 SQL 代码 的一部分被执行

假设某登录功能的 SQL 查询语句是:

select* from user where username = "+username+" and password = "+password+"

当 password 部分传入的参数为 1’ or’1’ = '1 时,该代码会变成:

select* from user where username = '123' and password ='1' or'1' = '1'

这样整个 sql 的判断语句始终为 True,无需正确密码就能登录系统

解决方式:

  1. ? 是JDBC原生的标准的参占位符,由数据库驱动直接处理。SQL 语句会先被预编译,? 位置的参数会被当作数据处理,而非 SQL 语句的一部分,JDBC 会根据参数的 Java 类型自动转换为对应数据库类型,例如字符串加引号、数字进行直接拼接,能有效防止 SQL 注入。
<select id="getUser" resultType="User">SELECT * FROM user WHERE id = ?
</select>
  1. #{id}:MyBatis的预编译占位符,底层会转换为JDBC的?占位符,同时也会自动根据参数值进行类型转换和转义,完全防SQL注入风险,安全性高,是MyBatis中推荐的参数传递方式。
<select id="getUser" resultType="User">SELECT * FROM user WHERE id = #{id}
</select>

等价于JDBC的 SELECT * FROM user WHERE id = ?,MyBatis会自动将 id 的参数值替换到?的位置上

补充 ${value}${value} 用于MyBatis的字符串拼接,将变量或表达式的结果插入到字符串中,底层不会转换为 ? 且不进行参数类型转换和转义,参数值会被当作SQL的一部分执行,仍然存在SQL注入风险,仅在必须动态生成SQL结构时使用。

<select id="findByUsername" resultType="com.qcby.entity.User" parameterType="java.lang.String">select * from user where username like concat('%',#{username},'%');
</select>

总结:

类型底层实现安全性适用场景自动转义
?JDBC原生高(防注入)替换参数值(JDBC直接使用)
#{id}转换为?高(防注入)替换参数值(MyBatis推荐)
${value}字符串直接拼接低(有注入风险)替换SQL关键字、表名、列名等

此外还有一个需要注意的内容

原数据库内容如下:

在这里插入图片描述

当执行update()方法时,传入的参数是

User user = new User();
user.setId(1);
user.setUsername(“小黑”);
user.setSex(“男”);
user.setAddress(“上海”);

发现这里并没有设置birthday这个属性,执行update()方法后数据库的值为:

在这里插入图片描述

这样birthday这个属性值直接为Null了,这是有问题的,我们使用非动态SQL的方式就能完美解决了,下面我们来详细讲解动态SQL。

MyBatis实现动态SQL

什么是动态sql

动态sql是在不同条件下拼接不同的sql。
Mybatis框架的动态sql技术是一种根据特定条件动态拼接SQl语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题。

动态sql的使用

  1. <where> --- <if> 标签

Mapping:

	<select id="selectUser" parameterType="com.qcby.entity.User" resultType="com.qcby.entity.User">select * from user<where><if test="username!=null">and username like concat('%',#{username},'%')</if><if test="sex!=null">and sex = #{sex}</if><if test="address!=null">and address like concat('%',#{address},'%')</if><if test="birthday!=null">and birthday = #{birthday}</if></where></select>

如果标签返回的内容是以 and 或 or 开头的,则它会剔除掉

UserDao.java

List<User> selectUser(User user);

测试:

在这里插入图片描述

  1. <set>、<if>标签

Mapping:

	<update id="updateUser" parameterType="com.qcby.entity.User">update user<set><if test="username!=null">username = #{username},</if><if test="birthday!=null">birthday = #{birthday},</if><if test="sex!=null">sex = #{sex},</if><if test="address!=null">address = #{address},</if></set>where id = #{id}</update>

用于 UPDATE 语句,自动处理逗号分隔问题

UserDao.java

public int update(User user);

测试:

在这里插入图片描述

  1. <choose>、<when>和<otherwise> 标签

这个标签相当于是java当中的 if…elseif…else,<choose> 标签是这个标签组合当中的父标签,<when> 标签就相当于 if 和 elseif,<othrtwise>标签相当于是 else,只执行第一个满足条件的 when

mapping:

    <select id="selectUserByChoose" parameterType="com.qcby.entity.User" resultType="com.qcby.entity.User">select * from user<where><choose><when test="id!='' and id!=null">id=#{id}</when><when test="username!='' and username!=null">and username=#{username}</when><otherwise>and sex=#{sex}</otherwise></choose></where></select>

UserDao.java

    List<User> selectUserByChoose(User user);

测试:

在这里插入图片描述

  1. <trim> 标签

trim标记是一个格式化的标记,可以完成set或者是where标记的功能,自定义字符串截取规则,可替代 where 和 set 标签
① 用 trim 改写上面第二点的 if+where 语句

	<select id="trimSelectUser" parameterType="com.qcby.entity.User" resultType="com.qcby.entity.User">select * from user<trim prefix="where" prefixOverrides="and | or"><if test="username!=null">and username like concat('%',#{username},'%')</if><if test="sex!=null">and sex = #{sex}</if><if test="address!=null">and address like concat('%',#{address},'%')</if><if test="birthday!=null">and birthday = #{birthday}</if></trim></select>

prefix:在 trim 包裹的内容前添加指定前缀(如 “WHERE”、“SET”)
suffix:在 trim 包裹的内容后添加指定后缀
prefixOverrides:去除内容前面多余的指定字符(如 “AND”、“OR”、“,”)
suffixOverrides:去除内容后面多余的指定字符(如 “,”)

② 用 trim 改写上面第三点的 if+set 语句

    <update id="trimUpdateUser" parameterType="com.qcby.entity.User">update user<trim prefix="set" suffixOverrides=","><if test="username!=null">username = #{username},</if><if test="birthday!=null">birthday = #{birthday},</if><if test="sex!=null">sex = #{sex},</if><if test="address!=null">address = #{address},</if></trim>where id = #{id}</update>
  1. <foreach> 标签

有些时候数据是以数组的形式出现的,比如进行批量删除和批量添加的时候

①:批量删除
mapping:

    <delete id="deleteAll">delete from user where id in<foreach collection="ids" item="id" separator="," open="(" close=")" >#{id}</foreach></delete>

collection:当前要循环的数组或者集合,item: 我们指定要循环的数组的每一个元素,separator:每一个元素应该用什么来做分割,open:当前循环是以什么开始,close:当前循环是以什么结束

UserDao.java

    int deleteAll(@Param("ids") Integer[] ids);

测试:

在这里插入图片描述

②:批量添加
mapping:

    <insert id="insertAll">insert into user(username,address,sex,birthday) values<foreach collection="users" item="user" separator=",">(#{user.username},#{user.address},#{user.sex},#{user.birthday})</foreach></insert>

UserDao.java

    int insertAll(@Param("users") List<User> users);

测试:

在这里插入图片描述

http://www.lryc.cn/news/611127.html

相关文章:

  • DM8日常运维命令总结(四)
  • jenkins插件Active Choices的使用通过参数动态控制多选参数的选项
  • 聚焦智能穿戴“下一代消费终端”之争,Meta/微美全息借AI+AR积淀定义行业未来
  • Swift 实战:用队列巧解 LeetCode 346 数据流中的移动平均数
  • 【RabbitMQ】高级特性—持久性、重试机制详解
  • 栈的输入与输出方式
  • 《算法导论》第 4 章 - 分治策略
  • Python Day23程序、进程、线程及多线程实现全解析 例题分析
  • 星图云开发者平台赋能商储油安全管控数字化转型
  • 为什么要选择时序数据库IoTDB?
  • Python爬虫08_Requests聚焦批量爬取图片
  • Pandas 入门:数据分析的得力工具
  • 嵌入式硬件中运放内部底层分析
  • 基于深度学习的医学图像分析:使用CycleGAN实现医学图像风格转换
  • 后量子时代已至?中国量子加密技术突破与网络安全新基建
  • 关于npx react-native run-android下载进程缓慢以及进程卡壳等问题的解决方案。
  • Java 大视界 -- Java 大数据在智能医疗电子病历数据分析与临床决策支持中的应用(382)
  • iOS混淆工具有哪些?技术演进与选型趋势全景解析
  • 企业如何用现代数仓架构挖掘新业务盈利点?AllData产品从目标、路径、结果给出答案
  • Go语言实战案例:使用sync.Mutex实现资源加锁
  • 查看 Redis 某个数据库的内存占用
  • 【前端】网站favicon图标制作
  • 力扣-208.实现Trie(前缀树)
  • C++ - 仿 RabbitMQ 实现消息队列--服务端核心模块实现(六)
  • Linux-Day11.WEB服务,虚拟主机
  • VUE丢失long类型精度,使用 json-bigint 库解析大整数
  • 人工智能领域、图欧科技、IMYAI智能助手2025年7月更新月报
  • 暑期算法训练.14
  • 关于如何SecureCRT软件连接开发板后默认显示大字体,且重启开发板或重新连接时不会重置的方法
  • Android原生项目集成Flutter模块极简指南