MyBatis-Plus 一、(基础应用)

news2024/9/30 13:30:50

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

        mybatis 、mybatis-plus 二者区别:

        MyBatis:

  • 所有SQL语句全部自己写
  • 手动解析实体关系映射转换为MyBatis内部对象注入容器
  • 不支持Lambda形式调用

        Mybatis Plus:

  • 强大的条件构造器,满足各类使用需求
  • 内置的Mapper,通用的Service,少量配置即可实现单表大部分CRUD操作
  • 支持Lambda形式调用
  • 提供了基本的CRUD功能,连SQL语句都不需要编写
  • 自动解析实体关系映射转换为MyBatis内部对象注入容器

        

一、什么是MybatisPlus 

MybatisPlus可以节省大量时间,所有的CRUD代码都可以自动化完成

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

特性:

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑

  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作

  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求

  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错

  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题

  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作

  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )

  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用

  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询

  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库

  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询

  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

二、快速入门

        1、准备数据库:

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '姓名',
  `job` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '工作',
  `salary` double NULL DEFAULT NULL COMMENT '工资',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
  `deleted` bigint(0) NULL DEFAULT 0 COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 191 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, '张三', '随便', 2000, '2024-08-19 17:15:34', '2024-08-19 17:15:34', 0);
INSERT INTO `user` VALUES (2, '李四', '程序员', 2800, '2024-08-19 17:15:34', '2024-08-19 17:15:34', 0);
INSERT INTO `user` VALUES (3, '王五', '程序员鼓励师', 2700, '2024-08-19 17:15:34', '2024-08-19 17:15:34', 0);
INSERT INTO `user` VALUES (4, '王二', '部门总监', 4200, '2024-08-19 17:15:34', '2024-08-19 17:15:34', 0);
INSERT INTO `user` VALUES (5, '麻子', '程序员', 3000, '2024-08-19 17:15:34', '2024-08-19 17:15:34', 0);
INSERT INTO `user` VALUES (6, '最帅三太子', '程序员', 3500, '2024-08-19 17:15:34', '2024-08-19 17:15:34', 0);
INSERT INTO `user` VALUES (7, '苍老师', '程序员', 3700, '2024-08-19 17:15:34', '2024-08-19 17:15:34', 0);
INSERT INTO `user` VALUES (8, '波多野结衣', 'CEO', 5000, '2024-08-19 17:15:34', '2024-08-19 17:15:34', 0);
INSERT INTO `user` VALUES (9, '测试3', '测试工作3', 500, '2024-08-19 17:15:34', '2024-08-19 17:15:34', 0);

        2、创建项目 略

        3、引入依赖:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.6.14</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId><!---->
            <artifactId>lombok</artifactId>
            <optional>true</optional>
            <version>1.18.22</version>
        </dependency>
        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>

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

    </dependencies>

         4、添加配置:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
    transaction-isolation: 2 #具体定义可在Transactional->Isolation属性查看
    #hikari数据库连接池
    hikari:
      pool-name: Retail_HikariCP
      minimum-idle: 1 #最小空闲连接数量
      idle-timeout: 180000 #空闲连接存活最大时间,默认600000(10分钟)
      maximum-pool-size: 10 #连接池最大连接数,默认是10
      auto-commit: true  #此属性控制从池返回的连接的默认自动提交行为,默认值:true
      max-lifetime: 1800000 #此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
      connection-timeout: 30000 #数据库连接超时时间,默认30秒,即30000

     在spring boot启动类中添加@MapperScan注解,扫描Dao文件夹:   

@SpringBootApplication
@MapperScan("com.test.demo.dao")
public class TESTApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(TESTApplication.class,args);
    }

}

        5、编码:

@Getter
@Setter
@ToString(callSuper = true)
@Accessors(chain = true)
@TableName("user")
public class User {

    private static final long serialVersionUID = 1L;


    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private Integer id;

    private String name;

    private String type;

    private String job;

    private Double salary;
    
    private LocalDateTime createTime;
    
    private LocalDateTime updateTime;
    
    private Long deleted;
    
    
}

        编写Mapper包下的Dao接口 


//再对应的mapper上面实现基本的接口 BaseMapper
@Mapper
public interface UserDao extends BaseMapper<User> {

    //所有的CRUD都已经完成
    //不需要像以前一样配置一大堆文件:pojo-dao(连接mybatis,配置mapper.xml文件)==>service-controller

}

注意:

要在主启动类上去扫描mapper包下的所有接口:@MapperScan("com.test.demo.dao")

        6、简单使用 

@RestController
@RequestMapping("/user")
public class UserController {
    
    @Autowired
    private UserDao userDao;

    @GetMapping("/get/{id}")
    public User findAll(@PathVariable("id") int id ){
        return userDao.selectById(id);
    }

}

        

三、增删改查

        上面说我们dao接口继承了BaseMapper之后,基本的crud方法就可以直接使用了,就像举例中的 userDao.selectById(id) 方法一样,我们在 userDao 中并没有写 selectById() 方法,但是继承了BaseMapper之后就可以直接用了。这是因为BaseMapper已经帮我们写好了基本的crud方法,可以简单看一下源码:

public interface BaseMapper<T> extends Mapper<T> {
    int insert(T entity);

    int deleteById(Serializable id);

    int deleteById(T entity);

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

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

    int deleteBatchIds(@Param("coll") Collection<?> 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);

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

    default boolean exists(Wrapper<T> queryWrapper) {
        Long count = this.selectCount(queryWrapper);
        return null != count && count > 0L;
    }

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

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

        上面的所有方法其实都比较容易理解,insert、delect、update、select开头的方法分别对应着增删改查,page方法代表着分页查询。

        上面的方法涵盖了单表查询的基本方法,所以如果不是复杂sql的,我们直接直接使用现成的方法进行查询。下面对各个方法做一个简单演示。

         以下面几个简单的增删改查方法为例:

    int insert(T entity);

    int deleteById(Serializable id);

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

    T selectById(Serializable id);

        这几个方法是最基本的增删改查的方法:

        1、insert 
        User user = new User();
        user.setName("测试1");
        user.setJob("测试工作");
        userDao.insert(user);

        说到增加,就得提一下主键的生成策略,在mybatis中主键的生成策略需要配合标签或者注解中的属性来设置。在mybatisplus中主键的生成策略可以通过 @TableId() 注解来设置。这个注解是直接加载主键字段上面的。例如User类中:

    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private Long id;

        上面这个设置代表的是,id字段对应数据库字段为id,且为主键。 type = IdType.ASSIGN_ID 代表 基于雪花算法的策略生成数据id。我们执行两次保存操作后,会发现数据自动填充了id:
        

         type 也可以设置为 IdType.AUTO ,代表:使用数据库的自增策略,注意:该类型需确保数据库设置了自增id,否则无效。

        加上这个注解之后,不需要再加任何配置,就可以生成主键id,这对于我们操作来说变得非常方便。至于其中的原理会放在后面说。

        还有一个注解顺便提一下:@TableId ,这个注解主要是解决属性所对应字段不一致的情况,比如说当user实体类中的name 属性命名的是 userName ,但是数据库中命名的是 name 这样不一致,查询就无法映射到对应字段,可以加上这个注解代表这两个字段是同一个:

    @TableField("name")
    private String userName;
         2、delect

        根据主键id 删除对应数据。

userDao.deleteById(1825476600460238849L);
         3、update

        根据主键id修改某条数据。

        User user = new User();
        user.setUserName("测试2");
        user.setJob("测试工作2");
        user.setId(1825476862067429378L);
        userDao.updateById(user);
        4、select

        根据主键id查询某条数据。

        User user = userDao.selectById(id);

        以上就是最简单的增删改查的用法,对于单表查询其实非常的方便。也就是这个其实帮我们提前写好了很多简单的sql操作,也是常用的sql操作。而且不光是 dao层 ,在service层也封装了很多操作,让我们在service层也可以非常方便进行处理。

        四、service 层增删改查

        如果想要在service层实现增删改查,就需要我们service继承 ServiceImpl 接口,其实也就是继承 Iservice 接口,Iservice 接口中含有方法如下:

public interface IService<T> {

    /**
     * 默认批次提交数量
     */
    int DEFAULT_BATCH_SIZE = 1000;

    /**
     * 插入一条记录(选择字段,策略插入)
     *
     * @param entity 实体对象
     */
    default boolean save(T entity) {
        return SqlHelper.retBool(getBaseMapper().insert(entity));
    }

    /**
     * 插入(批量)
     *
     * @param entityList 实体对象集合
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean saveBatch(Collection<T> entityList) {
        return saveBatch(entityList, DEFAULT_BATCH_SIZE);
    }

    /**
     * 插入(批量)
     *
     * @param entityList 实体对象集合
     * @param batchSize  插入批次数量
     */
    boolean saveBatch(Collection<T> entityList, int batchSize);

    /**
     * 批量修改插入
     *
     * @param entityList 实体对象集合
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean saveOrUpdateBatch(Collection<T> entityList) {
        return saveOrUpdateBatch(entityList, DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量修改插入
     *
     * @param entityList 实体对象集合
     * @param batchSize  每次的数量
     */
    boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

    /**
     * 根据 ID 删除
     *
     * @param id 主键ID
     */
    default boolean removeById(Serializable id) {
        return SqlHelper.retBool(getBaseMapper().deleteById(id));
    }

    /**
     * 根据 ID 删除
     *
     * @param id      主键(类型必须与实体类型字段保持一致)
     * @param useFill 是否启用填充(为true的情况,会将入参转换实体进行delete删除)
     * @return 删除结果
     * @since 3.5.0
     */
    default boolean removeById(Serializable id, boolean useFill) {
        throw new UnsupportedOperationException("不支持的方法!");
    }

    /**
     * 根据实体(ID)删除
     *
     * @param entity 实体
     * @since 3.4.4
     */
    default boolean removeById(T entity) {
        return SqlHelper.retBool(getBaseMapper().deleteById(entity));
    }

    /**
     * 根据 columnMap 条件,删除记录
     *
     * @param columnMap 表字段 map 对象
     */
    default boolean removeByMap(Map<String, Object> columnMap) {
        Assert.notEmpty(columnMap, "error: columnMap must not be empty");
        return SqlHelper.retBool(getBaseMapper().deleteByMap(columnMap));
    }

    /**
     * 根据 entity 条件,删除记录
     *
     * @param queryWrapper 实体包装类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default boolean remove(Wrapper<T> queryWrapper) {
        return SqlHelper.retBool(getBaseMapper().delete(queryWrapper));
    }

    /**
     * 删除(根据ID 批量删除)
     *
     * @param list 主键ID或实体列表
     */
    default boolean removeByIds(Collection<?> list) {
        if (CollectionUtils.isEmpty(list)) {
            return false;
        }
        return SqlHelper.retBool(getBaseMapper().deleteBatchIds(list));
    }

    /**
     * 批量删除
     *
     * @param list    主键ID或实体列表
     * @param useFill 是否填充(为true的情况,会将入参转换实体进行delete删除)
     * @return 删除结果
     * @since 3.5.0
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean removeByIds(Collection<?> list, boolean useFill) {
        if (CollectionUtils.isEmpty(list)) {
            return false;
        }
        if (useFill) {
            return removeBatchByIds(list, true);
        }
        return SqlHelper.retBool(getBaseMapper().deleteBatchIds(list));
    }

    /**
     * 批量删除(jdbc批量提交)
     *
     * @param list 主键ID或实体列表(主键ID类型必须与实体类型字段保持一致)
     * @return 删除结果
     * @since 3.5.0
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean removeBatchByIds(Collection<?> list) {
        return removeBatchByIds(list, DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量删除(jdbc批量提交)
     *
     * @param list    主键ID或实体列表(主键ID类型必须与实体类型字段保持一致)
     * @param useFill 是否启用填充(为true的情况,会将入参转换实体进行delete删除)
     * @return 删除结果
     * @since 3.5.0
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean removeBatchByIds(Collection<?> list, boolean useFill) {
        return removeBatchByIds(list, DEFAULT_BATCH_SIZE, useFill);
    }

    /**
     * 批量删除(jdbc批量提交)
     *
     * @param list      主键ID或实体列表
     * @param batchSize 批次大小
     * @return 删除结果
     * @since 3.5.0
     */
    default boolean removeBatchByIds(Collection<?> list, int batchSize) {
        throw new UnsupportedOperationException("不支持的方法!");
    }

    /**
     * 批量删除(jdbc批量提交)
     *
     * @param list      主键ID或实体列表
     * @param batchSize 批次大小
     * @param useFill   是否启用填充(为true的情况,会将入参转换实体进行delete删除)
     * @return 删除结果
     * @since 3.5.0
     */
    default boolean removeBatchByIds(Collection<?> list, int batchSize, boolean useFill) {
        throw new UnsupportedOperationException("不支持的方法!");
    }

    /**
     * 根据 ID 选择修改
     *
     * @param entity 实体对象
     */
    default boolean updateById(T entity) {
        return SqlHelper.retBool(getBaseMapper().updateById(entity));
    }

    /**
     * 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
     *
     * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
     */
    default boolean update(Wrapper<T> updateWrapper) {
        return update(null, updateWrapper);
    }

    /**
     * 根据 whereEntity 条件,更新记录
     *
     * @param entity        实体对象
     * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
     */
    default boolean update(T entity, Wrapper<T> updateWrapper) {
        return SqlHelper.retBool(getBaseMapper().update(entity, updateWrapper));
    }

    /**
     * 根据ID 批量更新
     *
     * @param entityList 实体对象集合
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean updateBatchById(Collection<T> entityList) {
        return updateBatchById(entityList, DEFAULT_BATCH_SIZE);
    }

    /**
     * 根据ID 批量更新
     *
     * @param entityList 实体对象集合
     * @param batchSize  更新批次数量
     */
    boolean updateBatchById(Collection<T> entityList, int batchSize);

    /**
     * TableId 注解存在更新记录,否插入一条记录
     *
     * @param entity 实体对象
     */
    boolean saveOrUpdate(T entity);

    /**
     * 根据 ID 查询
     *
     * @param id 主键ID
     */
    default T getById(Serializable id) {
        return getBaseMapper().selectById(id);
    }

    /**
     * 查询(根据ID 批量查询)
     *
     * @param idList 主键ID列表
     */
    default List<T> listByIds(Collection<? extends Serializable> idList) {
        return getBaseMapper().selectBatchIds(idList);
    }

    /**
     * 查询(根据 columnMap 条件)
     *
     * @param columnMap 表字段 map 对象
     */
    default List<T> listByMap(Map<String, Object> columnMap) {
        return getBaseMapper().selectByMap(columnMap);
    }

    /**
     * 根据 Wrapper,查询一条记录 <br/>
     * <p>结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")</p>
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default T getOne(Wrapper<T> queryWrapper) {
        return getOne(queryWrapper, true);
    }

    /**
     * 根据 Wrapper,查询一条记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     * @param throwEx      有多个 result 是否抛出异常
     */
    T getOne(Wrapper<T> queryWrapper, boolean throwEx);

    /**
     * 根据 Wrapper,查询一条记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    Map<String, Object> getMap(Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper,查询一条记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     * @param mapper       转换函数
     */
    <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

    /**
     * 查询总记录数
     *
     * @see Wrappers#emptyWrapper()
     */
    default long count() {
        return count(Wrappers.emptyWrapper());
    }

    /**
     * 根据 Wrapper 条件,查询总记录数
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default long count(Wrapper<T> queryWrapper) {
        return SqlHelper.retCount(getBaseMapper().selectCount(queryWrapper));
    }

    /**
     * 查询列表
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default List<T> list(Wrapper<T> queryWrapper) {
        return getBaseMapper().selectList(queryWrapper);
    }

    /**
     * 查询所有
     *
     * @see Wrappers#emptyWrapper()
     */
    default List<T> list() {
        return list(Wrappers.emptyWrapper());
    }

    /**
     * 翻页查询
     *
     * @param page         翻页对象
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {
        return getBaseMapper().selectPage(page, queryWrapper);
    }

    /**
     * 无条件翻页查询
     *
     * @param page 翻页对象
     * @see Wrappers#emptyWrapper()
     */
    default <E extends IPage<T>> E page(E page) {
        return page(page, Wrappers.emptyWrapper());
    }

    /**
     * 查询列表
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper) {
        return getBaseMapper().selectMaps(queryWrapper);
    }

    /**
     * 查询所有列表
     *
     * @see Wrappers#emptyWrapper()
     */
    default List<Map<String, Object>> listMaps() {
        return listMaps(Wrappers.emptyWrapper());
    }

    /**
     * 查询全部记录
     */
    default List<Object> listObjs() {
        return listObjs(Function.identity());
    }

    /**
     * 查询全部记录
     *
     * @param mapper 转换函数
     */
    default <V> List<V> listObjs(Function<? super Object, V> mapper) {
        return listObjs(Wrappers.emptyWrapper(), mapper);
    }

    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default List<Object> listObjs(Wrapper<T> queryWrapper) {
        return listObjs(queryWrapper, Function.identity());
    }

    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     * @param mapper       转换函数
     */
    default <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
        return getBaseMapper().selectObjs(queryWrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList());
    }

    /**
     * 翻页查询
     *
     * @param page         翻页对象
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default <E extends IPage<Map<String, Object>>> E pageMaps(E page, Wrapper<T> queryWrapper) {
        return getBaseMapper().selectMapsPage(page, queryWrapper);
    }

    /**
     * 无条件翻页查询
     *
     * @param page 翻页对象
     * @see Wrappers#emptyWrapper()
     */
    default <E extends IPage<Map<String, Object>>> E pageMaps(E page) {
        return pageMaps(page, Wrappers.emptyWrapper());
    }

    /**
     * 获取对应 entity 的 BaseMapper
     *
     * @return BaseMapper
     */
    BaseMapper<T> getBaseMapper();

    /**
     * 获取 entity 的 class
     *
     * @return {@link Class<T>}
     */
    Class<T> getEntityClass();

    /**
     * 以下的方法使用介绍:
     *
     * 一. 名称介绍
     * 1. 方法名带有 query 的为对数据的查询操作, 方法名带有 update 的为对数据的修改操作
     * 2. 方法名带有 lambda 的为内部方法入参 column 支持函数式的
     * 二. 支持介绍
     *
     * 1. 方法名带有 query 的支持以 {@link ChainQuery} 内部的方法名结尾进行数据查询操作
     * 2. 方法名带有 update 的支持以 {@link ChainUpdate} 内部的方法名为结尾进行数据修改操作
     *
     * 三. 使用示例,只用不带 lambda 的方法各展示一个例子,其他类推
     * 1. 根据条件获取一条数据: `query().eq("column", value).one()`
     * 2. 根据条件删除一条数据: `update().eq("column", value).remove()`
     *
     */

    /**
     * 链式查询 普通
     *
     * @return QueryWrapper 的包装类
     */
    default QueryChainWrapper<T> query() {
        return ChainWrappers.queryChain(getBaseMapper());
    }

    /**
     * 链式查询 lambda 式
     * <p>注意:不支持 Kotlin </p>
     *
     * @return LambdaQueryWrapper 的包装类
     */
    default LambdaQueryChainWrapper<T> lambdaQuery() {
        return ChainWrappers.lambdaQueryChain(getBaseMapper());
    }

    /**
     * 链式查询 lambda 式
     * kotlin 使用
     *
     * @return KtQueryWrapper 的包装类
     */
    default KtQueryChainWrapper<T> ktQuery() {
        return ChainWrappers.ktQueryChain(getBaseMapper(), getEntityClass());
    }

    /**
     * 链式查询 lambda 式
     * kotlin 使用
     *
     * @return KtQueryWrapper 的包装类
     */
    default KtUpdateChainWrapper<T> ktUpdate() {
        return ChainWrappers.ktUpdateChain(getBaseMapper(), getEntityClass());
    }

    /**
     * 链式更改 普通
     *
     * @return UpdateWrapper 的包装类
     */
    default UpdateChainWrapper<T> update() {
        return ChainWrappers.updateChain(getBaseMapper());
    }

    /**
     * 链式更改 lambda 式
     * <p>注意:不支持 Kotlin </p>
     *
     * @return LambdaUpdateWrapper 的包装类
     */
    default LambdaUpdateChainWrapper<T> lambdaUpdate() {
        return ChainWrappers.lambdaUpdateChain(getBaseMapper());
    }

    /**
     * <p>
     * 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
     * 此次修改主要是减少了此项业务代码的代码量(存在性验证之后的saveOrUpdate操作)
     * </p>
     *
     * @param entity 实体对象
     */
    default boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) {
        return update(entity, updateWrapper) || saveOrUpdate(entity);
    }
}

        Iservice 中方法非常多,也就说如果我们的servcie 层接口实现了 Iservice,那这些方法等于都已经默认帮我们实现了,所以我们在service层也就可以非常方便的进行增删改查的操作了。简单讲讲增删改查的操作:
        首先根据service层的规范先进行定义service接口,再用serviceImpl去实现service,如下:
        1、定义service接口 并继承 IService:

public interface IUserService extends IService<User> {
      
}

        2、定义service实现类,并继承ServiceImpl 。

@Service
public class UserService extends ServiceImpl<UserDao, User> implements IUserService {
    
}

        此时我们就可以使用上面所提供的方法了:

        1、save
        User user = new User();
        user.setUserName("测试2");
        user.setJob("测试工作2");
        userService.save(user);
        2、remove
        userService.removeById(user.getId());
      3、update
        User user = new User();
        user.setUserName("测试3");
        user.setJob("测试工作3");
        user.setId(1825476862067429378L);
        userService.updateById(user);
        4、get
        User user = userService.getById(user.getId());

        上面就是根据主键id进行的增删改查最基础的方法。

        因为IService提供了很多方法,有很多其实应用场景很小,所以下一篇会简单讲讲 其他方法的应用。

五、条件构造器

        BaseMapper 与 Iservice 提供的方法一般只能满足基本的sql语句,如果需要执行复杂的带条件的sql,mybatisplus支持原生的mabatis中的功能,所以如果进行复杂sql,例如多条件动态sql或者多表查询,我们依然可以使用 使用@Servcie、@Update 等注解或者在mapper.xml文件中使用标签书写sql。
        但是其实MybatisPlus中也为我们提供了条件构造器,来执行复杂的多条件动态查询等方法。

        例如通用service常用方法:

  • 新增:
    • default boolean save(T entity):新增记录
    • boolean saveBatch(Collection<T> entityList):批量插入
    • saveBatch(Collection<T> entityList, int batchSize):一次性批量插入batchSize条记录
  • 删除:
    • boolean removeById(Serializable id):根据id删除
    • boolean removeByMap(Map<String, Object> columnMap):根据条件删除
    • boolean remove(Wrapper<T> queryWrapper):使用Wrapper封装条件删除
    • boolean removeByIds(Collection<? extends Serializable> idList):删除一批
  • 修改:
    • boolean updateById(T entity):修改
    • boolean update(Wrapper<T> updateWrapper):根据Wrapper修改
    • boolean update(T entity, Wrapper<T> updateWrapper):使用Wrapper查询出结果,修改为entity
    • boolean updateBatchById(Collection<T> entityList):批量修改
    • updateBatchById(Collection<T> entityList, int batchSize):一次性批量修改batchSize条记录
    • boolean saveOrUpdate(T entity):如果id存在则修改,如果id不存在则新增
  • 查询:
    • T getById(Serializable id):根据id查询
    • List<T> listByIds(Collection<? extends Serializable> idList):根据一批id查询多条记录
    • List<T> listByMap(Map<String, Object> columnMap):根据条件查询多条记录
    • T getOne(Wrapper<T> queryWrapper):根据Wrapper查询一条记录,如果查询到多条则抛出异常
    • T getOne(Wrapper<T> queryWrapper, boolean throwEx):根据Wrapper查询一条记录,通过throwEx决定是否抛出异常
    • int count():查询总记录数
    • int count(Wrapper<T> queryWrapper):根据条件查询总记录数
  • 分页:
    • <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper):带条件分页查询,当前页数据为T类型
    • <E extends IPage<T>> E page(E page):无条件分页
    • List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper):带条件分页查询,当前页数据为HashMap类型
    • List<Map<String, Object>> listMaps():无条件分页

        在有一些方法中的参数类型是 Wrapper<T> queryWrapper ,这个就是条件构造器,例如 

T getOne(Wrapper<T> queryWrapper):根据Wrapper查询一条记录,如果查询到多条则抛出异常

        例如上面这个方法,使用如下所示:

        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("name","张三");
        User user = userService.getOne(wrapper);

        代表的意思就是查询 name 字段等于张三的数据,并且这条数据必须唯一。
        eq()这个就是Wrapper接口中提供的相关函数,映射在sql语句中的方法是 = ,代表相等。
        除了eq()还提供了其他很多相关映射函数:  

  1、QueryWrapper

  QueryWrapper用于构建查询条件,可以通过链式调用的方式组装各种查询条件。

        QueryWrapper的基本用法:

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

public class QueryWrapperExample {

    public static void main(String[] args) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        
        // 等值条件
        queryWrapper.eq("name", "John");

        // 不等值条件
        queryWrapper.ne("age", 25);

        // 大于条件
        queryWrapper.gt("create_time", "2022-01-01");

        // 小于等于条件
        queryWrapper.le("update_time", "2023-01-01");

        // 模糊查询
        queryWrapper.like("email", "@gmail.com");

        // 排序
        queryWrapper.orderByAsc("age");

        // 使用Lambda表达式
        queryWrapper.lambda().eq(User::getName, "Alice");

        // 打印SQL语句
        System.out.println("SQL: " + queryWrapper.getSqlSelect());
    }
}

  Lambda表达式的高级用法:
  QueryWrapper支持Lambda表达式,可以进一步简化代码,提高可读性。

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

public class LambdaQueryWrapperExample {

    public static void main(String[] args) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();

        // Lambda表达式
        queryWrapper.lambda().eq(User::getName, "John")
                             .ne(User::getAge, 25)
                             .like(User::getEmail, "@gmail.com");

        // 打印SQL语句
        System.out.println("SQL: " + queryWrapper.getSqlSelect());
    }
}

如果要使用Lambda表达式的用法,我们也可以直接使用 LambdaQueryWrapper。

        2、LambdaQueryWrapper

        可以把LambdaQueryWrapper看作是实现了Lambda语法的 QueryWrapper,使用方式跟QueryWrapper 一样,只不过可以不用 lambda() 就可以直接使用lambda语法。

        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getName,"张三");
        User user = userService.getOne(wrapper);
        3、UpdateWrapper

  UpdateWrapper用于构建更新条件,与QueryWrapper类似,也支持链式调用和Lambda表达式。
        注意:在调用更新方法的时候会用到这个。

        基本用法:

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;

public class UpdateWrapperExample {

    public static void main(String[] args)

 {
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();

        // 等值条件
        updateWrapper.eq("name", "John");

        // 不等值条件
        updateWrapper.ne("age", 25);

        // 大于条件
        updateWrapper.gt("create_time", "2022-01-01");

        // 小于等于条件
        updateWrapper.le("update_time", "2023-01-01");

        // 模糊查询
        updateWrapper.like("email", "@gmail.com");

        // 使用Lambda表达式
        updateWrapper.lambda().set(User::getName, "Alice").eq(User::getAge, 30);

        // 打印SQL语句
        System.out.println("SQL: " + updateWrapper.getSqlSet());
    }
}

        UpdateWrapper的基本用法与QueryWrapper类似,可以通过链式调用添加等值、不等值、范围、模糊等更新条件。

         Lambda表达式的高级用法:

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;

public class LambdaUpdateWrapperExample {

    public static void main(String[] args) {
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();

        // Lambda表达式
        updateWrapper.lambda().set(User::getName, "Alice")
                             .eq(User::getAge, 30)
                             .like(User::getEmail, "@gmail.com");

        // 打印SQL语句
        System.out.println("SQL: " + updateWrapper.getSqlSet());
    }
}

        通过lambda方法,我们可以使用实体类的属性名,使得代码更加简洁和可读。

        4、 LambdaUpdateWrapper

        与LambdaQueryWrapper 一样,用法与UpdateWrapper 也都一样,只是默认实现了Lambda语法。

        条件构造器的详细用法放下下一篇单独写。

六、分页查询

         mybatisplus有自己的内置分页插件,用起来非常方便,不像mybatis还需要引入第三方依赖。

        使用步骤非常简单,只要两步即可:

        1、配置拦截器组件
/**
 * 注册插件
 */
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
​
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    // 添加分页插件
    PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor();
    // 设置请求的页面大于最大页后操作,true调回到首页,false继续请求。默认false
    pageInterceptor.setOverflow(false);
    // 单页分页条数限制,默认无限制
    pageInterceptor.setMaxLimit(500L);
    // 设置数据库类型
    pageInterceptor.setDbType(DbType.MYSQL);
​
    interceptor.addInnerInterceptor(pageInterceptor);
    return interceptor;
}
        2、直接使用
    // 两个参数:current的值默认是1,从1开始,不是0。size是每一页的条数。
    // 意思是查询 第一页,每页10条数据
    Page<User> page = new Page<>(1, 10);
    userMapper.selectPage(page);
    page.getRecords().forEach(System.out::println);

        返回结果 第一页 10条数据成功:

         测试第二页 只有一条数据:

七、mybatisplus常用注解

        @TableName

        @TableName注解用于指定实体类对应的数据库表名。使用该注解可以省去手动编写SQL语句的繁琐过程。
        实例:表名为 db_user,对应的实体类 User

@TableName(value = "db_user")
public class User {
    // 实体类字段
}
        @TableField

         @TableField是MyBatis-Plus中的注解之一,用于在实体类中指定字段与数据库表中字段的映射关系。通过@TableField注解,可以将实体类中的字段与数据库表中的字段进行映射,从而简化数据访问层的开发。

        @TableField注解提供了多个属性,常用的属性包括:

  • value:指定数据库表字段的名称。用于映射非主键字段,还可以解决关键字报错
  • exist:指定该字段是否为数据库表字段,默认为true。是否忽略该字段
  • fill:指定字段填充策略,如插入时自动填充、更新时自动填充等。自动填充,将对象存入数据库的时候,由 MyBatis Plus 自动给某些字段赋值,例如:create_time、update_time
  • select:指定该字段是否参与查询,默认为true。是否查询该字段
  • update: 预处理set字段自定义注入
  • condition:指定该字段在查询条件中的匹配方式,如LIKE、EQUAL等。

        示例:

@Data
@TableName(value = "student")
public class Student {
    @TableId
    private Long id;

    // 当该字段名称与数据库名字不一致
    @TableField(value = "name")
    private String name;

    // 不查询该字段
    @TableField(select = false)
    private Integer age;

    // 当数据库中没有该字段,就忽略
    @TableField(exist = false)
    private String gender;

    // 执行的sql语句就变成`关键字`,这样sql就不会报错了
    @TableField(value = "`desc`")
    private string desc

    // 第一次添加填充
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

    // 第一次添加的时候填充,但之后每次更新也会进行填充
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

}

        自动填充演示:

        1、第一步像上面一样先添加注解,@TableField(fill = FieldFill.INSERT) 代表当数据第一次添加时更新,@TableField(fill = FieldFill.INSERT_UPDATE) 代表数据添加或者更新时都更新该字段。
        2、创建自动填充处理器

// 加入注解才能生效
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());

    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }

    
}

        3、测试就忽略了,可以自己试一下当调用update和save的方法时就会自动填充或者更新该字段。

        condition 预处理演示:

    /**
     * 角色名称
     */
    @TableField(condition = SqlCondition.LIKE)
    private String name;
    输出 SQL 为:select 表 where name LIKE CONCAT('%',值,'%')

        意思是说当我们将实体类的这个字段加上这个注解时,在使用该实体类作为条件 构建条件构造器时,会自动为我们拼接一个模糊查询的条件。

        例如:

        User user = new User();
        user.setName("张三");

        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(user);
        System.out.println(userService.list(wrapper));

 

         update 预处理set字段:

         比如我们使用mybatisplus自带的insert()方法向数据库插入数据时,假设我们给age字段赋值为1,但是我们在age字段上的@TableField注解里面加了update="%s+1",那么真真插入到数据库的值就是age=2,而不是age+1了)

例如:@TableField(.. , update="%s+1") 其中 %s 会填充为字段
输出 SQL 为:update 表 set 字段=字段+1 where ...

        如果给某个字段上@TableField注解里面写update=“now()”,那么最后我们使用mybatisplus自带的insert()方法向数据库插入数据时,这个字段插入到数据库中的值就为当前时间,看下面代码的sql语句即可明白

例如:@TableField(.. , update="now()") 使用数据库时间
输出 SQL 为:update 表 set 字段=now() where ...
        @TableId

        作用:用于标记实体类属性对应的数据库主键。

        @TableId是MyBatis-Plus中的注解之一,用于在实体类中指定主键与数据库表中主键的映射关系。通过@TableId注解,可以将实体类中的主键与数据库表中的主键进行映射,从而简化数据访问层的开发。

        使用@TableId注解时,可以指定主键的属性,例如主键的类型、是否为自增、是否为全局唯一标识符等

        试例:

public class User {
  
  @TableId(value = "id", type = IdType.ASSIGN_ID)
  private Long id;
  
  // 省略getter和setter方法
}

   value 映射主键字段名 ,type 设置主键类型,主键的生成策略。type有五个常用属性:

     (1)NONE,默认的,数据库主键id自增,使用雪花算法实现,实体类中的主键应设置为long类型,避免生成的主键id长度过长而溢出
(2)input,如果开发者手动赋值,则存入该值,如果开发者没有手动赋值,则数据库通过自增方式给主键赋值
(3)AUTO默认就是数据库自增,开发者无需赋值,如果开发者手动赋值,数据库也不会存入该值
(4)ASSIGN_ID,通过雪花算法自动生成id,生成主键非自增
(5)ASSIGN_UUID,主键类型必须为String类型,雪花算法自动生成
要求数据库字段类型,实体类型必须为String

  • 在某些情况下,我们想提前获取这个ID,调用com.baomidou.mybatisplus.core.toolkit.IdWorker.getId()方法即可

        @TableLogic

        映射逻辑删除,并不是真正的删除

1、数据表添加 deleted 字段,默认是0

2、实体类添加注解

package com.md.entity;

import com.baomidou.mybatisplus.annotation.*;
import com.md.enums.StatusEnum;
import lombok.Data;
import java.util.Date;

@Data
@TableName(value = "student")
public class Student {
    @TableId
    private Long id;
    @TableField(value = "name")
    private String name;
    @TableField(select = false)
    private Integer age;
    @TableField(exist = false)
    private String gender;
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    @Version
    private Integer version;
    private StatusEnum status;

    @TableLogic
    private Integer deleted;
}

3、主配置文件中添加配置

# 没有删除为0,删除了为1
mybatis-plus:
  global-config:
    db-config:
      logic-not-delete-value: 0
      logic-delete-value: 1

        删除的时候不是真正的删除数据库表中的数据,而是改变delete字段的值,当然了查询的时候也是查询delete=0,这都是框架自动实现的

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

EPCE-HDR

【GitHub】 【Paper】 摘要 由于相机能力的限制&#xff0c;数字图像通常比真实场景辐射更窄的动态光照范围。为了解决这个问题&#xff0c;高动态范围&#xff08;HDR&#xff09;重建被提出&#xff0c;以恢复动态范围&#xff0c;更好的表示真实世界的场景。然而&#xff0c…

haproxy编译安装

一、haproxy简介 HAProxy是一个使用C语言编写的自由及开放源代码软件&#xff0c;其提供高可用性、负载均衡&#xff0c;以及基于TCP和HTTP的应用程序代理。 HAProxy特别适用于那些负载特大的web站点&#xff0c;这些站点通常又需要会话保持或七层处理。HAProxy运行在当前的硬…

Ubuntu 22.04中MySQL 8 设置忽略大小

Ubuntu 22.04中MySQL 8 设置忽略大小 一、解决完整流程 //根据官网内容说的大概意思就是不能安装完了修改忽略大小写了&#xff0c;只能在初始化的时候做修改。我用的版本是8.0.39//更新软件包 1、sudo apt update //安装MySQL 如果安装了可以忽略这个步骤 2、sudo apt insta…

【计算机三级-数据库技术】数据库及数据库对象

数据库及数据库对象 第一节 创建及维护数据库 一、SQL server数据库分类 1&#xff09;系统数据库&#xff08;系统自动创建&#xff09;&#xff1a; master、msdb、tempdb、model、resource 2&#xff09;用户数据库 保存与用户业务有关的数据。 二、SQL server数据库组成…

【660线代】线性代数一刷错题整理

线代一阶 2024.8.7日 1. 2. 2024.8.8日 1. 2024.8.9日 1. 2. 3. 2024.8.12日 1. 2. 2024.8.13日 1. 2. 3. 2024.8.15日 1. 2. 3. 4. 5. 6. 线代二阶 2024.8.17日 1. 2. 3. 2024.8.18日 1. 至此&#xff0c;660线性代数部分完成。 祝各位一战成硕&#xff01;

鸿蒙关于可以实现滚动效果的容器组件的相关知识

一、滚动的用途 生活中&#xff0c;我们在使用各种APP应用的过程中&#xff0c;总是可以看到一些轮播图、内容页面的滑动、组件切换的效果等&#xff0c;这些都是为了提高用户的体验而设计的功能。在编程中&#xff0c;滚动的用途非常广泛&#xff0c;我们经常用在用户界面的交…

Umi-OCR 文字识别工具

免费开源的离线orc识别功能 git地址 感谢大佬的贡献 Umi-OCR 文字识别工具 使用说明 • 下载地址 • 更新日志 • 提交Bug 免费&#xff0c;开源&#xff0c;可批量的离线OCR软件 适用于 Windows7 x64 、Linux x64 免费&#xff1a;本项目所有代码开源&#x…

C++ 程序寻找通过 2 个点的线(Program to find line passing through 2 Points)

在数学和计算机科学中&#xff0c;找到通过两个点的线的方程是一个基础问题。假设我们有两个点 P1​(x1​,y1​) 和 P2​(x2​,y2​)&#xff0c;我们想要找到通过这两个点的直线方程。 直线方程的形式 直线的方程通常表示为 ymxb&#xff0c;其中 m 是斜率&#xff0c;b 是 …

PMP核心知识点—之项目管理基础

知识点1&#xff1a;项目的临时性 项目的临时性是指项目有明确的开始时间和结束时间&#xff0c;但并不能表示项目的周期短&#xff0c;项目的周期从几个月、几年到几十年都有。 知识点2&#xff1a;项目的独特性 独特的产品、服务或成果。 知识点3&#xff1a;项目创造商业价值…

ARCGIS 纸质小班XY坐标转电子要素面

1、准备好excel 坐标 小班号、点位链接的顺序、X、Y 4个缺一不可&#xff0c;需要注意的是&#xff0c;点位顺序的格式最好为数字&#xff0c;若为其他格式可能会出现排序混乱&#xff0c;会以1-9 11-19等字符串的排序连接。 excel文件转为csv才能识别&#xff0c;CSV只能保留第…

错过了游科的黑神话?别急,国内这些公司也在招聘中,都是做3A游戏的,速来!

近日&#xff0c;由游戏科学工作室打造的3A游戏——《黑神话:悟空》&#xff0c;不仅在国内引起了前所未有的关注&#xff0c;在全球范围内也引发了巨大轰动。 游戏玩家们举国欢庆的同时&#xff0c;无数游戏从业者也点燃了对国产3A游戏的憧憬与期待&#xff01; 据说游科在某…

Java 通用代码生成器光,电音之王尝鲜版八,完善数据库自动反射功能和多对多候选功能

Java 通用代码生成器光&#xff0c;电音之王尝鲜版八&#xff0c;完善数据库自动反射功能和多对多候选功能 Java 通用代码生成器光&#xff0c;电音之王尝鲜版八&#xff0c;此版本完善了数据库自动反射功能。完善了多对多候选功能。尝鲜版八在以前的版本上修复了大量缺陷和功…

PDF文件切割,无大小限制

前言 公司让学习一个东西&#xff0c;让写一个学习总结&#xff0c;我想这不是AI的拿手好戏&#xff0c;直接把近100M的PDF喂给他&#xff0c;然后他说吃不下&#xff0c;太大了 小事&#xff0c;那么多在线PDF工具网站&#xff0c;分分钟拆开&#xff0c;然后找了半天也都是…

Java开发笔记-小程序微信支付接入

步骤&#xff1a; 1.注册微信商户&#xff0c;开通小程序支付业务&#xff0c;获得必要接入参数。(Certificate、PrivateKey、merchantId、SerialNumbe、apiV3Key) 2.微信商户号关联小程序(需目标小程序审核) 3.java使用接入参数发起下单&#xff0c;获取下单参数。 4.小程…

新手设计师看一看!2024年PDF转CAD软件TOP4

在现在这个啥都能数字化的时代&#xff0c;文件格式得来回换已经成了我们工作的日常&#xff0c;特别是把PDF转成CAD&#xff0c;对工程师、设计师还有搞建筑的人来说&#xff0c;这事儿特别重要。技术越发展&#xff0c;市面上就出现了好些又快又方便的工具。今天&#xff0c;…

AI+新质生产力,化敌为友的新未来

“新质生产力”是今年的“高频词”。如何将“新质生产力”落到实处&#xff0c;业内纷纷瞄准了同一个关键词&#xff1a;“人工智能”&#xff0c;特别是 “人工智能” &#xff0c;这个小小的""号代表着无限的可能性。人工智能正在成为现代社会的基础设施&#xff0…

便签内容转移新设备攻略

在桌面使用便签软件记录工作和生活中的点点滴滴&#xff0c;已成为许多人的习惯。它不仅能帮助我们捕捉灵感、记录待办事项&#xff0c;还能提醒我们重要的日程安排。然而&#xff0c;随着设备的更新换代或工作地点的变更&#xff0c;我们有时需要将便签内容从一个设备转移到另…

多门店多端平台系统小程序源码

&#x1f525;【健身新纪元】多门店多端分开健身系统&#xff0c;打造你的个性化健身体验&#x1f31f; &#x1f3cb;️‍♀️ 一、告别单一&#xff0c;拥抱多元化健身时代 你还在为找不到合适的健身房而烦恼吗&#xff1f;或是想要随时随地开启健身模式&#xff0c;却受限…

聚水潭ERP集成用友NC(用友NC主供应链)

源系统成集云目标系统 用友NC介绍 用友NC是用友NC产品的全新系列&#xff0c;是面向集团企业的世界级高端管理软件。它以“全球化集团管控、行业化解决方案、全程化电子商务、平台化应用集成”的管理业务理念而设计&#xff0c;采用J2EE架构和先进开放的集团级开发平台UAP&am…

UDS 诊断 - ReadDataByPeriodicIdentifier(按周期性标识符读取数据)(0x2A)服务

UDS 诊断服务系列文章目录 诊断和通信管理功能单元 UDS 诊断 - DiagnosticSessionControl&#xff08;诊断会话控制&#xff09;&#xff08;0x10&#xff09;服务 UDS 诊断 - ECUReset&#xff08;ECU重置&#xff09;&#xff08;0x11&#xff09;服务 UDS 诊断 - SecurityA…