上篇博客 https://blog.csdn.net/weixin_41750142/article/details/113869062 中对 brand 品牌表 进行了增删改查,这篇博客将记录对 相册、规格、模板、参数 的增删改查的实战,记录下自己的思路和代码后,再与老师给的作比较和更正。
几个表结构如下所示:
- 相册 tb_allbum:
- 模板 tb_template:
- 规格 tb_spec:
- 参数 tb_para:
可以看到,模板表通过 template_id 属性和 规模表、参数表关联起来了。
一、相册管理
照例提供通用 Mapper 的继承接口:
public interface AlmubMapper extends Mapper<Album> {
}
在 service 层提供接口:
public interface AlbumService {
/* 查询所有相册信息 */
public List<Album> findAll();
/* 按照 ID 查询 */
public Album findById(Long id);
/* 添加相册信息 */
public void add(Album album);
/* 删除相册信息 */
public void delete(Long id);
/* 修改相册信息 */
public void update(Album album);
/* 分页查询 */
public PageInfo findPage(int page,int size);
/* 条件查询 */
public List<Album> findList(Album album);
/* 按条件分页查询 */
public PageInfo findPage(int page,int size,Album album);
}
实现类:
@Service
public class AlbumServiceImpl implements AlbumService {
@Autowired
AlbumMapper albumMapper;
@Override
public List<Album> findAll() {
return albumMapper.selectAll();
}
@Override
public Album findById(Long id) {
return albumMapper.selectByPrimaryKey(id);
}
@Override
public void add(Album album) {
albumMapper.insert(album);
}
@Override
public void delete(Long id) {
albumMapper.deleteByPrimaryKey(id);
}
@Override
public void update(Album album) {
albumMapper.updateByPrimaryKey(album);
}
/* 分页查询 */
@Override
public PageInfo findPage(int page, int size) {
PageHelper.startPage(page, size);
List<Album> albums = albumMapper.selectAll();
return new PageInfo(albums);
}
/* 把构建条件的逻辑提取出来 */
public static Example createExample(Album album) {
// 构建条件
Example example = new Example(Album.class);
Example.Criteria criteria = example.createCriteria();
if (album != null) {
// 对 id、title、image、imageItems 属性进行模糊查询
if (!StringUtils.isEmpty(album.getId())) {
criteria.andEqualTo("id", album.getId());
}
if (!StringUtils.isEmpty(album.getTitle()))
criteria.andLike("title", "%" + album.getTitle() + "%");
if (!StringUtils.isEmpty(album.getImage()))
criteria.andLike("image", "%" + album.getImage() + "%");
criteria.andLike("title", "%" + album.getTitle() + "%");
if (!StringUtils.isEmpty(album.getImageItems()))
criteria.andLike("image_items", "%" + album.getImageItems() + "%");
}
return example;
}
/* 条件查询 */
@Override
public List<Album> findList(Album album) {
return albumMapper.selectByExample(createExample(album));
}
/* 分页条件查询 */
@Override
public PageInfo findPage(int page, int size, Album album) {
// 分页
PageHelper.startPage(page, size);
List albums = albumMapper.selectByExample(createExample(album));
return new PageInfo(albums);
}
}
控制层:
@CrossOrigin
@RestController
@RequestMapping(value = "/album")
public class AlbumController {
@Autowired
private AlbumService albumService;
/* 查询所有相册信息 */
@GetMapping
public Result<List<Album>> findAll() {
List albums = albumService.findAll();
return new Result<List<Album>>(true, StatusCode.OK, "查找成功", albums);
}
/* 根据 ID 查询信息 */
@GetMapping("/{id}")
public Result<Album> findById(@PathVariable(value = "id")Long id){
Album album=albumService.findById(id);
return new Result<>(true,StatusCode.OK,"查询成功",album);
}
/* 添加相册信息 */
@PostMapping
public Result add(@RequestBody Album album) {
albumService.add(album);
return new Result(true, StatusCode.OK, "插入成功");
}
/* 删除相册信息 */
@DeleteMapping("/{id}")
public Result delete(@PathVariable(value = "id") Long id) {
albumService.delete(id);
return new Result(true, StatusCode.OK, "删除成功");
}
/* 更新 */
@PutMapping("/{id}")
public Result update(@PathVariable(value = "id") Long id, @RequestBody Album album) {
album.setId(id);
albumService.update(album);
return new Result(true, StatusCode.OK, "更新成功");
}
/* 分页查询 */
@GetMapping("/search/{page}/{size}")
public Result<PageInfo<Album>> findPage(@PathVariable(value = "page") int page,
@PathVariable(value = "size") int size) {
PageInfo<Album> pageInfo = albumService.findPage(page, size);
return new Result<>(true, StatusCode.OK, "分页查询成功", pageInfo);
}
/* 条件查询 */
@PostMapping("/search")
public Result<List<Album>> findList(@RequestBody Album album) {
List<Album> albums = albumService.findList(album);
return new Result<>(true, StatusCode.OK, "条件查询成功", albums);
}
/* 分页条件查询 */
@PostMapping("/search/{page}/{size}")
public Result<PageInfo<Album>> findPage(@PathVariable(value = "page") int page,
@PathVariable(value = "size") int size,
@RequestBody Album album) {
PageInfo<Album> pageInfo = albumService.findPage(page, size, album);
return new Result<>(true, StatusCode.OK, "分页条件查询成功", pageInfo);
}
}
方法都进行测试了,都是没问题的。
二、模板管理
提供通用 Mapper 的继承接口(要注意声明泛型,不然是要报错的):
public interface TemplateMapper extends Mapper<Template> {
}
在 service 层提供的接口 和 实现类,与相册管理里的逻辑是相似的,在这里只展示构建条件的代码 :
/* 封装构建条件的逻辑 */
public Example createExample(Template template) {
Example example = new Example(Template.class);
Example.Criteria criteria = example.createCriteria();
if(template!=null){
if (StringUtil.isEmpty(template.getName()))
criteria.andLike("name", "%" + template.getName() + "%");
if (!StringUtils.isEmpty(template.getParaNum()))
criteria.andEqualTo("paraNum");
if (!StringUtils.isEmpty(template.getSpecNum()))
criteria.andEqualTo("specNum");}
return example;
}
}
控制类也省略。
三、规模管理
提供通用 Mapper 的继承接口:
public interface SpecMapper extends Mapper<Spec> {
}
在 service 层提供接口,这里比以上 service 层 增加了个 根据Template ID 和 Category ID 查询规模信息的方法:
/* 根据 template_ID 查询 */
public List<Spec> findByTempId(Integer id);
/* 根据 category_ID 查询 */
public List<Spec> findByCateGory(Integer id);
实现类主要展示这两个方法的实现:
/* 根据 Template Id 查询 */
@Override
public List<Spec> findByTempId(Integer id) {
Example example = new Example(Spec.class);
Example.Criteria criteria = example.createCriteria();
// select * from tb_spec,tb_template where template_id=tb_template.id;
// 只需要在 tb_spec 表中查即可......
criteria.andEqualTo("templateId", id);
return specMapper.selectByExample(example);
}
@Override
public List<Spec> findByCateGory(Integer id) {
/*
Example example=new Example(Spec.class);
Example.Criteria criteria=example.createCriteria();
// select * from tb_spec,tb_category where template_id=
// (select template_id from tb_category where templated_id=id;)
// 先通过 id 查到 category 表里的记录,因为是 id 所有只会有一个对象
Category category= categoryMapper.selectByPrimaryKey(id);
// 通过对象的 templated_id 属性查询
criteria.andEqualTo("templateId",category.getTemplateId());
return specMapper.selectByExample(example);
*/
// 考虑不用 example
Category category=categoryMapper.selectByPrimaryKey(id);
Spec spec=new Spec();
spec.setTemplateId(category.getTemplateId());
return specMapper.select(spec);
}
控制层也省略。
四、参数管理
提供通用 Mapper 的继承接口 :
public interface ParaMapper extends Mapper<Para> {
}
在 service 层接口提供根据 Category ID 查询信息的方法:
/* 根据 Category ID 查找 */
public List<Para> findByCateId(Integer id);
}
实现:
@Override
public List<Para> findByCateId(Integer id) {
// 先根据 Category ID 查出 template_id
Category category = categoryMapper.selectByPrimaryKey(id);
Para para=new Para();
para.setTemplateId(category.getTemplateId());
return paraMapper.select(para);
}
控制层略,所有具体代码见 github,地址https://github.com/betterGa/ChangGou。
五、总结
在敲这些代码后,和老师给的相比较和更正的过程中,发现自己出现了以下问题:
(1)注意 MVC 的调用顺序,是 controller 层调用 service 层,service 层调用 dao 层。
Controller 层提供访问路径、传入参数、返回值,@CrossOrigin 允许跨域注解是用在控制层的;Service 层需要提供接口,规范功能,然后实现它,@Service 层是用在实现类上,而不是用在接口上;Dao 层只需要提供接口,并继承通用 Mapper 即可,注意通用 Mapper 需要声明泛型类型。
(2)还要注意,service 层只是调用 dao 层的方法,不需要其他逻辑,比如说,更新操作,需要在访问变量中提供 id 值,通过 requestbodu 提供需要更新的属性和对应值,根据 id 更新。这个逻辑应该写在 controller 层,在 controller 层取路径变量和更新值,比如:
@PutMapping(value="/{id}")
public Result update(@RequestBody Spec spec,@PathVariable Integer id){
//设置主键值
spec.setId(id);
//调用SpecService实现修改Spec
specService.update(spec);
return new Result(true,StatusCode.OK,"修改成功");
}
而不是在 service 层。
(3)在使用 Postman 对方法进行测试时,在 Postman 中提交的 json 是符合 pojo 类的,所以属性应该是 pojo 类对象的属性,而不是数据库表的字段(列名)。如果错误传入了表类型的参数,就会在返回结果中报错,比如:“当前实体类不包含名为image_items的属性!” 。
(4)MySQL 数据库中自增字段,比如说,现在有 {1,2,3},删除了 ID 为 3 的记录,接下来再添加记录,ID 将变成 4 ,而不是从 3 开始,这是为了保证序列的不重复性。
(5)进行条件查询时,对属性判空使用的是 SpringFramework.util 下的 StringUtils,可以对所有类型的参数进行判空:
比如说 org.apache.commons.lang.StringUtils 包下的,只能对 String 类型参数进行判空 :
还要注意,应该先对对象判空,再对对象的属性判空。
(6)因为 HTTP 协议明确规定,put、get、delete 请求都是具有幂等性的,而 post 为非幂等性的。
幂等的概念是指同一个请求方法执行多次和仅执行一次的效果完全相同。 引入幂等,主要是为了处理同一个请求重复发送的情况,比如在请求响应前失去连接,如果方法是幂等的,就可以放心地重发一次请求。
应该根据幂等性来决定是使用 post 还是 put,比如,插入新数据时,我们传入的 json 是不含有 ID的,数据库自增 ID,这时,插入操作就是非幂等的,多次执行,就会有多次插入新纪录,应该用 post 方法,Controller 层使用 @PostMapping;而更新数据库时,我们会在路径中指定 ID,对该记录进行更新,多次执行也是更新了同一条记录,是幂等的,应该用 put 方法,控制层使用 @PutMapping 。
(7)在控制层中,HTTP 方法一样时,路径一样,是要报错说方法使用重复的,比如在分页查询 和 分页条件查询时,HTTP 方法和路径相同,但是参数不同,方法是报错的,所以,参数不能作为区分。
/* 分页查询 */
@GetMapping("/search/{page}/{size}")
public Result<PageInfo<Spec>> findPage(@PathVariable(value = "page") Integer page,
@PathVariable(value = "size") Integer size) {
PageInfo pageInfo=specService.findPage(page,size);
return new Result<>(true, StatusCode.OK, "分页查询成功", pageInfo);
}
/* 分页条件查询 */
@GetMapping("/search/{page}/{size}")
public Result<PageInfo<Spec>> findPage(@PathVariable(value = "page") Integer page,
@PathVariable(value = "size") Integer size,
@RequestBody Spec spec) {
PageInfo pageInfo=specService.findPage(page,size,spec);
return new Result<>(true, StatusCode.OK, "分页条件查询成功", pageInfo);
}
(8)post 方法,比如,在分页条件查询中:
@PostMapping ("/search/{page}/{size}")
public Result<PageInfo<Spec>> findPage(@PathVariable(value = "page") Integer page,
@PathVariable(value = "size") Integer size,
@RequestBody Spec spec) {
PageInfo pageInfo=specService.findPage(page,size,spec);
return new Result<>(true, StatusCode.OK, "分页条件查询成功", pageInfo);
}
在 postman 里,如果参数烂空着,什么也不输入,会提示需要 requestbody;如果传入参数 {} ,通过 debug ,发现 对象是不为 null 的,不过它的各个属性都是 null。而且这样执行的结果,是按照全查来的,因为在 service 中,会先构建条件,然后执行 select * from xxx
,既然属性都是 null,也就不会拼接条件语句,也就是全查啦。
(9)数据库中 TEXT 类型的字段对应 Java 里的 String 类型;
数据库中 BIGINT 类型的字段,分以下情况:
如果不是无符号类型,BIGINT(20) 的取值范围为-9223372036854775808~9223372036854775807
。与 Java.lang.Long 的取值范围完全一致,mybatis会将其映射为 Long。
而 BIGINT(20) UNSIGNED 的取值范围是 0 ~ 18446744073709551615
,其中一半的数据超出了 Long 的取值范围,Mybatis 将其映射为 BigInteger。
不过,一般不会使用 BIGINT 的,因为 有符号 int 最大可以支持到约 22 亿,远远大于我们的需求 和 MySQL单表所能支持的性能上限,对于OLTP应用来说,单表的规模一般要保持在千万级别,不会达到22亿上限。
无符号 int,上限为 42 亿,这个预留量已经是非常的充足了。使用 BIGINT ,会占用更大的磁盘和内存空间,内存空间毕竟有限,无效的占用会导致更多的数据换入换出,额外增加了IO的压力,对性能是不利的。
(10)使用分页查询主要是先用 PageHelper.startPage(page, size); 传入页码和每页记录数,再做查询,最后返回 PageInfo 对象,把查询结果作为参数传入即可。
使用条件查询主要是构建条件,通过 Criteria 拼接条件语句。