mybatis 框架复习 (一) #{} 与 ${} 的区别 。模糊查询。resultType 和resultMap 的区别

目录

#{} 与 ${} 的区别

模糊查询

resultType 和resultMap 的区别

#{} 与 ${} 的区别

1.编译过程:

  1. #{} 占位符 :动态解析 -> 预编译 -> 执行

  2. ${} 拼接符 :动态解析 -> 编译 -> 执行

sql的预编译:在执行语句前就编译好,等执行时直接取编译结果去执行。省去编译时间。sql预编译后会在参数位置用占位符表示;

预编译:数据库驱动在发送sql和参数到DBMS之前,先对sql语句进行编译处理,之后DBMS则可以直接对sql进行处理,不需要再次编译,提高了性能。这一点【mybatis 默认情况下,将对所有的 sql 进行预编译处理。】

        预编译可以将多个操作步骤合并成一个步骤,一般而言,越复杂的sql,编译程度也会复杂,难度大,耗时,费性能,而预编译可以合并这些操作,预编译之后DBMS可以省去编译直接运行sql。 预编译语句可以重复利用 把一个 sql 预编译后产生的 PreparedStatement 对象缓存下来,下次对于同一个sql,可以直接使用这个缓存的 PreparedState 对象。

2.是否自动加单引号

1.#{} 【对应的变量】会自动加上单引号 ;

2.${} 对应的变量不会加上单引号;

3.安全性

  1. #{}能防止sql 注入

  2. ${}不能防止sql 注入

什么是SQL注入?以上面的角色查询为例:

如果此时的传参 name = "张三 or name = 李四",

select * from `user` where name = ${name}

-- 因为${}是拼接符,【会直接替换】,所以实际是:
select * from `user` where name = '张三' or name = '李四'

可以看到SQL语句的查询规则已经改变,原本是查一个叫名字叫“张三 or name=李四”的角色 ,现在变成了查一个名字叫“张三”或者叫“李四”的角色,这种通过传参就能改变SQL语句原本规则的操作就是SQL注入,这个在实际生产中是非常危险的!

那么使用#{} 会变成什么呢?

select * from `role` where name = #{name}

因为#{}是占位符,所以实际是:
select * from `role` where name = '张三 or name = 李四'

可以看到, #{} 不会改变原本的SQL规则,占位符 “?” 处会被完整替换,因此可以防止SQL注入;所以在开发过程中更加推荐使用 #{} 来接受后端传过来的数据

模糊查询

查询的字符中包含一个我们给它的一个字符;使用的是like关键字;而且在进行sql语句拼接的时候,不能使用#来作为占位符了,因为这里的sql语句是要拼接传进来的模糊字符串,#没有拼接的功能;当然sql中如果硬是要使用#{},那就在Java代码中进行字符串拼接就行;

mysql中模糊查询的知识补充:

【模糊查询】:当你想查询所有姓张的记录。用到【关键字like】。

select * from 表名 where 列名 like '张_';
(_代表匹配任意一个字符,%代表匹配0~n个任意字符)。

使用$:

 <select id="selectByUsername" resultType="com.ydlclass.entity.User">
-- 这里的sql中必须使用$ ,不能使用# ,因为#不能进行字符串的拼接; 当然sql中如果硬是要使用#{},
那就在Java代码中进行字符串拼接就行;也不能使用username,因为这个会打印出数据库的用户名 
        -- 这个查询语句相当于 模糊查询包含  i 的username
        select * from user where username like '%${username1}%'  
        -- 使用${}一定记得加''引号,这两个%号是拼接上去的,这个拼接的效果是${}符号提供的;
    </select>
/**
     * @param username
     * 这个方法是用来定义模糊查询用的
     */
    List<User> selectByUsername(String username);
    @Test
    public void selectByUserName(){
        try(SqlSession session = sqlSessionFactory.openSession()){
            UserMapper mapper = session.getMapper(UserMapper.class);
            List<User> users = mapper.selectByUsername("i");
            log.debug("users is [{}]", users);
        }
    }

使用#:

    @Test
    public void selectByUserName(){
        try(SqlSession session = sqlSessionFactory.openSession()){
            UserMapper mapper = session.getMapper(UserMapper.class);
            List<User> users = mapper.selectByUsername("%" + "i" + "%");
            log.debug("users is [{}]", users);
        }
    }
<select id="selectByUsername" resultType="com.ydlclass.entity.User">
        -- 这里使用# 的话,是可以使用usename的命名的
        select * from user where username like #{username}
    </select>

resultType 和resultMap 的区别

        如果数据库字段和实体的字段是一一对应,那么MyBatis会【自动映射】,但是如果不一致,比如一个叫user一个叫username,那 么就需要我们手动的建立一一映射的关系了。 有时候我们的数据库和字段和实例的字段可能不是一一对应;

        在mybatis中写【实体类的mapper的时候,里面的sql语句中的字段是要与数据库中存储的字段一样,sql才能去数据库中查询】,但是我们Java的代码的实体类的变量命名和数据库中字段的命名往往是不一样的,就会导致mybatis从数据库中查询出来的结果(查询的列名会将列名转化为小写,数据库不区分大小写) , 利用反射去对应的实体类中查找相应列名的set方法设值,如果查询结果的列名和实体类的属性名不一致,就会导致这个mybatis的自动映射不能生效,所以为了更加灵活的使用返回的结果集,就引入了resultMap,我们以后使用的返回结果集也是这个resultMap;

eg:

Java中的实体类设计:

public class User {
   private int id;    //id
   private String name;   //姓名,数据库为username
   private String password;     //密码,一致
  
   //构造
   //set/get
   //toString()
}

mapper:

//根据id查询用户
User selectUserById(int id);
mapper映射文件:
<select id="selectUserById" resultType="user">
 select * from user where id = #{id}
</select>
@Test
public void testSelectUserById() {
   UserMapper mapper =session.getMapper(UserMapper.class);
   User user = mapper.selectUserById(1);
   System.out.println(user);
   session.close();
}

结果:

  • User{id=1, name='null', password='123'}

  • 查询出来发现 name为空 . 说明出现了问题!

分析:

  • select * from user where id = #{id} 可以看做 select id,username,password from user where id = #{id}

  • mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 利用反射去对应的实体类中查找相应列名的set方法设值 ,当然找不到username;

解决方案:

方案一:为列名指定别名 , 别名和java实体类的属性名一致 (不推荐)

<select id="selectUserById" resultType="User">
 select id , username as name ,password from user where id = #{id}
</select>

方案二:使用结果集映射->ResultMap 【推荐】

<resultMap id="UserMap" type="User">
   <!-- id为主键 -->
   <id column="id" property="id"/>   这里面的column是数据库对应的字段名,property是实体类与数据库中这个字段名相对应的属性值,当然这里的id我们可以不用来写,因为实体类和数据库中该命名是完全一样的,我们只写实体类和数据库中不一样的字段名;
   <!-- column是数据库表的列名 , property是对应实体类的属性名 -->
   <result column="username" property="name"/>
   <result column="password" property="password"/>
</resultMap>
<select id="selectUserById" resultMap="UserMap">
select id,username,password from user where id= #{id}    这里面的查询字段是数据库中的字段!!!
</select>

注意:resultMap中这种字段与属性的对应并不是都要写的,只写完全不一样的对于遵循驼峰式命名和数据库中遵循下划线命名的可以不写---前提是你在配置中开启了驼峰式命名

开启驼峰式命名:

在mybatis-config.xml文件中配置:

<!--开启驼峰式命名        -->
<setting name="mapUnderscoreToCamelCase" value="true"/>

这个命名规则它会干两件事:

①会把数据库中带有下划线的字段名中的下划线给干掉;然后全部变成小写,数据库中不区分大小写的;

②然后查询到的字段名去匹配实体类中的属性,实体类中的属性命名大写小写都能和数据库中的字段名相匹配;

猜你喜欢

转载自blog.csdn.net/weixin_53142722/article/details/124102331