MyBatis操作数据库(3)

其它查询操作

#{}和${}

MyBatis参数赋值有两种方式, 咱们前面使用了#{}进行赋值, 接下来来看两者的区别:

#{}和${}的使用

1.先看Integer类型的参数:

@Select("select username, password, age, gender, phone from userinfo where id = #{id}")
UserInfo queryById(Integer id);

我们观察一下打印的日志:

 

 我们发现输输入的参数并没有在后面拼接, id使用的是 ? 进行占位. 这种SQL我们称之为"预编译SQL".

我们把#{}换为${}再观察打印的日志:

@Select("select username, password, age, gender, phone from userinfo where id = ${id}")
UserInfo queryById2(Integer id);

 

可以看到, 这次的参数是直接拼接在SQL中了.

2.接下来我们再看String类型的参数:

​
@Select("select username, password, age, gender, phone from userinfo where username = #{name}")
UserInfo queryByName(String name);

​

观察打印的日志, 发现正常返回. 

 

我们把#{}改为${}再观察打印的日志:

@Select("select username, password, age, gender, phone from userinfo where username = ${name}")
UserInfo queryByName(String name);

可以看到, 这次的参数依然是直接拼接在SQL语句中了, 但是字符串作为参数时, 需要添加引号 ' ', 使用${}而不添加引号, 会导致程序报错. 

@Select("select username, password, age, gender, phone from userinfo where username = '${name}'")
UserInfo queryByName(String name);

再次运行, 结果正常返回:

从上面两个栗子可以看出:

#{} 使用的是预编译SQL, 通过 ? 占位的方式, 提前对SQL进行编译, 然后把参数填充到SQL语句中. #{}会根据参数的类型, 自动拼接引号 ' '.

${} 会直接进行字符替换, 一起对SQL进行编译. 如果参数为字符串, 需要加上引号 ' '.

参数为数字类型时, 也可以加上, 查询结果不变, 但是可能导致索引失效, 性能下降. 

#{}和${}区别

简单回顾:

当客户发送一条SQL语句给服务器后, 大致流程如下:

1.解析语法和语义, 校验SQL语句是否正确.

2.优化SQL语句, 指定执行计划.

3.执行并返回结果

 一条SQL语句如果走上述流程, 我们称之为即时SQL.

1.性能更高

绝大多数情况下, 某一条SQL语句可能会被反复调用执行, 或者每次执行的时候只有个别的值不同(比如select的where子句值不同, update的set子句值不同, insert的values值不同). 如果每次都需要经过上面的语法解析, SQL优化, SQL编译等, 则效率明显就不行了.

 

 预编译SQL, 编译一次之后会将会将编译后的SQL语句缓存起来, 后面再执行这条语句时, 不会再次编译(只是输入的参数不同), 省去了解析优化的过程, 一次提高效率.

2.更安全(防止SQL注入)

SQL注入: 是通过操作输入的数据来修改事先定义好的SQL语句, 以达到执行代码对服务器进行攻击的方法.

由于没有对用户输入进行充分检查, 而SQL又是拼接而成, 在用户输入参数时, 在参数中添加一些SQL关键字, 达到改变SQL运行结果的目的, 也可以完成恶意攻击.

sql注入代码: ' or 1 = ' 1

先来看看SQL注入的栗子:

@Select("select username, password, age, gender, phone from userinfo where username = '${name}'")
 UserInfo queryByName(String name);

测试代码:

正常访问情况:

    @Test
    void queryByName() {
        List<UserInfo> userInfos = userInfoMapper.queryByName("admin");
        System.out.println(userInfos);
    }

结果运行正常: 

 SQL注入场景

    @Test
    void queryByName() {
        List<UserInfo> userInfos = userInfoMapper.queryByName("' or 1 = '1");
        System.out.println(userInfos);
    }

结果依然查询出来了, 其中参数or被当作了SQL语句的一部分.

 可以看出来, 查询的数据并不是自己想要的数据. 所以用于查询的字段, 尽量使用#{}预查询方式.

SQL注入是一种非常常见的数据库攻击手段, SQL注入漏洞也是网络世界中最普遍的漏洞之一. 如果发生在用户登录的场景中, 密码输入为 ' or 1 = '1, 就可能完成登录(不是一定会发生的场景, 需要看登录代码咋写). 

 排序功能

从上面的例子中, 可以得出结论: ${}会有SQL注入的风险, 所以我们尽量使用#{}完成查询. 既然如此, 是不是${}就没有存在的必要性了呢?

当然不是.

接下来我们来看一下${}的使用场景:

Mapper实现

@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time from userinfo order by id ${sort}")
List<UserInfo> queryAllUserBySort(String sort);

使用${sort}可以实现排序查询, 而使用#{sort}就不能实现排序查询了.

注意: 此处sort参数为String类型, 但是SQL语句中, 排序规则是不需要加引号 ' ' 的, 所以此时的${sort}也不加引号. 

我们把${}改成#{}

@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time from userinfo order by id #{sort}")
List<UserInfo> queryAllUserBySort(String sort);

运行结果: 

 可以发现, 当使用#{sort}查询时, asc前后自动给加了引号, 导致sql错误.

#{}会根据参数类型判断是否拼接引号 ' '

如果参数类型为String, 就会加上引号.

除此之外, 还有表名作为参数时, 也只能使用${}.

其实, 这样直接使用${}还是有一定风险的, 但是其实这无非就升序/降序两种情况. 我们可以直接写两个接口, 一个专门传"asc"以表示升序, 一个专门传"desc"以表示降序.

like查询

like使用#{}报错.

 @Select("select * from userinfo where username like '%#{key}%'")
List<UserInfo> queryAllUserByLike(String key);

把#{}改成${}可以正确查出来, 但是${}存在SQL注入的问题, 所以不能直接使用${}.

解决方法: 使用mysql的内置函数concat()来处理, 实现代码如下:

 @Select("select * from userinfo where username like concat('%', #{key}, '%')")
List<UserInfo> queryAllUserByLike(String key);

总结: #{}和${}区别

1.#{}: 预编译处理, ${}:直接字符替换

2.#{}可以防止SQL注入, ${}存在SQL注入的风险, 查询语句中, 可以使用#{}, 推荐使用#{}

3.但是一些场景, #{}不能完成, 比如排序功能, 表名, 字段名作为参数时, 这些情况需要使用${}

4.以上场景可以有更安全的方式替换${}. 

数据库连接池

在上面Mybatis讲解中, 我们使用了数据库连接池技术, 避免频繁地创建连接, 销毁连接, 下面我们来了解一下数据库连接池:

介绍

数据库连接池负责分配, 管理和释放数据库连接, 它允许应用程序重复使用一个现有的数据库连接, 而不是重新建立一个.

没有使用数据库连接池的情况:每次执行SQL语句, 要先创建一个新的连接对象, 然后执行SQL语句, SQL语句执行完, 再关闭连接对象释放资源, 这种重复的创建连接, 销毁连接比较消耗资源.

使用数据库连接池的情况:程序启动时, 会在数据库连接池中创建一定数量的Connection对象, 当客户请求数据库连接池, 会从数据库连接池中获取Connection对象, 然后执行SQL, SQL语句执行完, 再把Connection归还给连接池.

优点:

1.减小了网络开销

2.资源重用

3.提升了系统性能. 

  • 42
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
MyBatis 是一个开源的持久层框架,它可以帮助开发者简化数据库操作。下面是使用 MyBatis 操作数据库的一般步骤: 1. 配置数据库连接:在 MyBatis 的配置文件中,设置数据库连接信息,包括数据库驱动、URL、用户名和密码等。 2. 定义数据模型:创建 Java 类来表示数据库中的表,每个类对应一个表,类的属性对应表的列。 3. 编写 SQL 映射文件:创建一个 XML 文件,定义 SQL 语句和映射关系。在文件中,可以使用 MyBatis 提供的标签来编写 SQL 语句,还可以使用动态 SQL 来实现条件查询等功能。 4. 配置 SQL 映射文件:在 MyBatis 的配置文件中,引入 SQL 映射文件,告诉 MyBatis 哪些 SQL 语句对应哪些方法。 5. 创建 SqlSessionFactory:通过 MyBatis 提供的 SqlSessionFactoryBuilder 类,读取配置文件并创建 SqlSessionFactory 对象。SqlSessionFactory 是一个线程安全的类,用于创建 SqlSession。 6. 创建 SqlSession:通过 SqlSessionFactory 的 openSession 方法创建 SqlSession 对象。SqlSession 是一个用于执行 SQL 语句的接口,它提供了多种方法来操作数据库。 7. 执行 SQL 语句:通过 SqlSession 对象调用相应的方法,执行 SQL 语句。例如,可以使用 selectOne 方法执行查询操作,使用 insert、update 或 delete 方法执行增删改操作。 8. 提交事务和关闭资源:在操作完成后,需要调用 SqlSession 的 commit 方法提交事务,并调用 close 方法关闭资源。 以上是使用 MyBatis 操作数据库的一般步骤,具体的实现方式可以根据项目的需求进行调整和扩展。希望对你有帮助!如果有其他问题,请继续提问。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值