Spring:MyBatis操作数据库 2
1.MyBatis XML配置⽂件上一章学习了注解的⽅式, 接下来我们学习XML的⽅式使⽤Mybatis的注解⽅式主要是来完成⼀些简单的增删改查功能. 如果需要实现复杂的SQL功能建议使⽤XML来配置映射语句也就是将SQL语句写在XML配置⽂件中.1.1配置连接字符串和MyBatis如果是application.yml⽂件, 配置内容如下:1#数据库连接配置2spring:3datasource:4url:jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncodingutf8useSSLfalse5username:root6password:root7driver-class-name:com.mysql.cj.jdbc.Driver8#配置mybatis xml的⽂件路径在resources/mapper创建所有表的xml⽂件9mybatis:10mapper-locations:classpath:mapper/**Mapper.xml如果是application.properties⽂件, 配置内容如下:1#驱动类名称2spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver3#数据库连接的url4spring.datasource.urljdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncodingutf8useSSLfalse5#连接数据库的⽤⼾名6spring.datasource.usernameroot7#连接数据库的密码8spring.datasource.passwordroot9#配置mybatis xml的⽂件路径在resources/mapper创建所有表的xml⽂件10mybatis.mapper-locationsclasspath:mapper/**Mapper.xml1.2写持久层代码1.2.1添加 mapper 接⼝1importcom.example.demo.model.UserInfo;2importorg.apache.ibatis.annotations.Mapper;34importjava.util.List;56Mapper7public interfaceUserInfoXMlMapper{8ListUserInfoqueryAllUser();9}1.2.2添加 UserInfoXMLMapper.xml数据持久层的实现MyBatis 的固定 xml 格式1?xml version1.0encodingUTF-8?2!DOCTYPEmapper PUBLIC-//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd3mappernamespacecom.example.demo.mapper.UserInfoMapper45/mapper创建UserInfoXMLMapper.xml, 路径参考yml中的配置查询所有⽤⼾的具体实现 ?xml version1.0encodingUTF-8?!DOCTYPEmapper PUBLIC-//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtdmappernamespacecom.example.demo.mapper.UserInfoXMlMapperselectidqueryAllUserresultTypecom.example.demo.model.UserInfoselect username,password, age, gender, phone from user_info/select/mapper以下是对以上标签的说明•mapper 标签需要指定namespace属性表⽰命名空间值为 mapper 接⼝的全限定名包括全包名.类名。•select 查询标签是⽤来执⾏数据库的查询操作的◦id 是和Interface接⼝中定义的⽅法名称⼀样的表⽰对接⼝的具体实现⽅法。◦resultType 是返回的数据类型也就是开头我们定义的实体类.2.#{} 和 ${}MyBatis 参数赋值有两种⽅式, 咱们前⾯使⽤了#{}进⾏赋值, 接下来我们看下⼆者的区别2.1#{} 和${} 使⽤1.先看Interger类型的参数1Select(select username, password, age, gender, phone from user_info where id #{id} )2UserInfoqueryById(Integer id);观察我们打印的⽇志我们输⼊的参数并没有在后⾯拼接id的值是使⽤?进⾏占位. 这种SQL 我们称之为预编译SQL我们把#{}改成${}再观察打印的⽇志:1Select(select username, password, age, gender, phone from user_info where id ${id} )2UserInfoqueryById(Integer id);可以看到, 这次的参数是直接拼接在SQL语句中了2.接下来我们再看String类型的参数1Select(select username, password, age, gender, phone from user_info whereusername #{name} )2UserInfoqueryByName(String name);观察我们打印的⽇志, 结果正常返回我们把#{}改成${}再观察打印的⽇志:1Select(select username, password, age, gender, phone from user_info whereusername ${name} )2UserInfoqueryByName(String name);可以看到, 这次的参数依然是直接拼接在SQL语句中了, 但是字符串作为参数时, 需要添加引号, 使⽤ ${}不会拼接引号, 导致程序报错.修改代码如下:1Select(select username, password, age, gender, phone from user_info whereusername ${name} )2UserInfoqueryByName(String name);从上⾯两个例⼦可以看出:#{}使⽤的是预编译SQL, 通过?占位的⽅式, 提前对SQL进⾏编译, 然后把参数填充到SQL语句中.#{}会根据参数类型, ⾃动拼接引号.${}会直接进⾏字符替换, ⼀起对SQL进⾏编译. 如果参数为字符串, 需要加上引号.参数为数字类型时, 也可以加上, 查询结果不变, 但是可能会导致索引失效, 性能下降.2.2#{} 和 ${}区别#{} 和 ${} 的区别就是预编译SQL和即时SQL 的区别.简单回顾:当客⼾发送⼀条SQL语句给服务器后, ⼤致流程如下:1.解析语法和语义, 校验SQL语句是否正确2.优化SQL语句, 制定执⾏计划3.执⾏并返回结果⼀条 SQL如果⾛上述流程处理, 我们称之为 Immediate Statements(即时 SQL)1.性能更⾼绝⼤多数情况下, 某⼀条 SQL 语句可能会被反复调⽤执⾏, 或者每次执⾏的时候只有个别的值不同(⽐如 select 的 where ⼦句值不同, update 的 set ⼦句值不同, insert 的 values 值不同). 如果每次都需要经过上⾯的语法解析, SQL优化、SQL编译等则效率就明显不⾏了.预编译SQL编译⼀次之后会将编译后的SQL语句缓存起来后⾯再次执⾏这条语句时不会再次编译(只是输⼊的参数不同), 省去了解析优化等过程, 以此来提⾼效率2.更安全(防⽌SQL注⼊)SQL注⼊是通过操作输⼊的数据来修改事先定义好的SQL语句以达到执⾏代码对服务器进⾏攻击的⽅法。由于没有对⽤⼾输⼊进⾏充分检查⽽SQL⼜是拼接⽽成在⽤⼾输⼊参数时在参数中添加⼀些SQL关键字达到改变SQL运⾏结果的⽬的也可以完成恶意攻击。sql 注⼊代码 or 11先来看看SQL注⼊的例⼦1Select(select username, password, age, gender, phone from user_info whereusername ${name} )2ListUserInfoqueryByName(String name);测试代码:正常访问情况:1Test2voidqueryByName() {3ListUserInfo userInfos userInfoMapper.queryByName(admin);4System.out.println(userInfos);5}结果运⾏正常SQL注⼊场景:1Test2voidqueryByName() {3ListUserInfo userInfos userInfoMapper.queryByName( or 11);4System.out.println(userInfos);5}结果依然被正确查询出来了, 其中参数 or被当做了SQL语句的⼀部分,可以看出来, 查询的数据并不是⾃⼰想要的数据. 所以⽤于查询的字段尽量使⽤ #{}预查询的⽅式SQL注⼊是⼀种⾮常常⻅的数据库攻击⼿段, SQL注⼊漏洞也是⽹络世界中最普遍的漏洞之⼀.如果发⽣在⽤⼾登录的场景中, 密码输⼊为 or 11, 就可能完成登录(不是⼀定会发⽣的场景,需要看登录代码如何写)3.数据库连接池在上⾯Mybatis的讲解中, 我们使⽤了数据库连接池技术, 避免频繁的创建连接, 销毁连接下⾯我们来了解下数据库连接池3.1介绍数据库连接池负责分配、管理和释放数据库连接它允许应⽤程序重复使⽤⼀个现有的数据库连接 ⽽不是再重新建⽴⼀个.没有使⽤数据库连接池的情况: 每次执⾏SQL语句, 要先创建⼀个新的连接对象, 然后执⾏SQL语句, SQL语句执⾏完, 再关闭连接对象释放资源. 这种重复的创建连接, 销毁连接⽐较消耗资源使⽤数据库连接池的情况: 程序启动时, 会在数据库连接池中创建⼀定数量的Connection对象, 当客⼾请求数据库连接池, 会从数据库连接池中获取Connection对象, 然后执⾏SQL, SQL语句执⾏完, 再把Connection归还给连接池.优点:1.减少了⽹络开销2.资源重⽤3.提升了系统的性能3.2使⽤常⻅的数据库连接池•C3P0•DBCP•Druid•Hikari⽬前⽐较流⾏的是 Hikari, Druid1.Hikari : SpringBoot默认使⽤的数据库连接池Hikari 是⽇语光的意思(ひかり), Hikari也是以追求性能极致为⽬标2.Druid如果我们想把默认的数据库连接池切换为Druid数据库连接池, 只需要引⼊相关依赖即可dependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-3-starter/artifactIdversion1.2.21/version/dependency如果SpringBoot版本为2.X, 使⽤druid-spring-boot-starter 依赖dependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-starter/artifactIdversion1.1.17/version/dependency