SpringBoot开源项目个人博客——(6)分类管理

实现流程成登录类似,也是MVC的3层实现。

前端部分,不详细说明。根据链接、表单的提交路径,编写对应路径的controller方法接口,model携带数据返回时,key需要对应前端的thymeleaf变量接收的名字


这里把功能全部列出来,当时笔者练习时,跟着oneStar的博客这样做,到后面业务熟悉时,可以一点点地看前端知道需要什么接口,自己写出来。

实际开发肯定是要根据前端写对应的接口,但是我用的是已有的模板。

若想了解关于接口功能的详细分析,推荐看oneStar老哥的博客

笔者博客写来是做为一个记录,就不搬别人的分析过来了。


分类管理基本的增删改查,并判断分类重复。

1.DAO持久层

dao包下创建TypeDao接口

@Repository
@Mapper
public interface TypeDao {

    //新增保存分类
    int saveType(Type type);

    //根据id查询分类
    Type getType(Long id);//编辑时可传入对应分类

    //查询所有分类
    List<Type> getAllType();

    //根据分类名称 查询分类
    Type getTypeByName(String name);//阻止 重类

    //编辑修改
    int updateType(Type type);

    //删除分类
    void deleteType(Long id);

}

2.分类管理mapper

mapper文件夹下创建TypeDao.xml文件

当Dao接口传递的参数不是基本类型或String时,需要用parameterType标识其类型,又由于在框架搭建时,使用了别名配置。entity包下的实体类都可以直接用类名来填入,否则就要写包路径+类名

<?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.hxj.dao.TypeDao">

    <!--新增,保存分类-->
    <insert id="saveType" parameterType="Type">
        insert into t_type (id, name)
        values (#{id},#{name});
    </insert>

    <!--根据id查询分类-->
    <select id="getType" resultType="Type">
        select *
        from t_type where id=#{id};
    </select>

    <!--查询所有分类-->
    <select id="getAllType" resultType="Type">
        select *
        from t_type;
    </select>

    <!--根据分类名   查询分类-->
    <select id="getTypeByName" resultType="Type">
        select *
        from t_type where name = #{name};
    </select>

    <!--编辑修改 分类-->
    <update id="updateType" parameterType="Type">
        update t_type
        set name = #{name}
        where id = #{id};
    </update>

    <!--删除分类-->
    <delete id="deleteType" >
        delete
        from t_type
        where id=#{id};
    </delete>
</mapper>

3.分类管理Service业务层

service包创建用户业务层接口UserService

public interface TypeService {

    //新增保存分类
    int saveType(Type type);

    //根据id查询分类
    Type getType(Long id);

    //查询所有分类
    List<Type> getAllType();

    //根据分类名称 查询分类
    Type getTypeByName(String name);//阻止 重类

    //编辑修改
    int updateType(Type type);

    //删除分类
    void deleteType(Long id);

}

接口实现类impl

impl子包,在其中创建用户业务层接口实现类UserServiceImpl

原本oneStar老哥在每个方法都使用了@Transactional开启事务,但是我发现开和不开对项目来说好像没有什么大影响,用户可以自己判断。(仅供参考)

@Service
public class TypeServiceImpl implements TypeService {
	@Autowired
    private TypeDao typeDao;

    @Transactional
    @Override
    public int saveType(Type type) {
        return typeDao.saveType(type);
    }

    @Transactional
    @Override
    public Type getType(Long id) {
        return typeDao.getType(id);
    }

    @Transactional
    @Override
    public List<Type> getAllType() {
        return typeDao.getAllType();
    }

    @Override
    public Type getTypeByName(String name) {
        return typeDao.getTypeByName(name);
    }

    @Transactional
    @Override
    public int updateType(Type type) {
        return typeDao.updateType(type);
    }

    @Transactional
    @Override
    public void deleteType(Long id) {
        typeDao.deleteType(id);
    }

}

4.分类管理Controller控制器

  • 使用PageHepler 进行分页显示。

页显示使用PageHelper插件具体使用可以参考oneStar的:SpringBoot引入Pagehelper分页插件


重点注意:一定要在pagehelper.startPage() 紧接着写我们的数据库操作(即调用Service),不然数据读取也无法分页。

添加PageHelper分页插件,在pom.xml中添加:

<!--引入分页插件-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.4.1</version>
        </dependency>

然后在application.yml中添加如下配置。

pagehelper:
  helper-dialect: mysql
  params: count=countSql
  reasonable: true
  support-methods-arguments: true

排错:引入后出现循环依赖的bug(用户根据情况自行调整)

在这里插入图片描述

我的版本不兼容,提高pagehelper版本 改成 1.4.1

controller.admin包下,创建TypeController,代码如下:

@Controller
@RequestMapping("/admin")
public class TypeController {

    @Autowired
    private TypeService typeService;

    //获得所有分类,查询
    @GetMapping("/types")
    public String list(Model model, @RequestParam(value ="pageNum",defaultValue = "1") Integer pageNum){
        //按照id排序
        String orderBy = "id desc";
        PageHelper.startPage(pageNum,10,orderBy);//紧跟在startpage后面的sql语句才会被执行
        List<Type> allType = typeService.getAllType();
        PageInfo<Type> pageInfo = new PageInfo<>(allType); //PageInfo用来封装页面信息,返回给前台界面,一些PageInfo的参数
        System.out.println(pageInfo.getList());
        model.addAttribute("pageInfo",pageInfo);
        return "admin/types";
    }

    @PostMapping("/refreshType")
    public String refreshType(Model model, @RequestParam(value ="pageNum",defaultValue = "1") Integer pageNum){
        //按照id排序
        String orderBy = "id desc";
        PageHelper.startPage(pageNum,10,orderBy);//紧跟在startpage后面的sql语句才会被执行
        List<Type> allType = typeService.getAllType();
        PageInfo<Type> pageInfo = new PageInfo<>(allType); //PageInfo用来封装页面信息,返回给前台界面,一些PageInfo的参数
        System.out.println(pageInfo.getList());
        model.addAttribute("pageInfo",pageInfo);

        return "admin/types :: types-list";
    }



    //进入新增界面
    @GetMapping("/types/input")
    public String input(Model model){
        model.addAttribute("type",new Type());
        return "admin/types-input";
    }

    //新增 点击提交 type
    @PostMapping("/types")
    public String post(@Validated Type type,BindingResult result ,RedirectAttributes attributes){

        Type type1 = typeService.getTypeByName(type.getName());//是否重名
        if(type1 != null){
            attributes.addFlashAttribute("message","不能添加重复的分类");
            return "redirect:/admin/types/input";
        }

        if(result.hasErrors()){
            //校验出来,发现问题,把message传前端
            return "admin/types-input";
        }

        int t = typeService.saveType(type);//保存(到数据库)
        if(t==0){
            attributes.addFlashAttribute("message","新增失败");
        }else {
            attributes.addFlashAttribute("message","新增成功");
        }
        return "redirect:/admin/types"; //回到列表展示
    }

    //跳转到"修改"分类页面
    @GetMapping("/types/{id}/input")
    public String editInput(@PathVariable Long id,Model model){
        model.addAttribute("type",typeService.getType(id)); //把要修改的 分类传
        //此时id不为空, 提交会走 不同 post请求 /admin/types/{id}(id=*{id})
        return "admin/types-input";
    }

    //编辑分类
    @PostMapping("/types/{id}")
    public String editPost(@Validated Type type,BindingResult result, RedirectAttributes attributes){
        Type type1 = typeService.getTypeByName(type.getName());


        if(type1!=null){
            attributes.addFlashAttribute("message","不能添加重复的分类");
            return "redirect:/admin/types/{id}/input"; //回到新增(有id还是进 编辑)页面
        }

        if(result.hasErrors()){
            //校验出来,发现问题,把message传前端
            return "admin/types-input";
        }

        int t = typeService.updateType(type);
        if(t==0){
            attributes.addFlashAttribute("message","编辑失败");
        }else {
            attributes.addFlashAttribute("message","编辑成功");
        }
        return "redirect:/admin/types"; //回到列表展示
    }

    @GetMapping("/types/{id}/delete")
    public String delete(@PathVariable Long id,RedirectAttributes attributes){
        typeService.deleteType(id);
        attributes.addFlashAttribute("message","删除成功");
        return "redirect:/admin/types";
    }

}

5.前后端交互

这里引用oneStar的内容

1.新增:

<a href="#" th:href="@{/admin/types/input}">
        <button type="button" class="ui teal button m-mobile-wide m-margin-top"><i class="pencil icon"></i>新增</button>
</a>

2.编辑删除:

<a href="#" th:href="@{/admin/types/{id}/input(id=${type.id})}" class="ui mini teal basic button">编辑</a>
<a href="#" th:href="@{/admin/types/{id}/delete(id=${type.id})}"  onclick="return confirm('确定要删除该分类吗?三思啊! 删了可就没了!')" class="ui mini red basic button">删除</a>

3.查询分类列表:

<a href="#" th:href="@{/admin/types}" class="teal active item">列表</a>

分页是实现,我自己进行了改动,可以看下面的内容部分。(看不懂也可以先跟着oneStar老哥来实现,当做到后面就会懂得了这些要怎么改了。)


实现分页的局部刷新

这里是笔者自己改动的功能,因为原本oneStar老哥的项目中使用到分页并不是局部刷新,在全查询的时候选择的是只传递pageNum来达到跳页的操作(体验效果一般)。

但是在笔者自己后面的一些功能功能需要配合一些查询条件时,这个时候单单传递pageNum可不能实现,于是找了一些其他人的项目,发现他们实现了局部刷新的功能,于是就把全部分页都尽量使用了局部刷新。

(自己的水平有限,实现的时候用了比较笨的方法,代码并没有复用,不过还是实现了局部刷新分页。)

当时参考的是这篇博客,如果是看oneStar的已经做了它的不是局部刷新分类管理。到后面的博客管理的分页,也可以再回来看,如果学会了,用户也可以自行改动了(实现起来并不算难)。

https://blog.csdn.net/qq369392973/article/details/108257393

这篇简单明了:

https://blog.csdn.net/aawmx123/article/details/87862304

这两篇先看

(虽然这是对后面博客管理的分页,不过如果看懂了,那么后续其余的页面的同类改动也不是问题了)

1.改前端

  • 先使用theamleaf将需要局部动态更新的区域定义成一个fragment:

注意: 一定要定义一个 隐藏域,用来存pageNum

在这里插入图片描述

  • 分页部分的改动

虽然看得懂,自己写不出来,但是会看着别人的改就行。


这里的改动方法,可以沿用到后续一切要局部分页刷新的操作。(有其他条件则可以多写data-xx参数)

<div class="two wide column" align="center">
    <a class="item" style="cursor: pointer" onclick="page(this)" th:attr="data-page=1" th:unless="${pageInfo.isFirstPage}">首页</a>
</div>

<div class="two wide column" align="center">
    <a class="item" style="cursor: pointer" onclick="page(this)" th:attr="data-page=${pageInfo.hasPreviousPage}?${pageInfo.prePage}:1" th:unless="${pageInfo.isFirstPage}">上一页</a>
</div>

<div class="eight wide column" align="center">
    <p><span th:text="${pageInfo.pageNum}"></span> 页,共 <span th:text="${pageInfo.pages}"></span> 页,有 <span th:text="${pageInfo.total}"></span> 篇文章</p>
</div>

<div class="two wide column " align="center">
    <a class="item" style="cursor: pointer" onclick="page(this)" th:attr="data-page=${pageInfo.hasNextPage}?${pageInfo.nextPage}:${pageInfo.pages}" th:unless="${pageInfo.isLastPage}">下一页</a>
</div>

<div class="two wide column " align="center">
    <a class="item" style="cursor: pointer" onclick="page(this)" th:attr="data-page=${pageInfo.pages}" th:unless="${pageInfo.isLastPage}">尾页</a>
</div>
  • 前端定义ajax提交的方法

这里目前不需要传别的条件内容,所以传一个PageNum即可。

function page(obj) {
    $("[name='page']").val($(obj).data("page"));
    //把a标签中的data-page赋值给隐藏域
    loaddata();
}

function loaddata() {
    $("#types-container").load(/*[[@{/admin/refreshType}]]*/
        "/admin/refreshType",{
            pageNum : $("[name='page']").val()
            //拿刚刚存到隐藏域的page值做为pageNum传过去
        });
}

ajax提交是post方式提交

2.后端接口

在这里插入图片描述


(保留了/types ,它不再负责跳转页了,只能作为第一次进入页面的有用)

代码没有复用,但是功能实现了,最大的差别是这里:return "admin/blogs :: types-list";

在这里插入图片描述

点击跳转页的时候,就会实现ajax的局部动态刷新页面,将搜索条件提交到后台,后台查到对应的Page对象传回前台,在对应部位局部刷新数据。

关于后端校验

视频中虽然说做后台校验是为了防止用户绕过前端js校验向后端发数据,才做这个后端校验。但是oneStar的博客中,没有把这个写清楚(实际它那样写是没有任何作用的样子)

当时看了别人的 才知道原来应该是这样写的

https://blog.csdn.net/weixin_44341916/article/details/124307452

这里就简单说一下后端校验的步骤

  1. 改实体类

使用相应注解,上面的博客链接中有相关使用说明。

在这里插入图片描述

  1. 改controller控制器

在controller的接口上加上@Validated或@valid注解@Validated Type type,BindingResult result ,想把错误信息传递到前端显示需要BindingResult result,接收校验的结果。

切记,@validated的参数一定要和BindingResult 紧跟着写,否者会出现错误,实际测试过

在这里插入图片描述

在type-input页面中写这一段,就可以把后端校验信息返回到前端了。

(这是视频中老师找的方法,这里直接粘过来用,变量名到时候改改就可以在其他地方用了)

就是当后端传递了某个属性的错误信息,检测到时就会直接显示出来。(想测试时,可以先注掉前端js 的校验才行)

在这里插入图片描述

实现效果 : 没有输入直接提交时(前端js不生效的情况)

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一个开源的Java开发框架,可以帮助开发者快速构建独立的、可扩展的应用程序。对于开源项目的bug管理,Spring Boot提供了一些方便的工具和方法。 首先,Spring Boot支持集成常用的bug管理工具,比如JIRA、Bugzilla、GitHub等。开发者可以通过配置文件将bug管理工具与Spring Boot项目集成起来,实现bug的跟踪和管理。这样,当开发者发现bug时,可以直接将bug信息反馈到指定的bug管理工具中,方便团队成员跟进和解决。 其次,Spring Boot还提供了一些内置的调试和bug追踪功能。例如,开发者可以通过Spring Boot的调试模式来排查和定位bug,以及查看程序的运行日志。同时,Spring Boot集成了一些常用的调试工具,如Spring DevTools和Actuator,可以帮助开发者快速定位和解决bug。 此外,Spring Boot还支持单元测试和集成测试,可以在项目开发过程中及时发现和修复bug。开发者可以使用JUnit等单元测试框架编写测试用例,并通过集成测试工具对项目进行全面的自动化测试。这些测试工具可以帮助开发者捕获并定位bug,提高项目的质量和稳定性。 总而言之,Spring Boot为开源项目的bug管理提供了多种工具和方法,包括集成常用的bug管理工具、提供内置的调试和bug追踪功能,以及支持单元测试和集成测试。这些功能的使用可以帮助开发者更好地管理和解决bug,提高项目的开发效率和质量。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值