技术派整合MyBatis-Plus

Mybatis-Plus大家都熟悉了吧?是一个Mybatis的增强,提供了一些额外功能,比如条件构造器、分页插件、代码生成器等以便我们更专注于业务,而不是SQL语句的编写

官方教程:简介 | MyBatis-Plus

整合MyBatis-Plus

非常简单,第一步,在pom.xml中引入starter。

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

第二步,使用@MapperScan注解扫描mapper文件

@Configuration
@ComponentScan("com.github.paicoding.forum.service")
@MapperScan(basePackages = {"com.github.paicoding.forum.service.article.repository.mapper","com.github.paicoding.forum.service.user.repository.mapper","com.github.paicoding.forum.service.comment.repository.mapper","com.github.paicoding.forum.service.config.repository.mapper","com.github.paicoding.forum.service.statistics.repository.mapper","com.github.paicoding.forum.service.notify.repository.mapper",})
public class ServiceAutoConfig {
}

ServiceAutoConfig 是单独的配置类,mapper接口按照业务进行了分类,mapper.xml放在resources目录下。

第三步,在application.yml文件中增加MyBatis-Plus的统一配置


# mybatis 相关统一配置
mybatis-plus:configuration:#开启下划线转驼峰map-underscore-to-camel-case: true

map-underscore-to-camel-case: true 的作用是将数据库表中的下划线命名方式映射为Java对象中的驼峰命名方式。例如,数据库表中的列名为user_name,对应Java对象的属性名为userName。

上面三部就完成了MyBatis-Plus和Spring Boot项目的整合,接下来,我么来介绍用法,包括新增、注解、查询、条件构造器、自定义SQL、分页查询、更新删除、AR模式、主键策略,以及通过service。

MyBatis-Plus的基本使用

Service CRUD

技术派中的通用的增删改查是通过MyBatis-Plus的service CRUD接口实现的。

比如说我们要保存一个文章的标签,可以通过这种方式。

@Autowired
private TagDao tagDao;tagDao.save(tagDo);

1、tagDao是我们定义的数据访问对象(Data Access Object :DAO)它继承自MyBatis-Plus提供的ServiceImpl类。@Autowired注解将TagDao自动注入到当前类中。这是Spring提供的依赖注入(DI)功能,可以让我们在当前类中方便的使用TagDao

@Repository
public class TagDao extends ServiceImpl<TagMapper, TagDO> {

1.@Repository注解:这是Spring提供的注解,用于标识这个类是一个数据访问层(DAO)组件。Spring会自动扫描并将其实例化为一个Bean,方便在其他类中通过依赖注入(DI)使用

2. ServiceImpl<TagMapper, TagDO>: ServiceImpl是MyBatis-Plus提供的一个抽象类,提供了通用的CRUD方法。泛型参数<TagMapper, TagDO> 意味着TagDao类主要用来处理TagDO数据对象的数据库操作,并使用TagMapper接口定义的方法进行操作。

通过继承ServiceImpl类,TagDao就可以使用MyBatis-Plus提供的通用CRUD方法,如sava、getById、updateById等。这些方法已经实现了基本的数据库操作,通常无需自己编写SQL语句。

2、 参数tagDO是一个数据对象,表示数据库中的tag表。

@Data
@EqualsAndHashCode(callSuper = true)
@TableName("tag")
public class TagDO extends BaseDO {private static final long serialVersionUID = 3796460143933607644L;/*** 标签名称*/private String tagName;/*** 标签类型:1-系统标签,2-自定义标签*/private Integer tagType;/*** 状态:0-未发布,1-已发布*/private Integer status;/*** 是否删除*/private Integer deleted;
}

1.@Data注解是Lombok提供的,用于自动生成类的getter、setter、equals、hashCode和toString方法,简化了代码编写。

2. @EqualsAndHashCode(callSuper = true)注解也是lombok提供的注解,callSuper = true 表示要调用父类(BaseDO)的equals和hashCode方法。

BaseDO是我们自定义的DO基类,实现了 Serializable 接口,并且自定义了主键(@TableId(type = IdType.AUTO))表示自动增长,是MyBatis-Plus提供的注解,创建时间为 createTime 更新时间为 updateTime。

@Data
public class BaseDO implements Serializable {@TableId(type = IdType.AUTO)private Long id;private Date createTime;private Date updateTime;
}

3. @TableName("tag") 注解是MyBatis-Plus提供的注解,用于指定数据库表名。

4.另外定义了四个属性:tagName ( 标签名称) 、tagType( 标签类型)、status( 状态)、deleted ( 是否删除)。这些数据对映数据库表中的列。

来看技术派的演示实战

启动Redis、服务端、admin端,通过admin端新增一个标签。

在控制台就可以看到新增的标签了。

Mapper CRUD

MyBatis-Plus除了提供Service的CRUD,还提供了基于Mapper的CRUD。

技术派中的一些特殊的增删改查是通过MyBatis-Plus的Mapper CRUD 接口实现的。

比如说我们保存文章,可以通过下面这种方式。

@Repository
public class ArticleDao extends ServiceImpl<ArticleMapper, ArticleDO> {@Resourceprivate ArticleDetailMapper articleDetailMapper;/*** 保存文章正文** @param articleId* @param content* @return*/public Long saveArticleContent(Long articleId, String content) {ArticleDetailDO detail = new ArticleDetailDO();detail.setArticleId(articleId);detail.setContent(content);detail.setVersion(1L);articleDetailMapper.insert(detail);return detail.getId();}
}

1.  ArticleDetailMapper 是我们在当前类中注入的一个Mapper接口

public interface ArticleDetailMapper extends BaseMapper<ArticleDetailDO> {
}

他继承自MyBatis-Plus的BaseMapper接口


/**
Mapper 继承这个接口之后,无需编写 mapper.xml文件,即可获得·CRUD功能
<p>这个 Mapper支持id泛型*/
public interface BaseMapper<T> extends Mapper<T> {//插入一条记录 ,entity:实体对象int insert(T entity);//根据entity条件 ,删除记录  queryWrapper实体对象封装的操作类(可以为null,//里面的entity用于生成where语句)int delete(@Param("ew") Wrapper<T> queryWrapper);//根据wherEntity 条件,更新记录// entity 实体对象(set条件值 ,可以为null)//updateWrapper 实体对象操作类,可以为null,里面的entity用于生成where语句int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);//根据ID查询,id主键IDT selectById(Serializable id);}

这样ArticleDetailMapper也就具备了基本的增删改查功能。

在浏览器中访问 http://localhost:8080/ 并测试

可以在控制台看到文章的插入信息。

删改后面会讲到。

常用注解

  • @TableName: 用于指定数据库表名,通常在实体类上使用,如@TableName("user")
  • @TableId: 用于指定表中的主键字段。通常在实体类的主键属性上使用。如:@TableId(value = "id" , type = IdType.AUTO),其中value表示主键字段名,type表示主键生成策略。
  • @TableFiled: 用于指定表中的非主键字段。可以用于实体类的属性上,以映射属性和数据库字段。如@TableFiled(value = "user_name" , exist = true),其中value表示数据库中的字段名,exist表示该字段是否存在,默认为true存在,false表示不存在。
  • @TableLogic: 用于指定逻辑删除字段。逻辑删除字段是指数据库中标记某个记录已删除,而不是真正删除的记录。如: TableLogic(value = "0" ,delval = "1"),其中value表示未删除状态的默认值,delval删除状态的值。
  • @Version: 用于指定乐观锁字段。乐观锁是一种并发控制机制策略,用于解决解决多个线程同时修改同一条记录的问题,如@Version private Integer version;
  • @EnumValue: 用于指定枚举类型的字段的映射,如: @EnumValue private Integer status;
  • @InterceptorIngnore: 用于忽略MyBatis-Plus拦截器的处理。如:@InterceptorIngnore(tenantLine = "true"),表示忽略多租户拦截器。

这几个是常见注解。

MyBatis -Plus 查询方法

普通查询

其中它的BaseMapper提供了多种查询方法,如技术派中根据ID查找文章是这样用的:

   // 查询文章记录ArticleDO article = baseMapper.selectById(articleId);

除此之外还有根据ID批量查询的selectBatchlds::

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

用法也比较就简单

        List<Long> ids = list.stream().map(ReadCountDO::getDocumentId).collect(Collectors.toList());List<ArticleDO> result = baseMapper.selectBatchIds(ids);
baseMapper.selectBatchIds(Arrays.asList(1,2));

根据键值对查询的selectMap:

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

用法如下(id为15)

Map<String , Object> map = new Hash<>();map.put("id" , 15L);
List<ArticleDo> dtoList = baseMapper.selectByMap(map);

条件构造器

MyBatis-Plus 的Wrapper是一个条件构造器,用于简化复杂的SQL查询条件的构建。它提供了一系列易于使用的API,让你能够以来链式编程的方式编写查询条件,而不需要手动编写SQL语句。

加入我们来查询这样的一个结果,包含“j”且状态是已经发布的标签。我们可以这样来构建条件构造器:
 

QueryWrapper:  用于构建查询条件。它继承自AbstractWrapper,提供了各种查询条件的构造方法,如eq、ne、gt、ge、lt、le、like、isNull、orderBy等等。

通过上面的方法,我们将返回全部列,如果只想返回一部分,可以通过select 来设置查询字段。

但存在一个问题,数据库表发生变化,代码和数据库就会不匹配了,所以更好的使用Lambda的方式,技术派的条件构造器就用的这种方式。

比如查询标签。

    /*** 获取已上线 Tags 列表(分页)** @return*/public List<TagDTO> listOnlineTag(String key, PageParam pageParam) {LambdaQueryWrapper<TagDO> query = Wrappers.lambdaQuery();query.eq(TagDO::getStatus, PushStatusEnum.ONLINE.getCode()).eq(TagDO::getDeleted, YesOrNoEnum.NO.getCode()).and(!StringUtils.isEmpty(key), v -> v.like(TagDO::getTagName, key)).orderByDesc(TagDO::getId);if (pageParam != null) {query.last(PageParam.getLimitSql(pageParam));}List<TagDO> list = baseMapper.selectList(query);return ArticleConverter.toDtoList(list);}
  • 1.可以通过 Wrappers.lambdaQuery();静态方法创建一个Lambda条件构造器。
  • 2.eq(TagDO::getStatus, PushStatusEnum.ONLINE.getCode()):表示查询条件为status等于PushStatusEnum.ONLINE.getCode()的值(即查询线上的标签)
  • 3.eq(TagDO::getDeleted, YesOrNoEnum.NO.getCode()):表示查询条件为deleted等于esOrNoEnum.NO.getCode()的值(即查询未删除的记录)
  • 4.and(!StringUtils.isEmpty(key), v -> v.like(TagDO::getTagName, key)):表示如果key不为空,则添加一个查询条件,要求tag_name包含key。
  • 5.orderByDesc(TagDO::getId);:表示按照id字段降序排序。
  • 6.if (pageParam != null) { query.last(PageParam.getLimitSql(pageParam)); }:如果pageParam不为null,则添加分页参数。

这样的话,数据库字段和代码隔离,完全通过代码的方式去查询,再比如查询文章列表:

    /*** 文章列表(用于后台)** @param pageParam* @return*/public List<ArticleDO> listArticles(PageParam pageParam) {return lambdaQuery().eq(ArticleDO::getDeleted, YesOrNoEnum.NO.getCode()).last(PageParam.getLimitSql(pageParam)).orderByDesc(ArticleDO::getId).list();}

  • 1.lambdaQuery()是一个MyBatis-Plus的Iservice接口提供的一个默认方法,可以在Service中直接调用·返回一个Lambda条件构造器。
    default LambdaQueryChainWrapper<T> lambdaQuery() {return ChainWrappers.lambdaQueryChain(this.getBaseMapper());}
  • 2.eq(ArticleDO::getDeleted, YesOrNoEnum.NO.getCode()) :表示查询条件为delete等于YesOrNoEnum.No的值  ,即查询未删除的记录。
  • 3.  .last(PageParam.getLimitSql(pageParam)):在查询的最后添加一个分页语句,这里根据pageParam参数生成分页的SQL语句。
  • 4.  orderByDesc(ArticleDO::getId): 表示按照id字段降序排序。
  • 5.list():执行查询,并返回查询结果的列表。

MyBatis-Plus自定义SQL

MyBatis-Plus支持自定义SQL语句,我们可以在接口中编写自定义SQL方法,并使用注解加自定义的SQL语句。

技术派在位信登录的时候会执行这条SQL语句:

public interface UserMapper extends BaseMapper<UserDO> {/*** 根据三方唯一id进行查询** @param accountId* @return*/@Select("select * from user where third_account_id = #{account_id} limit 1")UserDO getByThirdAccountId(@Param("account_id") String accountId);
}

接口中定义了一个名为getByThirdAccountId的方法,它接收一个名为accountId的参数。该方法使用了@Select注解,这个注解用于编写自定义的SQL查询。select * from user where third_account_id = #{account_id} limit 1,他会根据传入的account_id参数查询user表中的记录。

同时,方法参数accountId使用了@Param注解,指定了参数在SQL语句中的名称为ccount_id,这样,在执行SQL语句时,MyBatis会将参数值替换到对应的位置上。

我们来测试一下。

结果:

除此之外,技术派中还使用了xml的方式,用来定义一些复杂的SQL。比如说,我们要统计网站的PV、UV,那么我们在resources目录下新建一个名为QueryCountMapper.xml的文件,内容如下:

<?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.github.paicoding.forum.service.statistics.repository.mapper.RequestCountMapper"><select id="getPvTotalCount" resultType="java.lang.Long">select sum(cnt) from request_count</select><select id="getPvDayList" resultType="com.github.paicoding.forum.api.model.vo.statistics.dto.StatisticsDayDTO">SELECT sum(cnt) as count, dateFROM request_countgroup by date order by date asclimit #{day};</select><select id="getUvDayList" resultType="com.github.paicoding.forum.api.model.vo.statistics.dto.StatisticsDayDTO">SELECT count(*) as count, dateFROM request_countgroup by date order by date asclimit #{day};</select></mapper>

1.在resources目录下的好处是,MyBatis-Plus默认帮我们配置了xml的位置,这样我们就不需要在application.yml中配置了。

该XML文件定义了一个名为RequestCountMapper的映射器,它包含了三个自定义查询:getPvTotalCount、getPvDayList、getUvDayList。他与

com.github.paicoding.forum.service.statistics.repository.mapper.RequestCountMapper相匹配。

/*** 请求计数mapper接口** @author louzai* @date 2022-10-1*/
public interface RequestCountMapper extends BaseMapper<RequestCountDO> {/*** 获取 PV 总数** @return*/Long getPvTotalCount();/*** 获取 PV 数据列表* @param day* @return*/List<StatisticsDayDTO> getPvDayList(@Param("day") Integer day);/*** 获取 UV 数据列表** @param day* @return*/List<StatisticsDayDTO> getUvDayList(@Param("day") Integer day);
}
3.  getPvTotalCount();查询:返回类型为java.lang.Long,查询语句为select sum(cnt) from request_count。此查询计算request_count表中所有记录的cnt列值之和。

4.getPvDayList查询: 返回类型为 StatisticsDayDTO 。此查询根据传入的day参数获取按日期分组的请求数量统计信息,并按日期升序排序。

5. getUvDayList查询: 返回类型同样为StatisticsDayDTO。此查询根据传入的day参数获取按日期分组的唯一访客数量统计信息,并按照日期升序排列。

打开admin端,查看这三项数据

MyBatis-Plus更新和删除

更新

我们来先看个最简单的,直接调用Service的updateById方法,也就时根据ID更新,比如更新标签的内容:

    public void saveTag(TagReq tagReq) {TagDO tagDO = TagStructMapper.INSTANCE.toDO(tagReq);// 先写 MySQLif (NumUtil.nullOrZero(tagReq.getTagId())) {tagDao.save(tagDO);} else {tagDO.setId(tagReq.getTagId());tagDao.updateById(tagDO);}

Service的update其实是对Mapper得update做一个封装。

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

后台把 “ 技术派” 的标签修改为 “ 技术派Π”。

后台可以看到SQL语句日志。

也可以通过xml的形式,当批量修改消息状态时,技术派是这样更新的。

<update id="updateNoticeRead">update notify_msg set `state` = 1 where `id` in<foreach collection="ids" open="(" close=")" separator="," item="id" index="index">#{id}</foreach></update>

对用的mapper是这样写得:

    /*** 标记消息为已阅读** @param ids*/void updateNoticeRead(@Param("ids") List<Long> ids);

删除

技术派中的删除都是逻辑上的删除,不是物理删除,就时修改delete字段,而不是真的把记录从表里面删除,所以,最终调用的还是update方法,比如删除文章。

    @Overridepublic void deleteArticle(Long articleId) {ArticleDO dto = articleDao.getById(articleId);if (dto != null && dto.getDeleted() != YesOrNoEnum.YES.getCode()) {// 查询该文章是否关联了教程,如果已经关联了教程,则不能删除long count = columnArticleDao.count(Wrappers.<ColumnArticleDO>lambdaQuery().eq(ColumnArticleDO::getArticleId, articleId));if (count > 0) {throw ExceptionUtil.of(StatusEnum.ARTICLE_RELATION_TUTORIAL, articleId, "请先解除文章与教程的关联关系");}dto.setDeleted(YesOrNoEnum.YES.getCode());articleDao.updateById(dto);// 发布文章删除事件SpringUtil.publishEvent(new ArticleMsgEvent<>(this, ArticleEventEnum.DELETE, dto));} else {throw ExceptionUtil.of(StatusEnum.ARTICLE_NOT_EXISTS, articleId);}}

MyBatis-Plus主键策略

技术派中的主键目前采用的是自增策略,也就是说,数据库表的ID会设置位Auto Increment(自动递增)。

然后,实体类DO会继承BaseDO,比如分类  CategoryDO:

@Data
@EqualsAndHashCode(callSuper = true)
@TableName("category")
public class CategoryDO extends BaseDO {private static final long serialVersionUID = 1L;/*** 类目名称*/private String categoryName;/*** 状态:0-未发布,1-已发布*/private Integer status;/*** 排序*/@TableField("`rank`")private Integer rank;private Integer deleted;
}

其中BaseDO为MyBatis-Plus提供的基类,内部的id字段已经添加了@TableId(type = IdType.AUTO) 注解 ,。

@Data
public class BaseDO implements Serializable {@TableId(type = IdType.AUTO)private Long id;private Date createTime;private Date updateTime;
}

在插入数据时,无需设置主键值,数据库会自动分配主键值。

除了IdType.AUTO,MyBatis-Plus 还提供了其他几种策略。比如说Idtype.NONE:无主键策略。表示不使用任何主键生成策略,主键值需要手动设置。

IdType.ID_WORKER: 使用雪花算法生成分布式唯一的ID。插入数据时,MyBtis-Plus会自动生成一个雪花ID作为主键值。

小结:

MyBatis-Plus得基本使用:

  • Spring Boot整合MyBatis-Plus:如何引入依赖,并配置数据源和配置类。
  • MyBatis-Plus的基本使用: 如何创建实体类和Mapper接口,并在Service层和Mapper层中使用MyBatis-Plus提供的通用CRUD方法。
  • MyBatis-Plus的查询方法: MyBatis-Plus各种查询方法,MyBatis-Plus条件构造器时中带你。(QueryWrapper和LambdaQueryWrapper)。
  • MyBatis-Plus自定义SQL: 如何在MyBatis-Plus中使用自定义SQL语句,包括在Mapper接口中使用注解定义SQL和在XML文件中编写SQL。
  • MyBatis-Plus更新和删除: MyBatis-Plus提供的更新和逻辑删除方法。
  • MyBatis-Plus主键策略: MyBatis-Plus如何生成主键,支持的主键生成策略,如何使用@TableId注解配置主键策略。

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

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

相关文章

【大数据开发--概念篇】

前言&#xff1a; &#x1f49e;&#x1f49e;大家好&#xff0c;书生♡&#xff0c;今天主要和大家分享一下大数据的相关概念&#xff0c;以及我们大数据开发的环境&#xff0c;希望对大家有所帮助。 &#x1f49e;&#x1f49e;路漫漫&#xff0c;希望大家坚持下去&#xff0…

【系统架构设计师】系统工程与信息系统基础 01

系统架构设计师 - 系列文章目录 01 系统工程与信息系统基础 文章目录 系列文章目录 前言 一、系统工程 ★ 二、信息系统生命周期 ★ 信息系统建设原则 三、信息系统开发方法 ★★ 四、信息系统的分类 ★★★ 1.业务处理系统【TPS】 2.管理信息系统【MIS】 3.决策支持系统…

ELK 安装部署

文章目录 1.日志收集规划2.Elasticsearch部署2.1.Elasticsearch安装2.2.Elasticsearch-head安装2.3.Elasticsearch设置分片数2.4.elasticsearch健康检查 3.Kibana部署4.Logstash部署5.Filebeat部署 开源中间件 # Elastic Stackhttps://iothub.org.cn/docs/middleware/ https:/…

【阿里云系列】-利用yaml文件部署NacosXxl-job到ACK

背景介绍 随着容器化的技术成熟落地&#xff0c;拥抱各种成熟的容器化集群平台是加速我们落地的必然之路&#xff0c;目前国内以阿里云、华为云、腾讯云为平台的供应商为主&#xff0c;国外则以AWS&#xff0c;Azure为主&#xff0c;让我们借助平台已有的优势进行快速落地提高…

VMware安装Ubuntu虚拟机

1. 安装VMware VMware中国官网&#xff1a;VMware - Delivering a Digital Foundation For Businesses VMware Workstation Player&#xff08;官方个人免费版&#xff09;&#xff1a;VMware Workstation Player | VMware VMware Workstation Pro&#xff08;商用收费版&am…

酷开系统走在前列,品牌重启增长,酷开科技成为品牌商合作目标

区别于火热的移动端&#xff0c;手机屏作为私密屏&#xff0c;往往面向的是用户个体&#xff0c;而电视作为家庭连接的重要枢纽&#xff0c;不仅仅定位于公共屏&#xff0c;同时也面向客厅场景发挥着其大屏传播的作用&#xff0c;这里不仅牵扯到大屏营销&#xff0c;也关联着大…

jQuery1.10.2升级到jQuery3.6.0返回结果异常

ajax请求代码&#xff1a; jQuery1.10.2 ajax返回结果&#xff1a; 取其TIPS的值&#xff0c;代码如下&#xff1a; let find $(data).find(TIPS); var resultfind[0].innerHTML; 返回值为空字符串。 jQuery3.6.0 ajax返回结果&#xff1a; 取其TIPS的值&#xff0c;代码如下&…

某赛通电子文档安全管理系统 DecryptApplication 任意文件读取漏洞复现

0x01 产品简介 某赛通电子文档安全管理系统(简称:CDG)是一款电子文档安全加密软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产,对电子文档进行全生命周期防护,系统具有透明加密、主动加密、智能…

力扣思路题:重复的子字符串

注意比较j与j-i是否相同 bool repeatedSubstringPattern(char* s) {int i;int nstrlen(s);bool flag;for(int i1;i<n/2;i){if(n%i0){flagtrue;}for(int ji;j<n;j){if(s[j]!s[j-i]){flagfalse;break;}}if(flagtrue){return true;}}return false; }

运行时错误‘53’:文件未找到:MathPage.WLL。Word粘贴复制时报错解决方案!

最近写文章使用 Word 时&#xff0c;粘贴复制总是出现这个报错&#xff0c;不能 ctrlc 和 v 好叫人苦恼。百度大致检索了一些过程&#xff0c;仍然有必要记录自己的问题解决过程。 快让本文进你的文件夹吃灰吧~ 报错如下&#xff1a; 运行时错误‘53’&#xff1a; 文件未找…

差旅补助解决方案|数字化差补赋能业务提效

长期以来&#xff0c;差旅补助一直是企业为了激励员工出差并表达对员工的关怀而采取的一种方式&#xff0c;以经济和福利支持来鼓励员工积极投入工作。然而&#xff0c;由于传统差旅补助的核算、发放和管理方式存在诸多问题&#xff0c;往往适得其反&#xff0c;无法实现企业的…

深入探索JavaScript:如何改变this的指向

目录 深入探索JavaScript&#xff1a;如何改变this的指向 一、call方法 二、apply方法 三、bind方法 四、箭头函数 区别&#xff1a; 执行方式和返回值&#xff1a; 参数传递方式&#xff1a; 使用场景&#xff1a; 总结 在JavaScript中&#xff0c;this关键字是一个…