关系映射Web框架 —— 漫长Mybatis学习历程
Posted Putarmor
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关系映射Web框架 —— 漫长Mybatis学习历程相关的知识,希望对你有一定的参考价值。
对象关系映射型框架
一.Mybatis前言
我们先回顾一下JDBC的操作步骤:
Java语言中对于数据库的原始方式就是通过JDBC操作数据库,基本包含以下步骤:
1.创建数据库连接池DataSource
2.通过DataSource获取数据库连接Connection
3.编写执行?占位符的SQL语句
4.通过Connection以及SQL创建操作命令对象Statement
5.替换占位符:指定要替换的数据库字段类型,占位符索引及要替换的值
6.使用Statement执行SQL语句
7.查询操作:返回结果集ResultSet,更新操作:返回更新的数量
8.处理结果集
9.释放相关资源
而Mybatis放弃了JDBC操作,利用一种称作ORM思想完成数据库操作。
ORM:对象关系映射
Object:程序中的对象
Relation:关系型数据库
Mapping:将对象映射到数据库的表中具体映射关系:
- 数据库表 ------ 类
- 记录(行数据)------ 对象
- 字段 ------ 对象的属性
框架介绍
:Mybatis是一款优秀且持久的框架,它支持自定义的SQL、存储过程以及高级映射,Mybatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。它可以通过简单的XML或注解来配置和映射原始类型、接口和Java POJO为数据库中的记录。
二.Mybatis使用
创建mybatis新项目
1.引入Mybatis依赖:
pom.xml中引入mysql依赖与mybatis依赖
2.修改配置文件
①配置数据路的连接信息
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mycnblog?charactionEncoding=utf8&useSSL=true
spring.datasource.username=root
spring.datasource.password=0627
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
②配置mybatis参数(mapper.xml)
3.添加Mybatis映射
①添加Mapper接口
接口方法
②添加Mapper.xml配置文件
写SQL语句
进行单元测试:
旧项目中引入Mybatis
1.引入mybatis和mysql依赖
进入maven官网中,搜索mybatis,与之前servlet项目依赖不同的是,这里选择的依赖是springboot专属的,可以理解为酒里的专供酒。对于mysql而言,没有
注:在IDEA中,有很好的插件叫EditStarters,它可以帮助我们在项目中快速添加外部引用。可能会出现第一次搜索不到,重新打开一次Settings就可以搜索到了。
添加步骤:
在pom.xml中右击鼠标找到Generate
点击Edit Starters,然后点击确定
此时在Search中就可以去搜索需要的引用,或者在Modules中去选择,Selected中展示的是我们已经添加了的外部引用。
切记:一定要使用maven将外部引用导入到项目中
2.更改配置文件
3.添加Mapper接口
比如我们在这里实现注册功能,需要在数据库中插入用户信息
4.添加Mapper.xml文件写MySQL语句
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<!-- id要和接口方法名一致 -->
<insert id = "addUser" parameterType="com.example.model.User"
insert into userinfo(username,password,photo)
values (#username,#password,#photo)
</insert>
</mapper>
需要注意的是:namespace指的是Mapper.xml对应域也就是对应的Mapper接口
parameterType指的是接口方法的参数类型
前端代码:
<form id = "form1" action = "api/user/reg" method = "post" enctype = "multipart/form-data">
<div class="nav">
<img src="img/logo2.jpg" alt="">
<span class="title">我的博客系统</span>
<!-- 用来占据中间位置 -->
<span class="spacer"></span>
<a href="blog_list.html">主页</a>
<a href="blog_edit.html">写博客</a>
<a href="login.html">登陆</a>
<!-- <a href="#">注销</a> -->
</div>
<div class="login-container">
<!-- 中间的登陆框 -->
<div class="login-dialog">
<h3>注册</h3>
<div class="row">
<span>用户名</span>
<input type="text" id="username" name = "username">
</div>
<div class="row">
<span>密码</span>
<input type="password" id="password" name = "password">
</div>
<div class="row">
<span>确认密码</span>
<input type="password" id="password1" name = "password1">
</div>
<div class="row">
<span>上传头像</span>
<input type = "file" id="file" name = "file">
</div>
<div class="row">
<button id="submit" onclick = "mySubmit()">提交</button>
</div>
</div>
</div>
</form>
</body>
<script src = "js/jquery.min.js"></script> <!-- 引入jQuery -->
<script>
function mySubmit()
//Todo 可以先进行数据效验比如判空,效验之后执行前端请求
//检查确认密码和密码是否一致
var password = jQuery("#password").val().trim();
var password1 = jQuery("#password1").val().trim();
if(password != "" && password1 != "" && password != password1)
alert("两次输入密码不一致,请重新输入!");
return false;
//利用jQuery提交表单(jQuery内置方法)
jQuery("#form1").submit();
</script>
后端业务:
在UserController中先注入UserMapper类,然后写注册方法
@Autowired
//Autowired需要一个非空对象,而它引入的是Mybatis,IDEA不能很好识别
private UserMapper userMapper;
注意:这里使用Autowired注解后,userMapper会出现红色线,根据源码可以看出@Autowired是springframework中注解,它需要一个非空的对象,而在项目中我们用@Autowired引入的是Mybatis中的@Mapper注解,IDEA不能很好识别Mybatis注解,因此不能确定当前注入的对象是否为非空对象。
解决方案
:
1.给UserMapper接口添加@Repository注解,其他类似@Controller也是可以的。
2.在IDEA中进行设置,将注解提示关闭。
3.使用Java本身注解@Resource,无需关闭IDEA注解提示
后端代码:
@RequestMapping("/reg")
//之前加了ResponseBody返回了Json字符串,没有返回视图,一定要记住
public String register(String username, String password, @RequestPart MultipartFile file
) throws IOException
//Todo 非空效验
//1.动态获取当前项目的路径
String path = ClassUtils.getDefaultClassLoader().getResource("static").getPath();
// String path = imagePath;
path += AppFinal.IMAGE_PATH;
log.error("path"+path); //打印出路径
//2.文件名(全局唯一,时间戳是不可以的,可以使用UUID) + 文件的原始类型(比如jpg/png)
String fileType = file.getOriginalFilename(); //例如拿到img.jpg
fileType = fileType.substring(fileType.lastIndexOf(".")); //拿到.jpg
String fileName = UUID.randomUUID().toString() + fileType; //拿到文件名称
//将文件保存在服务器
file.transferTo(new File(path+fileName)); //传入文件路径
User user = new User();
user.setUsername(username); //这里的username是前端传递过来的,password也是
user.setPassword(password);
user.setPhoto(AppFinal.IMAGE_PATH + fileName); //设置的头像地址,相对的url地址,前端将其和url拼接
int result = userMapper.addUser(user);
if(result > 0)
return "redirect:/reg.html";
else
return "redirect:/reg_error.html";
启动springboot,在浏览器中访问注册页面
数据库中该数据已经正常插入:
在浏览器中访问我们刚才上传的头像:
上述表明我们通过Mybatis框架已经初步实现了一个简单而又不简单的注册功能。。。
接下来我们继续学习
设置Mapper.xml模板,避免后续机械般重复操作
单表操作
插入用户数据:
useGeneratedKeys:为true时表示设置自增主键,keyProperty中的id代表
User中ide属性主键,keyColumn中id代表数据库表中的id字段,表中的字段名称可以和User中id属性名称不相同。
设置了id自增主键后,插入数据后可以得到id;
在这里我们开启MySQL执行的日志:
在UserMapperTest类前加入@Transactional注解,这样正常执行了后端业务,但是在数据库中没有操作记录,也就是说执行的过程发生了回滚,这样一来没有污染数据。
MySQL执行的日志:
单元测试类中添加用户方法返回的结果:
查询数据:根据用户id查询
需要重点注意的是resultMap是映射结果集,将表中字段与实体类中属性对应起来,这样做就保证了实体类属性名称和表字段名称不一致时能完美对应起来。
执行单元测试后结果:
结果表明根据id查询出了User对象的属性!
查询数据:根据用户名和密码
根据上面的id查询我们发现表中的字段可以和方法中的参数名一致,那么根据名字和密码查询的时候,是不是也同样适用呢,来看一下哈。
单元测试结果:
我们发现程序执行已经出现了异常,也就是说当方法中参数名和表中字段名不一致时,是没有办法执行相应查询操作滴。这是为啥呢?
原因分析
:在Mybatis中,当方法中参数只有一个,参数的名称叫什么都行;当参数的个数大于等于1时,Mapper.xml中字段名要和方法中参数名保持一致。
当然还有一种方式,给接口方法参数做映射,给username加@Param(“name”)注解,就可以正常查询了。
实现用户登录功能:
登录成功后跳转到个人文章列表页面
这里我们复习一下Session和Cookie,登录成功后用户就有了对应的session会话保存在服务器端,而服务器端会给客户端一个JSESSIONID保存在浏览器的Cookie中,当我们不断访问登录后的页面时,页面依然存在,这是因为客户端访问时携带了JSESSIONID,此标识在服务器端找到了对应的Session,当删除了JSESSIONID时,就回到了最开始的登陆页面。
查询多个用户信息:
执行单元测试后:
程序执行符合我们的预期,userinfo表中的用户信息都已经查了出来。
删除用户数据:
根据MySQL启动日志可以看出,成功更新了一条数据
修改用户数据:
执行单元测试后:
从数据库中可以看出,原来id为10的username已经改为了打工人,更新操作成功。
Mybatis动态参数标识
# :表示从接口声明的方法参数中获取参数,并设置到MySQL语句中
$ :表示从接口声明的方法参数中获取参数,并设置到MySQL语句中
二者区别
:# 会将参数设置为‘xxx’,当替换参数为MySQL系统关键字时将查询失败报错;因此当替换信息为系统关键字时使用$ ,当替换信息为非系统关键字时一定要使用# ,这样可以预防SQL注入现象。
但进行模糊查询时,如果使用# 将报错!
替换为$ 时,执行就不报错了,完成了模糊查询
但是!!!使用$ 会存在SQL注入问题
SQL注入:使用一个特殊的SQL查询到了本来不该查询到的内容
通过执行单元测试的结果来看,发生了SQL注入现象!
怎么解决SQL注入的问题呢?
1.尽量使用# 方式进行参数的替换,它的SQL会进行预处理,不会SQL注入。
2.SQL注入一般发生在Mapper类中,该类是供开发人员调用的,因此我们可以手动排查SQL注入的关键字,比如‘ or and
等,采取过滤方式。
此时从结果可以看出,防止了SQL注入,但与此同时也没有完成相应SQL操作。
3.使用系统提供的参数进行参数的拼接进行like模糊查询
可以看出使用了concat进行拼接后防止了SQL注入现象的发生!
多表联合查询
一对一:查询文章信息与文章所属用户
步骤:
1.先创建实体对象articleinfo,包含了uer属性
2.在ArticleInfoMapper接口中定义查询方法
3.在Mapper.xml中写具体的SQL语句,要声明resultMap
和association
查询文章信息以及文章所属的用户
第一查询时由于在SQL中没有定义userinfo的photo属性,而UserMapper.xml中resultMap是存在photo字段映射的,因此user对象photo属性为null;
正常查询后结果:
当我们不设置columnPrefix时存在的问题:相同数据的字段变得不一致,进行了覆盖:
一对多:查询用户以及用户的文章
执行单元测试后结果:
成功实现了用户与用户文章信息的查询!
动态SQL
动态SQL是Mybatis的强大特性之一,如果你使用JDBC或者其他类似的框架,你一定感受到根据不同条件进行拼接SQL的痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉最后一个列名的逗号,利用动态SQL,可以摆脱这种痛苦。
if标签
查询条件不确定,可能空,可能1个,可能多个
当title为必传参数,其他为非必传参数时:
可以发现SQL语句只拼接了title字段
if中动态变量不需要加#
当所有参数都为非必传参数时,可以看出where后没有拼接的条件,因此这是错误的SQL!
此时当传入参数都为空时,SQL未出错,正常查询得到了表中文章信息!
trim标签
配合if标签使用,可以添加前缀后缀,也可以对前缀后缀进行覆盖可以理解为删除前缀后缀
①.插入数据的情况
传递五个参数,三个为必传参数,两个为非必传参数
小结:当两个非必传参数没有传入时,没有对字段拼接,同时也没有触发设置的前缀以及后缀删除。
②.查询数据
小结:当传入参数都为空时,执行了select * from articleinfo
查询了此表所有信息;当传入的参数有1一个只要不为空,就会触发设置的前缀拼接,后缀删除,正常进行SQL拼接完成查询!
where标签
小结:使用where标签时全部加and,mybatis会自动去除and。
set标签
这里修改文章标题和内容
foreach标签
这里根据id进行多个文章删除
三.Mybatis和Hibernate差别
Mybatis特点:
1.入门的门槛相对较低
2.半自动化,需要自己手写SQL
3.灵活性比较高
4.跨数据库迁移比较难(手写SQL与数据库类型是强相关的)Hibernate:
1.入门相对比较困难
2.全自动化,有hql语言类似lambda表达式比较简洁,无需写SQL,hql可以生成不同平台的SQL
3.跨数据库平台比较容易
以上是关于关系映射Web框架 —— 漫长Mybatis学习历程的主要内容,如果未能解决你的问题,请参考以下文章