若依开发笔记

若依项目开发笔记(二期开发笔记)

开发前的碎碎记

springboot二期开发笔记

这个标题记录我二期开发过程中的问题。

问题1:

数据库怎么还会有数据,明明不是我的本地连接啊?

代码里面数据库配置是老哥的远程mysql服务器连接ip,所以我可以连接上

问题2:

我明明在navicat里面输的是老哥的电脑的ip地址,可为什么访问不到dict这个数据库?

你个笨蛋,mysql学创建用户,授权用户的时候全忘了?mysql是一个关系型数据库管理系统,注意,它本身就是一个系统软件,运行在你的电脑上,然后你的电脑就是一台MYSQL服务器。关系指的是表与表之间的关系,联系;数据库管理系统指的是它可以管理多个数据库,也就是你在navicat里面创建的多个数据库。
然后MYSQL分为客户端和服务端,mysql服务器可以创建多个用户,默认root本身就有且是超级管理员。多个用户从客户端访问,也就是navicat访问,别人访问你的mysql服务器,需要输入你给人家创建的用户和密码,然后创建的时候设置有权限,什么只能看到特定数据库什么的。

问题3:

感觉又对服务器是什么产生疑问了,什莫事服务器,什么是客户端?

首先明确两点:(1)服务器的本质是提供服务。(2)服务器是软硬结合体
所以:你的电脑是一台硬件,mysql是服务器软件,然后你的电脑运行mysql服务器软件后,就可以被称作mysql服务器,给客户端提供服务,客户端可以是你电脑上的navicat或者他人电脑上的navicat。
然后查看你的电脑的ip地址,一般就是wifi的ip

客户端的区分:客户端设备和客户端程序

注意术语和专业的区别

image-20250402001257601

问题4:

拓展延申java中的服务器软件有哪些?

image-20250401235802173

问题五

端口号是干什么的?我修改端口号怎么知道这个端口号有没有有效?

1. 端口的基本概念
端口号:端口号是一个 16 位的数字,范围是 0 到 65535。它用于标识一台计算机上运行的特定服务或应用程序。
IP 地址 + 端口号:IP 地址用于标识网络中的一台设备,而端口号用于标识这台设备上运行的特定服务。例如:
192.168.1.100:80 表示 IP 地址为 192.168.1.100 的设备上的 HTTP 服务。
192.168.1.100:3306 表示同一设备上的 MySQL 数据库服务

问题6

怎么设计端口号有效?怎么检查端口是否被占用?

1. 确认端口号是否有效
端口号是一个 16 位的数字,范围是 0 到 65535。根据 IANA 的规定,端口号分为以下几类:

0-1023:系统保留端口,通常用于常见的系统服务(如 HTTP、FTP 等),需要管理员权限才能绑定。
1024-49151:注册端口,通常用于用户应用程序(如 Tomcat、MySQL 等)。
49152-65535:动态或私有端口,通常用于临时连接。
建议:

选择 1024 到 49151 之间的端口号,避免使用系统保留端口。
确保端口号未被其他程序占用。
2. 检查端口是否被占用
在设置端口号之前,需要确认该端口是否已被其他程序占用。以下是检查方法:

方法 1:使用 netstat 命令
打开命令提示符(Win + R,输入 cmd,回车)。
运行以下命令:
cmd
netstat -ano | findstr :端口号
例如,检查端口 8080:
cmd
netstat -ano | findstr :8080
如果输出结果为空,说明该端口未被占用;如果有输出,说明端口已被占用。
方法 1:使用 netstat 和 findstr(Windows)
打开命令提示符(Win + R,输入 cmd,回车)。
使用 netstat -ano 命令列出所有端口,然后通过 findstr 过滤指定范围。 例如,检查端口范围 8000 到 9000:
cmd
netstat -ano | findstr /R ":800[0-9] :80[1-9][0-9] :9000"
解释:
:800[0-9] 匹配 8000 到 8009。
:80[1-9][0-9] 匹配 8010 到 8099。
:9000 匹配 9000。

问题7

为什么我这数据库创建@’%‘类型的用户后无法登录mysql本地服务器?

原来是匿名用户的问题,把匿名用户删除就行了。

参考文章:

  1. mysql新建用户无法登录解决方案_mysql创建新用户登录失败-CSDN博客
  2. MySQL创建用户时使用%通配符导致无法登录问题解析与解决方案 - 云原生实践

额外加一点用户创建的指令

##创建用户并设置密码和访问地址
CREATE USER 'yzh01'@'%' IDENTIFIED BY '123';


##授予用户对某个数据库的所用权限

GRANT ALL PRIVILEGES ON mybatis.* TO 'yzh01'@'%';
GRANT ALL PRIVILEGES ON springboot.* TO 'yzh01'@'%';

##授予权限后必须刷新权限生效
FLUSH PRIVILEGES;

问题8

rbac开发前,我想找一个工程开始做,经过对比数据库的表的分析。

1.jichu-master(springboot里面的)———-淘汰(原因:数据库的表不一样,是一个杂例子)

2.jichu-master(mybatis里面最先发的)——淘汰(原因:rbac杂表,是一个杂例子)

3.shiro—————-(保存)

shiro居然是Apach的一个安全框架的名字,这个项目刚好使用了shiro框架,用的springboot+mybatis的框架。

shiro是一个相对完整的网站项目,主要用它:

  • 生成代码功能对mybatis单表的增删改查自动生成,生成的代码mybatis-plus也可以用。
  • 后期有实力了再来看他项目是怎么写的。

4.mybatis-plus的自动生成三层架构用到一个mybatisplus插件。

5.老哥架构师官网——保存

http://124.221.43.118:8888/system/user

这个是老哥架构师管理系统的官网,是一个教学项目,是对shiro项目的功能完善。我没有源码

目前主要用他的页面和数据库练业务。

1 环境配置

导入的jichumaster123里面的pom坐标,另外加一下springboot起步依赖的坐标。

2 springboot_demo03笔记(springboot整合mybatis)

第一次整合mybatis,jsp三层开发练习记录(收费的单表)

2.1 笔记

1.
注意:
    (1)创建完springboot项目要在yml文件下配置一下数据源信息。
    (2)把生成的数据源信息删除,否则字符会导致错误。
    (3)把生成的模板类删除,要不然模板类的包路径需要自己移动。
2.
记录一下踩坑经过
1.springboot是不支持jsp的,所以需要在pom里面导入四个坐标,
2.之后直接在main目录下建立web文件夹就行,然后放jsp文件
3.在yml文件里配置前缀后缀。
4.注意:web文件夹里面的WEB-INF文件夹只能通过控制器类访问,直接在浏览器输入jsp地址无法访问。
5.然后如果你自己有jsp模板的话,不用把web文件夹添加到项目路径里,(添加这一步只是让idea知道只是web文件,然后可以给你提供
jsp模板,你自己有的话,不用添加也可以。
6.还有就是配置jsp的实时渲染方式:
#  添加项目路径和开启实时渲染jsp
  servlet:
    context-path: /springboot
    jsp:
      init-parameters:
        development: true
7.注意yml里面的层级要对应好,我第一次就是datasource和mvc不在同一级。
8.最后一点最重要,不要乱添加目录为根资源啥的,乱添加web文件夹啥的,要不然环境很乱。
我第一次就是乱添加,导致最后把.idea和iml文件删除再次打开项目也不行,只能重新建立项目才可以。一定不要乱添加。

8.使用Apipost工具测试的笔记
(1)数据库配置里面的主机地址是哥的数据库地址,所以我可以连接到数据库,这是远程库,我说咋没有库也可以连接到。
(2)重点测试了@PathVariable和@RequestParam的区别,然后写法,笔记本里面记录了。
(3)RequestBody和Responsebody是把请求数据和响应数据封装为json格式,一般是多个请求数据对应一个实体类的属性的时候封装。
(4)@RestController=@Controller+@responsebody,所以@RestController自动会把响应数据封装为json格式。

2.2 如何封装数据到jsp页面

/*1.留下一个问题:如果用@controller,怎么把数据返回到页面上
    1.在servlet中是借助request和response和session处理请求和封装数据的返回到jsp页面里面
    2.springmvc是用这个ModelAndView类(找时间学一下ModelAndView这个类)*/

image-20250330150233821

image-20250330150413272

3 rbac业务开发

一个菜单对应多个菜单项,一个菜单项对应多个功能模块

3.1 梳理表结构和关系

4.2
1.第一步:梳理这几张表
tb_user:用户表
tb_role:角色表
tb_menu:菜单表

tb_user_role:用户角色关系表,多对多
tb_role_menu:角色菜单关系表,多对多
rbac权限模型:role-base-access-control 基于角色的访问控制

一个用户多个角色,一个角色在菜单中不同权限,当添加用户时候只需要赋予对应角色就关联了权限。

tb_dept:部门表
tb_post:岗位表
部门-用户:一对多   部门-岗位:一对多
tb_post_user:用户岗位关系表,多对多

3.2 部门管理

1.展示所有部门

页面点击用户管理模块树形结构显示所有部门。

2.掌握树结构的实现方法

/*此类用于构建树结构
 *   1.创建map集合,存储所有树节点
 *      注意方法返回类型还是TreeNode类型,是一个嵌套的节点,扩散后是一个树结构*/
/*   2.遍历传入的节点集合,放到map集合里面*/
/*···3.再次遍历节点集合,根据每个节点的id判断是否是根节点
 *       是:把它付给root,初始化root是空的
 *       否:找到其父节点,把他放到父节点的子结点中*/

/*笔记中的方法有些问题:
* 1.需要在他笔记的基础上多处理三个异常
*        要处理三个空指针异常
             1.这里要判断一下父节点是不是为空
             2.children要初始化,否则默认为null
             3.root默认为null
  2.测试中输出json格式,他的那种方法不行,需要转换基本类型或者map,list
  * 使用这个方法
  *    // 使用 Jackson 序列化
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(tree);
        System.out.println(json); */


4.3
1. if (parentNode != null) {
                    if (parentNode.getChildren() == null) {
                        parentNode.setChildren(new ArrayList<Dept>());

                    }
                    parentNode.getChildren().add(node);//这个地方一定要在children判断外面执行
第一个查询所有数据并树形展示后,发现,树形结构数据缺失,是因为 parentNode.getChildren().add(node);这个方法
应该在children外面执行,因为当父节点豪创科技第一次初始化后,后面再加入豪创科技的子节点里面直接跳过if,所以,不会再添加,
从而导致数据缺失


2.

    /*lombok自动生成的toString方法默认展示所有数据,
    * 这样会导致最后页面中数据全部展示,不符合要求,所以要自己自定义一下*/
    说错了,嵌套形式自己没法修改,返回数据后让前端进行修改。

2.添加部门

问题1:

为什么除了填写的字段外,其他的插入后都自动生成了。我的插入怎么没有。

image-20250404175127423

4.

Jackson 的作用
Jackson 是一个用于处理 JSON 的 Java 库,它主要完成以下两个任务:
将 JSON 字符串反序列化为 Java 对象(JSON → 对象)。
将 Java 对象序列化为 JSON 字符串(对象 → JSON)。
在 Spring Boot 中,Jackson 是默认的 JSON 处理库。
所以:@RequestBody和@ResponseBody底层都是依赖jackson进行处理的。

Date和localDateTime类型在用Apipost测的时候自动生成的日期和时间格式是yyyy-MM-dd HH:mm:ss这种,
但是jackson默认支持2025-04-04T21:16:09日期和时间中间带个T的形式,所以我们自己要配置自定义的日期格式
方法1:yml里面配置全局日期格式为这种不带T的,idea自动生成这种不带T的。
spring
      jackson:
        date-format: yyyy-MM-dd HH:mm:ss
        time-zone: GMT+8

方法2:实体类属性上面加注解
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")

5.增加用户的时候,需要使用辅助方法查询用户是否存在,注意不能只通过名字查询,否则无法按树结构加入进去
     select * from tb_dept where dept_name = #{deptName} and parent_id = #{parentId}
 要按名字加上父节点id这样才能判断具体哪个节点里面是否存在,如果不加父节点id,默认就是整个表的数据,肯定存在。

3.查询和修改部门

4.5
删除部门功能

5.
/*下面这两个toAjax类是受保护类型,只能在同包下和继承这个类的子类可以访问。*/
    /**
     * 响应返回结果
     *
     * @param rows 影响行数
     * @return 操作结果
     */
    protected AjaxResult toAjax(int rows)
    {
        return rows > 0 ? success() : error();
    }

    /**
     * 响应返回结果
     *
     * @param result 结果
     * @return 操作结果
     */
    protected AjaxResult toAjax(boolean result)
    {
        return result ? success() : error();
    }

6.
前端传递的数据
如果前端未传递某个字段,后端接收到的值通常是 null。
如果前端传递了一个空字符串(例如用户未输入内容),后端接收到的值通常是 ''。
<if test="deptName != null and dept_name != ''">#{deptName}</if>
<if test="status != null and status != ''">#{status}</if>
这个if里面的test的属性是java中类的名称,别写成数据库的字段了,兄弟

7.
 <where>
            <if test="deptName != null and deptName != ''">and dept_name = #{deptName}</if>
            <if test="status != null and status != ''">and status = #{status}</if>
        </where>
兄弟,一直前端返回的数据出不来,原来是and和dept_name和status没加,兄弟,sql语句千万别忘了。

3.3 用户、角色、菜单管理

4.6
8.
idea又要重新破解了,我真服了,这是第三次需要重新破解了,这一次,点击插件后,输入激活码一直是无效。
我卸载又重新安装,再次还是不行,当我绝望之时,重新去那个提供工具的页面,重新下载了新的工具jetbrain,然后重新解压,就又好了,
就很玄学。这次要提前备份信息,以便下次下载还要重新配置。
    如果下次再次重新破解,先下载最新的jetbtain,再操作,下次出现这种情况如果脚本文件执行后还不行
    直接help-》Edit Custom VM Options,看看里面有没有那个脚本文件,如果没有,将那个脚本手动导入

9.
     <if test="parentId != null">parent_id = #{parentId},</if>
     更新的时候,输入parent_id为0的时候,数据库里面一直无法修改为0,但是其他数据可以修改,原来是parentId != ’‘空字符串限制了,
     需要将这个限制删去才可以,mybatis框架会把传入的0视为'0'等价于空字符串

10.

        <!--请求参数不为空校验-->
        <!-- validation -->
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>
================两个包都要引入,但是校验信息在日志里,没有打印到控制台和返回前端。留作一个问题======

4.7
用户表的操作

11.查询所有用户,包括其对应的部门信息
注意项:
    - <resultMap>里面的字段是根据查询结果映射的,先查询再映射,所以查询的别名要记得及时更改。
    - <resultMap>里面只写你查询结果中的映射即可,没写的,springboot不会映射,他只映射,你resultMap里面的字段。

12.如果create_time是Date类型,不要在<if>标签中直接与String类型的值进行比较。
 <if test="createTime != null and createTime != ''">and u.create_time = #{createTime}</if>
 要把上面if标签里面的and createTime != ''去掉,不能是字符串类型

 4.8
 13.
  public AjaxResult(Type type, String msg, Object data)
     {
         super.put(CODE_TAG, type.value);
         super.put(MSG_TAG, msg);
         if (data != null)
         {
             super.put(DATA_TAG, data);
         }
     }
     springboot使用jackson库自动将return的数据转为json格式,json对象的内容就是构造器方法的内容。


今天学到的还是有知识的:
14.
添加用户,更新用户

用户-角色-岗位,
用户更新的时候,角色-用户关系表和岗位-用户关系表,都要更新
怎么更新:
建立UserPostMapper接口和UserRoleMapper,然后在里面添加删除和更新语句
userService处理类里面执行多条sql语句,就可以实现关联更新。

15.
- 注意Role的类名大小写要写对,避免错误
- tb_user表的主键user_id居然没有勾选自动递增,低级错误啊
- 第二次遇见这个错误了  insert into tb_user_post values (#{user_id}, #{post_id})
一直报错org.apache.ibatis.binding.BindingException: Parameter 'user_id' not found. Available parameters are [postId, userId, param1, param2]
为什么找不到这个userId原来还是#里面写属性的时候没有写到对应的属性名,真实不好写。

------------顶级破案了,-----
当mapper接口方法里面有多个参数时候mybatis默认用arg0,arg1,param1,param2,来解析参数,所以我的postId一直识别不出来,
所以我加上@Param注解之后才可以成功识别,这就是旧知识的一样啊。

16.
 <update id="updateUser">
        update tb_user
        <trim prefix="set" suffixOverrides=",">
            <if test="nickName != null and nickName != ''">nick_name = #{nickName},</if>
            <if test="phonenumber != null and phonenumber != ''">phonenumber = #{phonenumber},</if>
<!--            <if test="userName != null and userName != ''">user_name = #{userName},</if>-->
            <if test="sex != null and sex != ''">sex = #{sex},</if>
            <if test="deptId != null and deptId != ''">dept_id = #{deptId},</if>
            <if test="email != null and email != ''">email = #{email},</if>
            <if test="password != null and password != ''">password = #{password},</if>
            <if test="status != null and status != ''">status = #{status},</if>
            <if test="remark != null and remark != ''">remark = #{remark},</if>
        </trim>
        where user_id = #{userId}
    </update>

这个用户修改的动态sql注意,,逗号一定是后缀的去掉,可别写成前缀了,这个地方注意啊,兄弟。

17
 @Override
    @Transactional(rollbackOn = Exception.class)//保证所有sql要么都成功,要么都回滚
    public Boolean isUpdateUser(User user, Integer postId, Integer roleId) throws Exception {
        int row = userMapper.updateUser(user);
        Integer userId = user.getUserId();
        if (row > 0) {
            int i = userPostMapper.updateUserPostId(userId, postId);

            /*这个地方注意,原来我自己写的有问题,执行关系表的插入和更新语句不用return,要不然if--else后面无法执行更新语句了
             * 这直接两种情况包圆了。*/
            if (i <= 0) {
                throw new Exception("更新用户岗位关系表错误");
            }
            int j = userRoleMapper.updateUserRoleId(userId, roleId);
            if (j <= 0) {
                throw new Exception("更新用户角色表错误");
            }
            return true;
        } else {
            return false;
        }
    }
    错误:一直可以插入但就是无法修改用户角色表的角色id,原来是这个地方逻辑有问题啊!


总结一下目前进度
-------------------------------------基本完成-----------------------------
3.4dept
增删改查list
list:难点在于树结构
删:要不要删除user 表中响应dept id的数据呢?

-------------------------------------基本完成-----------------------------
3.1 user -done
增删改查list
增:要有相关的role list和post list- 更新user role表,更新user post表
查:要有相关的role和post - 查询user role表拿到role list,查询user post表拿到post list
改:还要改role和post的关系表
list:要有dept的name
删:还要删除user role和user post里user相关数据

user表还差一个删除和相关的查询

4.9号明天开menu菜单表,感受多个根节点的代码奇妙之处。

4.9
接着写user表的删除和查询

18.
调用查询用户根据条件的时候,还是响应体的data里面没有数据,原来是创建时间是默认生成的,我只输入一个名字,但是没有注意到
创建时间也给我传过去了,所以这个创建时间没有对应的用户,然后创建时间别删错,不是关联属性的创建时间,不删直接为空也可以找到

19.
升级这个查询用户根据条件的方法,查找用户,部门名字,岗位名字,角色名字都给我出来
六张表连接就行了

20.
  <if test="userName != null and userName != ''">and u.user_name like concat('%', #{userName}, '%')</if>
  模糊查询用concat函数来实现,然后放到两个%之间标识,查询含用户输入的所有用户信息。

21.
总结一个很傻逼的问题。MybatisX这个插件bug很大啊,我<collection>标签里面的MenuId属性一直爆红,搞得我都自我怀疑不是映射外表
主键了,没想到在mybatis_demo04这个项目里面也爆红,原来是这个插件导致的,关闭就好了。
    后来我又手动去官网下载了低版本用,1.6.1,可以正常使用了,但是高亮那个错误还是不行。

22.
再补充一个响应数据没有外表集合的数据的原因:
<collection>标签里面的column字段没有用查询结果的别名,而是用原来的字段。

23.
再总结一个问题:
我测试角色的添加方法时候一直报500错误,于是心生一计,在查询用用户是否存在的下面sout这个查询结果,
果真发现,我输入新用户也有查询结果,自已一看查询结果居然是所有角色数据,然后我去sql语句里面看查询语句,发现,
select r.role_id rid, r.role_name rname, r.role_key rk, r.role_sort rss, r.status rs, m.menu_id mid, m.menu_name mname
from tb_role r
join tb_role_menu rm
on r.role_id = rm.role_id
join tb_menu m
on rm.menu_id = m.menu_id
sql语句只有查询,没有限制条件,这就导致我isaddRole一直返回false。

加上限制条件后,执行查询条件查询成功但是没有数据,又是一种没有数据的情况,补上,之所以没有数据
是因为角色表的这个角色和角色菜单关系表,菜单表没有共同的数据,这就导致sql连接后查出来的数据为空。

24.
写了这么多增删改查,有个困惑?
既然你xml里面的sql语句语法有错误,直接程序中断抛出异常了,我在service层判断row>0有什么意义?
    row>0是在sql语法正确的情况下,编译可以通过,但当时数据库里面添加的主键冲突,删除的数据不存在,查询的数据不存在等情况导致的row<=0
    所以还是有意义的。第二个困惑?
异常抛出程序会中断吗?我记得不会中断?
如果一直抛出,到处理的最后一层还抛出,就像controller层,出现异常就会再controller层中断,抛出后面的语句都不会执行,service层
抛出后面的语句也不会执行,直接抛出到coontroller层,只有再controller层catch捕获后,后面的语句才可以执行,不过业务逻辑不完整了,
相比于程序直接中断后面不执行,这就是区别。

25.
再补充一个congtroller层data没数据的情况
select里面少了parent_id,和menu_id,因为service层是根据这两个id来构建数结构的,所以不能少,展示是前端的事,后端不管。
   <!-- List<Menu> selectAllMenu();-->
    <select id="selectAllMenu" resultMap="selectAllMenuResult">
        select menu_id, parent_id, menu_name, order_num, path, menu_type, perms
        from tb_menu
    </select>
    <resultMap id="selectAllMenuResult" type="Menu">
        <id column="menu_id" property="menuId" />
        <result column="menu_name" property="menuName" />
        <result column="order_num" property="orderNum" />
        <result column="path" property="path" />
        <result column="menu_type" property="menuType" />
        <result column="perms" property="perms" />
    </resultMap>
</mapper>


26.
<select id="selectAllMenu" resultMap="selectAllMenuResult">
        select menu_id, parent_id, menu_name, order_num, path, menu_type, perms
        from tb_menu
    </select>
    <resultMap id="selectAllMenuResult" type="Menu">
        <id column="menu_id" property="menuId" />
        <result column="menu_name" property="menuName" />
        <result column="order_num" property="orderNum" />
        <result column="path" property="path" />
        <result column="menu_type" property="menuType" />
        <result column="perms" property="perms" />
    </resultMap>
注意这个地方,resultMap里面我没有配置parent_id到parentId的显式映射,到那时parentId还是可以传到service层进行
树结构的构造,说明这个值是有的,mybatis无法实现自动驼峰映射,除非显式配置,但是我用的是mybatis-plus,默认启用了驼峰映射


27.
  List<Menu> menus = menuMapper.selectMenuByCondition(menu);
        if (menus.isEmpty())
这个地方注意:不能够判断menus!=null,因为mybatis执行完sql语句默认返回空集合,所以null会导致永远为真。注意

28.
springboot中返回一个对象或者一个list集合,即使你在实体类里面不写toString方法,他也会返回json对象的形式。
json对象的内容是:
jackson 会序列化 User 对象的所有 非空字段(除非字段被标记为 transient 或使用 @JsonIgnore 注解忽略)。
字段的名称会直接作为 JSON 的键,字段的值会作为 JSON 的值。

然后总结三点:
    1.如果不想让某个字段在json对象里面出现,这样
        -  加transient private transient String password; // 不会被序列化
        - 字段上方加@JsonIgnore注解
    2.如果想让默认没有显式的null消失,这样
        - 在yml文件中配置:spring.jackson.default-property-inclusion=non_null

    3.如果想要显式的json键名不是属性名,这样:
        - @JsonProperty("userId")
              private Long id;


29.  @ExceptionHandler(MethodArgumentNotValidException.class)
       public AjaxResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e,
                                                               HttpServletRequest request) {
           String requestURI = request.getRequestURI();
           String message = e.getBindingResult().getFieldError().getDefaultMessage();
           log.error("请求地址{},参数验证失败{}", requestURI, e.getObjectName(),e);
           return AjaxResult.error(message);
       }

 String message = e.getBindingResult().getFieldError().getDefaultMessage();
 这个地方是获取你的注解@NotNULL里面的自定义的消息,e是控制台爆出的异常

   log.error("请求地址{},参数验证失败{}", requestURI, e.getObjectName(),e);
   然后这个地方的参数,如果有e的话,控制台把异常堆栈信息爆出来,如果不加e,只有2025-04-11 20:35:23.094 ERROR 25884 --- [nio-8080-exec-1] c.y.s.exception.ExceptionAdvice          : 请求地址/rbac/dept/addDept,参数验证失败dept
   这个异常堆栈信息不会爆出来,如果没有全局统一异常信息处理,这个不为空的信息会出现在日志中,以warn的形式。
   
   截至4.11,业务开发基本写完

3.4 rbac完整笔记

4.3
1.第一步:梳理这几张表
tb_user:用户表
tb_role:角色表
tb_menu:菜单表

tb_user_role:用户角色关系表,多对多
tb_role_menu:角色菜单关系表,多对多
rbac权限模型:role-base-access-control 基于角色的访问控制

一个用户多个角色,一个角色在菜单中不同权限,当添加用户时候只需要赋予对应角色就关联了权限。

tb_dept:部门表
tb_post:岗位表
部门-用户:一对多   部门-岗位:一对多
tb_post_user:用户岗位关系表,多对多

2.掌握树结构的实现方法

/*此类用于构建树结构
 *   1.创建map集合,存储所有树节点
 *      注意方法返回类型还是TreeNode类型,是一个嵌套的节点,扩散后是一个树结构*/
/*   2.遍历传入的节点集合,放到map集合里面*/
/*···3.再次遍历节点集合,根据每个节点的id判断是否是根节点
 *       是:把它付给root,初始化root是空的
 *       否:找到其父节点,把他放到父节点的子结点中*/

/*笔记中的方法有些问题:
* 1.需要在他笔记的基础上多处理三个异常
*        要处理三个空指针异常
             1.这里要判断一下父节点是不是为空
             2.children要初始化,否则默认为null
             3.root默认为null
  2.测试中输出json格式,他的那种方法不行,需要转换基本类型或者map,list
  * 使用这个方法
  *    // 使用 Jackson 序列化
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(tree);
        System.out.println(json); */


4.4
1. if (parentNode != null) {
                    if (parentNode.getChildren() == null) {
                        parentNode.setChildren(new ArrayList<Dept>());

                    }
                    parentNode.getChildren().add(node);//这个地方一定要在children判断外面执行
第一个查询所有数据并树形展示后,发现,树形结构数据缺失,是因为 parentNode.getChildren().add(node);这个方法
应该在children外面执行,因为当父节点豪创科技第一次初始化后,后面再加入豪创科技的子节点里面直接跳过if,所以,不会再添加,
从而导致数据缺失


2.

    /*lombok自动生成的toString方法默认展示所有数据,
    * 这样会导致最后页面中数据全部展示,不符合要求,所以要自己自定义一下*/
    说错了,因该在映射文件里面修改字段。



3.
添加部门
xml标签里面if判断,!=null指的是数据库里面不写值默认返回null,
               而!=‘’指的是用户表单不写值直接提交返回‘’。
一般都一起使用,细节先不管了。

4.

Jackson 的作用
Jackson 是一个用于处理 JSON 的 Java 库,它主要完成以下两个任务:
将 JSON 字符串反序列化为 Java 对象(JSON → 对象)。
将 Java 对象序列化为 JSON 字符串(对象 → JSON)。
在 Spring Boot 中,Jackson 是默认的 JSON 处理库。
所以:@RequestBody和@ResponseBody底层都是依赖jackson进行处理的。

Date和localDateTime类型在用Apipost测的时候自动生成的日期和时间格式是yyyy-MM-dd HH:mm:ss这种,
但是jackson默认支持2025-04-04T21:16:09日期和时间中间带个T的形式,所以我们自己要配置自定义的日期格式
方法1:yml里面配置全局日期格式为这种不带T的,idea自动生成这种不带T的。
spring
      jackson:
        date-format: yyyy-MM-dd HH:mm:ss
        time-zone: GMT+8

方法2:实体类属性上面加注解
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")

5.增加用户的时候,需要使用辅助方法查询用户是否存在,注意不能只通过名字查询,否则无法按树结构加入进去
     select * from tb_dept where dept_name = #{deptName} and parent_id = #{parentId}
 要按名字加上父节点id这样才能判断具体哪个节点里面是否存在,如果不加父节点id,默认就是整个表的数据,肯定存在。


4.5
删除部门功能

5.
/*下面这两个toAjax类是受保护类型,只能在同包下和继承这个类的子类可以访问。*/
    /**
     * 响应返回结果
     *
     * @param rows 影响行数
     * @return 操作结果
     */
    protected AjaxResult toAjax(int rows)
    {
        return rows > 0 ? success() : error();
    }

    /**
     * 响应返回结果
     *
     * @param result 结果
     * @return 操作结果
     */
    protected AjaxResult toAjax(boolean result)
    {
        return result ? success() : error();
    }

6.
前端传递的数据
如果前端未传递某个字段,后端接收到的值通常是 null。
如果前端传递了一个空字符串(例如用户未输入内容),后端接收到的值通常是 ''。
<if test="deptName != null and dept_name != ''">#{deptName}</if>
<if test="status != null and status != ''">#{status}</if>
这个if里面的test的属性是java中类的名称,别写成数据库的字段了,兄弟

7.
 <where>
            <if test="deptName != null and deptName != ''">and dept_name = #{deptName}</if>
            <if test="status != null and status != ''">and status = #{status}</if>
        </where>
兄弟,一直前端返回的数据出不来,原来是and和dept_name和status没加,兄弟,sql语句千万别忘了。

4.6
8.
idea又要重新破解了,我真服了,这是第三次需要重新破解了,这一次,点击插件后,输入激活码一直是无效。
我卸载又重新安装,再次还是不行,当我绝望之时,重新去那个提供工具的页面,重新下载了新的工具jetbrain,然后重新解压,就又好了,
就很玄学。这次要提前备份信息,以便下次下载还要重新配置。
    如果下次再次重新破解,先下载最新的jetbtain,再操作,下次出现这种情况如果脚本文件执行后还不行
    直接help-》Edit Custom VM Options,看看里面有没有那个脚本文件,如果没有,将那个脚本手动导入

9.
     <if test="parentId != null">parent_id = #{parentId},</if>
     更新的时候,输入parent_id为0的时候,数据库里面一直无法修改为0,但是其他数据可以修改,原来是parentId != ’‘空字符串限制了,
     需要将这个限制删去才可以,mybatis框架会把传入的0视为'0'等价于空字符串

10.

        <!--请求参数不为空校验-->
        <!-- validation -->
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>
================两个包都要引入,但是校验信息在日志里,没有打印到控制台和返回前端。留作一个问题======

4.7
用户表的操作

11.查询所有用户,包括其对应的部门信息
注意项:
    - <resultMap>里面的字段是根据查询结果映射的,先查询再映射,所以查询的别名要记得及时更改。
    - <resultMap>里面只写你查询结果中的映射即可,没写的,springboot不会映射,他只映射,你resultMap里面的字段。

12.如果create_time是Date类型,不要在<if>标签中直接与String类型的值进行比较。
 <if test="createTime != null and createTime != ''">and u.create_time = #{createTime}</if>
 要把上面if标签里面的and createTime != ''去掉,不能是字符串类型

 4.8
 13.
  public AjaxResult(Type type, String msg, Object data)
     {
         super.put(CODE_TAG, type.value);
         super.put(MSG_TAG, msg);
         if (data != null)
         {
             super.put(DATA_TAG, data);
         }
     }
     springboot使用jackson库自动将return的数据转为json格式,json对象的内容就是构造器方法的内容。


今天学到的还是有知识的:
14.
添加用户,更新用户

用户-角色-岗位,
用户更新的时候,角色-用户关系表和岗位-用户关系表,都要更新
怎么更新:
建立UserPostMapper接口和UserRoleMapper,然后在里面添加删除和更新语句
userService处理类里面执行多条sql语句,就可以实现关联更新。

15.
- 注意Role的类名大小写要写对,避免错误
- tb_user表的主键user_id居然没有勾选自动递增,低级错误啊
- 第二次遇见这个错误了  insert into tb_user_post values (#{user_id}, #{post_id})
一直报错org.apache.ibatis.binding.BindingException: Parameter 'user_id' not found. Available parameters are [postId, userId, param1, param2]
为什么找不到这个userId原来还是#里面写属性的时候没有写到对应的属性名,真实不好写。

------------顶级破案了,-----
当mapper接口方法里面有多个参数时候mybatis默认用arg0,arg1,param1,param2,来解析参数,所以我的postId一直识别不出来,
所以我加上@Param注解之后才可以成功识别,这就是旧知识的一样啊。

16.
 <update id="updateUser">
        update tb_user
        <trim prefix="set" suffixOverrides=",">
            <if test="nickName != null and nickName != ''">nick_name = #{nickName},</if>
            <if test="phonenumber != null and phonenumber != ''">phonenumber = #{phonenumber},</if>
<!--            <if test="userName != null and userName != ''">user_name = #{userName},</if>-->
            <if test="sex != null and sex != ''">sex = #{sex},</if>
            <if test="deptId != null and deptId != ''">dept_id = #{deptId},</if>
            <if test="email != null and email != ''">email = #{email},</if>
            <if test="password != null and password != ''">password = #{password},</if>
            <if test="status != null and status != ''">status = #{status},</if>
            <if test="remark != null and remark != ''">remark = #{remark},</if>
        </trim>
        where user_id = #{userId}
    </update>

这个用户修改的动态sql注意,,逗号一定是后缀的去掉,可别写成前缀了,这个地方注意啊,兄弟。

17
 @Override
    @Transactional(rollbackOn = Exception.class)//保证所有sql要么都成功,要么都回滚
    public Boolean isUpdateUser(User user, Integer postId, Integer roleId) throws Exception {
        int row = userMapper.updateUser(user);
        Integer userId = user.getUserId();
        if (row > 0) {
            int i = userPostMapper.updateUserPostId(userId, postId);

            /*这个地方注意,原来我自己写的有问题,执行关系表的插入和更新语句不用return,要不然if--else后面无法执行更新语句了
             * 这直接两种情况包圆了。*/
            if (i <= 0) {
                throw new Exception("更新用户岗位关系表错误");
            }
            int j = userRoleMapper.updateUserRoleId(userId, roleId);
            if (j <= 0) {
                throw new Exception("更新用户角色表错误");
            }
            return true;
        } else {
            return false;
        }
    }
    错误:一直可以插入但就是无法修改用户角色表的角色id,原来是这个地方逻辑有问题啊!


总结一下目前进度
-------------------------------------基本完成-----------------------------
3.4dept
增删改查list
list:难点在于树结构
删:要不要删除user 表中响应dept id的数据呢?

-------------------------------------基本完成-----------------------------
3.1 user -done
增删改查list
增:要有相关的role list和post list- 更新user role表,更新user post表
查:要有相关的role和post - 查询user role表拿到role list,查询user post表拿到post list
改:还要改role和post的关系表
list:要有dept的name
删:还要删除user role和user post里user相关数据

user表还差一个删除和相关的查询

4.9号明天开menu菜单表,感受多个根节点的代码奇妙之处。

4.9
接着写user表的删除和查询

18.
调用查询用户根据条件的时候,还是响应体的data里面没有数据,原来是创建时间是默认生成的,我只输入一个名字,但是没有注意到
创建时间也给我传过去了,所以这个创建时间没有对应的用户,然后创建时间别删错,不是关联属性的创建时间,不删直接为空也可以找到

19.
升级这个查询用户根据条件的方法,查找用户,部门名字,岗位名字,角色名字都给我出来
六张表连接就行了

20.
  <if test="userName != null and userName != ''">and u.user_name like concat('%', #{userName}, '%')</if>
  模糊查询用concat函数来实现,然后放到两个%之间标识,查询含用户输入的所有用户信息。

21.
总结一个很傻逼的问题。MybatisX这个插件bug很大啊,我<collection>标签里面的MenuId属性一直爆红,搞得我都自我怀疑不是映射外表
主键了,没想到在mybatis_demo04这个项目里面也爆红,原来是这个插件导致的,关闭就好了。
    后来我又手动去官网下载了低版本用,1.6.1,可以正常使用了,但是高亮那个错误还是不行。

22.
再补充一个响应数据没有外表集合的数据的原因:
<collection>标签里面的column字段没有用查询结果的别名,而是用原来的字段。

23.
再总结一个问题:
我测试角色的添加方法时候一直报500错误,于是心生一计,在查询用用户是否存在的下面sout这个查询结果,
果真发现,我输入新用户也有查询结果,自已一看查询结果居然是所有角色数据,然后我去sql语句里面看查询语句,发现,
select r.role_id rid, r.role_name rname, r.role_key rk, r.role_sort rss, r.status rs, m.menu_id mid, m.menu_name mname
from tb_role r
join tb_role_menu rm
on r.role_id = rm.role_id
join tb_menu m
on rm.menu_id = m.menu_id
sql语句只有查询,没有限制条件,这就导致我isaddRole一直返回false。

加上限制条件后,执行查询条件查询成功但是没有数据,又是一种没有数据的情况,补上,之所以没有数据
是因为角色表的这个角色和角色菜单关系表,菜单表没有共同的数据,这就导致sql连接后查出来的数据为空。

24.
写了这么多增删改查,有个困惑?
既然你xml里面的sql语句语法有错误,直接程序中断抛出异常了,我在service层判断row>0有什么意义?
    row>0是在sql语法正确的情况下,编译可以通过,但当时数据库里面添加的主键冲突,删除的数据不存在,查询的数据不存在等情况导致的row<=0
    所以还是有意义的。第二个困惑?
异常抛出程序会中断吗?我记得不会中断?
如果一直抛出,到处理的最后一层还抛出,就像controller层,出现异常就会再controller层中断,抛出后面的语句都不会执行,service层
抛出后面的语句也不会执行,直接抛出到coontroller层,只有再controller层catch捕获后,后面的语句才可以执行,不过业务逻辑不完整了,
相比于程序直接中断后面不执行,这就是区别。

25.
再补充一个congtroller层data没数据的情况
select里面少了parent_id,和menu_id,因为service层是根据这两个id来构建数结构的,所以不能少,展示是前端的事,后端不管。
   <!-- List<Menu> selectAllMenu();-->
    <select id="selectAllMenu" resultMap="selectAllMenuResult">
        select menu_id, parent_id, menu_name, order_num, path, menu_type, perms
        from tb_menu
    </select>
    <resultMap id="selectAllMenuResult" type="Menu">
        <id column="menu_id" property="menuId" />
        <result column="menu_name" property="menuName" />
        <result column="order_num" property="orderNum" />
        <result column="path" property="path" />
        <result column="menu_type" property="menuType" />
        <result column="perms" property="perms" />
    </resultMap>
</mapper>


26.
<select id="selectAllMenu" resultMap="selectAllMenuResult">
        select menu_id, parent_id, menu_name, order_num, path, menu_type, perms
        from tb_menu
    </select>
    <resultMap id="selectAllMenuResult" type="Menu">
        <id column="menu_id" property="menuId" />
        <result column="menu_name" property="menuName" />
        <result column="order_num" property="orderNum" />
        <result column="path" property="path" />
        <result column="menu_type" property="menuType" />
        <result column="perms" property="perms" />
    </resultMap>
注意这个地方,resultMap里面我没有配置parent_id到parentId的显式映射,到那时parentId还是可以传到service层进行
树结构的构造,说明这个值是有的,mybatis无法实现自动驼峰映射,除非显式配置,但是我用的是mybatis-plus,默认启用了驼峰映射


27.
  List<Menu> menus = menuMapper.selectMenuByCondition(menu);
        if (menus.isEmpty())
这个地方注意:不能够判断menus!=null,因为mybatis执行完sql语句默认返回空集合,所以null会导致永远为真。注意

28.
springboot中返回一个对象或者一个list集合,即使你在实体类里面不写toString方法,他也会返回json对象的形式。
json对象的内容是:
jackson 会序列化 User 对象的所有 非空字段(除非字段被标记为 transient 或使用 @JsonIgnore 注解忽略)。
字段的名称会直接作为 JSON 的键,字段的值会作为 JSON 的值。

然后总结三点:
    1.如果不想让某个字段在json对象里面出现,这样
        -  加transient private transient String password; // 不会被序列化
        - 字段上方加@JsonIgnore注解
    2.如果想让默认没有显式的null消失,这样
        - 在yml文件中配置:spring.jackson.default-property-inclusion=non_null

    3.如果想要显式的json键名不是属性名,这样:
        - @JsonProperty("userId")
              private Long id;


29.  @ExceptionHandler(MethodArgumentNotValidException.class)
       public AjaxResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e,
                                                               HttpServletRequest request) {
           String requestURI = request.getRequestURI();
           String message = e.getBindingResult().getFieldError().getDefaultMessage();
           log.error("请求地址{},参数验证失败{}", requestURI, e.getObjectName(),e);
           return AjaxResult.error(message);
       }

 String message = e.getBindingResult().getFieldError().getDefaultMessage();
 这个地方是获取你的注解@NotNULL里面的自定义的消息,e是控制台爆出的异常

   log.error("请求地址{},参数验证失败{}", requestURI, e.getObjectName(),e);
   然后这个地方的参数,如果有e的话,控制台把异常堆栈信息爆出来,如果不加e,只有2025-04-11 20:35:23.094 ERROR 25884 --- [nio-8080-exec-1] c.y.s.exception.ExceptionAdvice          : 请求地址/rbac/dept/addDept,参数验证失败dept
   这个异常堆栈信息不会爆出来,如果没有全局统一异常信息处理,这个不为空的信息会出现在日志中,以warn的形式。

AjaxResult笔记
1.
import static com.warrior.utils.BaseController.success;
这个是个新特性,静态导入特性:某个类的静态方法别的类使用有两种方式:
(1)import static com.warrior.utils.BaseController.success;静态导入后,直接在实用类里面调用方法名就行
(2)使用静态方法的类名.静态方法名调用。
漂亮,又学一个新特性。jdk5引入的。

2.
  /*枚举注意:
    * 1.枚举是一个类:枚举常量隐式调用枚举方法。
    * 2.枚举是一个类:有自己的属性:value
    * 3.枚举是一个类:有自己的构造方法:构造方法把传入的值付给自己的value属性
    * 4.枚举是一个类:有自己的方法:把属性作为值返回给外界
      5.  Type type = Type.SUCCESS;
          System.out.println(type); // 输出:SUCCESS
          System.out.println(type.value()); // 输出:0*/
    public enum Type
    {
        /** 成功 */
        SUCCESS(0),
        /** 警告 */
        WARN(301),
        /** 错误 */
        ERROR(500);
        private final int value;

        Type(int value)
        {
            this.value = value;
        }

        public int value()
        {
            return this.value;
        }
    }

3.
研究一遍后:发现这样使用:不用你自己写,知道流程就行
(1)BaseController中封装了controller层与业务处理无关的方法。
(2)具体的响应数据的处理方式由AjaxResult类进行数据的封装,BaseController只是调用。

4 springboot整合mybatis-plus

springboot_rbac和springboot_mybatisplus两个项目记录的笔记

1.导pom
   <!-- mybatis-plus起步依赖插件 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus-boot-starter.version}</version>
        </dependency>
        <!--mp自动生成代码,api文档注释需要swagger注解需要的依赖-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger2.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger2.version}</version>
        </dependency>


2.配置yml文件(#格线是mp的配置)

# tomcat服务器端口配置
server:
  port: 8080
  servlet:
    context-path: /rbac


# spring数据源配置
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/rbac?serverTimezone=Asia/Shanghai&useTimezone=true/&characterEncoding=utf8
    username: root
    password: Yin2040411
    max-wait: 10000
    initial-size: 5


# 配置jackson的日期时间格式
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

#####################################################################################################
# mybatis-plus框架配置
mybatis-plus:
# 配置映射文件和别名
  mapper-locations: classpath*:/com/yzh/springboot_rbac/mapper/*.xml
  type-aliases-package: com.yzh.springboot_rbac.entity
# mp逻辑删除配置:
  global-config:
    db-config:
      logic-delete-field: del_flag
      logic-delete-value: 0 #逻辑删除
      logic-not-delete-value: 1 #正常
#  数据库字段大写转小写配置
      capital-mode: true
  configuration:
#  下划线自动转托驼峰配置(不配置也是开启的,这里显式配置一下啦)
    map-underscore-to-camel-case: true
#   让sql语句日志打印到控制台配置
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl


#  控制台打印日志的详细程度配置
# trace:最详细日志信息
logging:
  level:
    root: info  # 默认就是info
    com.yzh.springboot_rbac.service.impl: trace
    com.yzh.springboot_rbac.controller: trace


#日志生成路径配置
logback:
  logDir: log/dev
  appName: springboot_rbac
  fileType: log
#######################################################################################


logback-spring.xml要注意一下该环境否则启动后一直卡在那里
logback-spring.xml里面这地方复制过来要改一下环境默认的default,这里需要改为default环境,
要不然项目一直卡在哪里启动不了,因为我没有配置dev的yml文件,所以只能使用default的文件。
<!--开发环境:打印控制台-->
    <!--这里需要改为default环境,要不然项目一直卡在哪里启动不了,因为我没有配置dev的yml文件,所以只能使用
    default的文件-->
    <springProfile name="default">
        <root level="info">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="DEBUG_FILE"/>
            <appender-ref ref="INFO_FILE"/>
            <appender-ref ref="WARN_FILE"/>
            <appender-ref ref="ERROR_FILE"/>
        </root>
    </springProfile>


3.
在springboot_mybatisplus项目中练习mp提供的方法记录

###################################################
如果想要在测试类中链式调用set方法设置对象的值,一定要把实体类里面的对应
set方法返回类型改为实体类形,retrun this
    public User setName(String name) {
        this.name = name;
        return this;
    }
    public User setAge(Integer age) {
        this.age = age;
        return this;
    }
    public User setBir(Date bir) {
        this.bir = bir;
        return this;
    }

        public void testInsert() {
            User user = new User();
            user.setAge(20).setName("张三").setBir(new Date());
            userMapper.insert(user);
        }
#############################################################

4.
springboot中自定义测试类实现测试两种方法
- 1.测试类要加@SpringBootTest这个注解,springboot项目测试类要加这个注解,
    否则无法测试,因为springboot项目需要依赖springboot来管理bean,否则usermapper无法注入。
- 2.自定义测试类继承默认生成的测试类,因为默认生成的测试类里面使用了这个注解

5.
雪花算法的:ASSIGN_ID实体类使用Long/String,数据库字段使用bigInt/varchar
          ASSIGN_UUID实体类使用String,数据库字段使用Varchar
          按照上述即可,实体类Integer和int数值范围对应数据库int范围太小,报错。
ASSIGN_ID设置后,mp自动生成1911670271488589825并插入到数据库中,数据库不用设置自增
ASSIGN_UUID:d7d3d88d136905d4362bcb7e1cef04cf,数据库不用设置自增
AUTO:需要依赖数据库设置自增才可以添加id,否则报错。

6.lambda表达式和引用方式如下:效果一样
   List<User> users = userMapper.selectList(null);
   users.forEach(System.out::println);
   users.forEach(user -> System.out.println(user));


7.常用的方法与条件构造器

@SpringBootTest
public class testBaseController {
    /*此测试对自定义mapper继承BaseController测试*/

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelectAllUser() {
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
        users.forEach(user -> System.out.println(user));
    }

    @Test
    public void testSelectByCondition() {
        /*查询常用的有两个:
         * selectById:根据id查,参数是id的值
         * selectList:查询所有用户,null空表示无条件所有用户*/
//        User user = userMapper.selectById(1);
//        System.out.println(user);

        /*基于条件的查询*/
//        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//        queryWrapper.like("name", "李");
//        List<User> list = userMapper.selectList(queryWrapper);
//        list.forEach(user -> System.out.println(user));

        /*lambda的查询*/
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.like(true, User::getName, "多");
        List<User> list = userMapper.selectList(lambdaQueryWrapper);
        list.forEach(user -> System.out.println(user));

    }

    @Test
    public void testInsert() {
        User user = new User();
        user.setAge(20).setName("李四").setBir(new Date());
        userMapper.insert(user);
        /*insert(T entity):插入方法只有一个,参数为实体类*/
    }

    @Test
    public void testdelete() {
        /*删除常用的有两个:
         * deleteById:genjuid删除,参数是id的值
         * delete:根据条件删除,参数是条件构造器*/
        /*普通删除*/
//        userMapper.deleteById(2);

        /*基于条件修改
         *   querywrapper形式*/
//        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//        queryWrapper.eq(true, "id", 3);
//        userMapper.delete(queryWrapper);

        /*lambdaqueryWrapper形式*/
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(true, User::getId, 4);
        userMapper.delete(lambdaQueryWrapper);
    }

    @Test
    public void testUpdate1() {
        /*updateById():参数是实体对象,基于id修改:先查询,把查询的对象赋值给user,然后修改*/
        User user = userMapper.selectById(1);
        user.setAge(10).setName("伍六七");
        userMapper.updateById(user);
    }

    @Test
    public void testUpdate2() {
        /*基于条件修改:
            update()参数是修改后的实体类和条件构造器里面的条件
        *   QueryWrapper相当于sql中的where表达式eq相当于where age = 10*/
        User user = userMapper.selectById("1");
        user.setName("多阔霍");
//        QueryWrapper<User> updateWrapper = new QueryWrapper<>();
//        updateWrapper.eq("age", 10);
//        userMapper.update(user, updateWrapper);

        /*写一个lambdaWrapper,这个是使用lambda的方法引用实现正向映射数据库字段的值
         *   注意,这里的方法引用不是lambda里面的获取的值,而是只获取方法名,即属性名
         *        方法的引用根据应用上下文来执行,lambda中方法引用会输出一个值,但是mp中输出列名*/
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(true, User::getAge, 10);
        userMapper.update(user, lambdaQueryWrapper);

    }


}


8.
注意:
- Iservice使用的话要和BaseMapper一起使用,所以一定要自定义一个具体的接口继承BaseMapper,
如果直接施一公Iservice,spring无法注入泛型接口BaseMapper,必须指定具体实例接口

- 但是可以不适用Iservice,只用BaseMapper的crud即可,Iservice提供了更复杂的方法,而且不用自己写方法在service层。

5 redis开发笔记

1.
工具类里面封装的set方法value写成了string,导致我无法存入user类型
//        redisUtil.set("userId" + user.getUserId(), "user{" + user + "}");
不用工具类,用原生的opsForValue的set方法可以存储user实体类类型
//        redisTemplate.opsForValue().set(user.getUserName(), user);

2.
public List<DictData> selectDictDataByType(String dictType) {
        Object dictDataList1 = redisUtil.get(dictType);
        //这个地方注意:要用!=null判断,如果redis里面没有这个键会返回null,这时候再用isEmpty()判断是否是空列表就不行了
        - mybatis返回list用isEmpty判断是否是空列表,mybatis不会返回空,他会返回空列表。
        - redis这里如果键不存在的话,返回的是null,而不是空列表
        if (dictDataList1 != null) {
            System.out.println("这是redis里面存储的数据" + dictDataList1);
            return (List<DictData>) dictDataList1;
        } else {
            List<DictData> dictDataList2 = dictDataMapper.selectDictDataBytype(dictType);
            System.out.println("这是mysql里面存储的数据" + dictDataList2);
            redisTemplate.opsForValue().set(dictType, dictDataList2);
            return dictDataList2;
        }


3.
<insert id="insertDictType" parameterType="DictType">
        insert into tb_dict_type(
        <if test="dictName != null and dictName != ''">dict_name,</if>
        <if test="dictType != null and dictType != ''">dict_type,</if>
        <if test="status != null">status,</if>
        <if test="remark != null and remark != ''">remark,</if>
        <if test="createBy != null and createBy != ''">create_by,</if>
        create_time
        )values(
        <if test="dictName != null and dictName != ''">#{dictName},</if>
        <if test="dictType != null and dictType != ''">#{dictType},</if>
        <if test="status != null">#{status},</if>
        <if test="remark != null and remark != ''">#{remark},</if>
        <if test="createBy != null and createBy != ''">#{createBy},</if>
        sysdate()
        )
    </insert>

 sysdate(),返回数据插入的时间,由数据库服务器生成

4.
看哪个完整系统接口的时候,发现他添加数据时候service层校验逻辑写在了controller层,
经过查找发现,传统的项目,service层的业务逻辑包括了从数据库查出来的校验逻辑,而现在的项目,校验方法还是写在
service层,但是校验逻辑写在了controller层也可以,看你自己了。

5.写了这几个接口,我现在总结一下我书写的命名规范:
(1)crud名字命名上(简单crud,不涉及校验逻辑的情况下):
    - mapper层方法名用:
        - insert
        - delete
        - update
        - select
    - service层和controller层方法名:
        - add/save
        - delete
        - update
        - select
    controller层的请求地址可以省略后缀重点表示crud就可以
(2)如果包含校验逻辑
    controller和mapper层方法命名不变
    -service层变化
        - isAdd (是否添加)
        - isUpdate(是否修改)
        - delete 删除 这个删除注意:(业务表用update逻辑删除,字典表用delete物理删除)
        - select
    校验逻辑一般简单开发就两种:
        - 用javaweb的传统方法:先执行一个selectByid/selectByCondition,查询数据库里面是否有值,然后再根据返回结果添加
        - 用抛出异常,这里分为两种情况:
            - 如果你自己不想自定义,也不用返回给前端响应结果,只想在控制台看到异常
                    IllegalStateException用于抛出查过来的用户为空时候的异常
                    表示“数据状态不合法”,适合业务逻辑校验(如订单不存在):
                    java
                    User user = userDao.findById(userId);
                    if (user == null) {
                        throw new IllegalStateException("用户不存在"); // ✅ 标准异常,无需自定义
                    }

                    IllegalArgumentException用于想要再service判断请求此参数的时候
                    当方法参数明显无效或违反基本约定时抛出:
                    public User getUserById(Long userId) {
                        if (userId == null || userId <= 0) {
                            throw new IllegalArgumentException("用户ID必须为正数");
                        }
            - 如果想要自己定义具体的不同类型异常,并响应给前端,用rbac业务里面的异常处理方法
                - 注意这里异常处理可以放在controller抛出,或者service抛出,我感觉还是再service
                   比较符合各类校验。


(3)补一个mybatis查询结果的常见情况
查询方法	返回值(无数据时)	判断方式
- User findById(Long id)	null	if (user == null)
- List<User> selectList()	空集合 ([])	if (list.isEmpty())
- Map<String, User> selectMap()	空 Map ({})	if (map.isEmpty())


6.
4.24 字典类型表的crud写完了
     准备些字典数据表的crud


7.
4.24晚上仔细看了看前后端分离和不分离区别
- 前后端不分离特征:
    - 路由控制权再controller层,controller层既要返回或者跳转页面路由还要把数据返回到页面上
    - 数据绑定:返回的页面中的数据由后端程序员绑定插入(jsp可能还会混合java代码)。由后端程序员决定插入数据到哪里。
    - 页面渲染:页面由服务器渲染完成
总体上:前端就写三大件,然后把三大件给后端。
    -后端如果用jsp,后端程序员要把html转为jsp,开发。
    -后端如果用thymlef模板,后端程序员不用转为jsp,直接再html里面加属性,绑数据。
    注意:这两种都是前后端不分离的情形,数据绑定都是由后端决定的。

-前后端分离特征:
    - 路由控制权在前端,前端控制页面路由的跳转,调用后端urlAPI。
    - 数据绑定自由,后端只返回数据格式json,前端获取到数据,自行决定如何展示,展示多少。
    - 页面渲染,前端页面渲染由浏览器完成。



7.今天写删除和更新字典数据的时候,发现很省力的地方。
- 删除的service层的删除返回类型void,只在controller层返回成功就行。
- 更新的service层更新返回row,只在controller层判断row就行,
原来是在service层判断row,在返回boolean类型,controller在判断boolea,这样只判断一次row更方便。
- 添加和查询该判断还是要判断的,就删除和修改可以省力点。

8.
在 MySQL 数据库中,DATE_FORMAT函数的语法为DATE_FORMAT(date, format),
其中date是要格式化的日期或日期时间表达式,format是指定的格式字符串。
- DATE_FORMAT(NOW(), '%Y-%m-%d')会将当前时间按照年-月-日的格式进行显示,如2025-04-25。
- 注意:%y表示两位,%Y表示四位。

9.
<if test="status != null and status != ''" >status = #{status},</if>
<if test="remark != null and remark != ''">remark = #{remark},</if>
再次巩固这里
- 前端不写任何东西,后端sql里面得到的是''空字符串
- 前端json中删除这个字段,后端sql里没有是null

10.throw new ServiceException(String.format("%1$s已经被分配,无法删除", dictType.getDictName()));
String.fommat()里面%1$s,别忘了s,,String.format("%1$s已经被分配,无法删除", dictType.getDictName()) 这行代码的作用是将 dictType.getDictName() 的值插入到格式字符串中 %1$s 的位置,生成一个新的字符串,

11.截至2025/4/25,字典数据表和字典类型表,基本的缓存已经全部完成和测试完成。
接下来,看那个缓存注解和刷新缓存,重置缓存的方法。

12.截至2025/4/25晚上22.46,缓存系列全部写完测完。

13.明天尽量把限流加进来。4.26,限流成功熟悉并加入

14.补充一下第七点前后端分离的情况
不论是前后端分离还是不分离,前端都是通过浏览器的网络接口向后端发送请求的。
只不过前后端不分离的html代码中的数据是由后端服务器生成的,js什么的都是前端写的,运行在浏览器里面。
- 前后端分离,前端服务器,后端服务器
1.浏览器根据输入的地址向服务器(前端服务器或与前端资源所在位置对应的服务器)发送请求,获取前端的 HTML、CSS、JavaScript 等静态资源。
2.浏览器接收并解析这些资源,构建出页面的初始结构和样式,并执行 JavaScript 代码来初始化页面的交互逻辑等。
3.当用户在页面上进行操作,触发与后端数据交互的请求时,浏览器会根据前端代码中定义的逻辑,向后端服务器发送请求,请求中通常会包含相关的参数和数据。
4.后端服务器接收到请求后,进行相应的处理,如查询数据库、进行业务逻辑计算等,然后将处理结果以 JSON、XML 等格式的数据响应给浏览器。
5.浏览器接收后端返回的数据后,会将数据交给前端的 JavaScript 代码进行处理。前端 JavaScript 代码会根据数据来更新页面的内容、样式或执行其他交互操作,而不是将数据交给前端服务器处理。前端服务器主要负责提供静态资源,在前后端分离架构中,一般不会处理后端返回给浏览器的数据。
6.所以说前端服务器只是存放静态资源用的,处理后端数据还是在浏览器里面根据js代码处理

- 前后端不分离,在浏览器上输入地址,直接访问后端服务器,后端服务器返回生成的页面和数据给浏览器解析。

- 浏览器是一个软件应用程序,负责解析前端代码,前端代码运行的环境。
- 前端是设计页面的,编写三大件代码,设计后由浏览器解析代码并渲染在浏览器显示屏上。
渲染是指将 HTML、CSS 和 JavaScript 代码转换为用户可见的页面的过程

6 rabbit MQ开发笔记

参数校验的问题汇总

参数校验分为这几种情况

1.用户密码是否为空校验

2.用户名或者密码错误校验(用户名错误直接返回空了,只能是密码错误)

3.用户不存在校验(根据用户名判断)

目前了解到的方法是

1.是否为空校验用@NotNull注解。

2.用户名不存在或者密码错误在service层校验,然后抛出自定义的异常

  • 只定义自定义异常,但是不处理,只会抛出在控制台

  • 如果捕获这个异常,需要定义捕获处理类,然后把异常以键值对形式返回给前端(就是在响应体中)

    然后前端根据错误,动态展示在页面上,以前jsp是自己写异常,自己展示页面上,没有前后端分离。

posted @ 2025-04-28 21:01  JSESSIONID  阅读(46)  评论(0)    收藏  举报