"#{}"占位符
作用
- 传参大部分使用"#{}",在数据库底层使用的是:PreparedStatement预编译处理对象
- 数据库底层被解析为"?",用来传值,是安全的数据库访问,可以防止sql注入
- 通过在SqlMapConfig.xml添加日志输出配置,在后文测试输出的结果中可以验证
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 设置日志输出--> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> </configuration>
写法
- "#{}"里参数的写法,要参考parameterType的类型
- 如果parameterType的类型是简单类型(8种基本类型(封装) + String类型),则"#{}"里变量名称任意
<!-- //根据用户主键查取用户信息 User getById(); --> <select id="getById" resultType="user" parameterType="int"> select id, username, birthday, sex, address from users where id=#{asYouLike} </select>
- 如果parameterType的类型是实体类的类型,则"#{}"里只能是类中成员变量的名称,而且区分大小写
//User实体类中的属性 public class User { private Integer id; private String userName; private Date birthday; private String sex; private String address; }
<!-- //向用户表中增加用户信息 int insert(User user); --> <insert id="insert" parameterType="user"> insert into users(username, birthday, sex, address) values(#{userName}, #{birthday}, #{sex}, #{address}) </insert>
"${}"占位符
用于字符串的拼接和字符串的替换
字符串拼接
作用
- 一般用于模糊查询,建议少用,因为存在sql注入风险
写法
- "${}"中参数名称的写法,分两种情况,与"#{}"的两种情况相同,可参考之
- 注意:对于parameterType的类型是简单类型时,"${}"里变量名称随便写,但是分版本
- 如果是3.5.1及以下版本,只能写"value"
模糊查询示例
- 未优化前,存在sql注入风险
<!-- //根据用户名模糊查询用户信息 List<User> getByName(String name); --> <select id="getByName" parameterType="string" resultType="user"> select id, username, birthday, sex, address from users where username like '%${name}%' </select>
- 优化后,使用"#{}"接受传参,底层是preparedStatement预编译对象,可以防止sql注入
<!-- //优化后的模糊查询 List<User> getByNameBetter(String name); --> <select id="getByNameBetter" parameterType="string" resultType="user"> select id, username, birthday, sex, address from users where username like concat('%', #{name}, '%') </select>
字符串替换("${}"的主要作用)
需求:在users表中,根据地址或者用户名模糊查询用户信息
sql语句:
select * from users where username like '%模糊查询条件%'
select * from users where address like '%模糊查询条件%'
存在的问题:两条sql语句的结构在本质上是相同的,写两条语句十分冗余,可以采用替换列名的方式进行优化
UsersMapper.java接口
package com.example.mapper; import com.example.pojo.User; import org.apache.ibatis.annotations.Param; import java.util.List; /** * 数据访问层的接口,定义对数据库完成的CRUD的操作 */ public interface UsersMapper { //根据用户名或者地址模糊查询 List<User> getByNameOrAddress( @Param("colName") String colName, @Param("userName") String userName ); }
- 接口分析:当接口中的方法的参数有多个时,用注解标识参数,sql标签可通过注解中声明的参数名获取参数
UsersMapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.mapper.UsersMapper"> <!-- //根据用户名或者地址模糊查询 List<User> getByNameOrAddress( @Param("colName") String colName, @Param("userName") String userName); --> <select id="getByNameOrAddress" resultType="user"> select id, username, birthday, sex, address from users where ${colName} like concat('%', #{userName}, '%') </select> </mapper>
- sql标签分析
- 当标签对应的接口中的方法有多个参数时,标签中的入参类型,即:parameterType,取消不写,通过方法中注解的参数名称获取参数
- 用于替换时只可以使用"${}",应该放在like前。"#{}"用来传值,应该放在like后用来占位传值
测试代码
package com.example.mapper; import com.example.pojo.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.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; public class TestUsersMapper { //时间刷 SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd"); //SqlSession对象 SqlSession sqlSession; //mybatis动态代理对象 UsersMapper usersMapper; //获取SqlSession @Before public void getSqlSession() throws IOException { //读取核心配置文件 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); //创建SqlSessionFactory对象 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); //获取SqlSession sqlSession = factory.openSession(); //获取mybatis动态代理对象 usersMapper = sqlSession.getMapper(UsersMapper.class); } //归还SqlSession @After public void closeSession(){ sqlSession.close(); } @Test public void testGetByNameOrAddress(){ List<User> users = usersMapper.getByNameOrAddress("username", "小"); //List<User> users = usersMapper.getByNameOrAddress("address", "市"); users.forEach(System.out::println); } }
测试输出
根据用户名模糊查询
Checking to see if class com.example.mapper.TestUsersMapper matches criteria [is assignable to Object] Checking to see if class com.example.mapper.UsersMapper matches criteria [is assignable to Object] Opening JDBC Connection Created connection 1293462056. Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4d18aa28] ==> Preparing: select id, username, birthday, sex, address from users where username like concat('%', ?, '%') ==> Parameters: 小(String) <== Columns: id, username, birthday, sex, address <== Row: 2, 小王, 2001-07-12, 1, 芜湖市 <== Row: 3, 小张, 1999-02-22, 1, 长沙 <== Row: 29, 小昕, 2001-03-14, 女, 忻州 <== Total: 3 Users{id=2, userName='小王', birthday=Thu Jul 12 00:00:00 CST 2001, sex='1', address='芜湖市'} Users{id=3, userName='小张', birthday=Mon Feb 22 00:00:00 CST 1999, sex='1', address='长沙'} Users{id=29, userName='小昕', birthday=Wed Mar 14 00:00:00 CST 2001, sex='女', address='忻州'} Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4d18aa28] Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4d18aa28] Returned connection 1293462056 to pool. Process finished with exit code 0
根据地址模糊查询
Checking to see if class com.example.mapper.TestUsersMapper matches criteria [is assignable to Object] Checking to see if class com.example.mapper.UsersMapper matches criteria [is assignable to Object] Opening JDBC Connection Created connection 1293462056. Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4d18aa28] ==> Preparing: select id, username, birthday, sex, address from users where address like concat('%', ?, '%') ==> Parameters: 市(String) <== Columns: id, username, birthday, sex, address <== Row: 2, 小王, 2001-07-12, 1, 芜湖市 <== Row: 7, 学委, 2001-05-13, 2, 平顶山市 <== Total: 2 Users{id=2, userName='小王', birthday=Thu Jul 12 00:00:00 CST 2001, sex='1', address='芜湖市'} Users{id=7, userName='学委', birthday=Sun May 13 00:00:00 CST 2001, sex='2', address='平顶山市'} Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4d18aa28] Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4d18aa28] Returned connection 1293462056 to pool. Process finished with exit code 0
测试结果分析
- sql标签在底层分别被解析为
==> Preparing: select id, username, birthday, sex, address from users where username like concat('%', ?, '%') ==> Parameters: 小(String)
==> Preparing: select id, username, birthday, sex, address from users where address like concat('%', ?, '%') ==> Parameters: 市(String)
- 由上可知
- 标签中的${colName}分别被替换成了传入的"username"和"address",起到了替换作用
- "#{}"被解析成"?",#{userName}分别拿到值:小(String)和市(String),起到占位传值作用