分两种情况;1:已经创建的项目进行mybaits升级;2:在新项目里进行mybatis装配。
1:添加MyBatis框架支持(上述两种情况都需要这步操作)
因为底层还是jdbc;我们用的是MySQL;添加MySQL Driver、MyBatis Framework
2:配置文件;连接数据库;映射当前项目路径(classpath);这里固定写法。
mybatis.mapper-locations 是用于指定 SQL 映射文件(Mapper 文件)的位置。数据库的操作语句保存在*Mapper.xml。(如果不映射;一大堆SQL文件写在配置文件里不美观;也不解耦)
1:添加实体类;
2:添加mapper接口
@Mapper 注解的作用是告诉 MyBatis 框架将该接口类视为映射器接口,并为其生成对应的实现类。这个实现类会通过动态代理技术在运行时自动生成,实现了映射器接口中定义的数据库操作方法的具体逻辑。
3:添加Mapper的xml文件(这个xml文件名;望文生义即可;并没有强制性要求)
这段代码是固定的;只需要我们中间添加sql语句即可;com.example.demo.mapper.UserMapper对应我们上一步创建的接口的位置。
<?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.demo.mapper.UserMapper">
</mapper>
resultType=“com.example.demo.entity.UserEntity”;表示查询结果将会被映射到 UserEntity 类型的对象中。
id="getAll"是和 Interface(接⼝)中定义的⽅法名称⼀样的,表示用这个sql对接⼝的具体实现⽅法。(底层是jdbc;是用这个sql语句来实现的方法?应该就是)
5:添加控制层(套娃;控制层调用服务层;服务层调用接口里的方法)
测试数据库:
-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8mb4;
-- 使用数据数据
use mycnblog;
-- 创建表[用户表]
drop table if exists userinfo;
create table userinfo(
id int primary key auto_increment,
username varchar(100) not null,
password varchar(32) not null,
photo varchar(500) default '',
createtime timestamp default current_timestamp,
updatetime timestamp default current_timestamp,
`state` int default 1
) default charset 'utf8mb4';
-- 创建文章表
drop table if exists articleinfo;
create table articleinfo(
id int primary key auto_increment,
title varchar(100) not null,
content text not null,
createtime timestamp default current_timestamp,
updatetime timestamp default current_timestamp,
uid int not null,
rcount int not null default 1,
`state` int default 1
)default charset 'utf8mb4';
-- 创建视频表
drop table if exists videoinfo;
create table videoinfo(
vid int primary key,
`title` varchar(250),
`url` varchar(1000),
createtime timestamp default current_timestamp,
updatetime timestamp default current_timestamp,
uid int
)default charset 'utf8mb4';
-- 添加一个用户信息
INSERT INTO `mycnblog`.`userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`) VALUES
(1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1);
-- 文章添加测试数据
insert into articleinfo(title,content,uid)
values('Java','Java正文',1);
-- 添加视频
insert into videoinfo(vid,title,url,uid) values(1,'java title','http://www.baidu.com',1);
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis获取动态参数两种实现:直接替换、占位符模式
上述传的是普通参数如果要传对象?xml怎么使用这个对象呢?
当使用 $ 引用参数时,MyBatis 将直接将参数值替换到 SQL 语句中,不会进行预编译或参数绑定。SQL 语句中的参数引用将直接使用参数的字符串表示,不会在运行时进行类型转换或防止 SQL 注入攻击。(不够安全;如果是int类型是没有区别的。String就有区别)
书写问题:
$:比如String类型;你不加引号;它也不会加引号;直接替换过去。select *from videoinfo where title=’ $ {hh} '。如果不加引号;mysql执行的就是select *from videoinfo where title=hh;必然报错title是varchar类型;title='hh’才对(如果换#则不用加引号也行)
安全问题:
在单元测试下;正常是成功查到用户名和密码;以下sql注入不正常的情况也能查询到密码
因为我们当前接收是一条数据;而当前sql注入会返回多条数据;所以当前最好是数据库的这个表里只有一条数据。
" ‘or 1=‘1 " :替换后select *from videoinfo where vid=‘1’ and url=’’ or 1=‘1’
问题在于;把我的字符串当sql去执行了;如果占位符就不会出现这种情况;占的这个位置就是这个字符串的。
$也有它的好处;当要传的就是一个sql、关键字语句;使用占位符#可不行。
如果使用#代码都报错。它认为这里是占位符是一个参数去传的。而不是sql的。但是 $有风险;在这种情况我们对前端传过来的参数能进行限制要么是升序、要么是降序,如果传其它就不给你通过;这种处理还是能接受。最好我们还是用#;在服务层;写两份代码;升序则调用升序的代码;降序就调用降序的代码。
返回受影响的行数;因为有可能管理员改多个人(重置密码);张三叫李四改密码。其实原密码可传可不传;因为到这一步骤说明前面已经验证了你的密码是正确的;只需要传新密码即可。
这个标签select是必须要设置这两个属性;update只需要有interface方法对应就行了;
可以看到我们的数据库有这样的数据;单元测试试试可不可行;@Transactional注解在单元测试的效果是不会污染数据库;会进行一个事务的回滚;我们之前也只是查询;并不会对数据库污染;但是修改会;所以需要加这个注解。
这个delete标签也是只需要interface里的方法即可;
添加这里涉及一个问题;是传对象还是参数好?当然是对象;后续要用户表要添加字段只需要改实体类即可
返回影响的行数以及id:
insert标签:在执行插入操作时,使用数据库自动生成的键值,并将生成的键值存储到名为id的属性中。这个id是源自interface传入对象参数里的实体类属性。
我们前面的数据id是1、2、3;可以看到我们并没有设置这个对象的id值却打印出id为4;因为这个id是来自数据库的;如果你没有给id设置成主键就会报错的。
根据用户名进行模糊查询:查询到的结果可能包含多条信息;使用list接收。
注意:在写模糊查询时需要单引号;select * from 表名 where 列 like ‘%’;而这里占位符;你传的string;它又加了一对单引号;语法报错。构造出来语句:select * from userinfo where username likr ‘% ‘zhang’ %’;
concat函数:拼接作用;不限制参数;想拼接啥都行。concat(‘%’,‘zhang’,‘%’)把这些参数都拼接一块
如果遇到实体类和数据库字段如果名字不匹配;那怎么办?数据库的结果没办法映射到你的实体类上。
当我把实体类的属性名改成pwd;其它还是原来的样子;在查找password jdbc已经查到这个对象的内容;但是对比的时候发现pwd!=password;就映射失败。
上述情况使用resultMap:type表示当前字典是要映射哪个实体类。但是这个要一个一个写确实挺麻烦的
另一种解决方案:取别名;jdbc返回的是别名
上述我们解决单表查询的问题;现在来到多表的问题处理。
我们可以在实体层上建一个VO(VO是一种用于封装和传输数据的简单对象)存放两个表共同对应的属性
这里得注意一下;lombok的tostring方法是不包含父类属性的;我们自己重写tostring方法;当用lombok后你自己又在代码重写这个方法;那么将会以你的为主。(这里我们可以在生成的.class文件看到)
select a.*,u.username from articleinfo a left join userinfo u on u.id=a.uid where a.id=#{id}
a. * , u.username: 这是查询的字段列表;from articleinfo a: 这是查询的主要表,指定了主表的别名为"a。left jion userinfo u ON u.id = a.uid: 这是一个左连接(LEFT JOIN)操作,将"articleinfo"表与"userinfo"表进行关联。它基于两个表之间的条件关系进行匹配,具体条件是"userinfo"表中的"id"字段等于"articleinfo"表中的"uid"字段。别名"u"用于引用"userinfo"表。
WHERE a.id = #{id}: 这是一个查询条件,限定了"articleinfo"表中的"id"字段等于给定的参数值"#{id}"。