1. 查询列表
相对查询最多1个数据,查询列表的区别在于:必须使用List
类型作为抽象方法的返回值类型,另外,在配置<select>
时,无论使用resultType
还是resultMap
,在指定封装返回结果的类型时,仍指定List
中的元素类型即可。
【需求】查询品牌列表,暂不考虑分页,结果按照sort
降序排列、id
升序(降序)。
需要执行的SQL语句大致是:
select
id, name, pinyin, logo, description,
keywords, sort, sales, product_count, comment_count,
positive_comment_count, enable
from pms_brand order by sort desc, id
通常,查询列表时,与查询单个数据使用的VO类应该是不同的。
则在项目的根包下创建pojo.vo.BrandListItemVO
类,在类中声明与以上字体列表匹配的属性:
package cn.tedu.csmall.product.pojo.vo;
import lombok.Data;
import java.io.Serializable;
@Data
public class BrandListItemVO implements Serializable {
private Long id;
private String name;
private String pinyin;
private String logo;
private String description;
private String keywords;
private Integer sort;
private Integer sales;
private Integer productCount;
private Integer commentCount;
private Integer positiveCommentCount;
private Integer enable;
}
在BrandMapper.java
接口中添加抽象方法:
/**
* 查询品牌列表
*
* @return 品牌列表,如果没有匹配的品牌,将返回长度为0的列表
*/
List<BrandListItemVO> list();
在BrandMapper.xml
中配置SQL:
<select id="list" resultMap="ListResultMap">
SELECT
<include refid="ListQueryFields"/>
FROM
pms_brand
ORDER BY
sort DESC, id
</select>
<sql id="ListQueryFields">
<if test="true">
id, name, pinyin, logo, description,
keywords, sort, sales, product_count, comment_count,
positive_comment_count, enable
</if>
</sql>
<resultMap id="ListResultMap"
type="cn.tedu.csmall.product.pojo.vo.BrandListItemVO">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="product_count" property="productCount"/>
<result column="comment_count" property="commentCount"/>
<result column="positive_comment_count" property="positiveCommentCount"/>
</resultMap>
完成后,在BrandMapperTests
中编写并执行测试:
@Test
void testList() {
List<BrandListItemVO> list = mapper.list();
System.out.println("查询品牌列表,查询结果中的数据的数量:" + list.size());
for (BrandListItemVO brand : list) {
System.out.println(brand);
}
}
2. 关于SLF4j日志
在Spring Boot项目中,在spring-boot-starter
依赖项中,默认已经集成了SLF4j日志框架。
在添加了Lombok
依赖项后,可以在需要输出日志的类上添加@Slf4j
注解,则Lombok框架会在编译期自动添加名为log
的日志变量。
日志的显示级别,根据日志信息内容的重要程度,从低到高依次为:
trace
:跟踪信息debug
:调试信息info
:一般信息warn
:警告信息error
:错误信息
在SLF4j中,调用log
变量时,有以上5种级别对应的方法,调用与日志显示级别对应的方法,则输出的日志就是对应的级别,例如调用log.debug()
时,输出的日志就是DEBUG级别的日志,调用log.warn()
时,输出的日志就是WARN级别的日志。
在Spring Boot项目中,默认的显示级别是info
,相比info
较低级别的日志不会被输出!
在Spring Boot项目中,可以在application.properties
中通过logging.level.包名
属性来设置日志的显示级别:
# 日志的显示级别
logging.level.cn.tedu.csmall=trace
关于以上配置:
- 包名部分,必须至少写1级包名,例如
logging.level.cn
,如果没有包名,则是错误的 - 配置的包名是”根包“,所以子孙包及其下的所有类的日志都会是以上配置的级别
- 如果认为有必要的话,甚至可以配置到具体的类
使用日志的好处:
- 可以通过简单的配置,实现控制日志的显示级别,所以,可以使得某些日志仅开发时能输出,在生产环境中不会输出
- 使用日志时,可以使用常量字符串,输出结果中的变量值均在此常量字符串中使用占位符表示即可,所以,字符串的处理效率非常高,并且,代码简洁
3. 关于Spring MVC
Spring MVC框架主要解决了接收请求、响应结果的相关问题。
在Spring Boot项目中,当需要使用Spring MVC框架及相关的功能时,应该添加spring-boot-starter-web
依赖项。由于spring-boot-starter-web
是基于spring-boot-starter
依赖项的,所以,spring-boot-starter-web
包含了spring-boot-starter
,在实际编码时,只需要将spring-boot-starter
改为spring-boot-starter-web
即可。
当添加了spring-boot-starter-web
依赖项后,当启动项目时,默认情况下,就会自动将当前项目编译、打包并部署到内置的Tomcat中,并启动Tomcat,默认占用8080
端口。
如果需要修改默认占用的端口号,可以在application.properties
中配置server.port
属性,例如:
# 服务端口
server.port=9080
4. 实现:增加品牌
在项目的根包下,创建pojo.dto.BrandAddNewDTO
类,此类用于封装”增加品牌“时客户端需要提交的请求参数:
@Data
public class BrandAddNewDTO implements Serializable {
private String name;
private String pinyin;
private String logo;
private String description;
private String keywords;
private Integer sort;
private Integer enable;
}
在项目的根包下,创建controller.BrandController
类,在类上添加@RestController
:
@RestController
public class BrandController {
@Autowired
BrandMapper brandMapper;
// http://localhost:9080/add-new?name=格力&pinyin=geli&sort=67&description=格力的简介&enable=1&keywords=不知道写什么关键词&logo=还没有上传logo
@RequestMapping("/add-new")
public String addNew(BrandAddNewDTO brandAddNewDTO) {
System.out.println("即将处理【添加品牌】的请求……");
System.out.println("brandAddNewDTO = " + brandAddNewDTO);
// 检查品牌名称是否已经被占用
int count = brandMapper.countByName(brandAddNewDTO.getName());
if (count > 0) {
return "增加品牌失败!品牌名称【" + brandAddNewDTO.getName() + "】已经被占用!";
}
Brand brand = new Brand();
BeanUtils.copyProperties(brandAddNewDTO, brand);
brand.setSales(0);
brand.setProductCount(0);
brand.setCommentCount(0);
brand.setPositiveCommentCount(0);
brandMapper.insert(brand);
return "已经完成处理【添加品牌】的请求";
}
// http://localhost:9080/delete
@RequestMapping("/delete")
public String delete() {
System.out.println("即将处理【删除品牌】的请求……");
return "已经完成处理【删除品牌】的请求";
}
}
关于控制器的基本使用:
- 仅当添加了
@Controller
注解后,此类才算是”控制器类“(才可以接收请求、响应结果) - 在方法上使用
@RequestMapping
可以配置某个路径,后续,客户端可以向此路径发起请求,则此方法将自动执行,所以,此方法可称之为”处理请求的方法“ - 在默认情况下,处理请求的方法的返回值是
String
时,返回的结果表示某个视图的名称 - 在方法上添加
@ResponseBody
注解,将表示此方法是”响应正文“的,方法的返回结果将直接响应到客户端 @ResponseBody
注解还可以添加在控制器类上,将表示此控制器类中所有处理请求的方法都是响应正文的@RestController
将同时具有@Controller
和@ResponseBody
的效果,这一点,可以从@RestController
中看到
关于处理请求的方法:
- 访问权限:应该是
public
- 返回值类型:暂时使用
String
- 方法名称:自定义
- 参数列表:按需设计,可以直接将所需的请求参数声明为方法的参数,或者,将多个请求参数封装到自定义类型中,并使用自定义类型作为处理请求的方法的参数,各参数可以按照期望的数据类型进行设计,如果有多个参数,不区分先后顺序
关于接收请求参数:
- 如果客户端正确的按照名称提交了请求参数,则服务器端可以正常接收到,如果不是字符串类型,会尝试自动的转换数据类型,如果转换失败,将出现错误,且响应
400
http://localhost:9080/add-new?name=小米&pinyin=xiaomi
- 如果客户端提交了对应的请求参数名称,却没有提交值,则服务器端默认视为空字符串,如果请求参数是其它类型(例如
Integer
),框架会放弃转换类型,仍保持为null
http://localhost:9080/add-new?name=&pinyin=
- 如果客户端没有提交对应名称的请求参数,则服务器端接收到的为
null
http://localhost:9080/add-new
作业
作业内容:
- 创建与12张数据表对应的12个实体类;
- 完成【相册:
pms_album
】的:插入、根据id删除、批量删除、更新、统计数量、根据名称统计数量、根据id查询详情、查询列表(不考虑分页问题,下同) - 完成【图片:
pms_picture
】的:插入、根据id删除、批量删除、根据id查询详情、根据相册(album_id
)查询列表 - 完成【属性:
pms_attribute
】的:插入、根据id删除、批量删除、更新、统计数量、根据id查询详情、根据属性模板(template_id
)查询列表 - 完成【属性模板:
pms_attribute_template
】的:插入、根据id删除、批量删除、更新、统计数量、根据名称统计数量、根据id查询详情、查询列表 - 完成【类别:
pms_category
】的:插入、根据id删除、批量删除、更新、统计数量、根据名称统计数量、根据id查询详情、根据父级类别(parent_id
)查询列表 - 完成【类别与品牌关联:
pms_brand_category
】:插入、根据id删除、批量删除、根据品牌(brand_id
)统计数量、根据类别(category_id
)统计数量、根据品牌(brand_id
)和类别(category_id
)统计数量、查询列表
作业要求:
- 所有的声明部分(类、接口、抽象方法、属性)应该添加注释
- 所有实现的功能必须添加相应测试,以确保功能可以正确运行