题外话:预计进度失误,发现这个知识点也是有必要总结一下,争取双更o(╥﹏╥)o
为方便大家跟着笔者做出来,每步代码齐全,请放心食用!
Generator生成器
上一节我们手写了mybaties框架实现查询,这一节我们使用generator生成器集成mybaties框架,这个想必大家都会,简单复习一下
1.建张数据库表(附sql语句)
drop table if exists `ebook`;
create table `ebook` (
`id` bigint not null comment 'id',
`name` varchar(50) comment '名称',
`category1_id` bigint comment '分类1',
`category2_id` bigint comment '分类2',
`description` varchar(200) comment '描述',
`cover` varchar(200) comment '封面',
`doc_count` int not null default 0 comment '文档数',
`view_count` int not null default 0 comment '阅读数',
`vote_count` int not null default 0 comment '点赞数',
primary key (`id`)
) engine=innodb default charset=utf8mb4 comment='电子书';
insert into `ebook` (id, name, description) values (1, 'Spring Boot 入门教程', '零基础入门 Java 开发,企业级应用开发最佳首选框架');
insert into `ebook` (id, name, description) values (2, 'Vue 入门教程', '零基础入门 Vue 开发,企业级应用开发最佳首选框架');
insert into `ebook` (id, name, description) values (3, 'Python 入门教程', '零基础入门 Python 开发,企业级应用开发最佳首选框架');
insert into `ebook` (id, name, description) values (4, 'Mysql 入门教程', '零基础入门 Mysql 开发,企业级应用开发最佳首选框架');
insert into `ebook` (id, name, description) values (5, 'Oracle 入门教程', '零基础入门 Oracle 开发,企业级应用开发最佳首选框架');
2.Pom文件引入依赖(放在</plugins>上面,版本如果报错就换你有的)
<!-- mybatis generator 自动生成代码插件 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.0</version>
<configuration>
<configurationFile>src/main/resources/generator/generator-config.xml</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
</dependencies>
</plugin>
application.properties中配置xml路径
mybatis.mapper-locations=classpath:/mapper/**/*.xml
3.在resource下新建generator包,命名generator-config.xml文件,代码如下,按照自己domain类,mapperxml,mapper类的位置设置,jdbcConnection改成自己数据库
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="Mysql" targetRuntime="MyBatis3" defaultModelType="flat">
<!-- 自动检查关键字,为关键字增加反引号 -->
<property name="autoDelimitKeywords" value="true"/>
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<!--覆盖生成XML文件-->
<plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />
<!-- 生成的实体类添加toString()方法 -->
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<!-- 不生成注释 -->
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/book?serverTimezone=Asia/Shanghai"
userId="admin"
password="123456">
</jdbcConnection>
<!-- domain类的位置 -->
<javaModelGenerator targetProject="src\main\java"
targetPackage="com.zhy.ebook.demos.domain"/>
<!-- mapper xml的位置 -->
<sqlMapGenerator targetProject="src\main\resources"
targetPackage="mapper"/>
<!-- mapper类的位置 -->
<javaClientGenerator targetProject="src\main\java"
targetPackage="com.zhy.ebook.demos.mapper"
type="XMLMAPPER"/>
<table tableName="ebook"/>
</context>
</generatorConfiguration>
4.点开右边工具栏的maven,运行生成器
接着你就可以发现多了四个文件分别是domain下的Ebook EbookExample mapper下的EbookMapper类和mapper下的EbookMapper.xml,你可能会好奇EbookExample的用途,接着往下看吧
5.需求是依然是查询图书列表,简单进行一下Service,Controller的处理就ok了,小伙伴可以运行一下
@Service
public class EbookService {
@Resource
//Resource和Autowired都可以将mapper注入
private EbookMapper EbookMapper;
public List<Ebook> list(){
return EbookMapper.selectByExample(null);
}
}
@RestController
@RequestMapping("/ebook")
public class EbookController {
@Resource
private EbookService EbookService;
@GetMapping("/list")
public List<Ebook> EbookList(){
return EbookService.list();
}
}
模糊查询
我们想采用关键词搜索某条数据,比如我们用图书名字(name)作为查询条件,那么我们应该怎么做?
1.前端发出的请求携带用户输入的name参数,以name是Spring为例
GET http://localhost:8080/ebook/list?name=Spring
2.Controller层接收String参数
public List<Ebook> EbookList(String name)
List<Ebook> list=EbookService.list(name);
3.Service层用EbookExample类自带的createCriteria函数进行模糊查询
@Service
public class EbookService {
@Resource
//Resource和Autowired都可以将mapper注入
private EbookMapper EbookMapper;
public List<Ebook> list(String name){
//ebookExample相当于一个查询条件的对象,之前写为null代表*
EbookExample ebookExample = new EbookExample();
EbookExample.Criteria criteria = ebookExample.createCriteria();
//以上两句为固定方法用于模糊查询
//查询
criteria.andNameLike("%"+name+"%");
}
return EbookMapper.selectByExample(ebookExample);
}
到这为止就结束了,是不是特别简单,测试一下,结果确实是只查询出一条语句
封装请求参数
上述我们只是带了个name参数,如果我们还想带上id,docCount等是不是写起来特别麻烦,所以我们将所有的请求参数封装成一个类。
1.新建个req包,新建个EbookReq类,我们将Ebook实体类进行复制,比如我们只想传递id,name参数,就把别的参数去掉,如下
public class EbookReq {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
sb.append("Hash = ").append(hashCode());
sb.append(", id=").append(id);
sb.append(", name=").append(name);
sb.append("]");
return sb.toString();
}
}
2.Controller层接收EbookReq对象,有了前面的铺垫想必你可以很轻松的写出来
public List<Ebook> EbookList(EbookReq req)
List<Ebook> list=EbookService.list(req);
3.Service层进行相应改动就ok了,测试结果也是相同的
public class EbookService {
@Resource
//Resource和Autowired都可以将mapper注入
private EbookMapper EbookMapper;
public List<Ebook> list(EbookReq req){
//ebookExample相当于一个查询条件的对象,之前写为null代表*
EbookExample ebookExample = new EbookExample();
EbookExample.Criteria criteria = ebookExample.createCriteria();
//以上两句为固定方法用于模糊查询
//查询
criteria.andNameLike("%"+req.getName()+"%");
}
return EbookMapper.selectByExample(ebookExample);
}
封装返回参数(二次封装)
首先前端发出请求,我们只有直接返回数据和报错两种可能,但是我们想给前端一些统一的提示,如果请求成功,我们就success并且返回data数据,如果请求失败,我们也给予他一些错误提示
新建包名为Resp,新建CommonResp类,模版如下。(想必小伙伴也能看出取名的意思,Request是请求参数,Response是响应数据)
public class CommonResp<T> {
/**
* 业务上的成功或失败
*/
private boolean success = true;
/**
* 返回信息
*/
private String message;
/**
* 返回泛型数据,自定义类型
*/
private T content;
public boolean getSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getContent() {
return content;
}
public void setContent(T content) {
this.content = content;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("ResponseDto{");
sb.append("success=").append(success);
sb.append(", message='").append(message).append('\'');
sb.append(", content=").append(content);
sb.append('}');
return sb.toString();
}
}
稍微修改一下Controller层,就是将原本的返回值赋值为resp的setContent属性
@RestController
@RequestMapping("/ebook")
public class EbookController {
@Resource
private EbookService EbookService;
@GetMapping("/list")
public CommonResp EbookList(EbookReq req){
CommonResp<List<Ebook>> resp=new CommonResp<>();
List<Ebook> list=EbookService.list(req);
resp.setContent(list);
return resp;
}
}
运行一遍,可以看到success,message这些都是我们新加的属性
现在我们思考一个问题,我们能够将整个数据库对应的实体类ebook直接暴露出来吗,这样一些敏感字段很容易被爬虫获取导致数据泄露,还有前端不需要的字段我们也不需要传给他,我们为了保护实体类,应该专门在封装一个返回类。
1.我们这里的返回类放到Resp软件包下直接复制ebook类未更改(小伙伴也可以自行更改尝试)名称为EbookResp,这里就无需贴图了,与ebook类内容一致
2.修改Controller层将Ebook改为EbookResp
@GetMapping("/list")
public CommonResp EbookList(EbookReq req){
//用CommonResp对返回信息进行二次封装,主要是添加响应成功/失败的提示等
CommonResp<List<EbookResp>> resp=new CommonResp<>();
//此时里面的Service返回的对象就变成了EbookResp
List<EbookResp> list=EbookService.list(req);
resp.setContent(list);
return resp;
}
3.修改Service层,每一步都写了注释,主要是进行一个集合的复制过程
public List<EbookResp> list(EbookReq req){
//ebookExample相当于一个查询条件的对象,之前写为null代表*
EbookExample ebookExample = new EbookExample();
EbookExample.Criteria criteria = ebookExample.createCriteria();
//以上为固定方法用于模糊查询
criteria.andNameLike("%"+req.getName()+"%");
List<Ebook> ebookList = EbookMapper.selectByExample(ebookExample);
//我们现在要做的是将list<Ebook>转化成list<EbookResp>,也就是进行集合的复制
//创建一个返回结果的实例
List<EbookResp> respList=new ArrayList<>();
//循环集合中的每个对象进行复制
for(Ebook ebook :ebookList){
//一个ebookResp对应接收一个ebook
EbookResp ebookResp = new EbookResp();
// ebookResp.setId(ebook.getId());
//如果按以上做法手写完成每个属性的拷贝是非常麻烦的,BeanUtils工具可以直接完成对象的复制
BeanUtils.copyProperties(ebook,ebookResp);
//将对象一一添加到集合里
respList.add(ebookResp);
}
return respList;
}
运行一遍没有问题
我们思考每次写Service函数都要写一遍集合浅克隆或者对象浅克隆是不是太过麻烦,我们直接将它抽出来形成一个工具类,执行浅克隆就调用一次,就很方便
新建软件包utils,类名为CopyUtil,里面有两个方法,对象复制和列表复制
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
public class CopyUtil {
/**
* 单体复制
*/
public static <T> T copy(Object source, Class<T> clazz) {
if (source == null) {
return null;
}
T obj = null;
try {
obj = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
BeanUtils.copyProperties(source, obj);
return obj;
}
/**
* 列表复制
*/
public static <T> List<T> copyList(List source, Class<T> clazz) {
List<T> target = new ArrayList<>();
if (!CollectionUtils.isEmpty(source)){
for (Object c: source) {
T obj = copy(c, clazz);
target.add(obj);
}
}
return target;
}
}
修改一下Service层代码,将整个复制过程改为调用工具类
public List<EbookResp> list(EbookReq req){
//ebookExample相当于一个查询条件的对象,之前写为null代表*
EbookExample ebookExample = new EbookExample();
EbookExample.Criteria criteria = ebookExample.createCriteria();
//以上为固定方法用于模糊查询
criteria.andNameLike("%"+req.getName()+"%");
List<Ebook> ebookList = EbookMapper.selectByExample(ebookExample);
//我们现在要做的是将list<Ebook>转化成list<EbookResp>,也就是进行集合的复制
List<EbookResp> list=CopyUtil.copyList(ebookList,EbookResp.class);
return list;
}
okk,最后运行一遍,结果也没有问题
补充一下,如何传参数就进行模糊查询,不传参数就是查询整个列表,动态的sql查询
只需要加一个if判断我们要查询的这个参数是不是空
if(!ObjectUtils.isEmpty(req.getName())) {
criteria.andNameLike("%" + req.getName() + "%");
}