项目开发笔记——传智健康项目二:简单的CRUD

目录

一、前端静态页面

以检查组部分为例子进行简单的CRUD的操作的阐述,检查组是一个典型的一对多的内容,一个检查组与多个检查项相关联,因此对其进行CRUD时涉及到其本身的表与关系表两个表的操作,所以拿出检查组来进行CRUD步骤的阐述。首先给出前端的代码

<!DOCTYPE html>
<html>
<head>
    <!-- 页面meta -->
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>传智健康</title>
    <meta name="description" content="传智健康">
    <meta name="keywords" content="传智健康">
    <meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport">
    <!-- 引入样式 -->
    <link rel="stylesheet" href="../plugins/elementui/index.css">
    <link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css">
    <link rel="stylesheet" href="../css/style.css">
    <!-- 引入组件库 -->
    <script type="text/javascript" src="../js/jquery.min.js"></script>
    <script src="../js/vue.js"></script>
    <script src="../plugins/elementui/index.js"></script>
    <script src="../js/axios-0.18.0.js"></script>
    <style>
        .datatable {
            position: relative;
            box-sizing: border-box;
            -webkit-box-flex: 1;
            width: 100%;
            max-width: 100%;
            font-size: 14px;
            color: rgb(96, 98, 102);
            overflow: hidden;
            flex: 1 1 0%;
        }

        .datatable td, .datatable th {
            padding: 12px 0;
            min-width: 0;
            -webkit-box-sizing: border-box;
            box-sizing: border-box;
            text-overflow: ellipsis;
            vertical-align: middle;
            position: relative;
            text-align: left;
        }
    </style>
</head>
<body class="hold-transition">
<div id="app">
    <div class="content-header">
        <h1>预约管理<small>检查组管理</small></h1>
        <el-breadcrumb separator-class="el-icon-arrow-right" class="breadcrumb">
            <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
            <el-breadcrumb-item>预约管理</el-breadcrumb-item>
            <el-breadcrumb-item>检查组管理</el-breadcrumb-item>
        </el-breadcrumb>
    </div>
    <div class="app-container">
        <div class="box">
            <div class="filter-container">
                <el-input placeholder="编码/名称/助记码" v-model="pagination.queryString" style="width: 200px;"
                          class="filter-item" @keyup.enter.native="handleFilter"></el-input>
                <el-button @click="_findPage()" class="dalfBut">查询</el-button>
                <el-button type="primary" class="butT" @click="handleCreate()">新建</el-button>
            </div>
            <el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row>
                <el-table-column type="index" align="center" label="序号"></el-table-column>
                <el-table-column prop="code" label="检查组编码" align="center"></el-table-column>
                <el-table-column prop="name" label="检查组名称" align="center"></el-table-column>
                <el-table-column label="适用性别" align="center">
                    <template slot-scope="scope">
                        <span>{{ scope.row.sex == '0' ? '不限' : scope.row.sex == '1' ? '男' : '女'}}</span>
                    </template>
                </el-table-column>
                <el-table-column prop="helpCode" label="助记码" align="center"></el-table-column>
                <el-table-column prop="remark" label="说明" align="center"></el-table-column>
                <el-table-column label="操作" align="center">
                    <template slot-scope="scope">
                        <el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
                        <el-button size="mini" type="danger" @click="handleDelete(scope.row)">删除</el-button>
                    </template>
                </el-table-column>
            </el-table>
            <div class="pagination-container">
                <el-pagination
                        class="pagiantion"
                        @current-change="handleCurrentChange"
                        :current-page="pagination.currentPage"
                        :page-size="pagination.pageSize"
                        layout="total, prev, pager, next, jumper"
                        :total="pagination.total">
                </el-pagination>
            </div>
            <!-- 新增标签弹层 -->
            <div class="add-form">
                <el-dialog title="新增检查组" :visible.sync="dialogFormVisible">
                    <template>
                        <el-tabs v-model="activeName" type="card">
                            <el-tab-pane label="基本信息" name="first">
                                <el-form label-position="right" label-width="100px">
                                    <el-row>
                                        <el-col :span="12">
                                            <el-form-item label="编码">
                                                <el-input v-model="formData.code"/>
                                            </el-form-item>
                                        </el-col>
                                        <el-col :span="12">
                                            <el-form-item label="名称">
                                                <el-input v-model="formData.name"/>
                                            </el-form-item>
                                        </el-col>
                                    </el-row>
                                    <el-row>
                                        <el-col :span="12">
                                            <el-form-item label="适用性别">
                                                <el-select v-model="formData.sex">
                                                    <el-option label="不限" value="0"></el-option>
                                                    <el-option label="男" value="1"></el-option>
                                                    <el-option label="女" value="2"></el-option>
                                                </el-select>
                                            </el-form-item>
                                        </el-col>
                                        <el-col :span="12">
                                            <el-form-item label="助记码">
                                                <el-input v-model="formData.helpCode"/>
                                            </el-form-item>
                                        </el-col>
                                    </el-row>
                                    <el-row>
                                        <el-col :span="24">
                                            <el-form-item label="说明">
                                                <el-input v-model="formData.remark" type="textarea"></el-input>
                                            </el-form-item>
                                        </el-col>
                                    </el-row>
                                    <el-row>
                                        <el-col :span="24">
                                            <el-form-item label="注意事项">
                                                <el-input v-model="formData.attention" type="textarea"></el-input>
                                            </el-form-item>
                                        </el-col>
                                    </el-row>
                                </el-form>
                            </el-tab-pane>
                            <el-tab-pane label="检查项信息" name="second">
                                <div class="checkScrol">
                                    <table class="datatable">
                                        <thead>
                                        <tr>
                                            <th>选择</th>
                                            <th>项目编码</th>
                                            <th>项目名称</th>
                                            <th>项目说明</th>
                                        </tr>
                                        </thead>
                                        <tbody>
                                        <tr v-for="c in tableData">
                                            <td>
                                                <input :id="c.id" v-model="checkitemIds" type="checkbox" :value="c.id">
                                            </td>
                                            <td><label :for="c.id">{{c.code}}</label></td>
                                            <td><label :for="c.id">{{c.name}}</label></td>
                                            <td><label :for="c.id">{{c.remark}}</label></td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </div>
                            </el-tab-pane>
                        </el-tabs>
                    </template>
                    <div slot="footer" class="dialog-footer">
                        <el-button @click="dialogFormVisible = false">取消</el-button>
                        <el-button type="primary" @click="handleAdd()">确定</el-button>
                    </div>
                </el-dialog>
            </div>

            <!-- 编辑标签弹层 -->
            <div class="add-form">
                <el-dialog title="编辑检查组" :visible.sync="dialogFormVisible4Edit">
                    <template>
                        <el-tabs v-model="activeName" type="card">
                            <el-tab-pane label="基本信息" name="first">
                                <el-form label-position="right" label-width="100px">
                                    <el-row>
                                        <el-col :span="12">
                                            <el-form-item label="编码">
                                                <el-input v-model="formData.code"/>
                                            </el-form-item>
                                        </el-col>
                                        <el-col :span="12">
                                            <el-form-item label="名称">
                                                <el-input v-model="formData.name"/>
                                            </el-form-item>
                                        </el-col>
                                    </el-row>
                                    <el-row>
                                        <el-col :span="12">
                                            <el-form-item label="适用性别">
                                                <el-select v-model="formData.sex">
                                                    <el-option label="不限" value="0"></el-option>
                                                    <el-option label="男" value="1"></el-option>
                                                    <el-option label="女" value="2"></el-option>
                                                </el-select>
                                            </el-form-item>
                                        </el-col>
                                        <el-col :span="12">
                                            <el-form-item label="助记码">
                                                <el-input v-model="formData.helpCode"/>
                                            </el-form-item>
                                        </el-col>
                                    </el-row>
                                    <el-row>
                                        <el-col :span="24">
                                            <el-form-item label="说明">
                                                <el-input v-model="formData.remark" type="textarea"></el-input>
                                            </el-form-item>
                                        </el-col>
                                    </el-row>
                                    <el-row>
                                        <el-col :span="24">
                                            <el-form-item label="注意事项">
                                                <el-input v-model="formData.attention" type="textarea"></el-input>
                                            </el-form-item>
                                        </el-col>
                                    </el-row>
                                </el-form>
                            </el-tab-pane>
                            <el-tab-pane label="检查项信息" name="second">
                                <div class="checkScrol">
                                    <table class="datatable">
                                        <thead>
                                        <tr>
                                            <th>选择</th>
                                            <th>项目编码</th>
                                            <th>项目名称</th>
                                            <th>项目说明</th>
                                        </tr>
                                        </thead>
                                        <tbody>
                                        <tr v-for="c in tableData">
                                            <td>
                                                <input :id="c.id" v-model="checkitemIds" type="checkbox" :value="c.id">
                                            </td>
                                            <td><label :for="c.id">{{c.code}}</label></td>
                                            <td><label :for="c.id">{{c.name}}</label></td>
                                            <td><label :for="c.id">{{c.remark}}</label></td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </div>
                            </el-tab-pane>
                        </el-tabs>
                    </template>
                    <div slot="footer" class="dialog-footer">
                        <el-button @click="dialogFormVisible4Edit = false">取消</el-button>
                        <el-button type="primary" @click="handleEdit()">确定</el-button>
                    </div>
                </el-dialog>
            </div>
        </div>
    </div>
</div>
</body>

这里面主要是用element-ui做了一些样式,不是我们的重点,我们主要看数据绑定与方法定义部分

<script>
    var vue = new Vue({
        el: '#app',
        data: {
            activeName: 'first',//添加/编辑窗口Tab标签名称
            pagination: {//分页相关属性
                currentPage: 1,
                pageSize: 10,
                total: 100,
                queryString: null,
            },
            dataList: [],//列表数据
            formData: {},//表单数据
            tableData: [],//新增和编辑表单中对应的检查项列表数据
            checkitemIds: [],//新增和编辑表单中检查项对应的复选框,基于双向绑定可以进行回显和数据提交
            dialogFormVisible: false,//控制添加窗口显示/隐藏
            dialogFormVisible4Edit: false//控制编辑窗口显示/隐藏
        },
        //启动即运行的钩子函数
        created() {},
        methods: {
            handleEdit() {},
            handleAdd() {},
          	findPage() {},
            resetForm() {},
            handleCreate() {},
            handleUpdate(row) {},
            handleCurrentChange(currentPage) {},
            handleDelete(row) {}
        }
    })
</script>

核心的数据是四个

 		dataList: [],//列表数据
        formData: {},//表单数据
        tableData: [],//新增和编辑表单中对应的检查项列表数据
        checkitemIds: [],//新增和编辑表单中检查项对应的复选框,基于双向绑定可以进行回显和数据提交
  • dataList 表示已有的检查组对象组成的数组(当然检查组对象是以JSON数据的形式存在的)
  • formData 表单当中的数据(表单指新建和编辑页面弹出来的表单)
  • tableData 检查组下属的检查项有多少可供选择
  • checkitemids 当前检查组已经旋律哪些检查项

明确了前端这些数据的意义之后就可以从后端拿数据了,下面就对这些数据的CRUD进行阐述

二、数据添加

数据的添加部分对应了handleCreate()与handleAdd(),当点击新建按钮时,触发handleCreate(),当在新建表单中点击确认按钮进行提交时,触发handleAdd()。

2.1、前端页面

该方法需要实现两个功能

  1. 重置表单,清除上次输入的数据(页面是会保存上次输入的数据的)
  2. 弹出新建的窗口
  3. 提供可供选择的检查项内容

重置表单只需要将formData置空即可,弹出新窗口也可以通过改变dialogFormVisible的值来实现,而可供选择的检查项就需要后端来提供了,这里采用axios的异步请求方式来写,下面给出代码

			// 重置表单
            resetForm() {
                this.formData = {};
            },
			// 弹出添加窗口
            handleCreate() {
                //重置表单数据
                this.resetForm();
                this.checkitemIds = [];
                this.activeName = 'first';
                //查询所有检查项,赋值给tableData
                axios.get("/checkItem/findAll.do").then((res) => {
                    if (res.data.flag == true) {
                        this.tableData = res.data.data;
                    } else {
                        this.$message.error(res.data.message);
                    }
                });
                //新增窗口可视化
                this.dialogFormVisible = true;
            },

这里的res.data是固定写法,代表了后端传来的JSON数据,我们的后端都是采用了如下的结果形式进行封装

public class Result implements Serializable{
    private boolean flag;//执行结果,true为执行成功 false为执行失败
    private String message;//返回结果信息,主要用于页面提示信息
    private Object data;//返回数据
    }

因此res.data.dara就可以拿到后端传来了数据了,而res.data.flag代表了数据处理成功与否,res.data.message代表了应该弹出怎样的提示信息(提示信息可以单独封装一个MessageConstant类,定义常量字符串)
axios使用get请求发送至“/checkItem/findAll.do”进行处理,得到可供选择的选项,最后进行可视化

			//添加
            handleAdd() {
                //对于axios,post请求的第二个参数一定要是json数据格式,否则的话就使用?拼接就好了
                axios.post("/checkGroup/add.do?checkItemIds=" + this.checkitemIds, this.formData).then((res) => {
                    if (res.data.flag == true) {
                        this.$message({
                            type: 'success',
                            message: res.data.message
                        })
                        this.dialogFormVisible = false;
                        this.findPage();
                    } else {
                        this.$message.error(res.data.message)
                    }
                })
            },

表单填写好之后的提交也与之类似,通过axios发送异步请求,但是这里的post方法给了两个参数,第一个参数采用?拼接了选好的检查项的id数组,因为这个数组不需要转化成json格式,直接往后端传就可以了,因此采用了?拼接的方式,而需要转成JSON数组的formData则作为了第二个参数
前端数据传送之后就可以进行处理了

2.2、检查项的数据显示

2.2.1、Controller

通过后台模块SpringMVC的配置

  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

可以知道,所有以.do结尾的请求都会交由SpringMVC处理,下面就对该部分进行阐述。
在持久层中写下如下的代码


/**
 * 检查项管理
 */
//RestController注解相当于Controller与RestBody注解的合体,返回的数据会自动地被Spring转换为json数据
@RestController
@RequestMapping("/checkItem")
public class CheckItemController {

    @Reference//用于从查找服务
    private CheckItemService checkItemService;

    @RequestMapping("/findAll")
    public Result findAll(){
        try {
            return new Result(true,MessageConstant.GET_ORDERSETTING_SUCCESS,checkItemService.findAll());
        } catch (Exception e) {
            return new Result(false, MessageConstant.GET_ORDERSETTING_FAIL);
        }
    }
}

采用了注解驱动开发的方式,RestController注解相当于RestBody注解与Controller注解合一,表示该类下方法的返回值都会被处理成Json数据的格式发送至前端,因此在各个方法中只需要最后统一返回Result即可,框架会自己将其处理成JSON数据并发送。

    @Reference//从注册中心查找服务
    private CheckItemService checkItemService;

表示checkItemService是从zookeeper注册中心中获得的服务,因为服务提供方是另一个注册于注册中心中的模块所提供的,因此此处不使用Spring中的Autowired注解,但是一定注意,这里的Reference注解是dubbo相关的,千万不要导错了包。

既然service类是从注册中心得到的,那这里为何能通过编译呢?
这是由于项目中install了一个单独的接口模块,这里标示着各种各个项目共同需要的接口,controller层使用Reference注解获得其实现类的服务时,
实现类那里也需要在Service注解上声明自己实现的是哪个接口,这个后面会说

RequestMapping注解很好理解,就是对应的url路径,需要说明的是,这里可以不写.do后缀,因为框架会自动加上.do后缀,同样的前面的斜线也是可以不加的,框架也会自动加,但是还是建议加上。
再看代码内部,采用了try catch进行处理,分别返回成功与失败的结果,如果成功,返回的结果里还包裹了查询出来的结果

2.2.2、Service

service层来自另一个项目,其Spring配置中也这样写了dubbo的配置,这样一来后台模块完全可以通过注册中心得到提供者的服务来使用了

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/mvc
                            http://www.springframework.org/schema/mvc/spring-mvc.xsd
                            http://code.alibabatech.com/schema/dubbo
                            http://code.alibabatech.com/schema/dubbo/dubbo.xsd
                            http://www.springframework.org/schema/context
                            http://www.springframework.org/schema/context/spring-context.xsd">

    <!--dubbo相关配置-->
    <!-- 指定应用名称 -->
    <dubbo:application name="health_service_provider"/>
    <!--指定暴露服务的端口,如果不指定默认为20880-->
    <dubbo:protocol name="dubbo" port="20887"/>
    <!--指定服务注册中心地址-->
    <dubbo:registry address="zookeeper://192.168.23.129:2181"/>
    <!--批量扫描,发布服务-->
    <dubbo:annotation package="com.cui.service.impl"/>
</beans>

业务层代码如下,注意这里的Service注解也是dubbo中的,不要写错了写成Spring里头的
Autowired就是Spring的Ioc了

/**
 * 检查项服务
 */
//使用了事务处理时,dubbo的Service注解必须声明具体实现了哪个接口
@Service(interfaceClass = CheckItemService.class)
@Transactional
public class CheckItemServiceImpl implements CheckItemService {
    //注入dao对象
    @Autowired
    private CheckItemDao checkItemDao;

    @Override
    public List<CheckItem> findAll() {
        return checkItemDao.findAll();
    }
}

业务层代码相对简单,直接调用了持久层

2.2.3、Dao

@Repository
public interface CheckItemDao {
    List<CheckItem> findAll();
}

持久层是个接口,加Repository注解以便Ioc注入,相对应的Mybatis的配置文件如下

<?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.cui.dao.CheckItemDao">
    <!--注意这里对于List写的是List内部的Type-->
    <select id="findAll" resultType="com.cui.pojo.CheckItem">
        select * from t_checkitem
    </select>
</mapper>

注意当接口的方法中,返回值类型为List是,Mybatis的映射配置文件只需要写泛型的类型即可,Mybatis会自动进行处理
如果是要传递多个参数,就需要进行封装了,比如id、name、code这一串是封装成了CheckItem对象,两个参数可以采用Map进行封装,在Mybatis中通过#{key}进行获取,当然也可以是多参数采用@Param注解的方式

这样一通操作之后,就可以显示出可供选择的检查项了

2.3、表单提交

因为Spring的配置,注册中心等内容前文都描述了,因此此处不再赘述,只给出代码的实现与部分说明

2.3.1、Controller

!!!!!!!!!重要!!!!!!!!!!
这里需要注意的是,在类上加了RequestBody注解能够使类中方法的返回值处理为JSON数据格式,而前端传来的数据则是置于形参中进行处理的,当形参是数组和基本数据类型时,不需要做处理,当形参是其他对象时,需要加入@RequestBody注解,使得传来的JSON数据能被解析成对应的对象


/**
 * 检查组控制层
 */
@RestController
@RequestMapping("/checkGroup")
public class CheckGroupController {

    @Reference//从注册中心查找服务
    private CheckGroupService checkGroupService;

    @RequestMapping("/add")
    public Result add(@RequestBody CheckGroup checkGroup, Integer[] checkItemIds) {
        try {
            checkGroupService.add(checkGroup, checkItemIds);
            return new Result(true, MessageConstant.ADD_CHECKGROUP_SUCCESS);
        } catch (Exception e) {
            e.printStackTrace();
            return new Result(false, MessageConstant.ADD_CHECKGROUP_FAIL);
        }
    }
}

2.3.2、Service

@Service(interfaceClass = CheckGroupService.class)
@Transactional
public class CheckGroupServiceImpl implements CheckGroupService{

    @Autowired
    private CheckGroupDao checkGroupDao;
    @Override
    public void add(CheckGroup checkGroup, Integer[] checkItemIds) {
        //新增检查组
        checkGroupDao.add(checkGroup);
        //配置检查组与检查项的关联关系
        //获取id(此处来自mybatis中的配置)
        Integer checkGroupId = checkGroup.getId();

        addRelationship(checkGroupId,checkItemIds);
    }
    public void addRelationship(Integer checkGroupId, Integer[] checkItemIds){
        if (checkItemIds != null && checkItemIds.length > 0)
            for (Integer checkItemId : checkItemIds) {
                //在向Mybatis传递参数时,必须要注重的一个点就是封装,把多个参数封装成一个对象
                //比如这里的双参数就封装成了HashMap对象
                Map<String, Integer> map = new HashMap<>();
                map.put("checkGroupId", checkGroupId);
                map.put("checkItemId",checkItemId);
                checkGroupDao.setCheckGroupAndItem(map);
            }
    }
}

Service层需要处理两件事情,第一是新增检查组本身的内容,第二是更新关系表中的内容,在Dao层中使用了两套方法,其中重点在于获取新增id

2.3.3、Dao

@Repository
public interface CheckGroupDao {
    void add(CheckGroup checkGroup);
    void deleteRelationship(Integer checkGroupId);
}

映射配置文件,注意如何获取最新的Id,这里采用的是selectKey标签中的 select LASAT_INSERT_ID()方法

<?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.cui.dao.CheckGroupDao">

    <!--插入检查组数据-->
    <insert id="add" parameterType="com.cui.pojo.CheckGroup">
        <!--获得最新产生的id值-->
        <selectKey resultType="Integer" order="AFTER" keyProperty="id">
            select LAST_INSERT_ID()
        </selectKey>
        insert into t_checkgroup
            (code, name, helpCode, sex, remark, attention)
        values
            (#{code}, #{name}, #{helpCode}, #{sex}, #{remark}, #{attention})
    </insert>

    <!--设置检查组和检查项多对多关系-->
    <insert id="setCheckGroupAndItem" parameterType="map">
        insert into t_checkgroup_checkitem(checkgroup_id,checkitem_id)
        values
        (#{checkGroupId},#{checkItemId})
    </insert>
</mapper>

三、数据分页显示

2.1、前端页面

            //分页查询
            findPage() {
                var param = {
                    currentPage: this.pagination.currentPage,
                    pageSize: this.pagination.pageSize,
                    queryString: this.pagination.queryString
                }
                axios.post("/checkGroup/findPage.do", param).then((res) => {
                    this.pagination.total = res.data.total;
                    this.dataList = res.data.rows;
                });
            },

分页查询需要四个参数

  • 数据
  • 数据总条数
  • 当前页码
  • 每页显示的数据条数

当前页码和每页显示的数据条数是给好的,只需要从后端得到数据和数据的总条数就可以了,当然这里的数据是在查询条件下得出的
因此总结下来,前端向后端传输三个参数:页码、每页记录数、查询条件,封装其为QueryPageBean类如下

public class QueryPageBean implements Serializable{
    private Integer currentPage;//页码
    private Integer pageSize;//每页记录数
    private String queryString;//查询条件
    }

后端向前端传递两个参数,数据总条数和数据,封装其为PageResult类如下

public class PageResult implements Serializable{
    private Long total;//总记录数
    private List rows;//当前页结果
    public PageResult(Long total, List rows) {
        super();
        this.total = total;
        this.rows = rows;
    }
}

注意这些需要在前后端通过JSON进行传输的结果类与查询类都要实现serializable接口

2.2、数据查询

2.2.1、Controller

代码如下,需要返回PageResult对象

/**
 * 检查组控制层
 */
@RestController
@RequestMapping("/checkGroup")
public class CheckGroupController {

    @Reference//从注册中心查找服务
    private CheckGroupService checkGroupService;

    @RequestMapping("/findPage")
    //前端传来的JSON数据在形参中一定要加RequestBody注解才能够被正确解析,一定注意
    public PageResult findPage(@RequestBody QueryPageBean queryPageBean) {
        return checkGroupService.findPage(queryPageBean);
    }
}

2.2.2、Service

这里主要是PageHelper的使用,先设置好当前页,再去查询数据,就可以得到Page对象,而Page对象由提供了getTotal和getResult方法,这样就可以封装PageResult对象了

@Service(interfaceClass = CheckGroupService.class)
@Transactional
public class CheckGroupServiceImpl implements CheckGroupService{

    @Autowired
    private CheckGroupDao checkGroupDao;

    @Override
    public PageResult findPage(QueryPageBean queryPageBean) {
		//获取相关参数
        Integer pageSize = queryPageBean.getPageSize();
        Integer currentPage = queryPageBean.getCurrentPage();
        String queryString = queryPageBean.getQueryString();

		//设置页面
        PageHelper.startPage(currentPage, pageSize);
        //根据关键字查询
        Page<CheckGroup> page = checkGroupDao.findByCondition(queryString);

		//封装PageResult对象
        PageResult pageResult = new PageResult(page.getTotal(), page.getResult());

        return pageResult;
    }
}

2.2.3、Dao

@Repository
public interface CheckGroupDao {
    Page<CheckGroup> findByCondition(String queryString);
}

同样的在映射配置文件中,parameterType只需要写泛型就可以了

    <!--分页查询-->
    <select id="findByCondition" parameterType="String" resultType="com.cui.pojo.CheckGroup">
        select * from t_checkgroup
        <if test="value != null and value.length > 0">
            where code = #{value} or name = #{value}
        </if>
    </select>

注意这里使用了动态sql语句进行相关的拼接

四、数据删除

4.1、前端界面

删除需要获得相对应的条目的id,这个id是通过row这个参数间接得到的,具体前端代码如下

 // 删除
            handleDelete(row) {
                this.$confirm("确定删除?", "提示", {
                    type: 'warning'
                }).then(() => {
                    var checkGroupId = row.id;
                    //发送异步请求
                    //如果还是采用双变量是会返回空的
                    axios.post("/checkGroup/delete.do?checkGroupId=" + checkGroupId).then((res) => {
                        //根据数据返回情况进行处理
                        if (res.data.flag == true) {
                            this.$message({
                                message: res.data.message,
                                type: 'success'
                            });
                            this.findPage();
                        } else {
                            this.$message({
                                message: res.error(data.message),
                            });
                        }
                    });
                }).catch(() => {
                    this.$message({
                        message: '操作已取消',
                        type: 'info'
                    });
                })

4.2、数据删除

4.2.1、Controller

Controller层具体实现如下

 @RequestMapping("/delete")
    public Result delete(Integer checkGroupId) {
        try {
            checkGroupService.delete(checkGroupId);
            return new Result(true, MessageConstant.DELETE_CHECKGROUP_SUCCESS);
        } catch (Exception e) {
            e.printStackTrace();
            return new Result(false, MessageConstant.DELETE_CHECKGROUP_FAIL);
        }
    }

4.2.1、Service

Service层具体实现如下

    @Override
    public void delete(Integer checkGroupId) {

        //一定注意有外键关系时要先删除外键关系,否则会报错

        //删除相对应的映射关系
        checkGroupDao.deleteRelationship(checkGroupId);

        //删除检查组本身的数据
        checkGroupDao.delete(checkGroupId);
    }

但是需要注意一个问题,checkgroup的主键id是与关系表相关联的,因此删除时必须先删除关系表中的内容再去删除Group本身的内容,如果顺序反了就会报错

Unknown error 1451; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Unknown error 1451

4.2.1、Dao

Dao层,朴实无华

    void delete(Integer checkGroupId);

    void deleteRelationship(Integer checkGroupId);
    <!--删除检查组数据-->
    <delete id="delete" parameterType="Integer">
        delete from t_checkgroup where id = #{value}
    </delete>

    <!--删除对应关系-->
    <delete id="deleteRelationship" parameterType="Integer">
        delete from t_checkgroup_checkitem where checkgroup_id = #{value}
    </delete>

五、数据编辑

5.1、前端页面

编辑页面涉及更多的表单回显,具体实现不再赘述,跟获得可供选择的选项基本一致,因此只说表单更新的部分

			//编辑
            handleEdit() {
                //对于axios,post请求的第二个参数一定要是json数据格式,否则的话就使用?拼接就好了
                axios.post("/checkGroup/edit.do?checkItemIds=" + this.checkitemIds, this.formData).then((res) => {
                    if (res.data.flag == true) {
                        this.$message({
                            type: 'success',
                            message: res.data.message
                        })
                        this.dialogFormVisible4Edit = false;
                        this.findPage();
                    } else {
                        this.$message.error(res.data.message)
                    }
                })
            },
            // 弹出编辑窗口
            handleUpdate(row) {
                this.activeName = 'first';
                var checkGroupId = row.id;
                //查询当前检查组的数据,进行回显
                axios.get("/checkGroup/findById.do?checkGroupId=" + checkGroupId).then((res) => {
                    if (res.data.flag == true) {
                        this.formData = res.data.data;
                    } else {
                        this.$message.error(res.data.message);
                    }
                });
            

5.2、表单提交

5.2.1、Controller

    @RequestMapping("/edit")
    public Result edit(@RequestBody CheckGroup checkGroup, Integer[] checkItemIds) {
        try {
            checkGroupService.edit(checkGroup, checkItemIds);
            return new Result(true, MessageConstant.EDIT_CHECKGROUP_SUCCESS);
        } catch (Exception e) {
            e.printStackTrace();
            return new Result(false, MessageConstant.EDIT_CHECKGROUP_FAIL);
        }
    }

5.2.2、Service

一是要更新checkGroup本身的内容,二是要把关系表进行更新,这里采取的方式是删除原来的,增加新的

    @Override
    public void edit(CheckGroup checkGroup, Integer[] checkItemIds) {
        //更新检查组数据
        checkGroupDao.update(checkGroup);
        //更新关系数据
        Integer checkGroupId = checkGroup.getId();
        checkGroupDao.deleteRelationship(checkGroupId);
        addRelationship(checkGroupId,checkItemIds);
    }

5.2.3、Dao

void update(CheckGroup checkGroup);

deleteRelationship在删除部分已经说了,添加关系表的操作在新增那里也有,因此不再赘述

    <update id="update" parameterType="com.cui.pojo.CheckGroup">
        update t_checkgroup
        set
            code = #{code},
            name = #{name},
            helpCode = #{helpCode},
            sex = #{sex},
            remark = #{remark},
            attention = #{attention}
        where id = #{id}
    </update>

至此,简单的CRUD已经完成说明

上一篇:一个基于SB_MYBATIS_JSP的CRUD演示项目


下一篇:20211027模拟赛