springboot+mybatis+mybatis-plus对crud项目进行改进

springboot+mybatis实现简单的增、删、查、改https://blog.csdn.net/heyl163_/article/details/132197201上一篇文章,已经详细地介绍了怎么通过springboot项目整合mybatis实现简单的数据库表的增删改查功能,是最简单的springboot项目的结构。所以有很多问题,包括简单sql语句需要重复编写、数据校验、异常处理、操作类方法没有返回响应等等。这篇文章我们通过使用springmvc的全局异常处理机制以及引入mybatis-plus和validation来解决这几个问题。

基于上一篇文章的项目,新建一个分支springboot1.0,保存最新的代码。

一、简单sql语句重复编写问题

通过引入mybatis-plus来解决重复编写简单sql语句的问题。

在pom.xml中引入mybatis-plus的依赖

<properties><mybatis-plus.version>3.5.1</mybatis-plus.version>
</properties><!--mybatis-plus-->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatis-plus.version}</version>
</dependency>

然后SongMapper继承BaseMapper接口,然后就能调用BaseMapper里预先定义的crud方法了。

package com.example.springboot.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.springboot.entity.Song;
import org.springframework.stereotype.Repository;/*** @author heyunlin* @version 1.0*/
@Repository
public interface SongMapper extends BaseMapper<Song> {}

BaseMapper的参数类型为对应实体类的类型;

 

因为BaseMapper里有几个方法selectById()、deleteById()、updateById()需要用到表的主键,因此,在实体类上需要通过注解@TableId来标注哪个字段是表的主键;

 

如果数据库表名和实体类的类名不一致,需要通过@TableName注解指明对应的数据库表;

 

如果数据库表名的字段名和实体类的属性名不一致,需要通过@TableField注解指明对应的数据库表的字段;

package com.example.springboot.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;import java.io.Serializable;
import java.time.LocalDateTime;/*** 歌曲* @author heyunlin* @version 1.0*/
@Data
@TableName("song")
public class Song implements Serializable {private static final long serialVersionUID = 18L;@TableId(type = IdType.INPUT)private String id;/*** 歌曲名*/@TableField("name")private String name;/*** 歌手*/@TableField("singer")private String singer;/*** 描述信息*/@TableField("note")private String note;/*** 最后一次修改时间*/@TableField("last_update_time")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private LocalDateTime lastUpdateTime;
}

然后,删除SongMapper.java中编写的方法和对应SongMapper.xml中的statement

<?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.example.springboot.mapper.SongMapper"></mapper>

删除之后惊奇地发现service中调用mapperde的方法并没有报错,因为这些方法已经预定义在BaseMapper中了。

mybatis-plus使用简单,归根于BaseMapper中预先帮我们实现的方法,接下来详细介绍这些方法的作用。

package com.baomidou.mybatisplus.core.mapper;import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;public interface BaseMapper<T> extends Mapper<T> {// 添加,如果字段值为null,则不会设置到insert语句中int insert(T entity);// 通过id删除,id字段的类型需要实现Serializable接口int deleteById(Serializable id);int deleteById(T entity);// 条件删除,把条件放到Map中,map的key是数据库表的字段名,map的value是字段的值int deleteByMap(@Param("cm") Map<String, Object> columnMap);// 条件删除,通过条件构造器来设置条件int delete(@Param("ew") Wrapper<T> queryWrapper);// 通过ID批量删除,相当于delete from xxx where id in idListint deleteBatchIds(@Param("coll") Collection<?> idList);// 通过id修改,如果字段值为null,则不会设置到update语句中int updateById(@Param("et") T entity);// 条件修改,通过条件构造器来设置条件,entity中的数据为Wrapper的set()方法设置的字段值int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);// 通过id查询,id字段的类型需要实现Serializable接口T selectById(Serializable id);// 通过ID批量查询,相当于select * from xxx where id in idListList<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);// 条件查询,通过条件构造器来设置查询条件List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);// 条件查询,通过条件构造器来设置查询条件// 如果确定查询最多只有一行结果,可以使用该方法,否则将报错default T selectOne(@Param("ew") Wrapper<T> queryWrapper) {List<T> ts = this.selectList(queryWrapper);if (CollectionUtils.isNotEmpty(ts)) {if (ts.size() != 1) {throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records", new Object[0]);} else {return ts.get(0);}} else {return null;}}// existes语句default boolean exists(Wrapper<T> queryWrapper) {Long count = this.selectCount(queryWrapper);return null != count && count > 0L;}// 条件查询记录数,通过条件构造器来设置查询条件Long selectCount(@Param("ew") Wrapper<T> queryWrapper);// 条件查询,通过条件构造器来设置查询条件// 当查询结果有多条时,不能使用selectOne,需要使用该方法List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);// 分页查询<P extends IPage<T>> P selectPage(P page, @Param("ew") Wrapper<T> queryWrapper);<P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param("ew") Wrapper<T> queryWrapper);
}

 

二、数据校验问题

1、ID不能为空的问题

delete()和detail()方法理论上需要ID不为空,否则查询结果必然是空,为了避免无效的查询,应该设置id不能为空,通过@RequestParam(requied = true)来限制id不能为空

SongController.java对应修改

@RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
public void delete(@RequestParam(required = true) @PathVariable("id") String id) {songService.delete(id);
}
@RequestMapping(value = "/detail/{id}", method = RequestMethod.GET)
public Song detail(@RequestParam(required = true) @PathVariable("id") String id) {return songService.detail(id);
}

如下图,当通过postman测试接口时,不传id会报错

cc8c86b7b65f4c908e0bb4616d3c8f0e.png

传正确的ID时,成功返回了查询结果。

0e911367c75249babdf03f6fe2761d03.png

 

2、添加校验

通过validation数据校验框架完成数据校验。

在pom.xml中引入validation的依赖

<!--validation-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>

然后创建一个对应前端传来的数据的dto对象,比如添加对应一个dto对象,用户只需要输入name、singer和note三个字段的值,而且name、singer这两个字段是必填的,也就是需要验证非空(ID是通过当前时间生成的)。

10579a2651764c7eb8376c8f57e3b840.png

 添加时的dto对象

package com.example.springboot.dto;import lombok.Data;/*** @author heyunlin* @version 1.0*/
@Data
public class SongInsertDTO {/*** 歌曲名*/private String name;/*** 歌手*/private String singer;/*** 描述信息*/private String note;
}

接着,在字段上使用validation定义的注解对字段值进行校验,校验失败会抛出BindException异常,然后对异常进行处理即可,详情请参考文章:

validation数据校验框架https://blog.csdn.net/heyl163_/article/details/132112153

SongInsertDTO.java

package com.example.springboot.dto;import lombok.Data;import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;/*** @author heyunlin* @version 1.0*/
@Data
public class SongInsertDTO {/*** 歌曲名*/@NotNull(message = "歌曲名不能为空")@NotEmpty(message = "歌曲名不能为空")private String name;/*** 歌手*/@NotNull(message = "歌手不能为空")@NotEmpty(message = "歌手不能为空")private String singer;
}

然后控制器SongController上的insert()方法的参数类型改为SongInsertDTO,然后在参数上添加@Valid或@Validated注解

@RequestMapping(value = "/insert", method = RequestMthod.POST)
public void insert(@Validated SongInsertDTO insertDTO) {
        songService.insert(insertDTO);
}

相应的,service也要修改

SongService

void insert(SongInsertDTO insertDTO);

SongServiceImpl 

@Override
public void insert(SongInsertDTO insertDTO) {Song song = new Song();song.setId(StringUtils.uuid());song.setName(insertDTO.getName());song.setSinger(insertDTO.getSinger());if (StringUtils.isNotEmpty(insertDTO.getNote())) {song.setNote(insertDTO.getNote());}songMapper.insert(song);
}

 

3、修改校验

修改时需要ID、name、singer不为空,则创建一个SongUpdateDTO类即可,然后按照相同的步骤在类的属性上和控制器方法的参数上添加对应的注解即可。

package com.example.springboot.dto;import lombok.Data;import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;/*** @author heyunlin* @version 1.0*/
@Data
public class SongUpdateDTO {/*** 歌曲编号/ID*/@NotNull(message = "歌曲编号不能为空")@NotEmpty(message = "歌曲编号不能为空")private String id;/*** 歌曲名*/@NotNull(message = "歌曲名不能为空")@NotEmpty(message = "歌曲名不能为空")private String name;/*** 歌手*/@NotNull(message = "歌手不能为空")@NotEmpty(message = "歌手不能为空")private String singer;/*** 描述信息*/private String note;
}

控制器SongController上的update()方法的参数类型改为SongUpdateDTO,然后在参数上添加@Valid或@Validated注解 

@RequestMapping(value = "/update", method = RequestMethod.POST)
public void update(@Valid SongUpdateDTO updateDTO) {songService.update(updateDTO);
}

相应的,service也要修改 

SongService

void update(SongUpdateDTO updateDTO);

SongServiceImpl 

@Override
public void update(SongUpdateDTO updateDTO) {Song song = new Song();song.setId(StringUtils.uuid());song.setName(updateDTO.getName());song.setSinger(updateDTO.getSinger());if (StringUtils.isNotEmpty(updateDTO.getNote())) {song.setNote(updateDTO.getNote());}song.setLastUpdateTime(LocalDateTime.now());songMapper.updateById(song);
}

 

三、操作类方法没有响应数据问题

无论操作成功还是失败,都应该向客户端返回操作的结果。

1、创建响应对象

新建一个响应对象实体类,包含状态码、数据和响应的消息(提示)。

package com.example.springboot.restful;import lombok.Data;@Data
public class JsonResult<T> {/*** 响应状态码*/private Integer code;/*** 响应提示信息*/private String message;/*** 响应数据*/private T data;public static JsonResult<Void> success() {return success(null);}public static JsonResult<Void> success(String message) {return success(message, null);}public static <T> JsonResult<T> success(String message, T data) {JsonResult<T> jsonResult = new JsonResult<>();jsonResult.setCode(ResponseCode.OK.getValue());jsonResult.setMessage(message);jsonResult.setData(data);return jsonResult;}public static JsonResult<Void> error(String message) {JsonResult<Void> jsonResult = new JsonResult<>();jsonResult.setCode(ResponseCode.ERROR.getValue());jsonResult.setMessage(message);return jsonResult;}public static JsonResult<Void> error(ResponseCode responseCode, Throwable e) {return error(responseCode, e.getMessage() != null ? e.getMessage() : "系统发生异常,请联系管理员!");}public static JsonResult<Void> error(ResponseCode responseCode, String message) {JsonResult<Void> jsonResult = new JsonResult<>();jsonResult.setCode(responseCode.getValue());jsonResult.setMessage(message);return jsonResult;}}

 

2、定义响应状态码

package com.example.springboot.restful;/*** 响应状态码* @author heyunlin* @version 1.0*/
public enum ResponseCode {/*** 请求成功*/OK(200),/*** 失败的请求*/BAD_REQUEST(400),/*** 未授权*/UNAUTHORIZED(401),/*** 禁止访问*/FORBIDDEN(403),/*** 找不到*/NOT_FOUND(404),/*** 不可访问*/NOT_ACCEPTABLE(406),/*** 冲突*/CONFLICT(409),/*** 服务器发生异常*/ERROR(500);private final Integer value;ResponseCode(Integer value) {this.value = value;}public Integer getValue() {return value;}}

 

3、增删改方法添加返回值

insert()、update()、delete()三个方法的返回值类型修改为JsonResult<Void>,JsonResult<T>的参数类型T表示返回的数据的类型,在这里不需要返回数据,只需要返回给用户看的提示信息。

SongController.java

package com.example.springboot.controller;import com.example.springboot.dto.SongInsertDTO;
import com.example.springboot.dto.SongUpdateDTO;
import com.example.springboot.entity.Song;
import com.example.springboot.restful.JsonResult;
import com.example.springboot.service.SongService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;import javax.validation.Valid;/*** @author heyunlin* @version 1.0*/
@RestController
@RequestMapping(path = "/song", produces="application/json;charset=utf-8")
public class SongController {private final SongService songService;@Autowiredpublic SongController(SongService songService) {this.songService = songService;}@RequestMapping(value = "/insert", method = RequestMethod.POST)public JsonResult<Void> insert(@Validated SongInsertDTO insertDTO) {songService.insert(insertDTO);return JsonResult.success("添加成功");}@RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)public JsonResult<Void> delete(@RequestParam(required = true) @PathVariable("id") String id) {songService.delete(id);return JsonResult.success("删除成功");}@RequestMapping(value = "/update", method = RequestMethod.POST)public JsonResult<Void> update(@Valid SongUpdateDTO updateDTO) {songService.update(updateDTO);return JsonResult.success("修改成功");}@RequestMapping(value = "/detail/{id}", method = RequestMethod.GET)public Song detail(@RequestParam(required = true) @PathVariable("id") String id) {return songService.detail(id);}}

这样的话,用户操作添加、删除、修改歌曲时,能得到响应,成功或者失败,以及失败的原因。

 

4、深入分析操作影响行数 

当然了,这里只要没报错就默认操作成功了,如果要求完美,可以获取BaseMapper的增删改方法的返回值,这个返回值是int类型,表示本次操作受影响的行数:插入行数、删除行数、修改行数,根据这个行数返回对应的提示给用户即可。

对应SongServiceImpl的修改

package com.example.springboot.service.impl;import com.example.springboot.dto.SongInsertDTO;
import com.example.springboot.dto.SongUpdateDTO;
import com.example.springboot.entity.Song;
import com.example.springboot.exception.GlobalException;
import com.example.springboot.mapper.SongMapper;
import com.example.springboot.restful.ResponseCode;
import com.example.springboot.service.SongService;
import com.example.springboot.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.time.LocalDateTime;/*** @author heyunlin* @version 1.0*/
@Service
public class SongServiceImpl implements SongService {private final SongMapper songMapper;@Autowiredpublic SongServiceImpl(SongMapper songMapper) {this.songMapper = songMapper;}@Overridepublic void insert(SongInsertDTO insertDTO) {Song song = new Song();song.setId(StringUtils.uuid());song.setName(insertDTO.getName());song.setSinger(insertDTO.getSinger());if (StringUtils.isNotEmpty(insertDTO.getNote())) {song.setNote(insertDTO.getNote());}int rows = songMapper.insert(song);if (rows < 1) {throw new GlobalException(ResponseCode.BAD_REQUEST, "添加时发生了异常,预计影响" + rows + "条记录,已经对本次操作影响的数据进行恢复");}}@Overridepublic void delete(String id) {int rows = songMapper.deleteById(id);if (rows == 0) {throw new GlobalException(ResponseCode.BAD_REQUEST, "删除失败,本次操作删除了" + rows + "条记录。");} else if (rows > 1) {throw new GlobalException(ResponseCode.BAD_REQUEST, "删除失败,操作过程中发生了不可预知的错误。");}}@Overridepublic void update(SongUpdateDTO updateDTO) {Song song = new Song();song.setId(StringUtils.uuid());song.setName(updateDTO.getName());song.setSinger(updateDTO.getSinger());if (StringUtils.isNotEmpty(updateDTO.getNote())) {song.setNote(updateDTO.getNote());}song.setLastUpdateTime(LocalDateTime.now());int rows = songMapper.updateById(song);if (rows == 0) {throw new GlobalException(ResponseCode.BAD_REQUEST, "修改失败,本次操作删除了" + rows + "条记录。");} else if (rows > 1) {throw new GlobalException(ResponseCode.BAD_REQUEST, "修改失败,操作过程中发生了不可预知的错误。");}}@Overridepublic Song detail(String id) {return songMapper.selectById(id);}}

 

四、异常处理问题

程序的健壮性作为3大重要指标,可见非常重要,当程序发生异常时,应该被很好的处理,而不是让用户看到500的页面,或者长时间无响应。

这篇文章通过springmvc统一异常处理机制来解决这个问题,不需要在每个方法上使用try...catch...finally来手动处理异常。

首先,创建一个自定义异常,继承RuntimeException,这时候可能你就会好奇:为什么是继承RuntimeException呢?能不能继承其他Exception,答案是可以。

但是这里需要了解检查异常和非检查异常的区别:

非检查异常:RuntimeException及其子类异常,不需要手动处理,也就是不需要通过try...catch捕获或者通过throws关键字在方法上面申明抛出异常。

检查异常:除了RuntimeException及其子类型异常以外的异常都是检查异常,必须手动处理,通过try...catch捕获或者通过throws关键字在方法上面申明抛出异常。如果不处理,编译就会报错。

所以,我们使用非检查异常的好处显而易见,当我们在代码里主动使用throw关键字抛出RuntimeException异常时不需要去处理就可以通过编译。

我们创建一个exception子包来存放异常相关的类,在该类下创建一个全局异常类GlobalException

package com.example.springboot.exception;import com.example.springboot.restful.ResponseCode;
import lombok.Data;
import lombok.EqualsAndHashCode;/*** 自定义异常* @author heyunlin* @version 1.0*/
@Data
@EqualsAndHashCode(callSuper = true)
public class GlobalException extends RuntimeException {private ResponseCode responseCode;public GlobalException(ResponseCode responseCode, String message) {super(message);setResponseCode(responseCode);}}

然后在当前的exception包下面创建一个handler,然后在handler包下创建一个统一异常处理类

package com.example.springboot.exception.handler;import com.example.springboot.exception.GlobalException;
import com.example.springboot.restful.JsonResult;
import com.example.springboot.restful.ResponseCode;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;import javax.servlet.http.HttpServletResponse;
import java.util.Objects;/*** 全局异常处理类* @author heyunlin* @version 1.0*/
@RestControllerAdvice
public class GlobalExceptionHandler {/*** 处理GlobalException* @param e GlobalException* @return JsonResult<Void>*/@ExceptionHandler(GlobalException.class)public JsonResult<Void> handlerGlobalException(HttpServletResponse response, GlobalException e) {e.printStackTrace();response.setStatus(e.getResponseCode().getValue());return JsonResult.error(e.getResponseCode(), e);}/*** 处理BindException* @param e BindException* @return JsonResult<Void>*/@ExceptionHandler(BindException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public JsonResult<Void> handlerBindException(BindException e) {e.printStackTrace();BindingResult bindingResult = e.getBindingResult();FieldError fieldError = bindingResult.getFieldError();String defaultMessage = Objects.requireNonNull(fieldError).getDefaultMessage();return JsonResult.error(ResponseCode.BAD_REQUEST, defaultMessage);}/*** 处理Exception* @param e Exception* @return JsonResult<Void>*/@ExceptionHandler(Exception.class)@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)public JsonResult<Void> handlerException(Exception e) {e.printStackTrace();return JsonResult.error(ResponseCode.ERROR, e);}}

这样的话,当发生对应的异常时,会执行对应的方法,比如数据校验失败时发生了BindException,会执行以下方法

/**
 * 处理BindException
 * @param e BindException
 * @return JsonResult<Void>
 */
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public JsonResult<Void> handlerBindException(BindException e) {
        e.printStackTrace();

 

        BindingResult bindingResult = e.getBindingResult();
        FieldError fieldError = bindingResult.getFieldError();
        String defaultMessage = Objects.requireNonNull(fieldError).getDefaultMessage();

 

        return JsonResult.error(ResponseCode.BAD_REQUEST, defaultMessage);
}

通过上面几个步骤,项目的结构已经相对完善了。

 

好了,这篇文章就分享到这里了,看完不要忘了点赞+收藏哦~

源码已经上传至git,按需获取:

springboot整合mybatis实现crud项目改进https://gitee.com/he-yunlin/springboot/tree/springboot1.0/

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/63271.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

易服客工作室:如何在WordPress网站中举办虚拟活动

您是否正在寻找举办和管理虚拟活动的最佳方式&#xff1f; 也许您想在线举行下一次会议或举办有关您的产品和服务的网络研讨会。您可能担心它太贵&#xff0c;或者您没有技术知识来实现​​它。 在本文中&#xff0c;我们将列出您所需的在线服务的所有设备&#xff0c;并教您…

【vue3】基础知识点-setup语法糖

学习vue3&#xff0c;都会从基础知识点学起。了解setup函数&#xff0c;ref&#xff0c;recative&#xff0c;watch、comptued、pinia等如何使用 今天说vue3组合式api&#xff0c;setup函数 在学习过程中一开始接触到的是这样的&#xff0c;定义数据且都要通过return返回 <…

Map中compute、putIfAbsent、computeIfAbsent、merge、computeIfPresent使用

目录 putIfAbsent computeIfAbsent computeIfPresent compute merge putIfAbsent 解释&#xff1a;【不存在则添加】&#xff0c;如果map中没有该key&#xff0c;则直接添加&#xff1b;如果map中已经存在该key&#xff0c;则value保持不变 default V putIfAbsent(K key,…

Linux文件属性与权限管理(可读、可写、可执行)

Linux把所有文件和设备都当作文件来管理&#xff0c;这些文件都在根目录下&#xff0c;同时Linux中的文件名区分大小写。 一、文件属性 使用ls -l命令查看文件详情&#xff1a; 1、每行代表一个文件&#xff0c;每行的第一个字符代表文件类型&#xff0c;linux文件类型包括&am…

百川智能发布首个530亿参数闭源大模型,今年追上GPT-3.5

4月官宣创业&#xff0c;6月15日发布第一款7B开源模型&#xff0c;7月11日发布第二款13B、130亿参数开源模型。 平均保持2个月一个版本发布速度&#xff0c;8月8日&#xff0c;百川智能发布了创业以来的首个530亿参数闭源大模型——Baichuan-53B&#xff08;以下简称“53B”&a…

企业产品手册5大核心功能,附产品手册在线制作工具Baklib

企业产品手册的5大核心功能 企业产品手册是企业向用户传达产品信息、功能和使用方法的重要工具。下面将介绍企业产品手册的五个核心功能。 1. 产品介绍和特点展示 产品手册的首要功能是介绍和展示企业的产品。它应该提供清晰、详细的产品信息&#xff0c;包括产品的特点、优势…

5G用户逼近7亿,5G发展迈入下半场!

尽管普遍认为5G投资高峰期正在过去&#xff0c;但是从2023年上半年的情况来看&#xff0c;我国5G建设仍在衔枚疾走。 近日举行2023年上半年工业和信息化发展情况新闻发布会上&#xff0c;工信部人士透露&#xff0c;截至今年6月底&#xff0c;我国5G基站累计达到293.7万个&…

InVEST模型使用

第一天&#xff1a; 1. 生态系统服务理论联系实践案例讲解 2. InVEST模型的开发历程、不同版本的差异及对数据需求的讲解 3. InVEST所需数据的要求&#xff08;分辨率、格式、投影系统等&#xff09;、获取及标准化预处理讲解 4. InVEST运行常见问题及处理解决方法讲解 5.…

新疆大学841软件工程考研

1&#xff0e;软件生产的发展经历了三个阶段&#xff0c;分别是____、程序系统时代和软件工程时代时代。 2&#xff0e;可行性研究从以下三个方面研究每种解决方法的可行性&#xff1a;经济可行性、社会可行性和_____。 3&#xff0e;HIPO图的H图用于描述软件的层次关系&…

uniapp 小兔鲜儿 - 首页模块(1)

目录 自定义导航栏 静态结构 安全区域​ 通用轮播组件 静态结构 自动导入全局组件 全局组件类型声明 .d.ts文件 注册组件 vue/runtime-core 首页 – 轮播图指示点 首页 – 获取轮播图数据 首页 – 轮播图数据类型并渲染 首页 – 轮播图总结 首页分类 首页 – 前…

五金仓库的管理数字化

随着信息技术的快速发展&#xff0c;数字化管理在各行各业中迅速普及。数字化管理可以让企业轻松高效地收集、存储和共享数据&#xff0c;并利用大数据分析和人工智能等工具进行精确分析和预测&#xff0c;从而更好地理解业务运作情况并做出相应调整&#xff0c;以提高企业效率…

AWS中lambda与DynamoDB的集成

前言&#xff1a;我在整个集成过程中&#xff0c;存在最大的问题有两个&#xff0c; 1. 没有考虑到lambda函数的权限&#xff0c;即对DynamoDB或者其他如Kinesis的权限授权&#xff0c;导致无法写入或者读取。 2.最初使用了异步方式调用&#xff0c;导致无法写数据到DynamoDB…