做服装的一般去什么网站找图片,网站开发文章怎么分类,ac域名的网站有啥不同,南京明辉建设集团有限公司网站1. ${ } 和 #{ }
MyBatis获取参数值的两种方式#xff1a;${ } 和 #{ } 对于初学者来说#xff0c;理解MyBatis中获取参数值的两种方式——#{}和${}#xff0c;关键在于明白它们如何影响SQL语句的构建以及为何在安全性、灵活性上有显著差异。下面我将用简单易懂的语言来解…1. ${ } 和 #{ }
MyBatis获取参数值的两种方式${ } 和 #{ } 对于初学者来说理解MyBatis中获取参数值的两种方式——#{}和${}关键在于明白它们如何影响SQL语句的构建以及为何在安全性、灵活性上有显著差异。下面我将用简单易懂的语言来解释这两者的本质、工作原理及使用注意事项。
1. ${}字符串拼接
本质${}在MyBatis中被视为字符串替换符。当你在SQL语句中使用${}时MyBatis会直接将它包围的变量名替换为实际传入的值。这种方式类似于在编程语言中进行字符串拼接操作即将传入的值作为文本片段插入到SQL语句中。
工作原理
SELECT * FROM users WHERE username ${username}
假设传入的username参数为 zhangsanMyBatis会将上述SQL语句中的${username}替换为 zhangsan生成如下已拼接好的SQL
SELECT * FROM users WHERE username zhangsan
特点与注意事项
手动处理引号由于${}直接参与字符串拼接因此对于字符串类型或日期类型的字段值你需要确保传入的值已经正确地加上了单引号。例如如果你传入的是 zhangsan 而不是 zhangsanMyBatis不会帮你自动加上单引号。SQL注入风险由于${}直接将传入的值作为原始文本插入到SQL语句中没有进行任何预处理或转义因此它不提供对SQL注入攻击的防护。如果传入的值来自不可信的用户输入恶意用户可能通过构造特定的输入来篡改SQL语句的结构和意图从而对数据库造成潜在威胁。因此除非有明确需求且能够确保输入安全否则应避免在条件查询等涉及用户输入的地方使用${}。适用场景${}通常用于那些需要原样插入到SQL语句中且不会引起SQL注入风险的情况如动态表名、列名虽然不推荐或者在你已经确保传入值安全的情况下。 2. #{}预编译占位符
本质#{}在MyBatis中是一个预编译占位符。当SQL语句中包含#{}时MyBatis会将其替换为一个问号?并在执行SQL时使用PreparedStatement来设置参数值。这种方式利用数据库的预编译机制确保了参数值的安全性和类型正确性。
工作原理
SELECT * FROM users WHERE username #{username}
同样假设传入的username参数为 zhangsanMyBatis会将上述SQL语句中的#{username}替换为 ?然后使用PreparedStatement向数据库发送如下预编译SQL
SELECT * FROM users WHERE username ?
同时MyBatis会将 zhangsan 作为参数值通过PreparedStatement的set方法安全地绑定到SQL语句中的对应位置。
特点与注意事项
自动处理引号使用#{}时MyBatis会根据参数的实际类型自动为其添加合适的引号或进行其他必要的类型转换。对于字符串和日期类型MyBatis会自动加上单引号。所以你不需要担心引号问题只需传入值即可。防SQL注入#{}利用预编译机制确保参数值与SQL语句主体分离由数据库在执行时负责正确的参数绑定。这从根本上杜绝了SQL注入攻击的可能性极大地增强了系统的安全性。适用场景#{}是推荐的参数传递方式适用于所有需要动态传入值的场景特别是涉及到用户输入或敏感信息的条件查询、更新操作等。 举个代码例子
1. ${}字符串拼接
想象一下我们有一个简单的executeQuery()方法它接受一个用户输入的字符串比如用户名然后拼接到SQL查询语句中。
public ListUser executeQuery(String unsafeUsername) {// 直接拼接字符串模拟${}的行为String sql SELECT * FROM users WHERE username unsafeUsername ;// 假设这里有代码执行SQL查询并返回结果...// executeSQL(sql);return null; // 这里仅作演示实际应返回查询结果
} 现在假设用户输入了一个恶意的用户名如
String maliciousUsername OR 11; // 模拟SQL注入攻击 调用executeQuery(maliciousUsername)后生成的SQL语句将是 SELECT * FROM users WHERE username OR 11 由于直接拼接字符串恶意用户成功篡改了SQL语句的结构导致查询结果不受控制暴露了SQL注入风险。 2. #{}预编译占位符
接下来我们用类似的方式模拟#{}的工作原理使用PreparedStatement来设置参数值。
public ListUser executePreparedQuery(String safeUsername) {String sql SELECT * FROM users WHERE username ?; // 使用占位符try (Connection conn getConnection();PreparedStatement pstmt conn.prepareStatement(sql)) {pstmt.setString(1, safeUsername); // 安全地设置参数值// 假设这里有代码执行PreparedStatement并返回结果...// ResultSet rs pstmt.executeQuery();// return processResultSet(rs); // 这里仅作演示实际应处理结果集并返回结果} catch (SQLException e) {throw new RuntimeException(e);}return null; // 这里仅作演示实际应返回查询结果
} 同样假设用户输入了恶意的用户名
String maliciousUsername OR 11; 调用executePreparedQuery(maliciousUsername)时尽管恶意用户名被传递给了PreparedStatement但由于预编译机制数据库会将它视为一个独立的字符串值而不是SQL语句的一部分。生成并执行的SQL实际上等同于
SELECT * FROM users WHERE username OR 11
可以看到尽管用户输入了恶意内容但预编译机制确保了参数值被正确地转义和隔离有效地防止了SQL注入攻击。 小总结
${}就像在Java代码中直接拼接字符串将用户输入的值原样插入到SQL语句中需要手动处理引号并且存在SQL注入风险。仅在确保输入安全且有特殊需求时考虑使用。#{}则是通过预编译机制如Java中的PreparedStatement将参数值与SQL语句主体分离由数据库在执行时负责正确的参数绑定。这样既自动处理了引号和类型转换又从根本上杜绝了SQL注入攻击是所有常规参数传递场景的推荐选择。 2. 单个字面量类型参数
当MyBatis的mapper接口中的方法参数仅为一个字面量类型如Integer、String、Date等基本类型或其包装类并且您需要在对应的SQL映射文件中引用这个参数时可以使用${}或#{}来获取该参数的值。 考虑到使用#{}更加安全可靠所以就用它举个代码例子
UserMapper.java接口
package com.sakurapaid.mybatis3.demo01.mapper;import com.sakurapaid.mybatis3.demo01.bean.User;import java.util.List;public interface UserMapper {// 1.添加用户public int addUser(User user);// 2.修改用户public int updateUser(User user);// 3.查询所有用户public ListUser findAllUser();// 4.根据id删除指定用户public int deleteUserById(int id);// 5. 根据用户名查询用户public User findUserByName(String name);
} UserMapper.xml映射文件
这里的parameterType不是全类名是因为我在配置文件中做了取别名操作
?xml version1.0 encodingUTF-8 ?
!DOCTYPE mapperPUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttps://mybatis.org/dtd/mybatis-3-mapper.dtd
!-- 定义mapper接口的命名空间 --
mapper namespacecom.sakurapaid.mybatis3.demo01.mapper.UserMapper!--1.添加用户--insert idaddUser parameterTypeUserinsert into user(name,age,sex) values(#{name},#{age},#{sex})/insert!--2.修改用户--update idupdateUser parameterTypeUserupdate user set name#{name},age#{age},sex#{sex} where id#{id}/update!--3.查询所有用户--select idfindAllUser resultTypeUserselect * from user/select!--4.根据id删除指定用户--delete iddeleteUserById parameterTypeintdelete from user where id#{id}/delete!--5.根据用户名查询用户--select idfindUserByName resultTypeUserselect * from user where name#{name}/select
/mapper 测试输出
package com.sakurapaid.mybatis3.demo01.test;import com.sakurapaid.mybatis3.demo01.bean.User;
import com.sakurapaid.mybatis3.demo01.mapper.UserMapper;
import com.sakurapaid.mybatis3.demo01.utils.SqlSessionUtils;
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.jupiter.api.Test;import java.io.IOException;
import java.io.InputStream;
import java.util.List;/*** 用户测试类*/
public class UserTest {Testpublic void test() throws IOException {// 从SqlSessionUtils工具类获取SqlSession对象SqlSession sqlSession SqlSessionUtils.getSqlSession();// 获取UserMapper接口的代理对象UserMapper userMapper sqlSession.getMapper(UserMapper.class);// 1.添加用户/*User user1 new User(0, 小明, 18, 男);int i userMapper.addUser(user1);if (i 0) {System.out.println(添加成功);} else {System.out.println(添加失败);}*/// 2.修改用户/*User user2 new User(1, 萨达姆, 26, 男);int i userMapper.updateUser(user2);if (i 0) {System.out.println(修改成功);} else {System.out.println(修改失败);}*/// 3.查询所有用户/*ListUser users userMapper.findAllUser();if (!users.isEmpty()) {for (User user : users) {System.out.println(user);}} else {System.out.println(没有数据);}*/// 4.根据id删除指定用户/*int i userMapper.deleteUserById(4);if (i 0) {System.out.println(删除成功);} else {System.out.println(删除失败);}*/// 5.根据用户名查询用户User user userMapper.findUserByName(萨达姆);if (user ! null) {System.out.println(查询成功);System.out.print(user);} else {System.out.println(没有数据);}}
} 3. 多个字面量类型参数
当MyBatis的mapper接口中的方法参数有多个字面量类型如Integer、String、Date等基本类型或其包装类MyBatis会以特定方式组织这些参数以便在SQL映射文件中引用它们。 参数组织方式
默认键名MyBatis会自动将这些参数放入一个Map集合中。每个参数以其在方法参数列表中的位置作为键名即arg0, arg1, arg2, ...。例如对于方法getUserInfo(int id, String name)参数id对应的键为arg0参数name对应的键为arg1。自定义键名如果在方法参数上使用Param(paramName)注解MyBatis会使用注解中指定的名称作为键。例如Param(userId) int id和Param(userName) String name则键分别为userId和userName。 引用参数
使用${}或#{}在SQL映射文件中可以通过${argN}或#{argN}或使用自定义键名的${paramName}、#{paramName}来访问Map集合中对应的值。其中N表示参数在方法参数列表中的位置从0开始计数paramName为使用Param注解指定的名称。手动添加单引号对于${}与单个参数情况相同对于字符串或日期类型的值需要手动添加单引号。而对于#{}仍然无需手动添加任何引号MyBatis会自动处理。 示例
假设mapper接口方法为
public User getUserInfo(Param(userId) int id, Param(userName) String name);
对应的SQL映射文件片段
SELECT * FROM users WHERE id #{userId} AND username ${userName} 再举一个代码例子
现在我要传入两个参数id和name来查询对应的用户信息
UserMapper.java
package com.sakurapaid.mybatis3.demo01.mapper;import com.sakurapaid.mybatis3.demo01.bean.User;import java.util.List;public interface UserMapper {// 1.添加用户public int addUser(User user);// 2.修改用户public int updateUser(User user);// 3.查询所有用户public ListUser findAllUser();// 4.根据id删除指定用户public int deleteUserById(int id);// 5.根据用户名查询用户public User findUserByName(String name);// 6.输入id和姓名查询用户public User findUserByIdAndName(int id, String name);
} UserMapper.xml
?xml version1.0 encodingUTF-8 ?
!DOCTYPE mapperPUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttps://mybatis.org/dtd/mybatis-3-mapper.dtd
!-- 定义mapper接口的命名空间 --
mapper namespacecom.sakurapaid.mybatis3.demo01.mapper.UserMapper!--1.添加用户--insert idaddUser parameterTypeUserinsert into user(name,age,sex) values(#{name},#{age},#{sex})/insert!--2.修改用户--update idupdateUser parameterTypeUserupdate user set name#{name},age#{age},sex#{sex} where id#{id}/update!--3.查询所有用户--select idfindAllUser resultTypeUserselect * from user/select!--4.根据id删除指定用户--delete iddeleteUserById parameterTypeintdelete from user where id#{id}/delete!--5.根据用户名查询用户--select idfindUserByName resultTypeUserselect * from user where name#{name}/select!--6.输入id和姓名查询用户--select idfindUserByIdAndName resultTypeUser!--select * from user where id#{param1} and name#{param2}--select * from user where id#{arg0} and name#{arg1}/select
/mapper 如果此时sql语句还像单个字面量直接写标识符编译就会报错
!--6.输入id和姓名查询用户--
select idfindUserByIdAndName resultTypeUser!--select * from user where id#{param1} and name#{param2}--!--select * from user where id#{arg0} and name#{arg1}--select * from user where id#{id} and name#{name}
/select
// 6.输入id和姓名查询用户
User user userMapper.findUserByIdAndName(1, 萨达姆);
if (user ! null) {System.out.println(查询成功);System.out.print(user);
} else {System.out.println(没有数据); 报错的关键简单总结就是在执行MyBatis的数据库查询时查询语句中引用了一个名为id的参数但在对应的Mapper接口方法调用时并没有提供这个参数。MyBatis在处理SQL映射时未能找到与#{id}占位符相对应的参数值因此抛出了org.apache.ibatis.binding.BindingException: Parameter id not found的错误。这意味着在编写Mapper接口方法时应当确保方法签名中包含所有在映射文件中使用的参数同时在实际调用该方法时也需要正确传递这些参数。 如何解决此时就可以使用也就是我注释上写的代码
默认键名MyBatis会自动将这些参数放入一个Map集合中。每个参数以其在方法参数列表中的位置作为键名即arg0, arg1, arg2, ...。或者param1, param2, param3, ...。
!--6.输入id和姓名查询用户--
select idfindUserByIdAndName resultTypeUser!--select * from user where id#{param1} and name#{param2}--!--select * from user where id#{arg0} and name#{arg1}--
/select 还有有种方法是--自定义键名如果在方法参数上使用Param(paramName)注解MyBatis会使用注解中指定的名称作为键。例如Param(userId) int id和Param(userName) String name则键分别为userId和userName。
这个到文章的后面再讲 4. map集合类型的参数
若mapper接口中的方法需要的参数为多个时此时可以手动创建map集合将这些数据放在map中 只需要通过${}和#{}访问map集合的键就可以获取相对应的值注意${ }需要手动加单引号 又举个代码例子 map集合方式输入id和姓名查询用户
在 MyBatis 中当一个 Mapper 接口方法需要接收多个参数时可以使用 Map 集合作为参数类型将各个参数以键值对的形式封装在 Map 中。这样只需一个参数即可传递多个值使得接口签名更为简洁。以下是具体的步骤和示例
1. 定义 Mapper 接口方法
首先在 UserMapper.java 中定义一个使用 Map 类型参数的方法如
public User findUserByIdAndName2(MapString, Object map);
此方法表示我们要根据 id 和 name 两个属性查询用户信息。 2. 编写 XML 映射文件
接着在 UserMapper.xml 中编写对应的 SQL 查询语句使用 #{} 占位符来引用 Map 中的键值
!-- 7.map集合方式输入id和姓名查询用户 --
select idfindUserByIdAndName2 resultTypeUserselect * from user where id #{id} and name #{name}
/select
这里#{id} 和 #{name} 分别代表 Map 参数中键为 id 和 name 的值。MyBatis 会在执行时自动从传入的 Map 中取出对应的值并将其作为预编译参数插入 SQL 查询中保证安全性。 3. 准备参数与调用查询
最后在应用程序中创建一个 HashMap 实例填充所需的 id 和 name 值然后调用 Mapper 方法执行查询
// 7.map集合方式输入id和姓名查询用户
MapString, Object map new HashMap();
map.put(id, 1);
map.put(name, 萨达姆);User userByIdAndName2 userMapper.findUserByIdAndName2(map);if (userByIdAndName2 ! null) {System.out.println(查询成功);System.out.println(userByIdAndName2); // 修改为 println 输出整行
} else {System.out.println(没有数据);
}
这段代码首先创建了一个 HashMap 并放入 id 和 name 的键值对。然后调用 userMapper.findUserByIdAndName2(map) 执行查询。根据查询结果判断是否找到了匹配的用户并打印相应的信息。 总结
通过上述步骤我们展示了如何使用 Map 类型参数在 MyBatis 中实现多参数查询。关键要点如下
使用 Map 集合封装多个查询参数简化接口定义。在 Mapper 接口中声明接受 Map 参数的方法。在 XML 映射文件中使用 #{mapKey} 格式引用 Map 中的键值确保 SQL 安全性。在应用中创建 Map 实例填充参数调用 Mapper 方法执行查询并处理查询结果。
这种做法尤其适用于参数数量不确定或变动频繁的场景有助于保持代码的灵活性和可维护性。初学者在实际编程中可以借鉴此模式根据需求适配自己的查询逻辑。 5. 实体类类型的参数
若mapper接口中的方法参数为实体类对象时 此时可以使用${}和#{}通过访问实体类对象中的属性名获取属性值注意${}需要手动加单引号 在 MyBatis 中当一个 Mapper 接口方法的参数为实体类对象时可以直接使用实体类属性作为 SQL 语句中的占位符值。这种方式简化了参数传递无需手动构建 Map 或其他数据结构。 以User实体类作为参数添加用户
1. 定义 Mapper 接口方法
首先在 UserMapper.java 中定义一个使用 User 实体类作为参数的方法如
public int addUser2(User user);
此方法表示我们要根据 User 实例的属性值来添加一个新的用户记录到数据库。 2. 编写 XML 映射文件
接着在 UserMapper.xml 中编写对应的 SQL 插入语句使用 #{属性名} 占位符来引用实体类对象的属性
!-- 8.以User实体类作为参数添加用户 --
insert idaddUser2insert into user values(0, #{name}, #{age}, #{sex})
/insert
这里#{name}, #{age}, 和 #{sex} 分别代表 User 对象的 name, age, 和 sex 属性值。MyBatis 会在执行时自动从传入的 User 实例中取出对应的属性值并将其作为预编译参数插入 SQL 插入语句中保证安全性。 3. 准备参数与调用添加方法
最后在应用程序中创建一个 User 实例填充所需的属性值然后调用 Mapper 方法执行添加操作
// 8.以User实体类作为参数添加用户
User user new User(0, 小明, 18, 男);int i userMapper.addUser2(user);if (i 0) {System.out.println(添加成功);
} else {System.out.println(添加失败);
} 这段代码首先创建了一个 User 对象并设置了 name, age, 和 sex 属性。然后调用 userMapper.addUser2(user) 执行添加操作。根据返回的受影响行数判断添加是否成功并打印相应的信息。 总结
通过上述步骤我们展示了如何使用实体类类型参数在 MyBatis 中实现用户添加操作。关键要点如下
直接使用实体类对象作为 Mapper 方法的参数简化参数传递。在 Mapper 接口中声明接受实体类对象的方法。在 XML 映射文件中使用 #{属性名} 格式引用实体类对象的属性确保 SQL 安全性。在应用中创建实体类实例填充属性值调用 Mapper 方法执行添加操作并根据返回结果判断操作是否成功。
这种做法适用于参数与实体类属性紧密关联的场景能够简化代码结构提高代码的可读性和一致性。初学者在设计数据操作接口时可以优先考虑使用实体类作为参数以充分利用 MyBatis 对实体类属性的自动映射功能。 6. 使用Param标识参数
可以通过Param注解标识mapper接口中的方法参数 此时会将这些参数放在map集合中以Param注解的value属性值为键以参数为值以 param1,param2...为键以参数为值只需要通过${}和#{}访问map集合的键就可以获取相对应的值 注意${}需要手动加单引号 在 MyBatis 中Param 注解可以用于为 Mapper 接口方法的参数提供别名特别是在方法包含多个参数时有助于清晰地标识每个参数的作用并在 XML 映射文件中通过别名引用它们。以下是如何使用 Param 注解查询用户的步骤和示例
使用Param输入id和姓名查询用户
1. 定义 Mapper 接口方法
首先在 UserMapper.java 中定义一个带有 Param 注解的方法为 id 和 name 参数赋予别名
// 9.使用Param输入id和姓名查询用户
public User findUserByIdAndName3(Param(id) int id, Param(name) String name);
这里Param(id) 和 Param(name) 分别为 int id 和 String name 参数指定了别名 id 和 name。这些别名将在 XML 映射文件中作为占位符的键来使用。 2. 编写 XML 映射文件
接着在 UserMapper.xml 中编写对应的 SQL 查询语句使用 #{别名} 占位符来引用带有 Param 注解的参数
!--9.使用Param输入id和姓名查询用户--
select idfindUserByIdAndName3 resultTypeUserselect * from user where id #{id} and name #{name}
/select
这里#{id} 和 #{name} 分别代表方法参数中被 Param 注解标记为 id 和 name 的值。MyBatis 会在执行时自动从方法参数中取出对应的值并将其作为预编译参数插入 SQL 查询中保证安全性。
不写id和name的话也可以以 param1,param2...为键以参数为值 3. 调用查询方法
最后在应用程序中直接调用 Mapper 方法传入 id 和 name 参数值
// 9.使用Param输入id和姓名查询用户
User userByIdAndName3 userMapper.findUserByIdAndName3(1, 萨达姆);if (userByIdAndName3 ! null) {System.out.println(查询成功);System.out.println(userByIdAndName3); // 修改为 println 输出整行
} else {System.out.println(没有数据);
} 这段代码直接调用 userMapper.findUserByIdAndName3(1, 萨达姆)传入 id 和 name 的具体值。根据查询结果判断是否找到了匹配的用户并打印相应的信息。 总结
通过上述步骤我们展示了如何使用 Param 注解在 MyBatis 中实现多参数查询。关键要点如下
使用 Param 注解为 Mapper 接口方法的参数指定别名增加代码可读性。在 Mapper 接口中声明带有 Param 注解的方法。在 XML 映射文件中使用 #{别名} 格式引用 Param 注解标记的参数确保 SQL 安全性。在应用中直接调用 Mapper 方法传入相应参数值并处理查询结果。
这种做法特别适用于方法参数较多且需要清晰标识其用途的场景增强了代码的可理解性和维护性。初学者在编写多参数的 MyBatis 查询方法时应优先考虑使用 Param 注解来提升代码质量。注意这里并不涉及将参数放入 Map 集合中而是直接使用注解提供的别名与方法参数关联。