我真的不想再用mybatis和其衍生框架了选择自研亦是一种解脱

这篇具有很好参考价值的文章主要介绍了我真的不想再用mybatis和其衍生框架了选择自研亦是一种解脱。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

我真的不想再用mybatis和其衍生框架了选择自研亦是一种解脱

文档地址 https://xuejm.gitee.io/easy-query-doc/

GITHUB地址 https://github.com/xuejmnet/easy-query

GITEE地址 https://gitee.com/xuejm/easy-query文章来源地址https://www.toymoban.com/news/detail-604439.html

为什么要用orm

众所邹知orm的出现让本来以sql实现的复杂繁琐功能大大简化,对于大部分程序员而言一个框架的出现是为了生产力的提升.。dbc定义了交互数据库的规范,任何数据库的操作都是只需要满足jdbc规范即可,而orm就是为了将jdbc的操作进行简化。我个人“有幸”体验过.net和java的两个大orm,只能说差距很大,当然语言上的一些特性也让java在实现orm上有着比较慢的进度,譬如泛型的出现,lambda的出现。

一个好的orm我觉得需要满足以下几点

  • 强类型,如果不支持强类型那么和手写sql没有区别
  • 能实现80%的纯手写sql的功能,好的orm需要覆盖业务常用功能
  • 支持泛型,“如果一个orm连泛型都不支持那么就没有必要存在”这是一句现实但是又很残酷的结论,但是泛型会大大的减少开发人员的编写错误率
  • 不应该依赖过多的组件,当然这并不是orm特有的,任何一个库其实依赖越少越不易出bug

其实说了这么多总结一下就是一个好的orm应该有ide的提示外加泛型约束帮助开发可以非常顺滑的把代码写下去,并且错误部分可以完全的在编译期间提现出来,运行时错误应该尽可能少的去避免。

为什么放弃mybatis

首先如果你用过其他语言的orm那么再用java的mybatis就像你用惯了java的stream然后去自行处理数据过滤,就像你习惯了kotlin的语法再回到java语法,很难受。这种难受不是自动挡到手动挡的差距,而且自动挡到手推车的差距。

xml配置sql也不知道是哪个“小天才”想出来的,先不说写代码的时候java代码和xml代码跳来跳去,而且xml下>,<必须要配合CDATA不然xml解析就失败,别说转义,我写那玩意在加转义你确定让我后续看得眼睛不要累死吗?美名其曰xml和代码分离方便维护,但是你再怎么方便修改了代码一样需要重启,并且因为代码写在xml里面导致动态条件得能力相对很弱。并且我也不知道mybatis为什么天生不支持分页,需要分页插件来支持,难道一个3202年的orm了还需要这样吗,很难搞懂mybatis的作者难道不写crud代码的吗?有些时候简洁并不是偷懒的原因,当然也有可能是架构的问题导致的。

逻辑删除的功能我觉得稍微正常一点的企业一定都会有这个功能,但是因为使用了myabtis,因为手写sql,所以常常会忘记往sql中添加逻辑删除字段,从而导致一些奇奇怪怪的bug需要排查,因为这些都是编译器无法体现的错误,因为他是字符串,因为mybatis把这个问题的原因指向了用户,这一点他很聪明,这个是用户的错误而不是框架的,但是框架要做的就是尽可能的将一些重复工作进行封装隐藏起来自动完成。

可能又会有一些用户会说所见即所得这样我才能知道他怎么执行了,但是现在哪个orm没有sql打印功能,哪个orm框架执行的sql和打印的sql是不一样的,不是所见即所得。总体而言我觉得mybatis充其量算是sqltemlate,比sqlhelper好的地方就是他是参数化防止sql注入。当然最主要的呀一点事难道java程序员不需要修改表,不需要动表结构,不需要后期维护的吗还是说java程序员写一个项目就换一个地方跳槽,还是说java程序员每个方法都有单元测试。我在转java后理解了一点,原来这就是你们经常说的java加班严重,用这种框架加班不严重就有鬼了。

为什么放弃mybatis衍生框架

有幸在201几年再网上看到了mybatis-plus框架,这块框架一出现就吸引了我,因为他在处理sql的方式上和.net的orm很相似,起码都是强类型,起码不需要java文件和xml文件跳来跳去,平常50%的代码也是可以通过框架的lambda表达式来实现,我个人比较排斥他的字符串模式的querywrapper,因为一门强类型语言缺少了强类型提示,在编写代码的时候会非常的奇怪。包括后期的重构,当然如果你的代码后续不需要你维护那么我觉得你用哪种方式都是ok的反正是一次性的,能出来结果就好了。

继续说mybatis-plus,因为工作的需要再2020年左右针对内部框架进行改造,并且让mybatis-plus支持强类型group by,sum,min,max,any等api。
我真的不想再用mybatis和其衍生框架了选择自研亦是一种解脱
这个时候其实大部分情况下已经可以应对了,就这样用了1年左右这个框架,包括后续的update的increment,decrement

update table set column=column-1 where id=xxx and column>1

全部使用lambda强类型语法,可以应对多数情况,但是针对join始终没有一个很好地方法。直到我遇到了mpj也就是mybatis-plus-join,但是这个框架也有问题,就是这个逻辑删除在join的子表上不生效,需要手动处理,如果生效那么在where上面,不知道现在怎么样了,当时我也是自行实现了让其出现在join的on后面,但是因为实现是需要实现某个接口的,所以并没有pr代码.
首先定义一个接口

public interface ISoftDelete {
    Boolean getDeleted();
}

//其中join mapper是我自己的实现,主要还是`WrapperFunction`的那段定义
  @Override
    public Scf4jBaseJoinLinq<T1,TR> on(WrapperFunction<MPJAbstractLambdaWrapper<T1, ?>> onFunction) {
        WrapperFunction<MPJAbstractLambdaWrapper<T1, ?>> join=  on->{
            MPJAbstractLambdaWrapper<T1, ?> apply = onFunction.apply(on);
            if(ISoftDelete.class.isAssignableFrom(joinClass)){
                SFunction deleted = LambdaHelper.getFunctionField(joinClass, "deleted", Boolean.class);
                apply.eq(deleted,false);
            }
            return apply;
        };
        joinMapper.setJoinOnFunction(query->{
            query.innerJoin(joinClass,join);
        });
        return joinMapper;
    }

虽然实现了join但是还是有很多问题出现和bug。

  • 比如不支持vo对象的返回,只能返回数据库对象自定义返回列,不然就是查询所有列
  • 再比如如果你希望你的对象update的时候填充null到数据库,那么只能在entity字段上添加,这样就导致这个字段要么全部生效要么全部不生效.
  • 批量插入不支持默认居然是foreach一个一个加,当然这也没关系,但是你真的想实现批处理需要自己编写很复杂的代码并且需要支持全字段。而不是null列不填充
  • MetaObjectHandler,支持entityinsertupdate但是不支持lambdaUpdateWrapper,有时候当前更新人和更新时间都是需要的,你也可以说数据库可以设置最后更新时间,但是最后修改人呢?
  • 非常复杂的动态表名,拜托大哥我只是想改一下表名,目前的解决方案就是try-finally每次用完都需要清理一下当前线程,因为tomcat会复用线程,通过threadlocal来实现,话说pagehelper应该也是这种方式实现的吧
    当然其他还有很多问题导致最终我没办法忍受,选择了自研框架,当然我的框架自研是参考了一部分的freesql和sqlsuagr的api,并且还有java的beetsql的实现和部分方法。毕竟站在巨人的肩膀上才能看的更远,不要问我为什么不参考mybatis的,我觉得mybatis已经把简单问题复杂化了,如果需要看懂他的代码是一件很得不偿失的事情,最终我发现我的选择是正确的,我通过参考beetsql的源码很快的清楚了java这边应该需要做的事情,为我编写后续框架节约了太多时间,这边也给beetsql打个广告 https://gitee.com/xiandafu/beetlsql

自研orm有哪些特点

easy-query一款无任何依赖的java全新高性能orm支持 单表 多表 子查询 逻辑删除 多租户 差异更新 联级一对一 一对多 多对一 多对多 分库分表(支持跨表查询分页等) 动态表名 数据库列高效加解密支持like crud拦截器 原子更新 vo对象直接返回

文档地址 https://xuejm.gitee.io/easy-query-doc/

GITHUB地址 https://github.com/xuejmnet/easy-query

GITEE地址 https://gitee.com/xuejm/easy-query

  • 强类型,可以帮助团队在构建和查询数据的时候拥有id提示,并且易于后期维护。
  • 泛型可以控制我们编写代码时候的一些低级错误,比如我只查询一张表,但是where语句里面可以使用不存在上下文的表作为条件,进一步限制和加强表达式
  • easy-query提供了三种模式分别是lambda,property,apt proxy其中lambda表达式方便重构维护,property只是性能最好,apt proxy方便维护,但是重构需要一起重构apt文件

单表查询

//根据条件查询表中的第一条记录
List<Topic> topics = easyQuery
                .queryable(Topic.class)
                .limit(1)
                .toList();
==> Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM t_topic t LIMIT 1
<== Total: 1

//根据条件查询id为3的集合
List<Topic> topics = easyQuery
                .queryable(Topic.class)
                .where(o->o.eq(Topic::getId,"3").eq(Topic::geName,"4")
                .toList();

==> Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM t_topic t WHERE t.`id` = ? AND t.`name` = ?
==> Parameters: 3(String),4(String)
<== Total: 1

多表

 Topic topic = easyQuery
                .queryable(Topic.class)
                //join 后面是双参数委托,参数顺序表示join表顺序,可以通过then函数切换
                .leftJoin(BlogEntity.class, (t, t1) -> t.eq(t1, Topic::getId, BlogEntity::getId))
                .where(o -> o.eq(Topic::getId, "3"))
                .firstOrNull();

==> Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM t_topic t LEFT JOIN t_blog t1 ON t.`id` = t1.`id` WHERE t.`id` = ? LIMIT 1
==> Parameters: 3(String)
<== Total: 1

List<BlogEntity> blogEntities = easyQuery
                .queryable(Topic.class)
                //join 后面是双参数委托,参数顺序表示join表顺序,可以通过then函数切换
                .innerJoin(BlogEntity.class, (t, t1) -> t.eq(t1, Topic::getId, BlogEntity::getId))
                .where((t, t1) -> t1.isNotNull(BlogEntity::getTitle).then(t).eq(Topic::getId, "3"))
                //join查询select必须要带对应的返回结果,可以是自定义dto也可以是实体对象,如果不带对象则返回t表主表数据
                .select(BlogEntity.class, (t, t1) -> t1.columnAll())
                .toList();

==> Preparing: SELECT t1.`id`,t1.`create_time`,t1.`update_time`,t1.`create_by`,t1.`update_by`,t1.`deleted`,t1.`title`,t1.`content`,t1.`url`,t1.`star`,t1.`publish_time`,t1.`score`,t1.`status`,t1.`order`,t1.`is_top`,t1.`top` FROM t_topic t INNER JOIN t_blog t1 ON t.`id` = t1.`id` WHERE t1.`title` IS NOT NULL AND t.`id` = ?
==> Parameters: 3(String)
<== Total: 1

子查询


```java
//SELECT * FROM `t_blog` t1 WHERE t1.`deleted` = ? AND t1.`id` = ?
 Queryable<BlogEntity> subQueryable = easyQuery.queryable(BlogEntity.class)
                .where(o -> o.eq(BlogEntity::getId, "1"));


List<Topic> x = easyQuery
        .queryable(Topic.class).where(o -> o.exists(subQueryable.where(q -> q.eq(o, BlogEntity::getId, Topic::getId)))).toList();


==> Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic` t WHERE EXISTS (SELECT 1 FROM `t_blog` t1 WHERE t1.`deleted` = ? AND t1.`id` = ? AND t1.`id` = t.`id`)
==> Parameters: false(Boolean),1(String)
<== Time Elapsed: 3(ms)
<== Total: 1


//SELECT t1.`id` FROM `t_blog` t1 WHERE t1.`deleted` = ? AND t1.`id` = ?
Queryable<String> idQueryable = easyQuery.queryable(BlogEntity.class)
            .where(o -> o.eq(BlogEntity::getId, "123"))
            .select(String.class, o -> o.column(BlogEntity::getId));//如果子查询in string那么就需要select string,如果integer那么select要integer 两边需要一致
List<Topic> list = easyQuery
        .queryable(Topic.class).where(o -> o.in(Topic::getId, idQueryable)).toList();


==> Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic` t WHERE t.`id` IN (SELECT t1.`id` FROM `t_blog` t1 WHERE t1.`deleted` = ? AND t1.`id` = ?)
==> Parameters: false(Boolean),123(String)
<== Time Elapsed: 2(ms)
<== Total: 0

自定义逻辑删除



//@Component //如果是spring
public class MyLogicDelStrategy extends AbstractLogicDeleteStrategy {
    /**
     * 允许datetime类型的属性
     */
    private final Set<Class<?>> allowTypes=new HashSet<>(Arrays.asList(LocalDateTime.class));
    @Override
    protected SQLExpression1<WherePredicate<Object>> getPredicateFilterExpression(LogicDeleteBuilder builder,String propertyName) {
        return o->o.isNull(propertyName);
    }

    @Override
    protected SQLExpression1<ColumnSetter<Object>> getDeletedSQLExpression(LogicDeleteBuilder builder, String propertyName) {
//        LocalDateTime now = LocalDateTime.now();
//        return o->o.set(propertyName,now);
        //上面的是错误用法,将now值获取后那么这个now就是个固定值而不是动态值
        return o->o.set(propertyName,LocalDateTime.now())
                .set("deletedUser",CurrentUserHelper.getUserId());
    }

    @Override
    public String getStrategy() {
        return "MyLogicDelStrategy";
    }

    @Override
    public Set<Class<?>> allowedPropertyTypes() {
        return allowTypes;
    }
}

//为了测试防止数据被删掉,这边采用不存在的id
logicDelTopic.setId("11xx");
//测试当前人员
CurrentUserHelper.setUserId("easy-query");
long l = easyQuery.deletable(logicDelTopic).executeRows();

==> Preparing: UPDATE t_logic_del_topic_custom SET `deleted_at` = ?,`deleted_user` = ? WHERE `deleted_at` IS NULL AND `id` = ?
==> Parameters: 2023-04-01T23:15:13.944(LocalDateTime),easy-query(String),11xx(String)
<== Total: 0

差异更新

  • 要注意是否开启了追踪spring-boot下用@EasyQueryTrack注解即可开启
  • 是否将当前对象添加到了追踪上下文 查询添加asTracking或者 手动将查询出来的对象进行easyQuery.addTracking(Object entity)
TrackManager trackManager = easyQuery.getRuntimeContext().getTrackManager();
try{
        trackManager.begin();
        Topic topic = easyQuery.queryable(Topic.class)
                .where(o -> o.eq(Topic::getId, "7")).asTracking().firstNotNull("未找到对应的数据");
        String newTitle = "test123" + new Random().nextInt(100);
        topic.setTitle(newTitle);
        long l = easyQuery.updatable(topic).executeRows();
}finally {

        trackManager.release();
}
==> Preparing: UPDATE t_topic SET `title` = ? WHERE `id` = ?
==> Parameters: test1239(String),7(String)
<== Total: 1

关联查询

一对一

学生和学生地址

//数据库对像查询
           List<SchoolStudent> list1 = easyQuery.queryable(SchoolStudent.class)
                        .include(o -> o.one(SchoolStudent::getSchoolStudentAddress).asTracking().disableLogicDelete())
                        .toList();
//vo自定义列映射返回
List<SchoolStudentVO> list1 = easyQuery.queryable(SchoolStudent.class)
                        .include(o -> o.one(SchoolStudent::getSchoolStudentAddress).asTracking().disableLogicDelete())
                        .select(SchoolStudentVO.class,o->o.columnAll()
                                .columnInclude(SchoolStudent::getSchoolStudentAddress,SchoolStudentVO::getSchoolStudentAddress))
                        .toList();

多对一

学生和班级

//数据库对像查询
 List<SchoolStudent> list1 = easyQuery.queryable(SchoolStudent.class)
                        .include(o -> o.one(SchoolStudent::getSchoolClass))
                        .toList();
//自定义列
 List<SchoolStudentVO> list1 = easyQuery.queryable(SchoolStudent.class)
                        .include(o -> o.one(SchoolStudent::getSchoolClass))
                        .select(SchoolStudentVO.class,o->o
                                .columnAll()
                                .columnInclude(SchoolStudent::getSchoolClass,SchoolStudentVO::getSchoolClass,s->s.column(SchoolClassVO::getId))
                        )
                        .toList();

//vo自定义列映射返回
   List<SchoolStudentVO> list1 = easyQuery.queryable(SchoolStudent.class)
                        .include(o -> o.one(SchoolStudent::getSchoolClass))
                        .select(SchoolStudentVO.class,o->o
                                .columnAll()
                                .columnInclude(SchoolStudent::getSchoolClass,SchoolStudentVO::getSchoolClass)
                        )
                        .toList();

一对多

班级和学生

//数据库对像查询
 List<SchoolClass> list1 = easyQuery.queryable(SchoolClass.class)
                        .include(o -> o.many(SchoolClass::getSchoolStudents))
                        .toList();
//vo自定义列映射返回
       List<SchoolClassVO> list1 = easyQuery.queryable(SchoolClass.class)
                        .include(o -> o.many(SchoolClass::getSchoolStudents))
                        .select(SchoolClassVO.class,o->o.columnAll()
                                .columnIncludeMany(SchoolClass::getSchoolStudents,SchoolClassVO::getSchoolStudents))
                        .toList();

多对多

班级和老师

      List<SchoolClass> list2 = easyQuery.queryable(SchoolClass.class)
                .include(o -> o.many(SchoolClass::getSchoolTeachers,1))
                .toList();
  List<SchoolClassVO> list2 = easyQuery.queryable(SchoolClass.class)
                    .include(o -> o.many(SchoolClass::getSchoolTeachers))
                    .select(SchoolClassVO.class,o->o.columnAll()
                            .columnIncludeMany(SchoolClass::getSchoolTeachers,SchoolClassVO::getSchoolTeachers))
                    .toList();

动态报名

List<BlogEntity> blogEntities = easyQuery.queryable(BlogEntity.class)
                .asTable(a -> "aa_bb_cc")
                .where(o -> o.eq(BlogEntity::getId, "123")).toList();


==> Preparing: SELECT t.`id`,t.`create_time`,t.`update_time`,t.`create_by`,t.`update_by`,t.`deleted`,t.`title`,t.`content`,t.`url`,t.`star`,t.`publish_time`,t.`score`,t.`status`,t.`order`,t.`is_top`,t.`top` FROM aa_bb_cc t WHERE t.`deleted` = ? AND t.`id` = ?
==> Parameters: false(Boolean),123(String)
<== Total: 0



List<BlogEntity> blogEntities = easyQuery.queryable(BlogEntity.class)
                .asTable(a->{
                    if("t_blog".equals(a)){
                        return "aa_bb_cc1";
                    }
                    return "xxx";
                })
                .where(o -> o.eq(BlogEntity::getId, "123")).toList();


==> Preparing: SELECT t.`id`,t.`create_time`,t.`update_time`,t.`create_by`,t.`update_by`,t.`deleted`,t.`title`,t.`content`,t.`url`,t.`star`,t.`publish_time`,t.`score`,t.`status`,t.`order`,t.`is_top`,t.`top` FROM aa_bb_cc1 t WHERE t.`deleted` = ? AND t.`id` = ?
==> Parameters: false(Boolean),123(String)
<== Total: 0




List<BlogEntity> x_t_blog = easyQuery
                .queryable(Topic.class)
                .asTable(o -> "t_topic_123")
                .innerJoin(BlogEntity.class, (t, t1) -> t.eq(t1, Topic::getId, BlogEntity::getId))
                .asTable("x_t_blog")
                .where((t, t1) -> t1.isNotNull(BlogEntity::getTitle).then(t).eq(Topic::getId, "3"))
                .select(BlogEntity.class, (t, t1) -> t1.columnAll()).toList();

==> Preparing: SELECT t1.`id`,t1.`create_time`,t1.`update_time`,t1.`create_by`,t1.`update_by`,t1.`deleted`,t1.`title`,t1.`content`,t1.`url`,t1.`star`,t1.`publish_time`,t1.`score`,t1.`status`,t1.`order`,t1.`is_top`,t1.`top` FROM t_topic_123 t INNER JOIN x_t_blog t1 ON t1.`deleted` = ? AND t.`id` = t1.`id` WHERE t1.`title` IS NOT NULL AND t.`id` = ?
==> Parameters: false(Boolean),3(String)
<== Total: 0

最后

感谢各位看到最后,希望以后我的开源框架可以帮助到您,如果您觉得有用可以点点star,这将对我是极大的鼓励

更多文档信息可以参考git地址或者文档

文档地址 https://xuejm.gitee.io/easy-query-doc/

GITHUB地址 https://github.com/xuejmnet/easy-query

GITEE地址 https://gitee.com/xuejm/easy-query

到了这里,关于我真的不想再用mybatis和其衍生框架了选择自研亦是一种解脱的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Go自研微服务框架-日志处理

    Golang标准日志库提供的日志输出方法有Print、Fatal、Panic Print用于记录一个普通的程序日志,开发者想记点什么都可以。 Fatal用于记录一个导致程序崩溃的日志,并会退出程序。 Panic用于记录一个异常日志,并触发panic。 标准日志库,一般是够使用,但是输出日志的时候,如果

    2024年01月17日
    浏览(43)
  • GO自研微服务框架-路由实现

    不用框架的路由实现 测试代码 测试代码 测试代码 测试代码 在前面实现的时候,我们的路径匹配实现的很简陋,不能实现更为复杂的需求,比如/user/get/:id 这种带有参数的,这种带有参数的路径,我们称之为 动态路由 。 除了带有参数的,一般情况下,我们可能还希望支持通

    2024年01月23日
    浏览(54)
  • GO自研微服务框架-页面渲染

    在实际开发中,接口返回需要支持返回HTML,JSON,XML等,在HTML返回中,要支持模板 渲染HTML,需要明确几个元素 content-type = text/html; charset=utf-8 模板Template 渲染数据 渲染页面的操作是用户来完成,所以需要在Context中提供对应的方法 1.1 加入模板支持 1.2 改造-提前将模板加载到

    2024年01月16日
    浏览(54)
  • GO自研微服务框架-中间件

    中间件的作用是给应用添加一些额外的功能,但是并不会影响原有应用的编码方式,想用的时候直接添加,不想用可以很轻松的去除,做到所谓的可插拔。 中间件的实现位置在哪里? 不能耦合在用户的代码中 需要独立存在,但又能拿到上下文,并能做出影响 位置:在处理器

    2024年01月18日
    浏览(49)
  • 因项目只做socket客户端,不想用workerman或者swoole框架,简单实现ws PHP客户端

    docs/Client.md · master · mirrors / Textalk / websocket-php · GitCode

    2024年02月13日
    浏览(63)
  • spring-boot集成mybatis真的很简单吗?

    在日常的后端开发中,使用mybatis作为DAO层的持久框架已经是惯例。但很多时候都是在别人搭好的框架中进行开发,对怎么搭建环境是一知半解,今天就来实践下。 来看下集成mybatis需要哪些步骤, 1、确定环境及依赖 2、配置文件; 3、测试 这里, 基于springboot集成mybatis。 先

    2024年02月08日
    浏览(56)
  • JavaScript的选择结构你真的了解吗?(看完这一篇就够了)

    ​🌈个人主页:前端青山 🔥系列专栏:JavaScript篇 🔖 人终将被年少不可得之物困其一生 依旧 青山 ,本期给大家带来JavaScript篇专栏内容:JavaScript-选择结构 目录 选择结构 实现选择结构的语句 三元(目)运算符 案例 判断一个年份是闰年还是平年 判断一个数是偶数还是奇数

    2024年02月04日
    浏览(37)
  • ECMAScript6和其常量变量的声明

    目录 1.介绍 2.babel--ES6代码转换为ES5的代码 1.安装转码工具 2.安装转换规则 3.指定转换规则 新建.babelrc 4.也可以将ES6转换为ES5之后的文件输入到另一个文件当中 5.将整个src目录下的es6文件转换成es5文件到dist目录 ​3.模块化 1-module1.js 2-module2.js ES6导出的是一个接口,接口存放的

    2024年02月15日
    浏览(40)
  • AI人工智能简介和其定义

    全称:人工智能(Artificial Intelligence) 缩写:AI / ai        亦称智械、机器智能,指由人制造出来的可以表现出智能的机器。通常人工智能是指通过普通计算机程序来呈现人类智能的技术。该词也指出研究这样的智能系统是否能够实现,以及如何实现。人工智能于一般教材中

    2023年04月18日
    浏览(42)
  • 【云存储】云存储技术的概念和其优势

    目录 云平台整体架构 什么是云存储? 云存储技术的发展 云存储与传统存储相比的优势?   云存储是云计算的存储部分 ,理解云存储架构的前提是理解云平台整体架构。云计算按照服务类型大致可以分为三类:基础设施即服务(IaaS)、平台即服务(PaaS)以及软件即服务(SaaS):  I

    2024年02月01日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包