一:#{}和${}的区别
在上一篇博客中,我们在博客的末尾简单的总结了#{},${},这两个注解的区别。那么,这篇博客我们来详细的了解两者的区别。
#{}:将其内容加上单引号。
${}:直接进行替换。常用于替换SQL中的关键字,eg:desc/asc。
1.1:实例
我们按照userinfo的id将用户的信息按降序排序。
1.1.1:Mapper中声明的方法:
List<UserInfo> getlistByOrder(@Param("order") String order);
1.1.2:XML中的配置
<select id="getlistByOrder" resultType="com.example.demo10.entity.UserInfo"> select * from userinfo order by id ${order} </select>
1.1.3:生成测试方法+运行结果
@Test void getlistByOrder() { String order="desc"; List<UserInfo> userInfos = userMapper.getlistByOrder(order); for (UserInfo userInfo : userInfos) { System.out.println(userInfo); } }
如上图所示,我们会发现当把order这个字符串传进去的时候,由${}进行处理,此时,直接进行了替换,并没有进行加''。这是最本质的区别。
二:SQL注入问题
从上文我们可以了解到,${}和#{}最本质的区别是一个加了单引号,一个直接进行替换。那么,如果我们将#{}替换成${}会发生什么?下面,我们将利用登录这一假设的实例:
2.1:实例
2.1.1Mapper中声明的方法
//输入用户的账户和密码 UserInfo login(@Param("username") String username,@Param("password")String password);
2.1.2:XML中的配置
<select id="login" resultType="com.example.demo10.entity.UserInfo"> select * from userinfo where username=${username} and password=${password} </select>
2.2.3:生成测试方法+运行结果
@Test void login() { String username="李明轩"; String password="123456"; UserInfo login = userMapper.login(username, password); System.out.println(login==null?"失败":"成功"); }
从测试用例不难看出,SQL语句出问题了,可见,${}是直接进行替换的。那么,我们在${}加上单引号呢?
<select id="login" resultType="com.example.demo10.entity.UserInfo"> select * from userinfo where username='${username}' and password='${password}' </select>
运行成功:从这一实例不难看出,${}是直接替换的,加上单引号之后,就和#{}拥有一样的功能了。
2.2:由${}引出的SQL注入问题
2.2.1测试的代码
@Test void login() { String username="李明轩"; String password="'or 1= '1 "; UserInfo login = userMapper.login(username, password); System.out.println(login==null?"失败":"成功"); }
2.2.2: XML中的配置
<select id="login" resultType="com.example.demo10.entity.UserInfo"> select * from userinfo where username='${username}' and password='${password}' </select>
2.2.3:运行结果:
通过上述测试,我们发现当用户存在时,密码不正确时,同样也可以将其用户信息打印出来,为什么呢?
2.3:什么时候用${}
三:like模糊查询
在前面我们学习SQL语句中,有模糊查询,为like。我们在mybatis来使用一下like查询。
3.1实例
3.1.1:Mapper中声明的方法
//查询用户名字中含有李的用户信息 List<UserInfo> getListByName(String username);
3.1.2:XML中的配置
<select id="getListByName" resultType="com.example.demo10.entity.UserInfo"> select * from userinfo where username like '%#{username}%' </select>
3.1.3:生成测试方法+运行结果
@Test void getListByName() { String username="李"; List<UserInfo> userInfos = userMapper.getListByName(username); System.out.println(userInfos); }
我们会发现,当这样运行的时候,SQL语句会爆出错误,为什么呢?
3.2如何进行解决
利用mysql中提供的concat进行拼接。我们来进行验证。
<select id="getListByName" resultType="com.example.demo10.entity.UserInfo"> select * from userinfo where username like concat('%',#{username},'%') </select>
运行结果:
可见,当我们使用SQL语句中的普通的like模糊查询时,要注意利用concat进行拼接。
四:多表查询
4.1:当数据库里面的字段名和对象的属性名不一致时
看如下两张图:我们会发现数据库中的字段名的username和对象的 name不一致,我们该如何解决?
数据库中的字段名:
对象的属性名:
@Data public class UserInfo { private int id; private String name; private String password; private String photo; private LocalDateTime createtime; private LocalDateTime updatetime; private int state; }
4.1.1解决方案一:
首先在*Mapper.xml声明对ResultMap的配置:
<resultMap id="baseMap" type="com.example.demo10.entity.UserInfo"> <id column="id" property="id"></id> <result column="usernmae" property="name"></result> <result column="password" property="password"></result> <result column="photo" property="photo"></result> <result column="createtime" property="createtime"></result> <result column="updatetime" property="updatetime"></result> <result column="state" property="state"></result> </resultMap>
<select id="getAll1" resultMap="baseMap"> select * from userinfo; </select>
Mapper中的声明方法:
//查询所有的用户 List<UserInfo> getAll1();
生成测试方法+运行结果:
@Test void getAll1() { List<UserInfo> all1 = userMapper.getAll1(); for (UserInfo userInfo : all1) { System.out.println(userInfo); } }
4.1.2解决方案二:(常用的方案
<select id="getAll1" resultType="com.example.demo10.entity.UserInfo" > select id,username as name ,password,photo,createtime,updatetime,state from userinfo </select>
生成测试方法+运行结果:
@Test void getAll1() { List<UserInfo> all1 = userMapper.getAll1(); for (UserInfo userInfo : all1) { System.out.println(userInfo); } }
运行结果:
4.2:一对一多表查询
目标要求;我们设计了两张表,用户表和文章表,那么,我们需要利用用户表的id对应文章表的uid,并查询出文章表的所有信息和用户名称
用户表:
文章表:
在entity的目录下创建视图类和文章类。
文章类
文章视图类
将所需要两张表查询的所有信息创建一个试图类,依据要求,此类需要包括用户的姓名和文章表的所有信息。
Mapper中声明的方法:
@Mapper public interface ArticleMapper { ArticleinfoView getAll(Integer id); }
XML中的配置:
<mapper namespace="com.example.demo10.mapper.ArticleMapper"> <select id="getAll" resultType="com.example.demo10.entity.ArticleinfoView"> select a.*,u.username from articleinfo a left join userinfo u on u.id=a.uid where a.id=#{u.id} </select> </mapper>
生成测试方法+运行结果:
@SpringBootTest @Transactional class ArticleMapperTest { @Autowired private ArticleMapper articleMapper; @Test void getAll() { ArticleinfoView all = articleMapper.getAll(1); System.out.println(all); } }
运行结果:
4.3:一对多查询
实现目标:查询一个用户有多少个文章时,我们可以用一条SQL语句进行查询。
<select id="getAll1" resultType="com.example.demo10.entity.ArticleinfoView"> select * from articleinfo where uid=#{id}; </select>
4.4:多对多查询
针对多对多查询,可以看成是两个一对多查询。
五:动态SQL
当我们使用普通的SQL解决不了问题时,可以采用动态SQL解决问题。有如下五个动态SQL。
5.1:<if>标签
5.1.1:使用案例
Mapper中声明的方法:
//添加用户信息 int add2(UserInfo userInfo);
XML中的配置:
<insert id="add2"> insert into userinfo( username, <if test="photo!=null"> photo, </if> password )values (#{username}, <if test="photo!=null"> #{photo}, </if> password=#{password}) </insert>
测试方法+运行结果
void add2() { UserInfo userInfo=new UserInfo(); userInfo.setUsername("李四"); userInfo.setPassword("12345"); userInfo.setPhoto(null); int i = userMapper.add2(userInfo); System.out.println(i); }
5.2:<trim>标签
5.2.1使用案例
Mapper中声明的方法:
//添加用户信息 int add3(UserInfo userInfo);
XML中的配置:
<insert id="add3"> insert into userinfo <trim prefix="(" suffix=")" suffixOverrides=","> <if test="username!=null"> username, </if> <if test="password!=null"> password, </if> <if test="photo=null"> photo </if> </trim> values <trim prefix="(" suffix=")" suffixOverrides="," > <if test="username!=null"> #{username}, </if> <if test="password!=null"> #{password}, </if> <if test="photo=null"> #{photo}, </if> </trim> </insert>
测试方法+运行结果:
@Test void add3() { UserInfo userInfo=new UserInfo(); userInfo.setUsername("老六"); userInfo.setPassword("123456"); userInfo.setPhoto(null); int i = userMapper.add2(userInfo); System.out.println(i); }
5.3:<set>标签
5.4:<where>标签
用
<where>
和<if>
进行组合.如果我们将and加在第一个<if>标签下,当第二个<if>标签不成立,此时,会对整个sql产生影响,整个SQL
select * from userinfo where username=#{username} and
上面这个语句便会报错,不符合整个SQL的语句规范。
当条件不成立时,if条件后的内容包括and也不会存在,因此会对整个sql语句产生影响。
注意and关键字要放在每个
<if>
语句中的库表字段赋值的前面。因为,一旦判断不成功,<where>
会把对应的and关键字去掉(还有or关键字)。
5.4.1使用案例:
Mapper中的声明的方法:
//查询用户的信息,当传入的用户,密码其中一条为空时,能不能查询到信息 UserInfo getall(String username,String password);
XML中的配置:
<select id="getall" resultType="com.example.demo10.entity.UserInfo"> select * from userinfo <where> <if test="username!=null"> username=#{username} </if> <if test="password!=null"> and password=#{password} </if> </where> </select>
测试方法+运行结果:
@Test void getall() { String usernmae="李明轩"; String password=null; UserInfo getall = userMapper.getall(usernmae, password); System.out.println(getall); }
5.5:<foreach>标签
当我们想要根据多个id删除多个数据时,我们使用的SQL语句如下:
delete from userinfo where id in(id1,id2,id23)
那么,我们借助<foreach>可以完成操作。
5.5.1使用案例:
Mapper中声明的方法:
//根据多条id删除数据 int dels(List<Integer> ids);
XML中的配置:
<delete id="dels"> delete from userinfo where id in <foreach collection="ids" item="id" open="(" close=")" separator=","> #{id} </foreach> </delete>
测试方法+运行结果:
@Test void dels() { List<Integer> list=new ArrayList<>(); list.add(6); list.add(7); int dels = userMapper.dels(list); System.out.println(dels); }