目录
- 一、Mybatis快速入门
- 1.创建Springboot工程,数据库表user,实体类User
- 2.引入Mybaties相关依赖
- 3.编写Sql语句
- 二、lombok
- 1.基本概念
- 2.使用方法
- 三、基础操作
- 1.环境准备
- a.数据库准备
- b.创建员工实体类Emp
- 数据类型对比
- 命名对比
- c.Mapper接口创建
- 2.删除操作
- 代码
- 测试
- 有返回值的删除
- 日志输出
- 参数占位符
- 3.添加操作
- 3.1 插入的sql语句
- 3.2 定义接口方法
- 3.3 测试
- 3.4 主键返回
- 4.修改操作
- 4.1 sql语句
- 4.2 接口实现
- 4.3测试
- 5.查询操作(根据id查询)
- 5.1 sql语句
- 5.2 接口文档
- 5.3 测试
- 5.4 数据封装解决命名不一致
- 6. 查询操作(条件查询)
- 6.1 sql语句
- 6.2 接口
- 6.3测试
- 6.4优化
- 四、动态sql
- 1.if
一、Mybatis快速入门
1.创建Springboot工程,数据库表user,实体类User
首先我们要先创建springboot工程,选择mysql和mybatis驱动
然后创建sql表,插入数据
create database spring;
use spring;
create table student(
id int auto_increment primary key ,
name char(20),
age int,
gender int,
phone char(20)
);
insert into student(name, age, gender, phone) value ('rosen',19,0,'10086');
insert into student(name, age, gender, phone) value ('rose',18,0,'10087');
实体类的创建,pojo包
package com.rosen.pojo;
public class User {
private Integer id;
private String name;
private Short age;
private Short gender;
private String phone;
public User() {
}
public User(Integer id, String name, Short age, Short gender, String phone) {
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
this.phone = phone;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Short getAge() {
return age;
}
public void setAge(Short age) {
this.age = age;
}
public Short getGender() {
return gender;
}
public void setGender(Short gender) {
this.gender = gender;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
2.引入Mybaties相关依赖
#驱动类名
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#连接数据库的url
spring.datasource.url=jdbc:mysql://localhost:3306/spring
#连接数据库的用户名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=123456
3.编写Sql语句
在mapper文件夹,相当于dao层,编写,UserMapper接口
@Mapper会自动进入容器,注释@Select来进行编写
package com.rosen.mapper;
import com.rosen.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface UserMapper {
@Select("select * from student")
public List<User> list();
}
在测试类中进行单元测试:
@SpringBootTest
class SpringMybatisApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
public void testUser()
{
List<User> userList=userMapper.list();
userList.stream().forEach(user -> {
System.out.println(user.toString());
});
}
}
二、lombok
我们看见User类,是不是除了属性,还要写get,set方法,tostring方法等,是不是很麻烦,也显得类很臃肿,我们只需要lombok给的注解,就可以简化
1.基本概念
Lombok是一个实用的Java类库,能够通过注解的形成自动生成构造器,并且自动化生成日志变量,简化开发
2.使用方法
因此一般直接在类上面加上@Data就可以
首先要maven导包
不用指定版本,springboot会自动选择
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
原有代码
package com.rosen.pojo;
public class User {
private Integer id;
private String name;
private Short age;
private Short gender;
private String phone;
public User() {
}
public User(Integer id, String name, Short age, Short gender, String phone) {
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
this.phone = phone;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Short getAge() {
return age;
}
public void setAge(Short age) {
this.age = age;
}
public Short getGender() {
return gender;
}
public void setGender(Short gender) {
this.gender = gender;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", gender=" + gender +
", phone='" + phone + '\'' +
'}';
}
}
注解修改,可以看到代码量减少非常多
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String name;
private Short age;
private Short gender;
private String phone;
}
lombok会在编译的时候,自动生成对应java代码
三、基础操作
1.环境准备
a.数据库准备
-- 部门管理
create table dept(
id int unsigned primary key auto_increment comment '主键ID',
name varchar(10) not null unique comment '部门名称',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间'
) comment '部门表';
insert into dept (id, name, create_time, update_time) values(1,'学工部',now(),now()),(2,'教研部',now(),now()),(3,'咨询部',now(),now()), (4,'就业部',now(),now()),(5,'人事部',now(),now());
-- 员工管理
create table emp (
id int unsigned primary key auto_increment comment 'ID',
username varchar(20) not null unique comment '用户名',
password varchar(32) default '123456' comment '密码',
name varchar(10) not null comment '姓名',
gender tinyint unsigned not null comment '性别, 说明: 1 男, 2 女',
image varchar(300) comment '图像',
job tinyint unsigned comment '职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师',
entrydate date comment '入职时间',
dept_id int unsigned comment '部门ID',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间'
) comment '员工表';
INSERT INTO emp
(id, username, password, name, gender, image, job, entrydate,dept_id, create_time, update_time) VALUES
(1,'jinyong','123456','金庸',1,'1.jpg',4,'2000-01-01',2,now(),now()),
(2,'zhangwuji','123456','张无忌',1,'2.jpg',2,'2015-01-01',2,now(),now()),
(3,'yangxiao','123456','杨逍',1,'3.jpg',2,'2008-05-01',2,now(),now()),
(4,'weiyixiao','123456','韦一笑',1,'4.jpg',2,'2007-01-01',2,now(),now()),
(5,'changyuchun','123456','常遇春',1,'5.jpg',2,'2012-12-05',2,now(),now()),
(6,'xiaozhao','123456','小昭',2,'6.jpg',3,'2013-09-05',1,now(),now()),
(7,'jixiaofu','123456','纪晓芙',2,'7.jpg',1,'2005-08-01',1,now(),now()),
(8,'zhouzhiruo','123456','周芷若',2,'8.jpg',1,'2014-11-09',1,now(),now()),
(9,'dingminjun','123456','丁敏君',2,'9.jpg',1,'2011-03-11',1,now(),now()),
(10,'zhaomin','123456','赵敏',2,'10.jpg',1,'2013-09-05',1,now(),now()),
(11,'luzhangke','123456','鹿杖客',1,'11.jpg',5,'2007-02-01',3,now(),now()),
(12,'hebiweng','123456','鹤笔翁',1,'12.jpg',5,'2008-08-18',3,now(),now()),
(13,'fangdongbai','123456','方东白',1,'13.jpg',5,'2012-11-01',3,now(),now()),
(14,'zhangsanfeng','123456','张三丰',1,'14.jpg',2,'2002-08-01',2,now(),now()),
(15,'yulianzhou','123456','俞莲舟',1,'15.jpg',2,'2011-05-01',2,now(),now()),
(16,'songyuanqiao','123456','宋远桥',1,'16.jpg',2,'2010-01-01',2,now(),now()),
(17,'chenyouliang','123456','陈友谅',1,'17.jpg',NULL,'2015-03-21',NULL,now(),now());
dept(部门)表:
emp(员工)表:
b.创建员工实体类Emp
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
private Integer id;
private String username;
private String password;
private String name;
private Short gender;
private String image;
private Short job;
private LocalDate entrydate;
private Integer deptId;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
数据类型对比
LocalDate对应date
LocalDateTime对应datetime
sql中:
-
date:日期:YYYY-MM-DD
-
datetime:日期+时间:YYYY-MM-DD HH:MM:SS
Java中: -
LocalDate:年月日
-
LocalTime:时分秒
-
LocalDateTime:年月日时分秒
命名对比
- sql中无下划线:username-username
- sql中由下划线:create_time-createTime:下划线的下一个字母大写就好
c.Mapper接口创建
@Mapper
public interface EmpMapper {
}
2.删除操作
代码
我们为了动态的删除,不可能只删除一个数据比如id=2,可以用#{id},变成动态的参数了
@Mapper
public interface EmpMapper {
@Delete("delete from emp where id=#{id}")
public void delete(Integer id);
}
测试
在测试单元测试,可以通过
@SpringBootTest
class SpringMybatisApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void test()
{
empMapper.delete(17);
}
}
有返回值的删除
@Mapper
public interface EmpMapper {
@Delete("delete from emp where id=#{id}")
public int delete(Integer id);
}
测试之后发现输出0,因为已经删除过了,所以再次删除失败返回0,反之返回1
@SpringBootTest
class SpringMybatisApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void test()
{
int delete=empMapper.delete(17);
System.out.println(delete);
}
}
日志输出
打开配置文件
配置日志,这样就可以在控制台上看到到底执行了哪些sql
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
可以看到,已经可以看到日志了,0代表没有真正删除
底下的带有?的sql语句代表预编译sql,执行的时候,参数和预编译sql一同发送给数据库
为什么不直接代替问号呢?
预编译sql优势
- 性能更高
- 更加安全(防止sql注入:通过操作输入的数据来修改事先定义好的sql语句,达到执行代码对服务器进行攻击的方法)
比如:```
输入账户:rosen 密码: 123456
select count(*) from emp where username='rosen' and password='123456'
成功则进入
但是如果账户:rosen,密码为:'or'1'='1这个,会发现
select count(*) from emp where username='rosen' and password=''or'1'='1'
这个表达式不管怎么样都是成立的因为1=1一直成立,所以sql注入
执行流程
因为缓存区如果有sql语句,那么就会直接拿取缓存,而每次如果id=不同的数字,那么缓存会一直没有,那么编译次数增加,那很慢,如果预编译sql,不会看id=多少,会在缓存找到,那么拼接后就会找到,那么不久加快了!
参数占位符
可以看出$,会产生sql注入,而#则不会,一般使用#
3.添加操作
3.1 插入的sql语句
id:自增长,不需要插入
密码又默认:不需要插入
create time和uodate_tima需要当前时间
insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)
values('Tom','汤姆',1,'1.jpg',1,'2005-01-01',1,now(),now());
3.2 定义接口方法
我们还利用删除时候创建的接口
我们会发现,如果还是像delete参数是自己设置的变量的话,那么要设置很多变量,所有我们传入一个对应类,占位符里面是这个类的属性名
@Mapper
public interface EmpMapper {
@Delete("delete from emp where id=#{id}")
public int delete(Integer id);
@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)" + "values(#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime});")
public void insert(Emp emp);
}
3.3 测试
@SpringBootTest
class SpringMybatisApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testInsert()
{
//构造员工对象
Emp emp=new Emp();
emp.setUsername("Tom");
emp.setName("汤姆");
emp.setImage("1.jpg");
emp.setGender((short)1);
emp.setJob((short)1);
emp.setEntrydate(LocalDate.of(2000,1,1));
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
emp.setDeptId(1);
//新增操作
empMapper.insert(emp);
}
}
测试成功
3.4 主键返回
在添加成功的时候,需要获取插入数据库的主键
如:添加套餐数据的时候,需要添加套餐,还有一个表是菜品,套餐对应菜品,需要知道套餐是说明才能选择菜品,因此需要返回主键
代码实现
在插入方法上面加上注解@Options
- KeyProperty:代表插入到这个类的哪一个属性
- useGeneratedKeys = true:是否返回主键
public interface EmpMapper {
@Delete("delete from emp where id=#{id}")
public int delete(Integer id);
//获取主键,会封装到对象的id属性中,true是获取主键
@Options(keyProperty = "id",useGeneratedKeys = true)
@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)" +
"values(#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime});")
public void insert(Emp emp);
}
测试
@Test
public void testInsert()
{
//构造员工对象
Emp emp=new Emp();
emp.setUsername("Tom1");
emp.setName("汤姆3");
emp.setImage("1.jpg");
emp.setGender((short)1);
emp.setJob((short)1);
emp.setEntrydate(LocalDate.of(2000,1,1));
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
emp.setDeptId(1);
//新增操作
empMapper.insert(emp);
System.out.println(emp.getId());
}
4.修改操作
4.1 sql语句
update emp set username = '',name='',gender='',image='',job='',entrydate='',dept_id='',update_time='' where id=1;
4.2 接口实现
@Update("update emp set username =#{username},name=#{name},gender=#{gender},image=#{image},job=#{job},entrydate=#{entrydate},dept_id=#{deptId},update_time=#{updateTime} where id=#{id};")
public void update(Emp emp);
4.3测试
@Test
public void testUpdate()
{
//构造员工对象
Emp emp=new Emp();
emp.setId(18);
emp.setUsername("Tom6");
emp.setName("汤姆6");
emp.setImage("1.jpg");
emp.setGender((short)1);
emp.setJob((short)1);
emp.setEntrydate(LocalDate.of(2000,2,2));
emp.setUpdateTime(LocalDateTime.now());
emp.setDeptId(1);
//新增操作
empMapper.update(emp);
System.out.println(emp.getId());
}
5.查询操作(根据id查询)
5.1 sql语句
select * from emp where id=20;
5.2 接口文档
查询是由返回值的,返回对象就行
@Select("select * from emp where id=#{id};")
public Emp getById(Integer id);
5.3 测试
@Test
public void select(){
Emp emp = empMapper.getById(20);
System.out.println(emp);
}
但是发现:有三个值没有进行封装?为什么呢
如果数据库和实体类的字段名一致因此mybatis会自动封装
如何解决问题——数据封装
5.4 数据封装解决命名不一致
方案1
sql起别名
方案2:
注解起别名
方案3
配置文件设置驼峰命名
mybatis.configuration.map-underscore-to-camel-case=true
成功展示
6. 查询操作(条件查询)
6.1 sql语句
包括模糊查询,时间排序,范围查询
select *from emp where name like '%张%' and
gender =1 and entrydate between '2010-01-01' and '2020-01-02' order by update_time desc ;
6.2 接口
因为模糊查询里面的引号不能使用注入,因此用$进行拼接处理。
@Select("select *from emp where name like '%${name}%' and gender =#{gender} and" +
" entrydate between #{begin} and #{end} order by update_time desc ")
public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);
6.3测试
@Test
public void select2()
{
List<Emp> empList = empMapper.list("张", (short) 1, LocalDate.of(2010, 1, 1), LocalDate.of(2020, 1, 1));
System.out.println(empList);
}
发现欸,这样用$符号,还是有问题啊,会sql注入,不安全,咋办呢,用sql的函数
6.4优化
contact字符串连接
@Select("select *from emp where name like concat('%',#{name},'%') and gender =#{gender} and" +
" entrydate between #{begin} and #{end} order by update_time desc ")
public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);
可以看到已经成功了
xml操作部分和动态sql,javaweb进行部分的描述,不在进行赘述
四、动态sql
xml配置在之前的非进阶学习过,直接给例子进行动态sql
1.if
可能多个属性,但是只用传输过来的条件
在resource创建对应的xml文件
com/rosen/mapper/EmpMapper.xml
注意名字要一样
- 上面是固定的
- namespace=“com.rosen.mapper.EmpMapper”:代表接口的位置
- id=‘list’:方法名
- resulttype:返回类型
- 因为如果是用where的话,可能多一个and或多一个where,因此<where>会自动去除
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rosen.mapper.EmpMapper">
<select id="list" resultType="com.rosen.pojo.Emp">
select *
from emp
<where>
<if test="name!=null">
name like concat('%',#{name},'%')
</if>
<if test="gender!=null">
and gender=#{gender}
</if>
<if test="begin!=null and end!=null">
and entrydate between #{begin} and #{end}
</if>
order by update_time desc
</where>
</select></mapper>
```
## 2.foreach
删除对应数据,用foreach实现动态sql
+ collections:参数
+ item:拿出来的实体:自己取名
+ separator:以什么分割
+ open:左边拼接
+ close:右边拼接
```xml
<mapper namespace="com.rosen.mapper.EmpMapper">
<delete id="delete" >
delete from where emp id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
</select>
</mapper>