• 一天快速掌握Mybaits[一]


    一、搭环境

    Spring Initializr的搭建

    在这里插入图片描述

    在这里插入图片描述

    创建完毕后的项目结构

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CLSvNTwT-1669084188543)(E:\localhost\mybatis\image-20221102131813833.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LdcrLAkQ-1669084188544)(E:\localhost\mybatis\image-20221102131911120.png)]

    此时application的后缀更名为yml,因为这样,看起来更简洁明了,而作用上,无差别

    数据库环境的搭建

    新建数据库

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kUjMAhmT-1669084188546)(E:\localhost\mybatis\image-20221102132318426.png)]

    执行SQL语句

    use `mybatis-demo`;
    CREATE TABLE `user` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `username` varchar(50) DEFAULT NULL,
      `age` int(11) DEFAULT NULL,
      `address` varchar(50) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
    insert  into `user`(`id`,`username`,`age`,`address`) values (1,'UZI',19,'上海'),(2,'PDD',25,'上海');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    在这里插入图片描述

    id设置为了主键自动递增

    在这里插入图片描述

    yml配置

    server:
      port: 8098
    
    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver     # msyql8 加cj mysql8以下去掉cj
        url: jdbc:mysql://localhost:3306/mybatis-demo?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8
        username: root   # url是表示用于与mysql的进行一个连接 如果是本机 可以用localhost 如果不是要更换成ip
        password: 123456   #username表示SQL 账号 password表示密码
    
    mybatis:
      mapper-locations: classpath:/Mapper/*.xml   #resources 目录下的 Mapper 目录下面的所有xml文件
      type-aliases-package: com.yhn.entity        #自动配置别名
      configuration:
        map-underscore-to-camel-case: true                      #开启驼峰命名
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl   #配置打印SQL语句
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    项目结构

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5cZG0Z0F-1669084188552)(E:\localhost\mybatis\mybatis.assets\image-20221108112353481.png)]

    二、基本的CRUD

    一般web开发需要这几个层面

    • Controller 控制层面 负责接收前端传过来的参数
    • Service 业务处理层 负责业务处理
    • Mapper/Dao 数据层 负责数据调用

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VQsVu88T-1669084188556)(E:\localhost\mybatis\mybatis.assets\image-20221107235541022.png)]

    结构搭建

    Controller

    package com.yhn.controller;
    
    import com.yhn.entity.User;
    import com.yhn.service.UserService;
    import org.springframework.web.bind.annotation.*;
    /**
     * CRUD
     * @Description
     * @Author YeHaoNan~
     * @Date 2/11/2022  23:37
     * @Version 1.0.0
     **/
    @RestController
    @RequestMapping("/user")
    public class UserController {
        @Resource
        private UserService service;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    Service

    public interface UserService {
    }
    
    • 1
    • 2
    ServiceImpl
    @Service
    public class UserServiceImpl implements UserService {
        @Resource
        private UserMapper mapper;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Mapper

    @Mapper
    public interface UserMapper {
    }
    
    • 1
    • 2
    • 3

    面试题

    @RestController 是哪几个注解的复合注解

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Controller
    @ResponseBody
    public @interface RestController {
        @AliasFor(
            annotation = Controller.class
        )
        String value() default "";
    }
    
    /*
    最主要的就是
    @Controller
    @ResponseBody
    
    @Controller :
    spring会遍历上面扫描出来的所有bean,过滤出那些添加了注解@Controller的bean,将Controller中所有添加了注解@RequestMapping的方法解析出来封装成RequestMappingInfo存储到RequestMappingHandlerMapping中的mappingRegistry。后续请求到达时,会从mappingRegistry中查找能够处理该请求的方法。
    
    @ResponseBody
    加上 @ResponseBody 后返回结果不会被解析为跳转路径,而是直接写入 HTTP response body 中。 比如异步获取 json 数据,加上 @ResponseBody 后,会直接返回 json 数据。@RequestBody 将 HTTP 请求正文插入方法中,使用适合的 HttpMessageConverter 将请求体写入某个对象。 可以加方法上面也可以加在类上面,加在类上面的话,那么就表示所有的方法都会自动添加@RequestBody
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    @Autowired与@Resource 的区别

    1、 @Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。

    2、 @Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用

    3、@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

    推荐使用:@Resource注解在字段上,这样就不用写setter方法了,并且这个注解是属于J2EE的,减少了与spring的耦合。这样代码看起就比较优雅。

    查询

    @RestController
    @RequestMapping("/user")
    public class UserController {
    
        @Resource
        private UserService service;
    
        /**
         *  查询所有
         * @author YeHaoNan~
         * @date 2/11/2022 23:37
         * @return List
         */
        @GetMapping("/findAll")
        public List<User> findAll(){
            return service.findAll();
        }
    
        /**
         * 根据id进行查询
         * @author YeHaoNan~
         * @date 2/11/2022 23:38
         * @param id
         * @return User
         */
        @GetMapping("/findById")
        public User findById(Integer id){
            return service.findById(id);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    public interface UserService {
    
        /**
         *  查询所有
         * @author YeHaoNan~
         * @date 2/11/2022 23:38
         * @return List
         */
        List<User> findAll();
    
        /**
         * 根据id进行查询
         * @author YeHaoNan~
         * @date 2/11/2022 23:38
         * @param id
         * @return User
         */
        User findById(Integer id);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    @Service
    public class UserServiceImpl implements UserService {
        @Resource
        private UserMapper mapper;
    
    
        @Override
        public List<User> findAll() {
            return mapper.findAll();
        }
    
        @Override
        public User findById(Integer id) {
            return mapper.findById(id);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    @Mapper
    public interface UserMapper {
    
        /**
         *  查询所有
         * @author YeHaoNan~
         * @date 2/11/2022   23:38
         * @return List
         */
        List<User> findAll();
    
        /**
         * 根据id进行查询
         * @author YeHaoNan~
         * @date 2/11/2022   23:38
         * @param id
         * @return User
         */
        // @Param 注解 后面会详细讲解  在当前你可以看做不存在
        User findById(@Param("id") Integer id);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    mybatis 映射文件 后缀 xml

    
    DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.yhn.mapper.UserMapper">
    
        <select id="findAll" resultType="com.yhn.entity.User">
            select * from user
        select>
    
        <select id="findById" resultType="com.yhn.entity.User">
            select * from user where id = #{id}
        select>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    解析 : Mybatis里面 Mapper中的namespace用于绑定Dao/Mapper接口的,即面向接口编程,它的功能和Dao接口的实现类Impl相当,但是他不用写接口实现类,通过namesapce(命名空间)的绑定直接通过id找到相应方法,执行相应的SQL语句。

    比如我目前写是 com.yhn.mapper.UserMapper 那么可以通俗的理解为,我这个mybatis 的映射文件是只属于 com.yhn.mapper
    包下的 UserMapper 使用

    如何更加明朗的看待? 可以下载一个idea 插件 MybatisX 插件

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2shxlITd-1669084188558)(E:\localhost\mybatis\mybatis.assets\image-20221108124201519.png)]

    装上插件后的效果,蓝红鸟以线为例,namespese 对应的是包下面的接口

    mybatis标签的意思

    <select>select> 
    <insert>insert> 
    <update>update> 
    <delete>delete> 
    你就可以这里理解 你现在要执行什么类型的SQL 你就使用什么样的标签
    
    • 1
    • 2
    • 3
    • 4
    • 5

    select insert update delete标签的的属性

    id : 代表着 namespace绑定的那个接口的方法

    resultType : 代表返回的类型 一般是返回实体类型

    后续还有,后面还会继续讲解

    为什么UserController 下面的 findById方法里面 有@RequestParam注解?

    首先,明确一点,就算这个地方不加,也没有影响,程序依旧能跑起来,并且还能返回值,一切正常

    那么? 为什么还需要加上@RequestParam注解?

    在这里先介绍 @RequestParam

    作用:

    ​ @RequestParam:将请求参数绑定到你控制器的方法参数上(是springmvc中接收普通参数的注解)

    语法:

    ​ @RequestParam(value=”参数名”,required=”true/false”,defaultValue=””)

    ​ value:参数名

    ​ required:是否包含该参数,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错。

    ​ defaultValue:默认参数值,如果设置了该值,required=true将失效,自动为false,如果没有传该参数,就使用默认值

    加上@RequestParam 和不加@RequestParam
    1.如果加上@RequestParam,
    1.1 defaultValue属性可以给参数设置默认值,
    1.2 required可以设置参数是否必须传,默认为true
    1.3 value可以将前端传来的值的key与你用来接收值的参数进行绑定,无需在意参数名字是否一致
    1.4 如果设置了defaultValue属性,那么required默认为false
    2.如果不加@RequestParam
    2.1 前端传来参数的key必须与你后端接受值的那个参数名一致,不然获取不到值
    2.2 后端设置的参数的类型如果是基本数据类型 如 int long 等8中基本类型,并且前端没有传这个参数,那么就会报一个错误
    "Optional char parameter ‘xxx’ is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type "
    这个意思是说,参数xxx是可选的,但是它本身是个基本类型数据,没有办法被转换成null值,让你考虑它把变为当前基本类型的包装类如Long,Integer等,那么你把它转换成对应的包装类就可以了
    2.3 后端设置的参数的类型如果是包装类或者String或者自定义的类那么,那个前端可传可不传,如果不传获取的就是null值

    为什么UserMapper 下面的 findById方法里面 有@Param注解?

    @Param的作用就是给参数命名,比如在mapper里面某方法A(int id),当添加注解后A(@Param(“userId”) int id),也就是说外部想要取出传入的id值,只需要取它的参数名userId就可以了。将参数值传如SQL语句中,通过#{userId}进行取值给SQL的参数赋值。

    其实还有多种方式,但是不推荐,但是后面会提到

    测试用例 此文档一律使用ApiPost 当然也可以使用其他的postman、Apifox

    因为findAll 接口没有任何参数 所有请求区为空

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-brZeYY1a-1669084188559)(E:\localhost\mybatis\mybatis.assets\image-20221108155039504.png)]

    findById 测试 需要传入一个id 的参数并且设定值

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DUFccShO-1669084188560)(E:\localhost\mybatis\mybatis.assets\image-20221108155709544.png)]

    新增

    在UserController里面添加

     	@PostMapping("/insert")
        public void insert(@RequestBody User user){
            service.insert(user);
        }
    
    • 1
    • 2
    • 3
    • 4

    在UserService添加

    /**
         * 新增一条数据
         * @author YeHaoNan~
         * @date 2/11/2022 23:40
         * @param user
         */
        void insert(User user);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在UserServiceImpl添加

      @Override
        public void insert(User user) {
            mapper.insert(user);
        }
    
    • 1
    • 2
    • 3
    • 4

    在UserMapper添加

    void insert(@Param("user") User user);
    
    • 1

    在UserMapper.xml里面添加

        <insert id="insert" >
            insert into user
            value (
                #{user.id},
                #{user.userName},
                #{user.age},
                #{user.address}
            )
        insert>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    测试

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zw4hAPSu-1669084188561)(E:\localhost\mybatis\mybatis.assets\image-20221108160716423.png)]

    点击发送,即可新增成功

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fBIuJL9t-1669084188563)(E:\localhost\mybatis\mybatis.assets\image-20221108160812280.png)]

    因为设置了自动递增,则id序列会自动递增

    开发知识,这里为什么实力类要用json格式发送?为什么新增的时候,controller层里面的参数要用@RequestBody注解?

    例如一个场景,用户在页面上面填写了信息,要新增的时候,前端就会把对应信息的含义和信息转成一个json字符串,发送到后端,后端来接收,但是,后端要直接接收json字符串,是不能接收的,会引起报错,那么就得需要用到@RequestBody 注解,在参数加上,就可以来处理接受前端传过来的json字符串数据

    修改

    在UserController里面添加

    	@PostMapping("update")
        public void update(@RequestBody User user){
            service.update(user);
        }
    
    • 1
    • 2
    • 3
    • 4

    UserService

    void update(User user);
    
    • 1

    UserserviceImpl

        @Override
        public void update(User user) {
            mapper.update(user);
        }
    
    • 1
    • 2
    • 3
    • 4

    UserMapper

        void update(@Param("user")User user);
    
    • 1

    Usermapper.xml

    <update id="update" >
            update user set
                            username = #{user.userName},
                            age = #{user.age},
                            address = #{user.address}
            where
                id = #{user.id}
        update>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    测试

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ARb9SU0S-1669084188565)(E:\localhost\mybatis\mybatis.assets\image-20221108162229627.png)]

    修改后

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k23ckpHD-1669084188566)(E:\localhost\mybatis\mybatis.assets\image-20221108162258307.png)]

    删除

    UserController

        @PostMapping("delete")
        public void delete(Integer id){
            service.delete(id);
        }
    
    • 1
    • 2
    • 3
    • 4

    UserService

        void delete(Integer id);
    
    • 1

    UserServiceImpl

      @Override
        public void delete(Integer id) {
             mapper.delete(id);
        }
    
    • 1
    • 2
    • 3
    • 4

    UserMapper

    void delete(@Param("id") Integer id);
    
    • 1

    UserMapper.xml

      	<delete id="delete" >
            delete from user where id = #{id}
        </delete>
    
    • 1
    • 2
    • 3

    测试

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1PFr2CJo-1669084188568)(E:\localhost\mybatis\mybatis.assets\image-20221108164337171.png)]

    控制台日志

    ==>  Preparing: delete from user where id = ?
    ==> Parameters: 20(Integer)
    <==    Updates: 1
    
    • 1
    • 2
    • 3

    完成了删除

    三、MyBatis获取参数值的两种方式(重点,面试常考)

    ${}和#{}

    ${}和#{} 区别

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

    ${}的本质就是字符串拼接,#{}的本质就是占位符赋值

    ${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;

    但是#{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号

    #{}是预编译)处理,是占位符,${}是字符串替换,是拼接符

    Mybatis在处理#{}的时候会将sql中的#{}替换成?号,调用PreparedStatement来赋值

    演示

    //Controller
    @GetMapping("findByUserName")
    public User findByUserName(@RequestParam("userName") String userName){
        return service.findByUserName(userName);
    }
    
    //Service 
        User findByUserName(String userName);
    
    //ServiceImpl
        @Override
        public User findByUserName(String userName) {
            return mapper.findByUserName(userName);
        }
    
    //Mapper
        User findByUserName(@Param("userName") String userName);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    测试

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nKT4ArLi-1669084188569)(E:\localhost\mybatis\mybatis.assets\image-20221108233433909.png)]

    当使用#{}

    UserMapper.xml

    <select id="findByUserName" resultType="com.yhn.entity.User">
            select * from user  where username = #{userName}
        select>
    
    • 1
    • 2
    • 3

    控制台日志输出:

    ==>  Preparing: select * from user where username = ?
    ==> Parameters: 武光职(String)
    <==    Columns: id, username, age, address
    <==        Row: 21, 武光职, 20, 湖北
    <==      Total: 1
    
    • 1
    • 2
    • 3
    • 4
    • 5

    当使用${} 加 ‘’

        <select id="findByUserName" resultType="com.yhn.entity.User">
            select * from user  where username =  '${userName}'
        select>
    
    • 1
    • 2
    • 3

    控制台输出

    JDBC Connection [HikariProxyConnection@1631783826 wrapping com.mysql.cj.jdbc.ConnectionImpl@751e3ee7] will not be managed by Spring
    ==>  Preparing: select * from user where username = '武光职'
    ==> Parameters: 
    <==    Columns: id, username, age, address
    <==        Row: 21, 武光职, 20, 湖北
    <==      Total: 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    当使用${} 不加 ‘’

       <select id="findByUserName" resultType="com.yhn.entity.User">
            select * from user  where username =  ${userName}
        select>
    
    • 1
    • 2
    • 3

    控制台输出

    threw exception [Request processing failed; nested exception is org.springframework.jdbc.BadSqlGrammarException: 
    ### Error querying database.  Cause: java.sql.SQLSyntaxErrorException: Unknown column '武光职' in 'where clause'
    ### The error may exist in file [G:xxx/xxx/xxx]
    ### The error may involve defaultParameterMap
    ### The error occurred while setting parameters
    ### SQL: select * from user  where username =  武光职
    ### Cause: java.sql.SQLSyntaxErrorException: Unknown column '武光职' in 'where clause'
    ; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: Unknown column '武光职' in 'where clause'] with root cause
    
    java.sql.SQLSyntaxErrorException: Unknown column '武光职' in 'where clause'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    意思就是SQL语法错误

    Unknown column ‘武光职’ in ‘where clause’

    意思是找不到

    那么为什么会有这样的情况呢?

    那是因为 #{} 会在自动在值两旁加上 ‘’ 而${}并不会

    也就是跟前面提到的 :

    #{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号

    而如果是 int类型呢?

    在来测试 UserController 下的 findById 方法

       
    	<select id="findById" resultType="com.yhn.entity.User">
            select * from user where id = ${id}
        select>
    
    • 1
    • 2
    • 3
    • 4

    控制台输出

    ==>  Preparing: select * from user where id = 1
    ==> Parameters: 
    <==    Columns: id, username, age, address
    <==        Row: 1, UZI, 19, 上海
    <==      Total: 1
    
    • 1
    • 2
    • 3
    • 4
    • 5

    照样能输出结果 因为此时是个int类型的 在SQL语句中 int类型的数据 本身就不需要 ‘’ 而日期 字符串类型就需要,否则就会报错

    ${}和#{} 的使用技巧

    性能考虑

    因为预编译语句对象可以重复利用,把一个sql预编译后产生的PreparedStatement对象缓存下来,下次对于同一个sql,可以直接使用缓存的PreparedStatement对象,mybatis默认情况下,对所有的sql进行预编译,这样的话#{}的处理方式性能会相对高些。

    安全考虑

    如果作为条件变量的话,那么使用 #{} 更安全

    性能不做案例,下面做安全的案例

    这是一条用户的账号、密码数据

    img

    当用户登录,我们验证账号密码是否正确时用这个sql:

    在这里插入图片描述

    select * from user where username=${username} and password=${password}
    
    • 1

    显然这条sql没问题可以查出来,但是如果有人不知道密码但是想登录账号怎么办

    我们不需要填写正确的密码:

    username=yyy ; password=1 or 1=1,sql执行的其实是

    select * from user where username='yyy' and password=1 or 1 =1
    
    • 1

    注意:这里的yyy外面的单引号不是 符 号 提 供 的 。 {}符号提供的。 {}没有这个功能,可以是sql手动拼接的,这里前后逻辑可能并不严密,但是sql入去最简单的例子就是这样。

    所以#{} 更安全 因为他会自动添加单引号

    select * from user where username=#{username} and password=#{password}
    
    • 1

    username=yyy ; password=1 or 1=1,sql执行的其实是

    在这里插入图片描述

    此时password密码直接错误,别人进不去

    如何选择使用 #{}和${}呢?

    表名、order by的排序字段作为变量时,使用${}。

    能使用#{}的时候尽量使用#{}

    我是程序员小孟,欢迎点赞关注!持续更新干货!

  • 相关阅读:
    Zookeeper概述
    Kafka 数据重复怎么办?(案例)
    AI全栈大模型工程师(二)课程大纲
    Docker ——Docker基本管理
    Flutter for Web:为什么 Flutter 最适合 Web 应用开发?
    为什么别人都不主动联系你
    使用微服务的最佳实践:编写、测试、保护和部署微服务(PDF)
    malloc,calloc,realloc的使用方法和注意事项
    含有稀土元素Tb荧光磁性高分子微球/可聚合端基双键铕多元配位复合物荧光微球的研究
    正向代理和反向代理
  • 原文地址:https://blog.csdn.net/mengchuan6666/article/details/127977946