尚庭公寓随笔

目录

4.类型转换错误   字符串     《---》     枚举类型

5. 仅靠sql实现多表查询 

7.minio使用 


1.@JsonIgnore 注解 忽略序列化字段 jackson提供

2.@TableLogic Mybatis-Plus 忽略逻辑删除 删除为0 未删除为1

3.MybatisPlus有自动填充功能  @TableField的fill属性  

        ① 指定填充字段

    @Schema(description = "创建时间")
    @JsonIgnore
    @TableField(value = "create_time",fill = FieldFill.INSERT)
    private Date createTime;

   ② 指定填充字段的内容

@Component
public class MybatisMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject,"createTime",Date.class,new Date());
    }
}

4.类型转换错误   字符串     《---》     枚举类型

   前端 ---->       WebDateBinder(处理类型转换)   SpringMVC     《-----》TypeHandler(处理类型     转换)   MyabtisPlus

   HttpMessageConverter(类型转换)  SpringMVC ---》 前端

    前端-》SpringMvc   发生类型转换错误 原因:WebDateBinder类型转换器 只能把枚举对象进行转换  不能进行值传递转换 。前端必须传递 WAITING.不能传递1. 为了解决这个问题 我们要自定义消息转换器

public enum AppointmentStatus implements BaseEnum {


    WAITING(1, "待看房"),

    CANCELED(2, "已取消"),

    VIEWED(3, "已看房");
}

消息转换器 这个类的目的就是 将字符串转换成枚举对象供WebDateBinder使用

@Component
public class StringToItemTypeConverter implements Converter<String, ItemType> {


    @Override
    public ItemType convert(String code) {

        // 获取所有枚举类对象
        ItemType[] values = ItemType.values();
        // 判断是否存在该类
        for (ItemType itemType : values) {
            if (itemType.getCode().equals(Integer.valueOf(code))){
                return itemType;
            }
        }


        throw  new IllegalArgumentException("code:"+code+"非法");
    }
}

WebMvc的装配

package com.atguigu.lease.web.admin.custom.config;

import com.atguigu.lease.web.admin.custom.converter.StringToItemTypeConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {


    @Autowired
    private StringToItemTypeConverter stringToItemTypeConverter;
    @Override
    public void addFormatters(FormatterRegistry registry) {

        registry.addConverter(this.stringToItemTypeConverter);


    }
}

更新消息转换器:如果还有字符串转枚举的需要 我们需要再写一个相似的类。那么还有很多个这种转换需求呢? ConvertFactory集中化转换类结构。

创建一个工厂类 实现String类型到BaseEnum类型转换

package com.atguigu.lease.web.admin.custom.converter;

import com.atguigu.lease.model.enums.BaseEnum;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.stereotype.Component;

//  S : 原始类型   R 目标父类  String类型到BaseEnum的每个子类类型转换

@Component
public class StringToBaseEnumConverterFactory implements ConverterFactory<String, BaseEnum> {

    // 方法返回值 应该是 String类型 --》 目标类型的convert对象
    @Override
    public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {


        // 匿名子类
        return new Converter<String, T>() {
            @Override
            public T convert(String code) {

                // 拿到枚举类型的全部实例  -- 泛型
                T[] enumConstants = targetType.getEnumConstants();
                for (T enumConstant : enumConstants) {
                    if(enumConstant.getCode().equals(Integer.valueOf(code))){
                        return enumConstant;
                    }
                }


                throw new IllegalArgumentException("code:"+code+"非法");
            }
        };
    }
}

注册到webmvc容器中

package com.atguigu.lease.web.admin.custom.config;

import com.atguigu.lease.web.admin.custom.converter.StringToBaseEnumConverterFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {




    @Autowired
    private StringToBaseEnumConverterFactory stringToBaseEnumConverterFactory;

    @Override
    public void addFormatters(FormatterRegistry registry) {

        registry.addConverterFactory(this.stringToBaseEnumConverterFactory);


    }
}

SpringMVC     《-----》TypeHandler(处理类型     转换)   MyabtisPlus 

在controller层已经将前端传来的字符串转换成枚举类型,然后和请求参数中的枚举类型进行匹配,成功将匹配参数传递到mapper层,但是TypeHandler默认的转换规则是枚举对象实例转换成实例名称即:APP.BAIDU 会转换成 "BAIDU"; MybatisPlus 提供了一个注解@EnumValue 它可以实现枚举对象到属性之间的相互映射。

public enum ItemType implements BaseEnum {

    APARTMENT(1, "公寓"),

    ROOM(2, "房间");


    @EnumValue
    @JsonValue
    private Integer code;
    private String name;
}

   HttpMessageConverter(类型转换)  SpringMVC ---》 前端

HttpMessageConverter本质是java对象和json字符串相互转换,使用jackson序列化框架 jackson序列化框架的序列化规则和TypeHandler方法一样 枚举类型默认的处理规则都是枚举实例和枚举类型的相互映射。但是提供@JsonValue进行转换 实现枚举实例和属性之间的相互映射。

5. 仅靠sql实现多表查询 

处理多表查询时不见可以在service层实现 编写sql也可以实现 

CTRL + SHIFT + L 格式化

关联了两个表 使用了左连接的方法 -- 想保留左表的所有属性 。 使用了and 和 where 确定数据的正确性

返回类型是一个嵌套复杂的格式 所以使用了resultmap注解进行自定义输出格式 。collection标签用作一个列表的返回类型 。 propertry为封装属性名 column为sql中查询的数据

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.lease.web.admin.mapper.AttrKeyMapper">
    
    
    <resultMap id="AttrKeyVoMap" type="com.atguigu.lease.web.admin.vo.attr.AttrKeyVo">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <collection property="attrValueList" ofType="com.atguigu.lease.model.entity.AttrValue">
            <id property="id" column="attr_value_id" />
            <result property="name" column="attr_value_name" />
            <result property="attrKeyId" column="attr_key_id" />
        </collection>
    </resultMap>

    <select id="listAttrInfo" resultMap="AttrKeyVoMap">
        select k.id,
               k.name,
               v.id   attr_value_id,
               v.name attr_value_name,
               v.attr_key_id
        from attr_key k
                 left join attr_value v
                           on k.id = v.attr_key_id and v.is_deleted = 0
        where k.is_deleted = 0
    </select>




</mapper>

另外一个超级复杂sql语句

    <select id="pageItem" resultType="com.atguigu.lease.web.admin.vo.apartment.ApartmentItemVo">

        select
        ai.id,
        ai.name,
        ai.introduction,
        ai.district_id,
        ai.district_name,
        ai.city_id,
        ai.city_name,
        ai.province_id,
        ai.province_name,
        ai.address_detail,
        ai.latitude,
        ai.longitude,
        ai.phone,
        ai.is_release,
        ifnull(tc.cnt,0) total_room_count,

        ifnull(tc.cnt,0) - ifnull(cc.cnt,0) free_room_count

        from


        ( select id,
        name,
        introduction,
        district_id,
        district_name,
        city_id,
        city_name,
        province_id,
        province_name,
        address_detail,
        latitude,
        longitude,
        phone,
        is_release
        from apartment_info
        <where>
            is_deleted = 0
            <if test="queryVo.provinceId != null">
                and province_id = #{queryVo.provinceId}
            </if>
            <if test="queryVo.cityId != null">
                and city_id = #{queryVo.cityId}
            </if>
            <if test="districtId != null">
                and district_id = #{queryVo.districtId}
            </if>
        </where>
        ) ai
        left join

        ( select
        apartment_id,
        count(*) cnt
        from room_info
        where is_deleted = 0
        and is_release = 1
        group by apartment_id;
        ) tc

        on ai.id = tc.apartment_id
        left join
        (
        select
        apartment_id,
        count(*) cnt
        from lease_agreement
        where is_deleted = 0
        and status in (2,5)
        group by apartment_id
        ) cc

        on ai.id = cc.apartment_id
    </select>

这个sql其实是看起来复杂 实际上也复杂。基本思路:首先我们要清楚我们的目的是什么:查询基本的公寓信息+统计房间总数+统计空房总数。 基本的公寓信息 我们有apartment_info这个表,房间总数我们有room_info 这个表 ,分组统计房间的个数。空间房间数我们可以采取在租赁表中查询已经租赁的房间数 用总房间数-租赁房间数 = 空闲房间数。

代码分析:将三张表查询结果join,此处的sql还用于条件查询,因此我们也要加入where标签和if标签进行动态sql的拼接。ifnull(属性值,默认值)方法用作如果没有属性值,便变成默认值
 

6. 配置属性映射

法1.application.yml文件中设置配置属性 然后通过注解@Value进行在目标类中使用

minio:
  endpoint: http://192.168.16.115:9000
  access-key: admin
  secret-key: admin123
  bucket-name: lease

属性映射

@Configuration
public class MinioConfiguration {

    @Value("${minio.endpoint}")
    private String endpoint;

    @Bean
    public MinioClient minioClient(){

        MinioClient.builder().endpoint(endpoint)


    };

}

法2.映射的属性太多 我们可以单独写一个类存放配置属性

@Data
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {


    private String endpoint;
    private String accessKey;
    private String secretKey;
    private String bucketName;

}

(在这里出现了一个新的注解 @ConfigurationProperties 他是用来指定前缀的-- 在yml文件中的根)

然后就可以使用到我们的目标类中了

@Configuration
@ConfigurationPropertiesScan("com.atguigu.lease.common.minio")
public class MinioConfiguration {


    @Autowired
    private MinioProperties minioProperties;

    @Bean
    public MinioClient minioClient(){

        return MinioClient.builder().endpoint(minioProperties.getEndpoint())
                .credentials(minioProperties.getAccessKey(),minioProperties.getSecretKey())
                .build();


    };

}

(这里也要讲两个注解一个是 @EnableConfigurationProperties(“MinioProperties.class”)它用在配置类上 单独指定配置类。意味着只有这一个配置属性类被赋值。另外一个就是代码上的@ConfigurationPropertiesScan()它是用来进行扫描配置类的,意味着可以多多个配置类被进行属性赋值)

7.minio使用 

在上述步骤中 我们已经创建了Minio的客户端并把它放进io容器中,然后现在就办它运用到我们的项目中。

但是我们发现了一个问题就是我们没有对异常进行处理 我们就去掉try catch 抛出异常 在controller层捕捉异常然后返回对应的数据

@Service
public class FileServiceImpl implements FileService {


    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioProperties minioProperties;


    // 上传图片
    @Override
    public String upload(MultipartFile file) {

        try {
            boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioProperties.getBucketName()).build());


            if (!bucketExists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioProperties.getBucketName()).build());
                //给桶写权限 只允许自己写 只允许其他人读
                minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(minioProperties.getBucketName()).config(createBucketPolicyConfig(minioProperties.getBucketName())).build());
            }


            String filename = new SimpleDateFormat("yyyyMMdd").format(new Date()) + "/" + UUID.randomUUID() + "-" + file.getOriginalFilename();
            minioClient.putObject(PutObjectArgs.builder().bucket(minioProperties.getBucketName()).stream(file.getInputStream(), file.getSize(), -1).object(filename).contentType(file.getContentType()).build());


            //拼装桶的名称
            String url = String.join("/",minioProperties.getEndpoint(),minioProperties.getBucketName(),filename);

            return url;
        } catch (Exception e) {
            e.printStackTrace();
        }


        return null;
    }


    private String createBucketPolicyConfig(String bucketName) {

        return """
                {
                  "Statement" : [ {
                    "Action" : "s3:GetObject",
                    "Effect" : "Allow",
                    "Principal" : "*",
                    "Resource" : "arn:aws:s3:::%s/*"
                  } ],
                  "Version" : "2012-10-17"
                }
                """.formatted(bucketName);
    }


}

(这个类的思路就是 判断有没有桶 ?继续执行:创建桶继续执行。权限模块 给桶给予权限。对文件进行存储 构建了一个独一无二的图片名 存储到桶里面 )

8.全局异常处理

语法:类上添加@ControllerAdvice 在类的方法上面i添加@ExceptionHandler。

@ControllerAdvice
public class GlobalExceptionHandler {


    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result handler(Exception e){
        e.printStackTrace();
        return Result.fail();

    }



}

(@ResponseBody 序列化数据返回)

10. @Builder注解是Lombok库的一部分可以快速建造一个对象

        List<Long> feeValueIds = apartmentSubmitVo.getFeeValueIds();

        if (!CollectionUtils.isEmpty(feeValueIds)) {
            ArrayList<ApartmentFeeValue> apartmentFeeValues = new ArrayList<>();
            for (Long feeValueId : feeValueIds) {
                ApartmentFeeValue apartmentFeeValue = ApartmentFeeValue.builder()
                        .apartmentId(apartmentSubmitVo.getId())
                        .feeValueId(feeValueId).build();
                apartmentFeeValues.add(apartmentFeeValue);
            }

            apartmentFeeValueService.saveBatch(apartmentFeeValues);
        }

11.分页插件

装入依赖 -- 配置分页插件

@Configuration
@MapperScan("com.atguigu.lease.web.*.mapper")
public class MybatisConfiguration {


    // 分页插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

}

new一个page对象 项目中我们还需要对查询的结果进行操作 因此我们就自定义sql


        Page<ApartmentItemVo> page = new Page<>(current, size);
        IPage<ApartmentItemVo> result = apartmentInfoService.pageitem(page,queryVo);

我们使用了Ipage进行接收 我们不需要书写sql的时候写分页信息 spring为我们在查询的语句自动添加limit这个。用以来非常方便。

12.自定义异常

前提:异常分类:编译时异常(受检异常)和运行时异常 (不受检异常)。编译时异常:编译器需要我们对这个异常进行处理 try catch ,抛出去。 后者就不需要我们对这个异常进行立刻处理.

1.自定义异常 

@Data
public class LeaseException extends RuntimeException {

    private Integer code;

    public LeaseException(ResultCodeEnum resultCodeEnum){
        super(resultCodeEnum.getMessage());
        this.code = resultCodeEnum.getCode();
    }


}

(我希望我抛出的异常 有两个属性可以带个前端 code message。RuntimeException里面自带错误信息属性,我只需要在编写构造器的时候用父类的方法就可以了)

2.异常捕获

这个环节和全局异常处理的逻辑是一致的

    @ExceptionHandler(LeaseException.class)
    @ResponseBody
    public Result handlerLease(LeaseException e){

        return Result.fail(e.getCode(),e.getMessage());
    }

(我们只需要添加注解,并且指定异常类,编写异常处理的结果。妙计1,如果你的传递参数太多可以直接封装一个类进行传递,在本项目,我们要需要传递枚举类中的一个实例就可以将code和message直接传递。须知1:我们只定义的异常处理不会和我们只定义的全局异常处理Exception相撞,匹配机制是:抛出异常的找最近的异常处理类进行处理)

13.contcat 字符串的连接

 select
            from view_appointment va
            left join apartment_info ai
            on va.apartment_id = ai.id and ai.is_deleted = 0
            <where>
                va.is_deleted = 0
            <if test="queryVo.provinceId != null">
                and ai.provinc_id = #{queryVo.provinceId}
            </if>
            <if test = "queryVi.cityId != null">
                and ai.city_id = #{queryVo.cityId}
            </if>
            <if test = "queryVo.districtId != null">
                and ai.district_id = #{queryVo.districtId}
            </if>
            <if test = "queryVo.apartmentId != null">
                and va.apartment_id = #{queryVo.apartmentId}
            </if>
            <if test = "queryVo.name != null and queryVo.name != '' ">
                and va.name like concat('%',#{queryVo.name},'%')
            </if>
            <if test = "queryVo.phone != null and queryVo.phone != '' ">
                and va.phone like concat('%',#{queryVo.phone},'%')
            </if>
            </where>

        

14. 时间返回类型

后端从数据库中获取到时间返回给前端,时间的格式是由json序列化框架决定。如果想修改时间格式,设置json序列化框架。本项目使用的是jackson。

单独配置:在指定的字段增加@jsonFormat注解 如下:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date appointmentTime;

全局配置:在application.yml中增加一下内容

Spring:
    jackson:
        date-format: yyyy-MM-dd HH:mm:ss

序列化相关问题:时区。序列化框架会根据当前时区进行序列化,如果想要数据库时间和序列化时间保持一致,需要设置序列化框架和数据库时区保持一致。

单独配置 在指定字段增加@JsonFormat注解 如下

@JsonFormat(timezone="GMT+8")
private Date appointmentTime;


     全局配置

spring:
    jackson:
       time-zone: GMT+8

14 MybatisPlus 的SaveUpdate方法不会将空值进行更新

15 Session 使用公共服务redis进行存储验证码

16 token

17.用户登录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值