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

Mybatis学习之获取参数值(四)

这里写目录标题

  • 一、JDBC原生的获取参数值的方式
  • 二、MyBatis获取参数值的两种方式
    • #{}的本质就是占位符赋值
    • ${}的本质就是字符串拼接
  • 三、MyBatis获取参数值的五种情况
    • 情况1:单个字面量类型的参数
    • 情况2:多个字面量类型参数
    • 情况3:map集合类型的参数
    • 情况4:实体类型的参数
    • 情况5:使用@Param标识参数
    • 总结
  • 四、@Param源码分析

一、JDBC原生的获取参数值的方式

  • 字符串拼接
  • 占位符拼接
@Test
public void testJDBC() throws Exception {String url="jdbc:mysql://192.168.247.128:3306/demo";String user="root";String password="123456";Class.forName("com.mysql.jdbc.Driver");Connection connection = DriverManager.getConnection(url, user, password);// 1. 字符串拼接 ->获得预编译对象 -》sql注入问题String uname= "test";PreparedStatement preparedStatement = connection.prepareStatement("select * from t_user where username = '" + uname + "'");// 2. 占位符PreparedStatement ps2 = connection.prepareStatement("select * from t_user where username = ?");ps2.setString(1, uname);//获取查询结果ResultSet rs = preparedStatement.executeQuery();while (rs.next()) {Integer id =rs.getInt("id");String pwd =rs.getString("password");System.out.println(id+":"+uname+":"+pwd);}
}

关于sql注入的问题如果uname的值是" 1 or 1=1 ",那么生成的SQL语句将会是:
SELECT * FROM t_user WHERE username = 1 or 1=1,这回严重影响查询结果。
占位符这种写法,对sql进行预编译处理,对应的变量上会自动加上单引号。

二、MyBatis获取参数值的两种方式

MyBatis获取参数值的两种方式:${}和#{}

#{}的本质就是占位符赋值

#{}用于预处理语句,它会将参数值进行预处理,防止SQL注入,例如

SELECT * FROM t_user WHERE id = #{id}

在这个例子中,#{id}会被MyBatis替换为一个预处理语句的占位符,然后MyBatis会为这个占位符设置一个参数值。这种方式在大多数情况下是安全的,但如果参数值为null或者参数值为一个含有特殊字符的字符串时,可能会导致问题。

${}的本质就是字符串拼接

${}则直接将参数值插入到SQL语句中,因此在使用时需要特别小心,以防止SQL注入攻击。
这两种取值方式对应上述的JDBC的原生的两种取值方式。

三、MyBatis获取参数值的五种情况

情况1:单个字面量类型的参数

若mapper接口中的方法参数为单个的字面量类型。此时可以使用#{}(或者${})以任意的名称获取参数的值。
UserMapper接口代码

 User getUserByUserName(String username);

对应在UserMapper.xml中配置。

<!--    User getUserByUserName(String username);-->
<!--    --><select id="getUserByUserName" resultType="User">select * from t_user where username = #{username}</select>

上述代码中,使用#{},里面内容可以随便写,都是传进来的username的值。
或者用${}方式(不推荐)。

	<select id="getUserByUserName" resultType="User"><!-- select * from t_user where username = ${username}如果使用这种方式,得到的sql语句是:Preparing: select * from t_user where username = test9而其中username的值‘test9’没有单引号,语句不正确,会报错。因此要手动添加单引号-->select * from t_user where username = '${username}'</select>

测试类

    @Testpublic void testgetUserByUserName() throws IOException {SqlSession sqlSession = SqlSessionUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = mapper.getUserByUserName("test9");System.out.println(user);}

工具类

    public static SqlSession getSqlSession() throws IOException {//加载核心配置文件InputStream is = Resources.getResourceAsStream("mybatis-config.xml");//获取sqlsessionfactorybuilderSqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();//获取factorySqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);//获取sqlsessionSqlSession sqlSession = sqlSessionFactory.openSession(true);return sqlSession;}

情况2:多个字面量类型参数

若mapper接口中的方法参数为多个时,此时MyBatis会自动将这些参数放在一个map集合中,以arg0,arg1…为键,以参数为值;
因此只需要通过#{}访问map集合的键就可以获取相对应的值。

    User checkLogin(String username, String password);

对应在UserMapper.xml中配置。

    <!--User checkLogin(String username,String password);--><select id="checkLogin" resultType="User">select * from t_user where username = #{arg0} and password = #{arg1}</select>

上述配置文件中写:select * from t_user where username = #{username} and password = #{password},会报错:Parameter ‘username’ not found。
如果想要接口文件和xml文件中参数名称保持一致,mabtis也是提供方法的,而且在日常中经常使用,上述方法反而不常使用。

情况3:map集合类型的参数

若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中
只需要通过#{}访问map集合的键就可以获取相对应的值。
接口的java代码

     User checkLoginByMap(Map<String, Object> map);

对应在UserMapper.xml中配置。

    <!--    User checkLoginByMap(Map<String, Object> map);--><select id="checkLoginByMap" resultType="User">select * from t_user where username = #{username} and password = #{password}</select>

测试代码

    @Testpublic void testCheckLoginByMap() throws IOException {SqlSession sqlSession = SqlSessionUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);Map<String, Object> map = new HashMap<String, Object>();map.put("username","test9");map.put("password","123456");User user = mapper.checkLoginByMap(map);System.out.println(user);}

情况4:实体类型的参数

在日常开发中,方法参数经常为实体类对象,此时可以使用#{},通过访问实体类对象中的属性名获取属性值。
接口的java代码

     int insertUser(User user);

对应在UserMapper.xml中配置。

   <insert id="insertUser">insert into t_user values(null, #{username}, #{password}, #{age}, #{gender}, #{email})</insert>

上述xml代码会找到相对应的get方法,如username->找getUsername(),然后向里面赋值。
测试代码

    @Testpublic void testInsertUser() throws IOException {SqlSession sqlSession = SqlSessionUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = new User(null, "Demo", "111111", 66, "m", "1111@gmail.com");mapper.insertUser(user);}

情况5:使用@Param标识参数

可以通过@Param注解标识mapper接口中的方法参数,我们要记住@Param注解里面的名称和xml里面的#{}名称保持一致即可。
原理是:mybatis将这些参数放在map集合中,以@Param注解的value属性值为键,以参数为值;只需要通过#{}访问map集合的键就可以获取相对应的值。
接口的java代码

    User checkLoginByParam(@Param("username") String username, @Param("password") String password);

对应在UserMapper.xml中配置。

    <select id="checkLoginByParam" resultType="User">select * from t_user where username = #{username} and password = #{password}</select>

注:
上述代码中resultType 是一个类型参数,它指定了返回结果对象的类型。User是在mybatis-config.xml配置的别名。
测试代码

       public void testCheckLoginByParam() throws IOException {SqlSession sqlSession = SqlSessionUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = mapper.checkLoginByParam("Demo","111111");System.out.println(user);}

总结

上述五种情况一般分成两种情况进行处理:

  1. 实体类类型的参数
  2. 使用@Param标识参数

四、@Param源码分析

前文中我们提到,如果不用@Param注解的时候,多于多个入参,此时MyBatis会自动将这些参数放在一个map集合中,以arg0,arg1…为键,以参数为值;此时#{}中就需要填写arg0,arg1来进行传值,访问map集合的键就可以获取相对应的值。
sql语句的唯一标识
分析代码
目的:验证@Param存储参数的两种方式
(1)打断点,进入debug模式

	User getUserById(@Param("id") Integer id);

在这里插入图片描述
选择step into进行调试
在这里插入图片描述
(2)Mapper底层使用代理模式创建,我们按step into进入invoke()方法
在这里插入图片描述
这里的三个参数,第一个是代理对象,第二个是目标方法,第三个是目标方法的参数。
然后会进入缓存cachedInvoker的invoke方法。

(3)进入了invoke(),再进一步进入了mapperMethod的execute()方法(step into)。

execute()方法中包含一个switch语句,分别对应增删改查等关键字对应的处理流程。
在这里插入图片描述
(4)点击step over跳进switch语句内部,光标放在command上,点击加号,查看command内容。name为该sql语句的唯一标识,type为对应操作,即SELECT。
在这里插入图片描述
在这里插入图片描述

再点击step into,发现convertArgsToSqlCommandParam()方法是由paramNameResolver参数名称解析器中的getNamedParams()获取命名参数方法解析的,进一步step into进这个方法。
在这里插入图片描述

(5)进入getNamedParams()方法,查看names,发现names是一个map集合,包含了传进来的"id"参数。
在这里插入图片描述
在这里插入图片描述
(6)回到getNamedParams()方法,继续step over
两次循环entrySet后,发现param集合中得到两种映射
在这里插入图片描述

  • 以@Param的value作为key,参数值为value
  • 以param1, param2…作为key,参数值为value

至此,@Param源码分析完毕,印证结论:可以使用#{param1}和#{username}两种方式获得参数值。

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

相关文章:

  • 第14届蓝桥杯Python青少组中/高级组选拔赛(STEMA)2023年1月15日真题
  • STM32学习记录--Day6
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘seaborn’问题
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现道路车辆事故的检测识别(C#代码UI界面版)
  • SpringBoot 服务器配置
  • 面经——电子电路技术知识详解
  • 【Python3教程】Python3高级篇之网络编程
  • 文心4.5开源测评:国产大模型的轻量化革命与全栈突破
  • GaussDB 约束的使用举例
  • 高效轻量的C++ HTTP服务:cpp-httplib使用指南
  • Redis核心机制与实践深度解析:从持久化到分布式锁
  • 路面障碍物识别漏检率↓76%:陌讯多模态融合算法实战解析
  • 基于 LFU 策略的存储缓存系统设计与实现
  • 人工智能之数学基础:离散型随机事件概率(古典概型)
  • 兰空图床部署教程
  • LQR个人笔记
  • Unity_数据持久化_C#处理XML文件
  • ollama 多实例部署
  • 睡岗识别误报率↓76%:陌讯动态时序融合算法实战解析
  • JP3-3-MyClub后台后端(三)
  • 小迪23-28~31-js简单回顾
  • 解决mac在安装nvm过程中可能遇到的一些问题
  • 小迪23年-22~27——php简单回顾(2)
  • (nice!!!)(LeetCode 每日一题) 2561. 重排水果 (哈希表 + 贪心)
  • 【自动化运维神器Ansible】YAML支持的数据类型详解:构建高效Playbook的基石
  • 译| Netflix内容推荐模型的一些改进方向
  • Tlias案例-登录 退出 打包部署
  • Leetcode 11 java
  • 论文笔记:Bundle Recommendation and Generation with Graph Neural Networks
  • (1-8-1) Java -XML