企业OA办公系统开发笔记:2、MyBatis-Plus

文章目录

  • 企业办公系统:2、MyBatis-Plus
    • 一、MyBatis-Plus
      • 1、简介
      • 2、主要特点
      • 3、依赖
    • 二、MyBatis-Plus入门
      • 1、配置文件
      • 2、启动类
      • 3、实体类
      • 4、添加Mapper类
      • 5、测试Mapper接口
      • 6、CRUD测试
        • 6.1、insert添加
          • 6.1.1、示例
          • 6.1.2、主键策略
        • 6.2、更新
        • 6.3、删除
          • 6.3.1、根据id删除
          • 6.3.2、批量删除
        • 6.4、MyBatis-Plus条件构造器
      • 6、MyBatis-Plus封装service层
        • 6.1、添加service接口
        • 6.2、添加service接口实现
        • 6.3、测试Service接口
    • 三、角色管理
      • 1、测试controller层
        • 1.1、添加Controller
        • 1.2、测试Controller接口
      • 2、定义统一返回结果对象
        • 2.1、定义统一返回结果对象
        • 2.2、改造controller方法
        • 2.3、测试接口
      • 3、knife4j
        • 3.1、Swagger介绍
        • 3.2、集成knife4j
          • 3.2.1 添加依赖
          • 3.2.2 添加knife4j配置类
          • 3.2.3 Controller层添加注解
          • 3.2.4、测试
      • 4、分页查询
        • 4.1、配置分页插件
        • 4.2、分页controller
      • 5、其他controller方法
      • 6、统一异常处理
        • 6.1、制造异常
        • 6.2、全局异常处理
          • 6.2.1、创建统一异常处理器
          • 6.2.1、测试
        • 6.3、处理特定异常
          • 6.3.1、添加异常处理方法
          • 6.3.2、测试
        • 6.4、处理自定义异常
          • 6.4.1、创建自定义异常类
          • 6.4.2、业务中需要位置抛出
          • 6.4.3、添加异常处理方法

企业办公系统:2、MyBatis-Plus

一、MyBatis-Plus

官网:https://baomidou.com/

1、简介

MyBatis-Plus(简称 MP)是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

2、主要特点

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库

3、依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.1</version>
</dependency>

二、MyBatis-Plus入门

前面介绍了MyBatis-Plus,当前就以角色管理为例讲解MyBatis-Plus的使用

1、配置文件

配置 MySQL 数据库的相关配置及Mybatis-Plus日志

application.yml

spring:
  application:
    name: service-oa
  profiles:
    active: dev

application-dev.yml

server:
  port: 8800
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 查看日志
spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/survey-oa?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8
    username: root
    password: 123456

2、启动类

在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹:

package com.atsurvey;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

/**
 * @Author tigerhhzz
 * @Date 2024 05 01 13 01
 **/

@SpringBootApplication
//@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
@ComponentScan("com.atsurvey")
//@MapperScan("com.atsurvey.*.mapper")
@MapperScan("com.atsurvey.auth.mapper")
public class ServiceAuthApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceAuthApplication.class, args);
    }

}

3、实体类

已引入,实体类说明:

实体类注解详细文档:https://baomidou.com/pages/223848/

@TableName:表名注解,标识实体类对应的表

@TableId:主键注解,type = IdType.AUTO(数据库 ID 自增)

@TableField:字段注解(非主键)

@TableLogic:逻辑删除

package com.atsurvey.model.system;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.atsurvey.model.base.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;


@Data
@ApiModel(description = "角色")
@TableName("sys_role")
public class SysRole extends BaseEntity {
	
	private static final long serialVersionUID = 1L;

	//@NotBlank(message = "角色名称不能为空")
	@ApiModelProperty(value = "角色名称")
	@TableField("role_name")
	private String roleName;

	@ApiModelProperty(value = "角色编码")
	@TableField("role_code")
	private String roleCode;

	@ApiModelProperty(value = "描述")
	@TableField("description")
	private String description;

}


4、添加Mapper类

package com.atsurvey.auth.mapper;

import com.atsurvey.model.system.SysRole;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

/**
 * @Author tigerhhzz
 * @Date 2024 05 01 13 04
 **/
@Repository
public interface SysRoleMapper extends BaseMapper<SysRole> {

}

com.baomidou.mybatisplus.core.mapper.BaseMapper这是Mybatis-Plus提供的默认Mapper接口。

package com.baomidou.mybatisplus.core.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
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> {
    int insert(T entity);

    int deleteById(Serializable id);

    int deleteByMap(@Param("cm") Map<String, Object> columnMap);

    int delete(@Param("ew") Wrapper<T> queryWrapper);

    int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    int updateById(@Param("et") T entity);

    int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);

    T selectById(Serializable id);

    List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);

    T selectOne(@Param("ew") Wrapper<T> queryWrapper);

    Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);

    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);

    <E extends IPage<T>> E selectPage(E page, @Param("ew") Wrapper<T> queryWrapper);

    <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper);
}

5、测试Mapper接口

package com.atsurvey.auth;

import com.atsurvey.auth.mapper.SysRoleMapper;
import com.atsurvey.model.system.SysRole;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;


import java.util.Arrays;
import java.util.List;

/**
 * @Author tigerhhzz
 * @Date 2024 05 01 16 50
 **/
@SpringBootTest
public class TestMpDemo1 {

    //注入
    @Autowired
    private SysRoleMapper sysRoleMapper;

    //查询所有记录
    @Test
    public void getAll() {
        List<SysRole> list = sysRoleMapper.selectList(null);
        System.out.println(list);
    }

    //添加操作
    @Test
    public void add() {
        SysRole sysRole = new SysRole();
        sysRole.setRoleName("角色管理员2");
        sysRole.setRoleCode("role2");
        sysRole.setDescription("角色管理员2");

        int rows = sysRoleMapper.insert(sysRole);
        System.out.println(rows);
        System.out.println(sysRole.getId());
    }

    //修改操作
    @Test
    public void update() {
        //根据id查询
        SysRole role = sysRoleMapper.selectById(10);
        //设置修改值
        role.setRoleName("atsurvey角色管理员");
        //调用方法实现最终修改
        int rows = sysRoleMapper.updateById(role);
        System.out.println(rows);
    }

    //删除操作
    @Test
    public void deleteId() {
        int rows = sysRoleMapper.deleteById(10);
    }

    //批量删除
    @Test
    public void testDeleteBatchIds() {
        int result = sysRoleMapper.deleteBatchIds(Arrays.asList(1, 2));
        System.out.println(result);
    }

    //条件查询
    @Test
    public void testQuery1() {
        //创建QueryWrapper对象,调用方法封装条件
        QueryWrapper<SysRole> wrapper = new QueryWrapper<>();
        wrapper.eq("role_name","总经理");
        //调用mp方法实现查询操作
        List<SysRole> list = sysRoleMapper.selectList(wrapper);
        System.out.println(list);
    }

    @Test
    public void testQuery2() {
        //LambdaQueryWrapper,调用方法封装条件
        LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysRole::getRoleName,"总经理");
        //调用mp方法实现查询操作
        List<SysRole> list = sysRoleMapper.selectList(wrapper);
        System.out.println(list);
    }
}

注意:

IDEA在sysRoleMapper处报错,因为找不到注入的对象,因为类是动态创建的,但是程序可以正确的执行。

为了避免报错,可以在 mapper 层 的接口上添加 @Repository 或直接使用 @Resource 代替 @Autowired。

控制台输出:

在这里插入图片描述

通过以上几个简单的步骤,我们就实现了 User 表的 CRUD 功能,甚至连 XML 文件都不用编写!

6、CRUD测试

6.1、insert添加
6.1.1、示例
    //添加操作
    @Test
    public void add() {
        SysRole sysRole = new SysRole();
        sysRole.setRoleName("角色管理员2");
        sysRole.setRoleCode("role2");
        sysRole.setDescription("角色管理员2");

        int rows = sysRoleMapper.insert(sysRole);
        System.out.println(rows);
        System.out.println(sysRole.getId());
    }
6.1.2、主键策略

1、ID_WORKER

MyBatis-Plus默认的主键策略是:ID_WORKER 全局唯一ID

2、自增策略

  • 要想主键自增需要配置如下主键策略

    • 需要在创建数据表的时候设置主键自增
    • 实体字段中配置 @TableId(type = IdType.AUTO)
@TableId(type = IdType.AUTO)
private Long id;

其它主键策略:分析 IdType 源码可知

public enum IdType {
     /**
     * 数据库ID自增
     */
    AUTO(0),
    
    /**
     * 该类型为未设置主键类型
     */
    NONE(1),
    
    /**
     * 用户输入ID
     * 该类型可以通过自己注册自动填充插件进行填充
     */    
    INPUT(2),
    
    /**
     * 全局唯一ID
     */    
    ASSIGN_ID(3),
    
    /**
     * 全局唯一ID (UUID)
     */
    ASSIGN_UUID(4),
    
    /** @deprecated */
    @Deprecated
    ID_WORKER(3),
    /** @deprecated */
    @Deprecated
    ID_WORKER_STR(3),
    /** @deprecated */
    @Deprecated
    UUID(4);
    private final int key;
    private IdType(int key) {
        this.key = key;
    }
    public int getKey() {
        return this.key;
    }
}
6.2、更新
@Test
public void testUpdateById(){
    SysRole sysRole = new SysRole();
    sysRole.setId(1L);
    sysRole.setRoleName("角色管理员1");

    int result = sysRoleMapper.updateById(sysRole);
    System.out.println(result);

}
6.3、删除
6.3.1、根据id删除
/**
 * application-dev.yml 加入配置
 * 此为默认值,如果你的默认值和mp默认的一样,则不需要该配置
 * mybatis-plus:
 *   global-config:
 *     db-config:
 *       logic-delete-value: 1
 *       logic-not-delete-value: 0
 */
@Test
public void testDeleteById(){
    int result = sysRoleMapper.deleteById(2L);
    System.out.println(result);
}
6.3.2、批量删除
@Test
public void testDeleteBatchIds() {
    int result = sysRoleMapper.deleteBatchIds(Arrays.asList(1, 1));
    System.out.println(result);
}
6.4、MyBatis-Plus条件构造器

在这里插入图片描述

Wrapper : 条件构造抽象类,最顶端父类

AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件

​ QueryWrapper : Entity 对象封装操作类,不是用lambda语法

​ UpdateWrapper : Update 条件封装,用于Entity对象更新操作

AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。

​ LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper

​ LambdaUpdateWrapper : Lambda 更新封装Wrapper

注意:以下条件构造器的方法入参中的 column 均表示数据库字段

@Test
public void testSelect1() {
    QueryWrapper<SysRole> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("role_code", "role");
    List<SysRole> users = sysRoleMapper.selectList(queryWrapper);
    System.out.println(users);
}

@Test
public void testSelect2() {
    LambdaQueryWrapper<SysRole> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(SysRole::getRoleCode, "role");
    List<SysRole> users = sysRoleMapper.selectList(queryWrapper);
    System.out.println(users);
}

其他条件构造可自行测试

6、MyBatis-Plus封装service层

6.1、添加service接口
package com.atsurvey.auth.service;

import com.atsurvey.model.system.SysRole;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public interface SysRoleService extends IService<SysRole> {

}

com.baomidou.mybatisplus.extension.service.IService这是Mybatis-Plus提供的默认Service接口。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.baomidou.mybatisplus.extension.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.baomidou.mybatisplus.extension.kotlin.KtQueryChainWrapper;
import com.baomidou.mybatisplus.extension.kotlin.KtUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.transaction.annotation.Transactional;

public interface IService<T> {
    int DEFAULT_BATCH_SIZE = 1000;

    default boolean save(T entity) {
        return SqlHelper.retBool(this.getBaseMapper().insert(entity));
    }

    @Transactional(
        rollbackFor = {Exception.class}
    )
    default boolean saveBatch(Collection<T> entityList) {
        return this.saveBatch(entityList, 1000);
    }

    boolean saveBatch(Collection<T> entityList, int batchSize);

    @Transactional(
        rollbackFor = {Exception.class}
    )
    default boolean saveOrUpdateBatch(Collection<T> entityList) {
        return this.saveOrUpdateBatch(entityList, 1000);
    }

    boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

    default boolean removeById(Serializable id) {
        return SqlHelper.retBool(this.getBaseMapper().deleteById(id));
    }

    default boolean removeByMap(Map<String, Object> columnMap) {
        Assert.notEmpty(columnMap, "error: columnMap must not be empty", new Object[0]);
        return SqlHelper.retBool(this.getBaseMapper().deleteByMap(columnMap));
    }

    default boolean remove(Wrapper<T> queryWrapper) {
        return SqlHelper.retBool(this.getBaseMapper().delete(queryWrapper));
    }

    default boolean removeByIds(Collection<? extends Serializable> idList) {
        return CollectionUtils.isEmpty(idList) ? false : SqlHelper.retBool(this.getBaseMapper().deleteBatchIds(idList));
    }

    default boolean updateById(T entity) {
        return SqlHelper.retBool(this.getBaseMapper().updateById(entity));
    }

    default boolean update(Wrapper<T> updateWrapper) {
        return this.update((Object)null, updateWrapper);
    }

    default boolean update(T entity, Wrapper<T> updateWrapper) {
        return SqlHelper.retBool(this.getBaseMapper().update(entity, updateWrapper));
    }

    @Transactional(
        rollbackFor = {Exception.class}
    )
    default boolean updateBatchById(Collection<T> entityList) {
        return this.updateBatchById(entityList, 1000);
    }

    boolean updateBatchById(Collection<T> entityList, int batchSize);

    boolean saveOrUpdate(T entity);

    default T getById(Serializable id) {
        return this.getBaseMapper().selectById(id);
    }

    default List<T> listByIds(Collection<? extends Serializable> idList) {
        return this.getBaseMapper().selectBatchIds(idList);
    }

    default List<T> listByMap(Map<String, Object> columnMap) {
        return this.getBaseMapper().selectByMap(columnMap);
    }

    default T getOne(Wrapper<T> queryWrapper) {
        return this.getOne(queryWrapper, true);
    }

    T getOne(Wrapper<T> queryWrapper, boolean throwEx);

    Map<String, Object> getMap(Wrapper<T> queryWrapper);

    <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

    default int count() {
        return this.count(Wrappers.emptyWrapper());
    }

    default int count(Wrapper<T> queryWrapper) {
        return SqlHelper.retCount(this.getBaseMapper().selectCount(queryWrapper));
    }

    default List<T> list(Wrapper<T> queryWrapper) {
        return this.getBaseMapper().selectList(queryWrapper);
    }

    default List<T> list() {
        return this.list(Wrappers.emptyWrapper());
    }

    default <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {
        return this.getBaseMapper().selectPage(page, queryWrapper);
    }

    default <E extends IPage<T>> E page(E page) {
        return this.page(page, Wrappers.emptyWrapper());
    }

    default List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper) {
        return this.getBaseMapper().selectMaps(queryWrapper);
    }

    default List<Map<String, Object>> listMaps() {
        return this.listMaps(Wrappers.emptyWrapper());
    }

    default List<Object> listObjs() {
        return this.listObjs(Function.identity());
    }

    default <V> List<V> listObjs(Function<? super Object, V> mapper) {
        return this.listObjs(Wrappers.emptyWrapper(), mapper);
    }

    default List<Object> listObjs(Wrapper<T> queryWrapper) {
        return this.listObjs(queryWrapper, Function.identity());
    }

    default <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
        return (List)this.getBaseMapper().selectObjs(queryWrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList());
    }

    default <E extends IPage<Map<String, Object>>> E pageMaps(E page, Wrapper<T> queryWrapper) {
        return this.getBaseMapper().selectMapsPage(page, queryWrapper);
    }

    default <E extends IPage<Map<String, Object>>> E pageMaps(E page) {
        return this.pageMaps(page, Wrappers.emptyWrapper());
    }

    BaseMapper<T> getBaseMapper();

    Class<T> getEntityClass();

    default QueryChainWrapper<T> query() {
        return ChainWrappers.queryChain(this.getBaseMapper());
    }

    default LambdaQueryChainWrapper<T> lambdaQuery() {
        return ChainWrappers.lambdaQueryChain(this.getBaseMapper());
    }

    default KtQueryChainWrapper<T> ktQuery() {
        return ChainWrappers.ktQueryChain(this.getBaseMapper(), this.getEntityClass());
    }

    default KtUpdateChainWrapper<T> ktUpdate() {
        return ChainWrappers.ktUpdateChain(this.getBaseMapper(), this.getEntityClass());
    }

    default UpdateChainWrapper<T> update() {
        return ChainWrappers.updateChain(this.getBaseMapper());
    }

    default LambdaUpdateChainWrapper<T> lambdaUpdate() {
        return ChainWrappers.lambdaUpdateChain(this.getBaseMapper());
    }

    default boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) {
        return this.update(entity, updateWrapper) || this.saveOrUpdate(entity);
    }
}

6.2、添加service接口实现
package com.atsurvey.auth.service.impl;

import com.atsurvey.auth.mapper.SysRoleMapper;
import com.atsurvey.auth.mapper.SysUserRoleMapper;
import com.atsurvey.auth.service.SysRoleService;
import com.atsurvey.model.system.SysRole;
import com.atsurvey.model.system.SysUserRole;
import com.atsurvey.vo.system.AssignRoleVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

}

com.baomidou.mybatisplus.extension.service.impl.ServiceImpl这是Mybatis-Plus提供的默认Service接口实现。

6.3、测试Service接口
package com.atsurvey;

import com.atsurvey.model.system.SysRole;
import com.atsurvey.system.mapper.SysRoleMapper;
import com.atsurvey.system.service.SysRoleService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SysRoleServiceTest {

    @Autowired
    private SysRoleService sysRoleService;

    @Test
    public void testSelectList() {
        System.out.println(("----- selectAll method test ------"));
        //UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper
        //所以不填写就是无任何条件
        List<SysRole> users = sysRoleService.list();
        users.forEach(System.out::println);
    }

    @Test
    public void testInsert(){
        SysRole sysRole = new SysRole();
        sysRole.setRoleName("角色管理员");
        sysRole.setRoleCode("role");
        sysRole.setDescription("角色管理员");

        boolean result = sysRoleService.save(sysRole);
        System.out.println(result); //影响的行数
        System.out.println(sysRole); //id自动回填
    }

    @Test
    public void testUpdateById(){
        SysRole sysRole = new SysRole();
        sysRole.setId(1L);
        sysRole.setRoleName("角色管理员1");

        boolean result = sysRoleService.updateById(sysRole);
        System.out.println(result);

    }

    @Test
    public void testDeleteById(){
        boolean result = sysRoleService.removeById(2L);
        System.out.println(result);
    }

    @Test
    public void testSelect1() {
        QueryWrapper<SysRole> queryWrapper = new QueryWrapper<>();
        queryWrapper.ge("role_code", "role");
        List<SysRole> users = sysRoleService.list(queryWrapper);
        System.out.println(users);
    }

    @Test
    public void testSelect2() {
        LambdaQueryWrapper<SysRole> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.ge(SysRole::getRoleCode, "role");
        List<SysRole> users = sysRoleService.list(queryWrapper);
        System.out.println(users);
    }
}

三、角色管理

1、测试controller层

1.1、添加Controller
package com.atsurvey.auth.controller;

import com.atsurvey.auth.service.SysRoleService;
import com.atsurvey.common.handler.ClfwzxException;
import com.atsurvey.common.result.Result;
import com.atsurvey.model.system.SysRole;
import com.atsurvey.vo.system.AssignRoleVo;
import com.atsurvey.vo.system.SysRoleQueryVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@Api(tags = "角色管理")
@RestController
@RequestMapping("/admin/system/sysRole")
public class SysRoleController {

    @Autowired
    private SysRoleService sysRoleService;


    @ApiOperation(value = "获取全部角色列表")
    @GetMapping("findAll")
    public Result<List<SysRole>> findAll() {
        List<SysRole> roleList = sysRoleService.list();

        try{
            int i = 10/0;
        }catch (Exception e){
            //抛出异常
            throw new ClfwzxException(20001,"执行了自定义异常处理...");
        }
        return Result.ok(roleList);
    }
}
1.2、测试Controller接口

http://localhost:8800/admin/system/sysRole/findAll

2、定义统一返回结果对象

项目中我们会将响应封装成json返回,一般我们会将所有接口的数据格式统一, 使前端(iOS Android, Web)对数据的操作更一致、轻松。

一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。但是一般会包含状态码、返回消息、数据这几部分内容

例如,我们的系统要求返回的基本数据格式如下:

列表:

{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "id": 2,
      "roleName": "系统管理员"
    }
  ],
  "ok": true
}

分页:

{
  "code": 200,
  "message": "成功",
  "data": {
    "records": [
      {
        "id": 2,
        "roleName": "系统管理员"
      },
      {
        "id": 3,
        "name": "普通管理员"
      }
    ],
    "total": 10,
    "size": 3,
    "current": 1,
    "orders": [],
    "hitCount": false,
    "searchCount": true,
    "pages": 2
  },
  "ok": true
}

没有返回数据:

{
  "code": 200,
  "message": "成功",
  "data": null,
  "ok": true
}

失败:

{
  "code": 201,
  "message": "失败",
  "data": null,
  "ok": false
}
2.1、定义统一返回结果对象

操作模块:common-util

后续其他模块也会用到,故抽取到common-util模块

package com.atsurvey.common.result;


import lombok.Data;

/**
 * 全局统一返回结果类
 *
 */
@Data
public class Result<T> {

    //返回码
    private Integer code;

    //返回消息
    private String message;

    //返回数据
    private T data;

    public Result(){}

    // 返回数据
    protected static <T> Result<T> build(T data) {
        Result<T> result = new Result<T>();
        if (data != null)
            result.setData(data);
        return result;
    }

    public static <T> Result<T> build(T body, Integer code, String message) {
        Result<T> result = build(body);
        result.setCode(code);
        result.setMessage(message);
        return result;
    }

    public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
        Result<T> result = build(body);
        result.setCode(resultCodeEnum.getCode());
        result.setMessage(resultCodeEnum.getMessage());
        return result;
    }

    public static<T> Result<T> ok(){
        return Result.ok(null);
    }

    /**
     * 操作成功
     * @param data  baseCategory1List
     * @param <T>
     * @return
     */
    public static<T> Result<T> ok(T data){
        Result<T> result = build(data);
        return build(data, ResultCodeEnum.SUCCESS);
    }

    public static<T> Result<T> fail(){
        return Result.fail(null);
    }

    /**
     * 操作失败
     * @param data
     * @param <T>
     * @return
     */
    public static<T> Result<T> fail(T data){
        Result<T> result = build(data);
        return build(data, ResultCodeEnum.FAIL);
    }

    public Result<T> message(String msg){
        this.setMessage(msg);
        return this;
    }

    public Result<T> code(Integer code){
        this.setCode(code);
        return this;
    }
}

统一返回结果状态信息类

下面的状态后续都会用到,所以直接引入了

package com.atsurvey.common.result;

import lombok.Getter;

/**
 * 统一返回结果状态信息类
 *
 */
@Getter
public enum ResultCodeEnum {

    SUCCESS(200,"成功"),
    FAIL(201, "失败"),
    SERVICE_ERROR(2012, "服务异常"),
    DATA_ERROR(204, "数据异常"),

    LOGIN_AUTH(208, "未登陆"),
    PERMISSION(209, "没有权限")
    ;

    private Integer code;

    private String message;

    private ResultCodeEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}
2.2、改造controller方法
@GetMapping("findAll")
public Result<List<SysRole>> findAll() {
    List<SysRole> roleList = sysRoleService.list();
    return Result.ok(roleList);
}
2.3、测试接口

http://localhost:8800/admin/system/sysRole/findAll

3、knife4j

文档地址:https://doc.xiaominfo.com/

knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。

3.1、Swagger介绍

前后端分离开发模式中,api文档是最好的沟通方式。

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。

1、及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)

2、规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)

3、一致性 (接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)

4、可测性 (直接在接口文档上进行测试,以方便理解业务)

3.2、集成knife4j

knife4j属于service模块公共资源,因此我们集成到service-uitl模块

3.2.1 添加依赖

操作模块:service-uitl

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>

说明:guigu-auth-parent已加入版本管理

3.2.2 添加knife4j配置类

操作模块:service-uitl

package com.atsurvey.common.config.knife4j;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

import java.util.ArrayList;
import java.util.List;

/**
 * knife4j配置信息
 */
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfig {

    @Bean
    public Docket adminApiConfig(){
        List<Parameter> pars = new ArrayList<>();
        ParameterBuilder tokenPar = new ParameterBuilder();
        tokenPar.name("token")
                .description("用户token")
                .defaultValue("")
                .modelRef(new ModelRef("string"))
                .parameterType("header")
                .required(false)
                .build();
        pars.add(tokenPar.build());
        //添加head参数end

        Docket adminApi = new Docket(DocumentationType.SWAGGER_2)
                .groupName("adminApi")
                .apiInfo(adminApiInfo())
                .select()
                //只显示admin路径下的页面
                .apis(RequestHandlerSelectors.basePackage("com.atsurvey"))
                .paths(PathSelectors.regex("/admin/.*"))
                .build()
                .globalOperationParameters(pars);
        return adminApi;
    }

    private ApiInfo adminApiInfo(){

        return new ApiInfoBuilder()
                .title("后台管理系统-API文档")
                .description("本文档描述了后台管理系统微服务接口定义")
                .version("1.0")
                .contact(new Contact("atsurvey", "http://atsurvey.com", "atsurvey@qq.com"))
                .build();
    }


}
3.2.3 Controller层添加注解
package com.atsurvey.system.controller;

import com.atsurvey.system.service.SysRoleService;
import com.atsurvey.common.result.Result;
import com.atsurvey.model.system.SysRole;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@Api(tags = "角色管理")
@RestController
@RequestMapping("/admin/system/sysRole")
public class SysRoleController {

    @Autowired
    private SysRoleService sysRoleService;

    @ApiOperation(value = "获取全部角色列表")
    @GetMapping("findAll")
    public Result<List<SysRole>> findAll() {
        List<SysRole> roleList = sysRoleService.list();
        return Result.ok(roleList);
    }
}
3.2.4、测试

http://localhost:8800/doc.html

在这里插入图片描述

4、分页查询

4.1、配置分页插件

操作模块:service-uitl,service公共资源

说明:我们将@MapperScan(“com.atsurvey.auth.mapper”)提取到该配置类上面,统一管理,启动类就不需要了。

package com.atsurvey.common.config.mp;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.atsurvey.auth.mapper")
public class MybatisPlusConfig {

    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.setUseDeprecatedExecutor(false);
    }
}
4.2、分页controller
//条件分页查询
//page 当前页  limit 每页显示记录数
//SysRoleQueryVo 条件对象
@ApiOperation("条件分页查询")
@GetMapping("{page}/{limit}")
public Result pageQueryRole(@PathVariable Long page,
                            @PathVariable Long limit,
                            SysRoleQueryVo sysRoleQueryVo) {
    //调用service的方法实现
    //1 创建Page对象,传递分页相关参数
    //page 当前页  limit 每页显示记录数
    Page<SysRole> pageParam = new Page<>(page,limit);

    //2 封装条件,判断条件是否为空,不为空进行封装
    LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();
    String roleName = sysRoleQueryVo.getRoleName();
    if(!StringUtils.isEmpty(roleName)) {
        //封装 like模糊查询
        wrapper.like(SysRole::getRoleName,roleName);
    }

    //3 调用方法实现
    IPage<SysRole> pageModel = sysRoleService.page(pageParam, wrapper);
    return Result.ok(pageModel);
}

5、其他controller方法

说明:通过knife4j测试接口

@ApiOperation(value = "获取")
@GetMapping("get/{id}")
public Result get(@PathVariable Long id) {
    SysRole role = sysRoleService.getById(id);
    return Result.ok(role);
}

@ApiOperation(value = "新增角色")
@PostMapping("save")
public Result save(@RequestBody @Validated SysRole role) {
    sysRoleService.save(role);
    return Result.ok();
}

@ApiOperation(value = "修改角色")
@PutMapping("update")
public Result updateById(@RequestBody SysRole role) {
    sysRoleService.updateById(role);
    return Result.ok();
}

@ApiOperation(value = "删除角色")
@DeleteMapping("remove/{id}")
public Result remove(@PathVariable Long id) {
    sysRoleService.removeById(id);
    return Result.ok();
}

@ApiOperation(value = "根据id列表删除")
@DeleteMapping("batchRemove")
public Result batchRemove(@RequestBody List<Long> idList) {
    sysRoleService.removeByIds(idList);
    return Result.ok();
}

配置日期时间格式

application-dev.yml添加以下内容

  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

6、统一异常处理

6.1、制造异常

除以0

int a = 10/0;

在这里插入图片描述

我们想让异常结果也显示为统一的返回结果对象,并且统一处理系统的异常信息,那么需要统一异常处理。

6.2、全局异常处理
6.2.1、创建统一异常处理器

操作模块:service-util

package com.atsurvey.common.handler;

import com.atsurvey.common.result.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;


/**
 * 全局异常处理类
 *
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception e){
        e.printStackTrace();
        return Result.fail();
    }
}
6.2.1、测试
6.3、处理特定异常
6.3.1、添加异常处理方法

GlobalExceptionHandler.java中添加

@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public Result error(ArithmeticException e){
	e.printStackTrace();
	return Result.fail().message("执行了特定异常处理");
}
6.3.2、测试
6.4、处理自定义异常
6.4.1、创建自定义异常类
package com.atsurvey.common.execption;

import com.atguigu.common.result.ResultCodeEnum;
import lombok.Data;

/**
 * 自定义全局异常类
 *
 */
@Data
public class ClfwzxException extends RuntimeException {

    private Integer code;

    private String message;

    /**
     * 通过状态码和错误消息创建异常对象
     * @param code
     * @param message
     */
    public ClfwzxException(Integer code, String message) {
        super(message);
        this.code = code;
        this.message = message;
    }

    /**
     * 接收枚举类型对象
     * @param resultCodeEnum
     */
    public ClfwzxException(ResultCodeEnum resultCodeEnum) {
        super(resultCodeEnum.getMessage());
        this.code = resultCodeEnum.getCode();
        this.message = resultCodeEnum.getMessage();
    }

    @Override
    public String toString() {
        return "GuliException{" +
                "code=" + code +
                ", message=" + this.getMessage() +
                '}';
    }
}

6.4.2、业务中需要位置抛出
try {
    int a = 10/0;
}catch(Exception e) {
    throw new ClfwzxException(20001,"出现自定义异常");
}
6.4.3、添加异常处理方法

GlobalExceptionHandler.java中添加

@ExceptionHandler(ClfwzxException.class)
@ResponseBody
public Result error(ClfwzxExceptione){
	e.printStackTrace();
	return Result.fail().message(e.getMsg()).code(e.getCode());
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

eNSP PRO安装完整版(超详细)

目录 eNSP PRO包&#xff1a; 安装步骤&#xff1a; eNSP PRO包&#xff1a; 链接: https://pan.baidu.com/s/12oBJ708OHMZlhj8nS21HSw?pwdr64q 提取码: r64q 安装步骤&#xff1a; 将ensp pro的包下载并且解压出来 在Oracle VM VirtualBox&#xff0c;将我们解压好的en…

WMS仓储管理系统如何让仓库管理有过程

在当今竞争激烈的商业环境中&#xff0c;WMS仓储管理系统的智能化与过程化管理显得尤为重要。一个具有过程管理的WMS仓储管理系统不仅能够帮助企业实时监控、分析和调度仓库作业&#xff0c;还能显著提升作业效率和成本控制能力。下面&#xff0c;我们就来深入探讨一下这种“有…

学校为何更热衷于使用SOLIDWORKS教育版教学

在当今的教育环境中&#xff0c;SOLIDWORKS教育版因其独特的优势&#xff0c;越来越受到学校的青睐。为什么学校更热衷于使用SolidWorks教育版进行教学呢&#xff1f;本文将从以下几个方面进行阐述。 首先&#xff0c;SOLIDWORKS教育版为学生们提供了一个与实际工程应用紧密结…

记录MySQL数据库查询不等于xxx时的坑

目录 一、背景 二、需求 三、方法 四、示例 一、背景 在使用MySQL数据库查询数据时&#xff0c;需要查询字段name不等于xxx的记录&#xff0c;通过where name ! xxx查询出来的记录不符合预期&#xff0c;通过检查发现少了name字段为null的记录&#xff0c;后经查询得知在My…

Java面试八股之集合类为什么没实现cloneable和serilizable接口

Java集合类为什么没实现cloneable和serilizable接口 设计原则与职责分离&#xff1a; 集合类的主要职责是管理和组织对象的存储结构&#xff0c;提供增删查改等操作。而Cloneable接口用于实现对象的复制&#xff08;克隆&#xff09;&#xff0c;Serializable接口用于实现对象…

C# 在Excel中添加筛选器并执行筛选 (日期筛选、文本筛选、数字筛选)

自动筛选器是 Excel 中的一个基本但极其有用的功能&#xff0c;它可以让你根据特定的条件来自动隐藏和显示你的数据。当有大量的数据需要处理时&#xff0c;这个功能可以帮你快速找到你需要的信息&#xff0c;从未更加有效地分析和处理相关数据。 下面将介绍如何使用免费.NET …

LabVIEW二维码生成与识别

LabVIEW二维码生成与识别 随着数字化时代的快速发展&#xff0c;QR二维码作为一种高效的信息传递和识别手段&#xff0c;已广泛应用于各行各业。利用LabVIEW软件及其NI视觉开发模块(VDM)来实现一个高效的QR二维码生成与识别系统。该系统不仅能够快速生成带有自定义信息的二维码…

安装ps提示找不到msvcp140.dll,无法继续执行此代码如何修复

MSVCP140.dll&#xff0c;作为Windows操作系统中的一个关键组件&#xff0c;扮演着不可或缺的角色&#xff0c;尤其对于基于C开发的应用程序而言。本文旨在深入探讨这一动态链接库文件的功能、重要性、常见问题及解决方案&#xff0c;为您提供全面的MSVCP140.dll指南。 一、MSV…

【转载】【亲测可用】Ubuntu18.04安装VTK-8.2.0

这篇文章转载大部分都参考自下面的链接&#xff0c;注意cmake的版本&#xff0c;如果版本不对是编译失败的 https://blog.csdn.net/qq_42257666/article/details/124574029?ops_request_misc%257B%2522request%255Fid%2522%253A%2522171566768016800182751339%2522%252C%2522…

vs2019中__cplusplus一直显示199711

vs2019中__cplusplus一直显示199711&#xff0c;如何修改&#xff1f; 打开属性->C/C->命令行&#xff0c;其他选项&#xff0c;输入&#xff1a;/Zc:__cplusplus

Qt学习笔记1.3.3QtCore-隐式共享

文章目录 概述隐式共享细节类列表 Qt中的许多c类使用隐式数据共享来最大化资源使用并最小化复制。隐式共享类作为参数传递时既安全又高效&#xff0c;因为只传递指向数据的指针&#xff0c;并且只有当函数写入数据时才会复制数据&#xff0c;即写时复制(copy-on-write)。 概述 …

python继承和call魔术方法

文章目录 前言一、类的继承1 继承示例1:继承父类的所有属性和方法示例2:继承的传递性示例3:单继承与多继承2 继承中属性和方法的重写2.1 直接覆盖父类原来方法2.2 在父类的方法后增加新的内容(继承中重写__init__方法经常用到)示例1:简单的例子示例2:复杂点的例子二、ca…

C# Winform+Halcon结合标准视觉工具

介绍 winform与halcon结合标准化工具实例 软件架构 软件架构说明 基于NET6 WINFORMHALCON 实现标准化视觉检测工具 集成相机通讯 集成PLC通讯 TCP等常见通讯 支持常见halcon算子 图形采集blob分析高精度匹配颜色提取找几何体二维码提取OCR识别等等 。。。 安装教程 …

C++哈希(个人笔记)

C哈希 1.unordered_mapd1.1unordered_map的构造函数1.2unorder_map的容量1.3unordered_map的迭代器1.4unordered_map的元素访问1.5unorder_map的查找1.6unordered_map的修改操作1.7unordered_map的桶操作 2.unordered_set3.unordered_set和unordered_set的笔试题4.哈希4.1哈希概…

LVS负载均衡超详细入门介绍

LVS 一、LVS入门介绍 1.1.LVS负载均衡简介 1.2.负载均衡的工作模式 1.2.1.地址转换NAT&#xff08;Network Address Translation&#xff09; 1.2.2.IP隧道TUN&#xff08;IP Tunneling&#xff09; 1.2.3.直接路由DR&#xff08;Direct Routing&#xff09; 1.3.…

AI图像生成-原理

一、图像生成流程总结 【AI绘画】深入理解Stable Diffusion&#xff01;站内首个深入教程&#xff0c;30分钟从原理到模型训练 买不到的课程_哔哩哔哩_bilibili 二、如果只是用comfy UI生成图片 1、找到下面几个文件&#xff0c;把对应模型移动到对应文件夹即可使用 2、选择对…

(八)SQL基础知识练习题(选择题)(下)#CDA学习打卡

本文整理了SQL基础知识相关的练习题&#xff0c;共133道&#xff0c;可作为CDA一级的补充习题&#xff0c;也适用于刚入门初级SQL想巩固基础的同学。来源&#xff1a;如荷学数据科学题库&#xff08;技术专项-SQL&#xff09;。暂时按照原题库顺序present&#xff0c;如有需要之…

Android XML的使用详解

一、布局文件&#xff1a; 在layout目录下&#xff0c;使用比较广泛&#xff1b;我们可以为应用定义两套或多套布局&#xff0c;例如&#xff1a;可以新建目录layout_land(代表手机横屏布局)&#xff0c;layout_port(代表手机竖屏布局)&#xff0c;系统会根据不同情况自动找到…

Linux安装MySQL(CentOS 7)

安装步骤 下载的MySQL版本为mysql-8.0.26 进入网站MySQL&#xff0c;点击下载 找到mysql社区版 点击Archive&#xff0c;查看所有相关不同版本 点击MySQL Community Server 注意下载MySQL对应的Linux版本&#xff0c;CentOS7 对应 Linux7&#xff0c;如果下成Linux 8 则后面…

【文末福利送资料】深度探索GPT模型,竟然10个字都不会说?

目录 导读 自回归模型 那么什么时候停下呢&#xff1f; 该停下来&#xff0c;但是概率不让啊 GPT欠缺的两种能力 目录 导读 自回归模型 那么什么时候停下呢&#xff1f; 该停下来&#xff0c;但是概率不让啊 GPT欠缺的两种能力 缺少规划 反省和修订 所有的人工智能…