目录
方案二:通过mybatis中的@Results,@Result注解手动映射封装
方案三:Mybatis驼峰命名自动映射的开关 a-column =》 aColumn
环境准备
①Emp.sql
-- 部门管理
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());
②Emp.java
package com.itheima.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.time.LocalDateTime;
@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;
}
一、删除
mybatis的参数占位符#{}
①Mapper层
package com.itheima.mapper;
import org.apache.ibatis.annotations.*;
@Mapper
public interface EmpMapper {
// 根据ID删除数据
@Delete("delete from emp where id=#{id}")
// public void deltte(Integer id);
//返回值代表此次操作影响的记录数
public int delete(Integer id);
}
②测试类
package com.itheima;
import com.itheima.mapper.EmpMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testDelete(){
int delete= empMapper.delete(17);
System.out.println(delete);
}
}
③预编译SQL(查看mybatis日志)
1.性能
在JAVA项目中,要想执行SQL语句,需要链接上数据库,然后把SQL语句发送给数据库服务器,数据库服务器并不是立即执行SQL语句,而是先对SQL语句进行语法解析检查=》优化SQL=》编译SQL(编译为可执行函数)=》执行SQL语句
为了提高效率,数据库服务器会把优化编译的SQL缓存起来,下次再执行SQL,会先检查是否已有SQL缓存
然而因为三条语句的id不一致,数据库服务器会三次执行编译3次
如果采用预编译的SQL,不会把字段值直接拼接到SQL语句,而是使用?占位符,把SQL语句和字段值发送给数据库服务器,先对SQL语句进行语法解析检查=》优化SQL=》编译SQL(编译为可执行函数)=》执行SQL语句
执行SQL语句的时候,会用参数值替代掉占位符
为了提高效率,数据库服务器会把优化编译的SQL缓存起来,下次再执行SQL,会先检查是否已有SQL缓存
三条语句的id不一致,但是缓存中的SQL语句是一致的,数据库服务器只会执行编译1次
2.安全
预编译防止SQL注入的原理就是将敏感字符转义成普通字符
④总结
二、新增
当传递参数有多个的时候,可以用实体类来传递,占位符里写的是实体类的属性名(驼峰命名)
①Mapper层
package com.itheima.mapper;
import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.*;
@Mapper
public interface EmpMapper {
@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);
}
②测试类
package com.itheima;
import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDate;
import java.time.LocalDateTime;
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testInsert(){
//构造员工对象
Emp emp=new Emp();
emp.setUsername("Tom");
emp.setName("tom");
emp.setImage("1.jpg");
emp.setGender((short)1);
emp.setJob((short)1);
emp.setEntrydate(LocalDate.of(200,1,1));
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
emp.setDeptId(1);
//执行新增员工信息操作
empMapper.insert(emp);
}
}
③结果
④新增(主键返回)
1.Mapper层
package com.itheima.mapper;
import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.*;
@Mapper
public interface EmpMapper {
@Options(useGeneratedKeys = true,keyProperty = "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);
}
需要在插入后获得返回的主键,在方法上面加Options注解,指定两个属性,userGeneratedKeys=true代表我们要返回的主键,keyProperty代表把返回的主键往emp的Id属性封装
2.测试类
package com.itheima;
import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDate;
import java.time.LocalDateTime;
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testInsert(){
//构造员工对象
Emp emp=new Emp();
emp.setUsername("Tom4");
emp.setName("tom4");
emp.setImage("1.jpg");
emp.setGender((short)1);
emp.setJob((short)1);
emp.setEntrydate(LocalDate.of(200,1,1));
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
emp.setDeptId(1);
//执行新增员工信息操作
empMapper.insert(emp);
System.out.println(emp.getId());
}
}
⑤总结![](https://i-blog.csdnimg.cn/blog_migrate/11fdc7b218999ca18cfe2c74f5b05891.png)
三、更新(修改)
案例
业务
第一步,根据主键ID查询数据回显展示
第二步,在界面数据修改完毕后点击保存,此时进行修改操作,(根据主键ID修改,因为他不会改变)
点击编辑,会根据当前这条数据的主键Id来查询数据,并且将该记录回显展示出来,此时我们就可以在原有数据的基础上对其进行修改,操作完毕点击保存按钮,就会将该表单数据提交到服务端,最终修改表中的字段值
根据主键修改员工信息
①Mapper层
package com.itheima.mapper;
import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.*;
@Mapper
public interface EmpMapper {
@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);
}
②测试类
package com.itheima;
import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDate;
import java.time.LocalDateTime;
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
//更新员工
@Test
public void testUpdate(){
//构造员工对象
Emp emp=new Emp();
emp.setId(18);
emp.setUsername("Tom1");
emp.setName("Tom1");
emp.setImage("1.jpg");
emp.setGender((short)1);
emp.setJob((short)1);
emp.setEntrydate(LocalDate.of(2000,1,1));
emp.setUpdateTime(LocalDateTime.now());
//执行更新员工操作
empMapper.update(emp);
}
}
四、查询
(一)根据主键ID查询数据回显展示
①Mapper层
package com.itheima.mapper;
import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.*;
@Mapper
public interface EmpMapper {
//根据ID查询员工信息
@Select("select * from emp where id=#{id};")
public Emp getById(Integer id);
}
②测试类
package com.itheima;
import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDate;
import java.time.LocalDateTime;
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
//根据ID查询员工信息
@Test
public void testGetById(){
Emp emp=empMapper.getById(20);
System.out.println(emp);
}
}
③解决数据无法封装的问题
方案一:给字段起别名,让别名与实体类属性一致
修改Mapper层的接口中的SQL即可
public void update(Emp emp);
//根据ID查询员工信息
@Select("select id, username, password, name, gender, image, job, entrydate, dept_id deptId, create_time createTime, " +
"update_time updateTime from emp where id=#{id};")
public Emp getById(Integer id);
结果![](https://i-blog.csdnimg.cn/blog_migrate/83d49ca7f027715ca93260ebb2716463.png)
方案二:通过mybatis中的@Results,@Result注解手动映射封装
修改Mapper层的接口中的SQL即可
//根据ID查询员工信息
@Results({@Result(column = "dept_id",property ="deptID"),
@Result(column = "create_time",property ="createTime"),
@Result(column = "update_time",property ="updateTime")
})
@Select("select * from emp where id=#{id}")
public Emp getById(Integer id);
@Results注解中的Values是@Result数组,每一个@Result表示映射一个字段和属性,column是表中的字段名,property是类中的属性名
结果![](https://i-blog.csdnimg.cn/blog_migrate/823164815a9fba4ca66d57c460794d9e.png)
方案三:Mybatis驼峰命名自动映射的开关 a-column =》 aColumn
前提:表中字段名带下划线分割,实体中的属性名是驼峰命名
将字段名带下划线的自动封装为实体类中的驼峰式属性
在application.properties中配置Mybatis驼峰命名自动映射的开关,原来的Mapper层接口不需要修改
加入以下代码
#开启Mybatis驼峰命名自动映射的开关
mybatis.configuration.map-underscore-to-camel-case=true
结果
总结
思考
思考:为什么Java中实体类的属性名不更改为和数据库一样的下划线呢?
约定俗成,Java中实体类的属性名应该是驼峰命名
思考:为什么数据库中字段名不更改为与Java相同的驼峰命名呢?
数据库不区分大小写,在数据库用不了驼峰,如果你Java用了驼峰就映射不上
思考:数据库字段为什么要用下划线命名 ?
数据库字段使用下划线命名是一种规范化命名方法,目的是使数据命名更加清晰,易读性更强,并且易于被程序识别。
(二)根据条件查询数据回显展示
①Mapper层
package com.itheima.mapper;
import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.*;
import java.time.LocalDate;
import java.util.List;
@Mapper
public interface EmpMapper {
//条件查询员工信息
@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);
}
entryDate是一个范围,而属性entrydate是一个值,无法封装范围,故而直接用参数传递
'%#{name}%' 单引号中的%表示要进行模糊匹配,而#{name}进行预编译后会被?取代,?是不能被放入单引号内的,
所以要把#改为$,$是字符串拼接,直接将传递过来的name和两个字符串%拼接起来就可以了
②测试类
package com.itheima;
import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testList(){
List<Emp> empList= empMapper.list("z",(short)1,LocalDate.of(2010,1,1),LocalDate.of(2020,1,1));
System.out.println(empList);
}
}
③结果
④Concat()
然而使用$拼接字符串存在安全问题并且效率不高
故而可以使用mybatis中的concat()函数对字符进行拼接
concat('%',#{name},'%') ,这样#{name}就不会出现在单引号内
1.Mapper层
package com.itheima.mapper;
import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.*;
import java.time.LocalDate;
import java.util.List;
@Mapper
public interface EmpMapper {
@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);
}
2.结果
这里生成的就是预编译的SQL
五、XML映射文件(配置文件)
源文件放在java中,而配置文件放在resources中
①EmpMapper.xml
<?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.itheima.mapper.EmpMapper">
<!-- resultType:单条记录所封装的类型-->
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp where name like concat('%',#{name},'%') and gender=#{gender} and
entrydate between #{begin} and #{end} order by update_time desc
</select>
</mapper>
②Mapper层
package com.itheima.mapper;
import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.*;
import java.time.LocalDate;
import java.util.List;
@Mapper
public interface EmpMapper {
public List<Emp> list(String name, Short gender, LocalDate begin,LocalDate end);
}
③测试类
package com.itheima;
import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testList(){
List<Emp> empList= empMapper.list("z",(short)1,LocalDate.of(2010,1,1),LocalDate.of(2020,1,1));
System.out.println(empList);
}
}
④思考
mapper映射文件还有一个好处,修改sql语句不用重启项目
在方法上实现动态的条件查询就会使接口过于臃肿
如果操作语句多了,直接也在注解上面比较混乱
如果要做手动映射封装实体类的时候 xml方便,项目中会常用
用xml,因为查询的条件会变化,直接写在注解里面的话会使接口过于臃肿
这两个各自找各自对应的,原来是注解绑定,现在是通过路径和方法名绑定
多条件查询要写动态sql用映射文件比较合适,简单的可以直接注解方式
终于找到问题了,xml里的sql语句不能拼接,只能是一长条,运行才不报错
执行list()方法时,根据全限定类名找到对应的namespace ,再找到id为这个方法的SQL语句就可以执行了