第四阶段第二天笔记(10.25)

6. 关于Lombok框架

Lombok框架的主要作用是通过注解可以在编译期生成某些代码,例如Setters & Getters、hashCode()equals()toString()方法等,可以简化开发。

由于源代码(.java)文件中并没有相关代码,所以,默认情况下,开发工具无法智能提示,直接写出相关代码也会提示错误,则需要在开发工具中安装Lombok插件,安装方式:打开IntelliJ IDEA的设置,在Plugins一栏,在Marketplace中找到Lombok并安装即可。

Lombok的常用注解有:

  • @Data:添加在类上,可在编译期生成全部属性对应的Setters & Getters、hashCode()equals()toString(),使用此注解时,必须保证当前类的父类存在无参数构造方法
  • @Setter:可以添加在属性上,将仅作用于当前属性,也可以添加在类上,将作用于类中所有属性,用于生成对应的Setter方法
  • @Getter:同上,用于生成对应的Getter方法
  • @EqualsAndHashCode:添加在类上,用于生成规范的equals()hashCode(),关于equals()方法,如果2个对象的所有属性的值完全相同,则返回true,否则返回false,关于hashCode()也是如此,如果2个对象的所有属性的值完全相同,则生成的HashCode值相同,否则,不应该相同
  • @ToString:添加在类上,用于生成全属性对应的toString()方法
  • @Slf4j:添加在类上,日志输出程序信息

7. 关于自动装配Mapper对象时报错

当自动装配Mapper接口的对象时,IntelliJ IDEA可能会报错,提示无法装配此对象,但是,并不影响运行!

如果要解决IntelliJ IDEA错误的提示,可以:

  • 使用@Resource注解替换@Autowired注解
    • 不推荐,只要是使用自动装配,都应该使用@Autowired注解
  • 不再使用@MapperScan来指定Mapper接口的包,而改为在各Mapper接口上添加@Mapper注解
    • 不推荐,使用@MapperScan是一劳永逸的做法,更加省事
  • 在各Mapper接口上添加@Repository注解
    • 推荐
    • 与添加@Mapper注解的本质不同,添加@Mapper注解是为了标识此接口是Mybatis框架应该处理的接口,添加@Repository注解是为了引导IntelliJ IDEA作出正确的判断
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cn.tedu.csmall.product.mapper.AlbumMapper' available: expected at least 1 bean whi

8. 关于Slf4j日志

在Spring Boot项目中,基础依赖项(spring-boot-starter)中已经包含了日志的相关依赖项。

在添加了Lombok依赖项后,可以在类上添加@Slf4j注解,则Lombok框架会在编译期生成名为log的变量,可调用此变量的方法来输出日志。

日志是有显示级别的,根据日志内容的重要程度,从不重要到重要,依次为:

  • trace:跟踪信息,可能包含不一定关注,但是包含了程序执行流程的信息
  • debug:调试信息,可能包含一些敏感内容,比如关键数据的值
  • info:一般信息
  • warn:警告信息
  • error:错误信息

使用Slf4j时,可以使用log变量调用以上5个级别对应的方法,来输出不同级别的日志!

在Spring Boot项目中,默认的日志显示级别为【info】,将只会显示此级别及更重要级别的日志!可以在application.properties中配置logging.level.根包=日志显示级别来设置当前显示级别,例如:

# 日志的显示级别
logging.level.cn.tedu.csmall=error

在开发实践中,应该使用tracedebug级别的日志来输出与流程相关的、涉及敏感数据的日志,使用info输出一般的、被显示在控制台也无所谓的信息,使用warnerror输出更加重要的、需要关注的日志。

输出日志时,通常建议使用void trace(String message, Object... args)方法(也有其它级别日志的同样参数列表的方法),在第1个参数String message中,可以使用{}作为占位符,表示某变量的值,然后,通过第2个参数Object... args来表示各占位符对应的值,例如:

int x = 1;
int y = 2;
log.info("{}+{}={}", x, y, x + y);

使用以上做法,可以避免字符串的拼接,提高了代码的可阅读性,也提高了程序的执行效率!并且,在Slf4j日志中,以上第1个参数由于是字符串常量,将被缓存,如果反复执行此日志输出,执行效率也会更高!

另外,SLF4j是一个日志标准,并不是具体的实现,通常,是通过logbacklog4j等日志框架实现的,当前主流的Spring Boot版本中,都是使用logback来实现的。

9. 关于Profile配置

同一个项目,在不同的环境中(例如开发环境、测试环境、生产环境),需要的配置值可能是不同的,例如日志的显示级别、连接数据库的配置参数等,如果把同一个配置文件的多个属性的值反复修改是不现实的!

Spring框架提供了Profile配置机制,在Spring Boot中更是简化了此项操作,它允许使用application-自定义名称.properties作为Profile配置文件的文件名,这类配置文件默认是不加载的!

例如,在application.properties的同级路径下创建application-dev.properties,添加配置:

# ######################### #
# 此配置文件是【开发环境】的配置 #
#  此配置文件需要被激活才会生效 #
# ######################## #

# 连接数据库的配置参数
spring.datasource.url=jdbc:mysql://localhost:3306/mall_pms?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root

# 日志的显示级别
logging.level.cn.tedu.csmall=trace

然后,在application.properties中,根据以上配置文件的名称(application-dev.properties)来激活以上配置文件:

# 激活Profile配置
spring.profiles.active=dev

所以,application.properties是始终加载的配置,而application-自定义名称.properties是必须激活才会加载的配置!

如果application.properties与被激活的Profile配置中存在同名的属性,配置值却不相同,在执行时,将以Profile配置为准!

10. 关于YAML配置

YAML是一种编写配置文件的语法,表现为使用.yml作为扩展名的配置文件,Spring框架默认并不支持此类配置文件,而Spring Boot的基础依赖项中已经包含解析此类文件的依赖项,所以,在Spring Boot项目可以直接使用此类配置文件。

在Spring Boot项目中,使用.properties.yml配置是等效的,均可以正常识别并使用!

在YAML语法中,其典型特征是:

  • 如果属性名中有小数点,则可以改为冒号,并且,冒号的右侧应该换行且缩进2个空格
    • 在IntelliJ IDEA中编辑YAML语法的配置时,会自动将按下的TAB键的内容转换成2个空格
  • 如果多个属性名称中有相同的部分,不必(也不可)重复配置,只需要保持正确的缩进即可
  • 属性名与属性值之间使用1个冒号和1个空格进行分隔
  • 对于纯数值类型的属性值,可能需要使用双引号框住
  • 也能识别例如xx.xx.xx这类属性名

例如:在.properties中的配置为:

spring.datasource.username=root
spring.datasource.password=root

.yml中则配置为:

spring:
  datasource:
    username: root
    password: root

注意:YAML的解析相对更加严格,如果在此类配置文件中出现了错误的语法,甚至只是一些不应该出现的字符,都会导致解析失败!并且,如果直接复制粘贴整个文件,还可能出现乱码问题!

11. 插入数据时获取自动编号的id

如果表中的id是自动编号的,在<insert>标签上,可以配置useGeneratedKeys="true"keyProperty="属性名",将可以获取自动编号的id值,并由Mybatis自动赋值到参数对象的属性(keyProperty配置的值)上,例如:

<!-- int insert(Album album); -->
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO pms_album (
        name, description, sort
    ) VALUES (
        #{name}, #{description}, #{sort}
    )
</insert>

提示:如果表的id不是自动编号的,则插入数据时必须由方法的调用者给出id值,所以,对于方法的调用者而言,id值是已知的,则不需要配置这2个属性。

12. 关于BindingException

当调用的方法找不到绑定的SQL语句时,将出现错误,例如:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): cn.tedu.csmall.product.mapper.AlbumMapper.insert

出现此错误的原因可能是:

  • 在XML文件中,<mapper>标签的namespace值有误
  • 在XML文件中,<insert>或类似标签的id值有误
  • 在配置文件(application.properties / application.yml)中,配置的mybatis.mapper-locations属性有误,可能属性名写错,或属性值写错

注意:以上异常信息中已经明确表示了哪个接口的哪个方法缺少对应的SQL语句,可以以此为线索来排查错误。

13. 批量插入相册数据

首先,应该在AlbumMapper接口中添加新的抽象方法:

int insertBatch(List<Album> albums);

然后,在AlbumMapper.xml中配置以上抽象方法映射的SQL语句:

<!-- int insertBatch(List<Album> albums); -->
<insert id="insertBatch" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO pms_album (
    	name, description, sort
    ) VALUES
    <foreach collection="list" item="album" separator=",">
        (#{album.name}, #{album.description}, #{album.sort})
    </foreach>
</insert>

最后,在AlbumMapperTests中编写并执行测试:

@Test
void insertBatch() {
    List<Album> albums = new ArrayList<>();
    for (int i = 1; i <= 5; i++) {
        Album album = new Album();
        album.setName("批量插入测试相册" + i);
        album.setDescription("批量插入测试相册的简介" + i);
        album.setSort(200);
        albums.add(album);
    }
    
    int rows = mapper.insertBatch(albums);
    log.debug("批量插入完成,受影响的行数:{}", rows);
}

14. 根据id删除相册数据

首先,应该在AlbumMapper接口中添加新的抽象方法:

int deleteById(Long id);

然后,在AlbumMapper.xml中配置以上抽象方法映射的SQL语句:

<!-- int deleteById(Long id); -->
<delete id="deleteById">
    DELETE FROM pms_album WHERE id=#{id}
</delete>

最后,在AlbumMapperTests中编写并执行测试:

@Test
void deleteById() {
    Long id = 1L;
    int rows = mapper.deleteById(id);
    log.debug("删除完成,受影响的行数:{}", rows);
}

15. 根据若干个id批量删除相册数据

首先,应该在AlbumMapper接口中添加新的抽象方法:

int deleteByIds(Long[] ids);

然后,在AlbumMapper.xml中配置以上抽象方法映射的SQL语句:

<!-- int deleteByIds(Long ids); -->
<delete id="deleteByIds">
    DELETE FROM pms_album WHERE id IN (
    	<foreach collection="array" item="id" separator=",">
    		#{id}
    	</foreach>
    )
</delete>

最后,在AlbumMapperTests中编写并执行测试:

@Test
void deleteByIds() {
    Long[] ids = {1L, 3L, 5L};
    int rows = mapper.deleteByIds(ids);
    log.debug("批量删除完成,受影响的行数:{}", rows);
}

16. 修改相册数据

首先,应该在AlbumMapper接口中添加新的抽象方法:

int update(Album album);

然后,在AlbumMapper.xml中配置以上抽象方法映射的SQL语句:

<!-- int update(Album album); -->
<update id="update">
    UPDATE pms_album
    <set>
    	<if test="name != null">
    		name=#{name},
        </if>
    	<if test="description != null">
            description=#{description},
    	</if>
    	<if test="sort != null">
            sort=#{sort},
    	</if>
    </set>
    WHERE id=#{id}
</update>

最后,在AlbumMapperTests中编写并执行测试:

@Test
void update() {
    Album album = new Album();
    album.setName("新-测试相册010");
    album.setDescription("新-测试相册简介010");
    album.setSort(166);
    
    int rows = mapper.update(album);
    log.debug("更新完成,受影响的行数:{}", rows);
}

17. 统计相册数据的数量

首先,应该在AlbumMapper接口中添加新的抽象方法:

int count();

在设计“查询”的抽象方法时,关于返回值类型,只需要保证所设计的返回值类型足够“装得下”所需的查询结果即可。

然后,在AlbumMapper.xml中配置以上抽象方法映射的SQL语句:

<!-- int count(); -->
<select id="count" resultType="int">
    SELECT count(*) FROM pms_album
</select>

注意:每个<select>标签必须配置resultTyperesultMap这2个属性中的其中1个。

最后,在AlbumMapperTests中编写并执行测试:

@Test
void count() {
    int count = mapper.count();
    log.debug("统计完成,表中的数据的数量:{}", count);
}

18. 根据id查询相册数据的详情

根据阿里巴巴的Java开发手册中的规约,查询数据时,不允许使用星号作为字段列表,且,每张表都有gmt_creategmt_modified字段,但是,在实际查询时,这2个时间字段通常并不需要查询出来!所以,根据id查询相册数据的详情的SQL语句大致是:

select id, name, description, sort from pms_album where id=?

由于并不是查询所有字段,则使用实体类作为查询结果是不合适的,因为存在获取数据时语义不清晰的问题!(例如获取某属性的值为null时,是因为没有查询对应的字段?还是确实查询了但表中的数据就是null?),在规范的项目开发中,通常建议创建专门的POJO类型,用于封装查询结果!

首先,应该在项目的根包下创建pojo.vo.AlbumStandardVO类型,在此类型中设计与查询的字段列表匹配的属性:

package cn.tedu.csmall.product.pojo.vo;

import lombok.Data;

import java.io.Serializable;

/**
 * 相册数据的标准VO类
 *
 * @author java@tedu.cn
 * @version 0.0.1
 */
@Data
public class AlbumStandardVO implements Serializable {

    /**
     * 记录id
     */
    private Long id;

    /**
     * 相册名称
     */
    private String name;

    /**
     * 相册简介
     */
    private String description;

    /**
     * 自定义排序序号
     */
    private Integer sort;

}

应该在AlbumMapper接口中添加新的抽象方法:

AlbumStandardVO getStandardById(Long id);

然后,在AlbumMapper.xml中配置以上抽象方法映射的SQL语句:

<!-- AlbumStandardVO getStandardById(Long id); -->
<select id="getStandardById" resultType="cn.tedu.csmall.product.pojo.vo.AlbumStandardVO">
    SELECT id, name, description, sort FROM pms_album WHERE id=#{id}
</select>

注意:每个<select>标签必须配置resultTyperesultMap这2个属性中的其中1个。

最后,在AlbumMapperTests中编写并执行测试:

@Test
void getStandardById() {
    Long id = 1L;
    AlbumStandardVO queryResult = mapper.getStandardById(id);
    log.debug("根据id【{}】查询数据详情完成,查询结果:{}", id, queryResult);
}

19. 查询相册列表

首先,应该在项目的根包下创建pojo.vo.AlbumListItemVO类型,在此类型中设计与查询的字段列表匹配的属性:

package cn.tedu.csmall.product.pojo.vo;

import lombok.Data;

import java.io.Serializable;

/**
 * 相册数据的列表项VO类
 *
 * @author java@tedu.cn
 * @version 0.0.1
 */
@Data
public class AlbumListItemVO implements Serializable {

    /**
     * 记录id
     */
    private Long id;

    /**
     * 相册名称
     */
    private String name;

    /**
     * 相册简介
     */
    private String description;

    /**
     * 自定义排序序号
     */
    private Integer sort;

}

应该在AlbumMapper接口中添加新的抽象方法:

List<AlbumListItemVO> list();

然后,在AlbumMapper.xml中配置以上抽象方法映射的SQL语句:

<!-- List<AlbumListItemVO> list(); -->
<select id="list" resultType="cn.tedu.csmall.product.pojo.vo.AlbumListItemVO">
    SELECT id, name, description, sort FROM pms_album ORDER BY sort DESC, id DESC
</select>

注意:每个<select>标签必须配置resultTyperesultMap这2个属性中的其中1个。

最后,在AlbumMapperTests中编写并执行测试:

@Test
void list() {
    List<AlbumListItemVO> list = mapper.list();
    log.debug("查询列表完成,列表中的数据的数量:{}", list.size());
    for (AlbumListItemVO item : list) {
        log.debug("{}", item);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值