项目讲解
具体功能如何实现
需要到的技术(纯后端开发)
josn+spring boot+mybatis puls+mysql数据库
什么是josn:
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。
翻译人能听懂的话就是 我从后端传到前端的数据能显示出来前端需要的格式
什么是spring boot:
从最根本上来讲,Spring Boot就是一些库的集合,它能够被任意项目的构建系统所使用。
翻译成人能听懂的话就是spring boot 就是一个框架
特点: 自动装配,控制反转
什么是Mybatis:
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
翻译成人能听懂的话就是Mybatis就是一个高级的JDBC
在翻译就是java语言怎么和数据库连接
这就是Mybatis!
什么是mysql数据库:
不想说...
一个java web项目主要分为一下三层
- contorller
- servise
- dao
流程图:
controller层
- 最重要的一点就是提供api接口与前端交互
以下是我截取的片段代码(controller层)具体的一个前端交互实现
@Api(tags = "设备台账-设备列表")
@RestController
@RequestMapping("/ledger")
@Slf4j
public class LedgerController {
@Autowired
private ApparatusInfoService apparatusInfoService;
@Autowired
private SysDdictionariesService sysDdictionariesService;
@Autowired
private ApparatusProcessService apparatusProcessService;
@Autowired
private ApparatusStatsService apparatusStatsService;
@SysLog("创建设备")
@ApiOperation("创建")
@PostMapping("/apparatus")
@RequiresPermissions("ledger:apparatusinfo:save")
public R create(@RequestBody ApparatusInfoDTO apparatusInfoDTO) {
String categoryName = apparatusInfoDTO.getCategoryName();
if (StringUtils.isEmpty(categoryName) ||
StringUtils.isEmpty(apparatusInfoDTO.getSn()) ||
StringUtils.isEmpty(apparatusInfoDTO.getUsestate())) {
log.error("faield to ledger create, 必要参数为空");
return R.ok("创建失败,请填写完整信息");
}
boolean b = apparatusInfoService.createCheckSn(apparatusInfoDTO.getSn());
if (!b) {
log.error("faield to ledger create, 设备编码已存在");
return R.error("设备编码已存在");
}
JSONObject infoTemplate = null;
String statsInfo = null;
try {
statsInfo = JsonUtils.getStrToJson(categoryName, "realtime");
} catch (Exception e) {
log.error("faield to ledger create statsInfo", e);
return R.error("创建失败");
}
ApparatusStatsEntity status = new ApparatusStatsEntity();
String infoString = apparatusInfoDTO.getInfoString();
if (StringUtils.isEmpty(infoString)) {
try {
infoTemplate = apparatusInfoService.getInfoTemplate(categoryName, "static");
apparatusInfoDTO.setInfo(infoTemplate);
} catch (Exception e) {
log.error("faield to ledger create infoTemplate", e);
return R.error("创建失败");
}
} else {
JSONObject info = JSONObject.parseObject(infoString);
apparatusInfoDTO.setInfo(info);
}
SysUserEntity user = (SysUserEntity) SecurityUtils.getSubject().getPrincipal();
apparatusInfoDTO.setCreatetime(new Date());
JSONObject statusTemplate = (JSONObject) JSONObject.parse(statsInfo);
apparatusInfoDTO.setStatusthreshold(statusTemplate);
apparatusInfoDTO.setCreateuser(user.getUsername());
ApparatusInfoEntity apparatusInfo = ApparatusInfoDTO.getInfoEntity(apparatusInfoDTO);
try {
apparatusInfoService.save(apparatusInfo);
Long infoid = apparatusInfoService.selectInfoid(apparatusInfoDTO.getSn());
status.setInfoid(infoid);
status.setInfo(statsInfo);
status.setCreatetime(new Date());
status.setCreateuser(user.getUsername());
apparatusStatsService.createStatus(status);
log.info("ledger create, infoEntity:{}, apparatusInfoDTO:{}", apparatusInfo, apparatusInfoDTO);
return R.ok("创建成功");
} catch (Exception e) {
log.error("faield to ledger create createStatus", e);
return R.error("创建失败");
}
}
}
//Class R 返回状态码与信息
public class R extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
public R() {
put("code", 0);
put("msg", "success");
}
public static R error() {
return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员");
}
public static R error(String msg) {
return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);
}
public static R error(int code, String msg) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static R ok(String msg) {
R r = new R();
r.put("msg", msg);
return r;
}
public static R ok(Map<String, Object> map) {
R r = new R();
r.putAll(map);
return r;
}
public static R ok() {
return new R();
}
public R put(String key, Object value) {
super.put(key, value);
return this;
}
}
程序开头 有这4个注解
注解:spring 4.0特性 帮助开发
分析一下这4个注解
@Api(tags = "设备台账-设备列表")这是个swagerr注解
表示标识这个类是swagger的资源
@RestController
@RestController注解是@Controller和@ResponseBody的合集,表示这是个控制器bean,并且是将函数的返回值直 接填入HTTP响应体中,是REST风格的控制器。
翻译一下就是 后台返回数据用这个
@RequestMapping(/ledger)
@RequestMapping:提供路由信息,负责URL到Controller中的具体函数的映射。
就是相当于返回映射到这个url下 http://localhost :8080/xxx/ledger
@Slf4j
log的注解 打印日志到哪里的注解 (java代码规范有写:log可以设定级别,可以控制输出到哪里,容易区分是在代码的什么地方打印的,而System.out.print则不行。而且,System.out.print的速度很慢。所以,除非是有意的,否则,都要用log。至少在提交到svn之前把System.out.print换成log。--来源gittab)
再往下看代码
@Autowired
@Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired的使用来消除 set ,get方法
翻译一下就是 把你的Controller与service连接起来
再看
点进来
就进到了你的Service
extends继承
IService<> 一个方法
ApparatusStatsEntity 这个是其他类中的class代码具体就是把数据库有的字段定义出来
Entity中具体代码
package cn.galaiot.modules.ledger.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* @author zhangtong
* @date 2020-09-04 14:06:29
*/
@Data //注解 有了这个就不需要get set方法了
@TableName(value = "apparatus_process", autoResultMap = true)
public class ApparatusProcessEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
@TableId
private Long id;
/**
* 设备id
*/
private Long infoid;
/**
* 操作时间
*/
@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd")
private Date operationtime;
/**
* 管理内容
*/
private String managecontent;
/**
* 实施方式
*/
private String implement;
/**
* 实施方
*/
private String executor;
/**
* 费用
*/
private Double expense;
;
/**
* 完好待用时间
*/
private Long readyforusetime;
/**
* 运转时间
*/
private Long runningtime;
/**
* 故障时间
*/
private Long faulttime;
/**
*正常保养时间
*/
private Long normalmaintenancetime;
/**
* 维保时间
*/
private String maintenancetime;
/**
* 备注
*/
private String remark;
/**
* 值班人
*/
private String inspector;
/**
* 开始维保时间
*/
@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd")
private Date starttime;
/**
* 维保结束时间
*/
@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd")
private Date endtime;
/**
* 责任人
*/
private String responsible;
/**
* 任务描述
*/
private String description;
/**
* 配套设备
*/
private String equipment;
/**
* 详细信息
*/
@TableField(typeHandler = FastjsonTypeHandler.class)
private String info;
/**
* 创建时间
*/
private Date createtime;
/**
* 创建人
*/
private String createuser;
/**
* 修改时间
*/
private Date updatetime;
/**
* 修改人
*/
private String updateuser;
/**
* 设备状态, -1表示删除, 0表示正常
*/
private Integer status;
}
暂且不谈Entity包是个什么东西
继续上面代码
又是4个注解
@SysLog
与上面 @Slf4j对应 写出log打印到哪里
@ApiOperation
首先@ApiOperation注解不是Spring自带的,它是是swagger里的
注解@ApiOperation是用来构建Api文档的
@ApiOperation(value = “接口说明”, httpMethod = “接口请求方式”, response =
“接口返回参数类型”, notes = “接口发布说明”;其他参数可参考源码;
提到swagger不得不提到RESTful风格
RESTFUL是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用XML格式定义或JSON格式定义。RESTFUL适用于移动互联网厂商作为业务使能接口的场景,实现第三方OTT调用移动网络资源的功能,动作类型为新增、变更、删除所调用资源。
一个@ApiOperation的通用写法 下面是举例
@ApiOperation(value="创建用户", notes="根据User对象创建用户")
@ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
@RequestMapping(value="", method=RequestMethod.POST)
public String postUser(@RequestBody User user) {
users.put(user.getId(), user);
return "success";
}
下一个的@PostMapping注解
- @PostMapping
- @PutMapping
- @DeleteMapping
- @PatchMapping
- @GetMapping
为什么这么写:
@PostMapping("/apparatus")
这里的和Swagger上显示的一样就行
什么是@PostMapping:
映射一个POST请求 处理post请求
等价于@RequestMapping(value = "/user/login",method = RequestMethod.POST)
Spring官方文档说:
@GetMapping是一个组合注解,是@RequestMapping(method = RequestMethod.GET)的缩写。该注解将HTTP Get 映射到 特定的处理方法上。
@PostMapping 是一个组合注解,是@RequestMapping(method = RequestMethod.POST)的缩写。该注解将HTTP Post 映射到 特定的处理方法上。
到public R create(@RequestBody ApparatusInfoDTO apparatusInfoDTO)
@ResponseBody:表示该方法的返回结果直接写入HTTP response body中,一般在异步获取数据时使用,用于构建RESTful的api。在使用@RequestMapping后,返回值通常解析为跳转路径,加上@responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。
比如异步获取json数据,加上@responsebody后,会直接返回json数据。该注解一般会配合@RequestMapping一起使用。
点击ApparatusInfoDTO 跳转到public class ApparatusInfoDTO extends ApparatusInfoEntity
写到这里可以缓一缓了 不要急来到service层
至于为什么 就因为这个程序里的第一行代码~
HashMap<String, Object> date = apparatusInfoService.getAll(params);
分析代码
HashMap<String,Object> date 定义一个hashmap集合 名字为 date
apparatusInfoService.getAll(params)
apparatusInfoService.得到所有 参数
这就是重点了 因为apparatusInfoService在这!
这里实现了Controller层交互service层。
service层
service下有两个
一个叫做xxxxService
另一个叫xxxxServiceImpl
Service 是接口
Impl 是实现
xxxService 代码
public interface ApparatusInfoService extends IService<ApparatusInfoEntity> {
//获取所有数据列表,支持条件搜索和分页
HashMap<String,Object> getAll(Map<String, Object> params);
}
上面是controller里的代码
下面是service里的代码
注意是怎么连接在一起的 命名与取名
当你在这里写好一个接口以后你的同级目录下的xxxImpl就会报错
因为实现可以没有方法 但一定要实现。
xxxserviceImpl 代码
@Service("apparatusInfoService")
public class ApparatusInfoServiceImpl extends ServiceImpl<ApparatusInfoDao, ApparatusInfoEntity> implements ApparatusInfoService {
@Resource
private ApparatusInfoDao apparatusInfoDao;
public HashMap<String, Object> getAll(Map<String, Object> params) {
HashMap<String, Object> date = new HashMap<>();
List categorys = null;
List useStates = null;
List departments = null;
String likeKey = StringUtils.defaultIfEmpty((String) params.get("likeKey"), "name");
String likeValue = (String) params.get("likeValue");
likeValue = "%" + likeValue + "%";
//分页参数
Long curPage = 0L;
Long limit = 10L;
if (params.get(Constant.PAGE) != null) {
curPage = Long.parseLong((String) params.get(Constant.PAGE)) - 1L;
}
if (params.get(Constant.LIMIT) != null) {
limit = Long.parseLong((String) params.get(Constant.LIMIT));
}
Long start = curPage * limit;
String json = (String) params.get("preciseValue");
if (!StringUtils.isEmpty(json)) {
HashMap<String, String> map = JSON.parseObject(json, HashMap.class);
Set<String> keys = map.keySet();
for (String key : keys) {
switch (key) {
case "category":
String categorysString = map.get(key);
if (StringUtils.isEmpty(categorysString)) {
break;
}
categorys = Arrays.asList(categorysString.split(","));
break;
case "usestate":
String useStatesString = map.get(key);
if (StringUtils.isEmpty(useStatesString)) {
break;
}
useStates = Arrays.asList(useStatesString.split(","));
break;
case "department":
String departmentsString = map.get(key);
if (StringUtils.isEmpty(departmentsString)) {
break;
}
departments = Arrays.asList(departmentsString.split(","));
break;
}
}
}
List<String> unit = UserUtils.getUserDepartment();
List<ApparatusInfoEntity> apparatusInfos = apparatusInfoDao.getAllByKey(likeKey, likeValue, start, limit, categorys, useStates, departments, unit);
Long count = apparatusInfoDao.getCount(likeKey, likeValue, categorys, useStates, departments, unit);
date.put("apparatusInfos", apparatusInfos);
date.put("num", count);
return date;
}
}
代码分析!
注解:@service :一般用于修饰service层的组件
翻译:连接service与controller
注解:@Resource :这个注解属于J2EE,默认安装名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
翻译:不用写setter方法,减少了与spring的耦合
private ApparatusInfoDao apparatusInfoDao;
直观的就是用在了这行代码
要注意同样的代码在不同的层如和耦合在一起的
继续分析Impl代码 我把实现的第一代码整个copy
前几个List定义一些数组
List categorys = null;
List useStates = null;
List departments = null;
List newname=null;
这里使用了unitl类 实现了一个分页功能
String likeKey = StringUtils.defaultIfEmpty((String) params.get("likeKey"), "name");
String likeValue = (String) params.get("likeValue");
likeValue = "%" + likeValue + "%";
//分页参数
Long curPage = 0L;
Long limit = 10L;
if (params.get(Constant.PAGE) != null) {
curPage = Long.parseLong((String) params.get(Constant.PAGE)) - 1L;
}
if (params.get(Constant.LIMIT) != null) {
limit = Long.parseLong((String) params.get(Constant.LIMIT));
}
Long start = curPage * limit;
String json = (String) params.get("preciseValue");
代码分析!
String likeKey = StringUtils.defaultIfEmpty((String) params.get("likeKey"), "name");
//String likeKey =跳转了一个Utils类.跳转到defaultIfEmpty里实现isEmpty(str) ? defaultStr : str这个 params.get("这个是在dao层里一会看")
StringUtils 跳转了一个Utils类
//Utils类功能
public class StringUtils {
public static final String EMPTY = "";
public static final int INDEX_NOT_FOUND = -1;
private static final int PAD_LIMIT = 8192;
public StringUtils() {
}
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
public static boolean isNotEmpty(String str) {
return !isEmpty(str);
}
public static boolean isBlank(String str) {
int strLen;
if (str != null && (strLen = str.length()) != 0) {
for(int i = 0; i < strLen; ++i) {
if (!Character.isWhitespace(str.charAt(i))) {
return false;
}
}
return true;
} else {
return true;
}
}
public static String defaultIfEmpty(String str, String defaultStr) {
return isEmpty(str) ? defaultStr : str;
}
源代码的下一行代码
String likeValue = (String) params.get("likeValue");
传过来的在dao层里的参数 ^likeValue^