crud开发流程
序言:为吃透一个Java开发增删改查而写的项目
一、需求分析
需求:1、具体点的增删改查: 一个表(用户表:用户id(自增主键)、用户编号、用户名称、QQ、手机号、出生日期、排序),,前端界面完成该表的增删改查
2、前后端分离的模式,前端可以尝试用 vue + Element UI 来做
分析:通过一个页面展现增删改所有功能
1、navicat 建库,建表
2、后端用Java的springboot微服务搭框架
3、前端用element-admin-template前端框架,页面用vue实现
二、建数据库
1、库名:db_crud 表名:t_user 软件工具:navicat
CREATE TABLE `t_user` ( `id` int(20) NOT NULL AUTO_INCREMENT COMMENT '用户id', `user_num` int(20) DEFAULT NULL COMMENT '用户编号', `user_name` varchar(255) DEFAULT NULL COMMENT '用户名称', `qq_num` varchar(100) DEFAULT NULL COMMENT 'QQ', `phone_num` varchar(100) DEFAULT NULL COMMENT '手机号', `birth_day` date DEFAULT NULL COMMENT '出生日期', `sort` int(20) DEFAULT NULL COMMENT '排序', `pass_word` varchar(255) DEFAULT NULL COMMENT '登录密码', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8;
三、后端编码
语言:Java 软件工具:Idea(Jdk1.8、maven)
1、搭建SpringBoot项目
文件名:crud
2、pom环境
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mulan</groupId>
<artifactId>crud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>crud</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.12</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.3.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3、application.yml配置文件
server:
port: 8201
spring:
application:
name: service-crud
profiles:
active: dev
datasource:
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/db_crud?useSSL=false
jackson:
date-format: yyyy-MM-dd
time-zone: Asia/Shanghai
mybatis:
mapper-locations: classpath:/mapper/*.xml
type-aliases-package: com.mulan.crud.entity
4、建实体类entity、统一返回类R和REnum
entity
package com.mulan.crud.entity;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* chao
*/
@Data
public class User implements Serializable {
private Integer id;//用户id'
private Integer userNum;//'用户编号'
private String userName;//'用户名称'
private String qqNum;//'QQ'
private String phoneNum;//'手机号'
private Date birthDay;//'出生日期'
private Integer sort;//'排序
private String passWord;//密码
}
R
package com.mulan.crud.entity;
import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
@Getter
public class R {
private Integer code;
private String message;
private boolean success;
private Map<String,Object> data=new HashMap<String,Object>();
private R(){
}
//http协议:响应状态码200 成功
// 响应码: 20000 成功
public static R ok(){
R r = new R();
r.code=REnum.SUCCESS.getCode();
r.message=REnum.SUCCESS.getMessage();
r.success=REnum.SUCCESS.isSuccess();
return r;
}
public static R error(){
R r = new R();
r.code=REnum.ERROR.getCode();
r.message=REnum.ERROR.getMessage();
r.success=REnum.ERROR.isSuccess();
return r;
}
public R code(Integer code){
this.code=code;
return this;
}
public R message(String message){
this.message=message;
return this;
}
public R success(boolean success){
this.success=success;
return this;
}
public R data(String key,Object value){
this.data.put(key,value);
return this;
}
public R data(Map<String,Object> map){
this.data=map;
return this;
}
}
Enum
package com.mulan.crud.entity;
public enum REnum {
SUCCESS(20000, "成功", true),
ERROR(20001, "失败", false);
private Integer code;
private String message;
private boolean success;
REnum(Integer code, String message, boolean success) {
this.code = code;
this.message = message;
this.success = success;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
public boolean isSuccess() {
return success;
}
}
5、带条件首页查询
5.1、controller层
@RestController
@RequestMapping("/admin/user")
@CrossOrigin
public class UserController {
@Resource
private UserService userService;
@PostMapping("/page/{pageNum}/{pageSize}")
public R page(@PathVariable Integer pageNum,
@PathVariable Integer pageSize,
HttpServletRequest request) {
String userName = request.getParameter("userName");
Map dataMap = userService.list(pageNum, pageSize,userName);
return R.ok().data("dataMap", dataMap);
}
}
5.2、service接口
public interface UserService {
Map list(Integer pageNum, Integer pageSize,String userName);
}
5.3、service实现类
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDao;
@Override
public Map list(Integer pageNum, Integer pageSize,String userName) {
Map dataMap = new HashMap<String,Object>();
pageNum = pageNum == 0?1 : pageNum;
Integer offSet = (pageNum-1)*pageSize;
List<User> list = userDao.getUserList(offSet,pageSize,userName);
Integer totalCount = userDao.getUserTotalCount(userName);
dataMap.put("list",list);
dataMap.put("totalCount",totalCount);
return dataMap;
}
}
5.4、dao接口
@Mapper
public interface UserDao {
List<User> getUserList(@Param("offSet") Integer offSet,@Param("pageSize")
Integer getUserTotalCount(@Param("userName") String userName);
}
5.5、mapper.xml
<mapper namespace="com.mulan.crud.dao.UserDao">
<resultMap id="BaseResultMap" type="com.mulan.crud.entity.User">
<result column="user_num" property="userNum" jdbcType="INTEGER"/>
<result column="user_name" property="userName" jdbcType="VARCHAR"/>
<result column="qq_num" property="qqNum" jdbcType="VARCHAR"/>
<result column="phone_num" property="phoneNum" jdbcType="VARCHAR"/>
<result column="birth_day" property="birthDay" jdbcType="DATE"/>
<result column="sort" property="sort" jdbcType="INTEGER"/>
<result column="pass_word" property="passWord" jdbcType="VARCHAR"/>
</resultMap>
<select id="getUserList" resultMap="BaseResultMap">
select id,t_user.user_num,t_user.user_name,t_user.qq_num,t_user.phone_num,t_user.birth_day,t_user.sort
from t_user
<where>
<if test="userName != null and userName != ''">
t_user.user_name LIKE CONCAT('%', #{userName} ,'%')
</if>
</where>
LIMIT #{offSet},#{pageSize}
</select>
<select id="getUserTotalCount" resultType="java.lang.Integer">
select count(0) from t_user
<where>
<if test="userName != null and userName != ''">
t_user.user_name LIKE CONCAT('%', #{userName} ,'%')
</if>
</where>
</select>
<select id="getUserTotalCount" resultType="java.lang.Integer">
select count(0) from t_user
<where>
<if test="userName != null and userName != ''">
t_user.user_name LIKE CONCAT('%', #{userName} ,'%')
</if>
</where>
</select>
</mapper>
6、添加
6.1、controller层路由
@PostMapping("/save")
public R save(@RequestBody User user) {
userService.save(user);
return R.ok();
}
6.2、service接口
void save(User user);
6.3、serviceImpl实现类
@Override
public void save(User user) {
if(user.getId() == null){
String userName = user.getUserName();
User exUser = userDao.findUserByName(userName);
if(exUser == null){
userDao.addUser(user);
}
}else{
userDao.updateUser(user);
}
}
6.4、dao接口
void addUser( User user);
6.5、mapper.xml
<insert id="addUser" useGeneratedKeys="true" keyProperty="id" parameterType="com.mulan.crud.entity.User">
insert into t_user (user_num,user_name,qq_num,phone_num,birth_day,sort)
values(#{userNum},#{userName},#{qqNum},#{phoneNum},#{birthDay},#{sort})
</insert>
7、单个删除
7.1、controller层
@DeleteMapping("/remove/{id}")
public R remove(@PathVariable Integer id) {
boolean b = userService.removeById(id);
if (b) {
return R.ok();
} else {
return R.error();
}
}
7.2、service接口
Boolean removeById(Integer id);
7.3、serviceImpl实现类
@Override
public Boolean removeById(Integer id) {
int ret = userDao.deleteUser(id);
return ret > 0 ;
}
7.4、dao接口
int deleteUser(@Param("id") Integer id);
7.5、mapper.xml
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from t_user where id = #{id}
</delete>
8、批量删除
8.1、controller层
@DeleteMapping("/batchRemove")
public R batchRemove(@RequestBody List<Integer> ids) {
userService.removeByIds(ids);
return R.ok();
}
8.2、service接口
void removeByIds(List<Integer> ids);
8.3、serviceImpl实现类
@Override
public void removeByIds(List<Integer> ids) {
for (Integer id : ids) {
userDao.deleteUser(id);
}
}
8.4、dao接口
int deleteUser(@Param("id") Integer id);
9、修改之回显
9.1、controller层
@GetMapping("/edit/{id}")
public R edit(@PathVariable Integer id) {
User user = userService.getById(id);
return R.ok().data("user", user);
}
9.2、service接口层
User getById(Integer id);
9.3、serviceImpl实现类
@Override
public User getById(Integer id) {
return userDao.getUserById(id);
}
9.4、dao层
User getUserById(@Param("id") Integer id);
9.5、mapper.xml
<select id="getUserById" resultMap="BaseResultMap">
select id,t_user.user_num,t_user.user_name,t_user.qq_num,t_user.phone_num,t_user.birth_day,t_user.sort from t_user
where id = #{id}
</select>
10、修改之更新
10.1、controller层
@PutMapping("/update")
public R update(@RequestBody User user) {
userService.updateById(user);
return R.ok();
}
10.2、service接口
void updateById(User user);
10.3、serviceImpl
@Override
public void updateById(User user) {
userDao.updateUser(user);
}
10.4、dao接口
void updateUser(User user);
10.5、mapper
<update id="updateUser" parameterType="com.mulan.crud.entity.User">
update t_user
<set>
<if test="userNum != '' and userNum != null">
user_num = #{userNum},
</if>
<if test="userName != '' and userName != null">
user_name = #{userName},
</if>
<if test="qqNum != '' and qqNum != null">
qq_num = #{qqNum},
</if>
<if test="phoneNum != '' and phoneNum != null">
phone_num = #{phoneNum},
</if>
<if test="birthDay != null">
birth_day = #{birthDay},
</if>
<if test="sort != '' and sort != null">
sort = #{sort}
</if>
</set>
<where>
id=#{id}
</where>
</update>
11、登录
11.1、controller层
@PostMapping("/login")
public R login(@RequestBody Map<String,String> map){
return R.ok().data("token","admin-token");
}
@GetMapping("/info")
public R info(String token){
List<String> list=new ArrayList<String>();
list.add("admin");
return R.ok()
.data("roles",list)
.data("introduction","I am a super administrator")
.data("avatar","https://qna.smzdm.com/202011/09/5fa8c12bc0668869.gif_e1080.jpg")
.data("name","Super Admin");
}
12、启动类
12.1、CrudApplication
@SpringBootApplication
public class CrudApplication {
public static void main(String[] args) {
SpringApplication.run(CrudApplication.class, args);
}
}
13、配置类
10.1、MybatisConfig(Springboot整合mybatis配置)
@Configuration
@MapperScan("com.mulan.crud.dao")
public class MybatisConfig {
}
10.2、 PaginationConfig(分页配置)
@Configuration
public class PaginationConfig {
@Bean
public PaginationInterceptor getPaginationInterceptor(){
return new PaginationInterceptor();
}
}
四、前端编码
软件工具: VsCode
1、前端框架采用vue-element-admin的基础模板vue-element-template
GitHub地址:https://github.com/PanJiaChen/vue-admin-template
1.1、src下route中的index.js模板文件,会定位到页面
{
path: '/crud/user',
component: Layout,
redirect: '/crud/user/list',
name: 'Example',
meta: { title: '用户管理', icon: 'el-icon-open' },
alwaysShow:true,
children: [
{
path: 'list',
name: '用户列表',
component: () => import('@/views/crud/user/list'),
meta: { title: '用户列表', icon: 'table' }
},
{
path: 'save',
name: '用户添加',
component: () => import('@/views/crud/user/save'),
meta: { title: '用户添加', icon: 'table' },
hidden:true
},
{
path: 'edit/:id',
name: '用户修改',
component: () => import('@/views/crud/user/save'),
meta: { title: '用户修改', icon: 'table' },
hidden:true
}
]
},
1.2、src下views下list.vue展示功能页面
1.2.1、页面采用vue技术
vue页面包括<template></template> <script></script>
页面中的组件可以在element-ui中拷贝
list.vue
<template>
<div class="app-container">
<el-form :inline="true" :model="user" class="demo-form-inline">
<el-form-item label="用户名称">
<el-input v-model="user.userName" size="mini" placeholder="用户名称"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" size="mini" @click="findPage(1)" plain>查询</el-button>
<el-button type="danger" size="mini" @click="clear()" plain>清空</el-button>
</el-form-item>
</el-form>
<router-link :to="'/crud/user/save'">
<el-button type="primary" size="mini">添加</el-button>
</router-link>
<el-table
v-loading="loading"
:data="list"
stripe
style="width: 100%"
@selection-change="handleSelectionChange">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
prop="userNum"
label="用户编号"
width="180">
</el-table-column>
<el-table-column
prop="userName"
label="用户名称"
width="180">
</el-table-column>
<el-table-column
prop="qqNum"
label="QQ号">
</el-table-column>
<el-table-column
prop="phoneNum"
label="手机号"
width="180">
</el-table-column>
<el-table-column
prop="birthDay"
label="出生日期">
</el-table-column>
<el-table-column
prop="sort"
label="排序">
</el-table-column>
<el-table-column
label="操作">
<template slot-scope="scope">
<el-button-group>
<router-link :to="'/crud/user/edit/'+scope.row.id">
<el-button type="success" icon="el-icon-edit" size="mini">修改</el-button>
</router-link>
<el-button type="danger" icon="el-icon-delete" size="mini" @click="remove(scope.row.id)">删除</el-button>
</el-button-group>
</template>
</el-table-column>
</el-table>
<el-button type="danger" style="margin-top:15px" size="mini" @click="batchRemove()">批量删除</el-button>
<el-pagination
style="margin-top:15px;text-align:center"
@current-change="findPage"
:current-page="pageNum"
:page-size="pageSize"
layout="total, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</template>
<script>
import userApi from '@/api/user.js'
export default{
data() {
return {
loading:true,
pageNum: 1,
pageSize: 3,
user: {
userName:null
},
list: [],
total: 0,
selection:[]
};
},
methods: {
batchRemove(){
this.$confirm('此操作将做批量删除, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
var ids=[];
for(var i=0;i<this.selection.length;i++){
ids.push(this.selection[i].id);
}
userApi.batchRemove(ids).then(resp=>{
this.$message({
type: 'success',
message: '批量删除成功!'
});
this.findPage(this.pageNum);
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
handleSelectionChange(val) {
this.selection=val;
},
remove(id){
this.$confirm('此操作将永久删除该用户, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
userApi.remove(id).then(resp=>{
this.$message({
type: 'success',
message: '删除成功!'
});
this.findPage(this.pageNum);
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
clear(){
this.user={};
this.findPage();
},
findPage(page=1){
this.pageNum=page;
userApi.getUserListPage(this.pageNum, this.pageSize,this.user).then(resp => {
this.total= resp.data.dataMap.totalCount;
this.list = resp.data.dataMap.list;
this.loading=false
})
}
},
created() {
this.findPage();
}
}
</script>
save.vue
<template>
<div class="app-container">
<el-form ref="user" :rules="rules" :model="user" label-width="auto">
<el-form-item label="用户编号" prop="userNum">
<el-input v-model="user.userNum"></el-input>
</el-form-item>
<el-form-item label="用户名称" prop="userName">
<el-input v-model="user.userName"></el-input>
</el-form-item>
<el-form-item label="QQ" prop="qqNum">
<el-input v-model="user.qqNum"></el-input>
</el-form-item>
<el-form-item label="手机号" prop="phoneNum">
<el-input v-model="user.phoneNum"></el-input>
</el-form-item>
<el-form-item label="日期" prop="birthDay">
<el-date-picker type="date" placeholder="选择日期" v-model="user.birthDay" style="width: 100%;"></el-date-picker>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="user.sort"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="add" :disabled="disableButton">立即添加</el-button>
<el-button @click="$router.push('/crud/user/list');">取消</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import userApi from '@/api/user'
import { parseTime } from '@/utils/index.js'
export default {
data() {
var validatePhone = (rule, value, callback) => {
if (!/^1[3456789]\d{9}$/.test(value)) {
callback(new Error('手机号格式有误'));
} else {
callback();
}
};
return {
disableButton: false,
rules: {
userName: [
{ required: true, message: '请输入用户编号', trigger: 'blur' },
{ min: 3, max: 15, message: '长度在 3 到 15 个字符', trigger: 'blur' }
],
userName: [
{ required: true, message: '请输入用户名称', trigger: 'blur' },
{ min: 3, max: 15, message: '长度在 3 到 15 个字符', trigger: 'blur' }
],
qqNum: [
{ required: true, message: '请输入用户QQ号', trigger: 'blur' },
{ min: 3, max: 15, message: '长度在 3 到 15 个字符', trigger: 'blur' }
],
// phoneNum: [
// { required: true, message: '联系人电话不能为空', trigger: 'blur' },
// { validator: validatePhone, trigger: 'blur' }
// ],
birthDay: [
{ type: 'date', required: true, message: '请选择日期', trigger: 'change' }
]
},
user: {
// userNum:null
}
}
},
methods: {
add() {
this.disableButton = true;
this.$refs['user'].validate((valid) => {
if (valid) {
if (this.user.birthDay) {
this.user.birthDay = parseTime(this.user.birthDay, '{y}-{m}-{d}')
console.log( this.user)
}
if (!this.user.id) {
userApi.add(this.user).then(resp => {
this.$message.success("添加成功!");
})
} else {
userApi.update(this.user).then(resp => {
this.$message.success("修改成功!");
})
}
this.$router.push("/crud/user/list");
} else {
this.$message.error("表单有误!");
return false;
}
});
}
},
created() {
if (this.$route.params && this.$route.params.id) {
userApi.edit(this.$route.params.id).then(resp => {
this.user = resp.data.user
this.user.birthDay = new Date(this.user.birthDay);
})
}
}
}
</script>
1.3、src下api的Js文件,通过js绑定后端的controller层实现数据库的数据调用
user.js
import request from '@/utils/request'
const API = "/admin/user";
export default {
getUserListPage(pageNum, pageSize, user) {
return request({
url: `${API}/page/${pageNum}/${pageSize}`,
method: 'post',
params: user
})
},
add(user) {
return request({
url: `${API}/save`,
method: 'post',
data: user
})
},
remove(id) {
return request({
url: `${API}/remove/${id}`,
method: 'delete'
})
},
batchRemove(ids) {
return request({
url: `${API}/batchRemove`,
method: 'delete',
data: ids
})
},
update(user) {
return request({
url: `${API}/update`,
method: 'put',
data: user
})
},
edit(id) {
return request({
url: `${API}/edit/${id}`,
method: 'get',
})
}
}
export function login(data) {
return request({
url: `${API}/login`,
method: 'post',
data
})
}
export function getInfo(token) {
return request({
url: `${API}/info`,
method: 'get',
params: { token }
})
}
export function logout() {
return request({
url: `admin/user/logout`,
method: 'post'
})
}
1.4、回到页面中,调用js中定义的方法,完成前端展示数据和数据库数据调用
2、修改env.development文件API的地址,和后端端口号一致,完成前后端数据整合
# just a flag ENV = 'production' # base api VUE_APP_BASE_API = 'http://localhost:8201'
五、项目部署
1、后端部署
1.1、pom.xml文件中加maven插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
1.2、通过maven打包文件package打包工具打包,target文件下会生成一个Jar文件

1.3、通过cmd命令运行java -jar 文件名,即可完成部署
2、前端部署
2.1、env.production文件API地址改成和后端微服务地址
# just a flag ENV = 'production' # base api VUE_APP_BASE_API = 'http://localhost:8201'
2.2、main.js文件中26到29行默认文件注释掉
// if (process.env.NODE_ENV === 'production') {
// const { mockXHR } = require('../mock')
// mockXHR()
// }
2.3、VsCode终端执行 npm run build:prod命令,项目中会生成一个dist文件

2.4、在nginx的配置文件中新增sever配置,写入dist文件和端口等配置,把dist文件夹复制到nginx的根目录下,运行nginxj即可完成部署
server {
listen 9528;
server_name localhost;
location / {
root dist;
index index.html index.htm;
}
}

浙公网安备 33010602011771号