献给转java的c#和java程序员的数据库orm框架

一个好的程序员不应被语言所束缚,正如我现在开源java的orm框架一样,如果您是一位转java的c#程序员,那么这个框架可以带给你起码没有那么差的业务编写和强类型体验。如果您是一位java程序员,那么该框架可以提供比Mybatis-Plus功能更加丰富、性能更高,更加轻量和完全免费的体验来做一个happy coding crud body。

背景

easy-query该框架是我在使用Mybatis-Plus(下面统称MP) 2年后开发的,因为MP不支持多表(不要提join插件(逻辑删除子表不支持)),并且Mybatis原本的xml十分恶心,导致项目中有非常多的代码需要编写sql,并且整体数据库架构因为存在逻辑删除字段和多租户字段所以编写的sql基本上多多少少都会有问题,我不相信大家没遇到过,而且MP得一些功能还需要收费这大大让我鉴定还是自己开发一款。

介绍

easy-query 是一款无任何依赖的JAVA ORM 框架,十分轻量,拥有非常高的性能,支持单表查询、多表查询、union、子查询、分页、动态表名、VO对象查询返回、逻辑删、全局拦截、数据库列加密(支持高性能like查询)、数据追踪差异更新、乐观锁、多租户、自动分库、自动分表、读写分离,支持框架全功能外部扩展定制,拥有强类型表达式。

文档

GITHUB地址 | GITEE地址

缺点

先说一下缺点,目前只适配了MySql,不过基本上如果你是pgsql很少需要改动就直接可以用了,其他数据库可能因为自己的语法和特性会需要稍微做一下修改但是整体而言无需过多的变动,框架已经全部抽象好了。

功能点

  • 实体对象insert,update,delete全部支持
  • 单表查询、多表join查询,in子查询,exists子查询,连表统计(select a,(select count(1) from b) from c),联合查询union | all,分组group | having
  • 分页
  • 动态表名:运行时修改表名
  • 原生sql执行,查询
  • select查询map结果返回
  • select支持直接返回DTO对象实现自定义列查询返回,而不是全部列返回
  • select支持标记large字段不返回(默认返回)
  • 逻辑删除,自定义逻辑删除,支持多字段逻辑删除填充,支持运行时禁用
  • 全局拦截器,支持运行时选择性使用某几个或者不使用,支持entity操作 insert,update,条件拦截 select、update、delete的where条件拦截,update set字段拦截器
  • 多租户,支持表的列范围多租户模式
  • 数据库列加密,支持高性能的like模糊搜索匹配(不是单纯的调用数据库加密函数或者单纯的调用框架加密解密函数)
  • 数据追踪差异更新,而不是全列更新,用过efcore的肯定很熟悉
  • 版本号、乐观锁,支持自定义乐观锁
  • 支持分库分表(身为sharding-core作者不支持说不过去),全自动分库分表,仅需用户新增表和告知easy-query系统中有的表
  • 高性能分库分表分页,支持顺序分页,反向分页,支持高性能顺序分页和反向分页
  • 分库分表多字段分片
  • 分库分表自定义分片路由规则
  • 支持读写分离,一主多从支持分片下读写分离

目前项目正处于起步阶段后续会随着用户不断地完善各数据库的适配和功能的支持

开始使用

安装

以下是spring-boot环境和控制台模式的安装

spring-boot

<properties>
<easy-query.version>0.8.10</easy-query.version>
</properties>
<dependency>
<groupId>com.easy-query</groupId>
<artifactId>sql-springboot-starter</artifactId>
<version>${easy-query.version}</version>
</dependency>

console

以mysql为例

<properties>
<easy-query.version>0.8.10</easy-query.version>
</properties>
<dependency>
<groupId>com.easy-query</groupId>
<artifactId>sql-mysql</artifactId>
<version>${easy-query.version}</version>
</dependency>
//初始化连接池
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/easy-query-test?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true");
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setMaximumPoolSize(20);
//创建easy-query
EasyQuery easyQuery = EasyQueryBootstrapper.defaultBuilderConfiguration()
.setDefaultDataSource(dataSource)
.useDatabaseConfigure(new MySQLDatabaseConfiguration())
.build();

开始

sql脚本

create table t_topic
(
id varchar(32) not null comment '主键ID'primary key,
stars int not null comment '点赞数',
title varchar(50) null comment '标题',
create_time datetime not null comment '创建时间'
)comment '主题表'; create table t_blog
(
id varchar(32) not null comment '主键ID'primary key,
deleted tinyint(1) default 0 not null comment '是否删除',
create_by varchar(32) not null comment '创建人',
create_time datetime not null comment '创建时间',
update_by varchar(32) not null comment '更新人',
update_time datetime not null comment '更新时间',
title varchar(50) not null comment '标题',
content varchar(256) null comment '内容',
url varchar(128) null comment '博客链接',
star int not null comment '点赞数',
publish_time datetime null comment '发布时间',
score decimal(18, 2) not null comment '评分',
status int not null comment '状态',
`order` decimal(18, 2) not null comment '排序',
is_top tinyint(1) not null comment '是否置顶',
top tinyint(1) not null comment '是否置顶'
)comment '博客表';

查询对象



@Data
public class BaseEntity implements Serializable {
private static final long serialVersionUID = -4834048418175625051L; @Column(primaryKey = true)
private String id;
/**
* 创建时间;创建时间
*/
private LocalDateTime createTime;
/**
* 修改时间;修改时间
*/
private LocalDateTime updateTime;
/**
* 创建人;创建人
*/
private String createBy;
/**
* 修改人;修改人
*/
private String updateBy;
/**
* 是否删除;是否删除
*/
@LogicDelete(strategy = LogicDeleteStrategyEnum.BOOLEAN)
private Boolean deleted;
} @Data
@Table("t_topic")
@ToString
public class Topic { @Column(primaryKey = true)
private String id;
private Integer stars;
private String title;
private LocalDateTime createTime;
} @Data
@Table("t_blog")
public class BlogEntity extends BaseEntity{ /**
* 标题
*/
private String title;
/**
* 内容
*/
private String content;
/**
* 博客链接
*/
private String url;
/**
* 点赞数
*/
private Integer star;
/**
* 发布时间
*/
private LocalDateTime publishTime;
/**
* 评分
*/
private BigDecimal score;
/**
* 状态
*/
private Integer status;
/**
* 排序
*/
private BigDecimal order;
/**
* 是否置顶
*/
private Boolean isTop;
/**
* 是否置顶
*/
private Boolean top;
}

单表查询

Topic topic = easyQuery
.queryable(Topic.class)
.where(o -> o.eq(Topic::getId, "3"))
.firstOrNull();
==> Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic` t WHERE t.`id` = ? LIMIT 1
==> Parameters: 3(String)
<== Time Elapsed: 15(ms)
<== Total: 1

多表查询

Topic topic = easyQuery
.queryable(Topic.class)
.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 t1.`deleted` = ? AND t.`id` = t1.`id` WHERE t.`id` = ? LIMIT 1
==> Parameters: false(Boolean),3(String)
<== Time Elapsed: 2(ms)
<== Total: 1

复杂查询

join + group +分页


EasyPageResult<BlogEntity> page = easyQuery
.queryable(Topic.class).asTracking()
.innerJoin(BlogEntity.class, (t, t1) -> t.eq(t1, Topic::getId, BlogEntity::getId))
.where((t, t1) -> t1.isNotNull(BlogEntity::getTitle))
.groupBy((t, t1)->t1.column(BlogEntity::getId))
.select(BlogEntity.class, (t, t1) -> t1.column(BlogEntity::getId).columnSum(BlogEntity::getScore))
.toPageResult(1, 20);

==> Preparing: SELECT t1.`id`,SUM(t1.`score`) AS `score` FROM `t_topic` t INNER JOIN `t_blog` t1 ON t1.`deleted` = ? AND t.`id` = t1.`id` WHERE t1.`title` IS NOT NULL GROUP BY t1.`id` LIMIT 20
==> Parameters: false(Boolean)
<== Time Elapsed: 5(ms)
<== Total: 20

动态表名


String sql = easyQuery.queryable(BlogEntity.class)
.asTable(a->"aa_bb_cc")
.where(o -> o.eq(BlogEntity::getId, "123"))
.toSQL();
 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` = ?

新增


Topic topic = new Topic();
topic.setId(String.valueOf(0));
topic.setStars(100);
topic.setTitle("标题0");
topic.setCreateTime(LocalDateTime.now().plusDays(i)); long rows = easyQuery.insertable(topic).executeRows();

//返回结果rows为1
==> Preparing: INSERT INTO `t_topic` (`id`,`stars`,`title`,`create_time`) VALUES (?,?,?,?)
==> Parameters: 0(String),100(Integer),标题0(String),2023-03-16T21:34:13.287(LocalDateTime)
<== Total: 1

修改

//实体更新
Topic topic = easyQuery.queryable(Topic.class)
.where(o -> o.eq(Topic::getId, "7")).firstNotNull("未找到对应的数据");
String newTitle = "test123" + new Random().nextInt(100);
topic.setTitle(newTitle); long rows=easyQuery.updatable(topic).executeRows();
==> Preparing: UPDATE t_topic SET `stars` = ?,`title` = ?,`create_time` = ? WHERE `id` = ?
==> Parameters: 107(Integer),test12364(String),2023-03-27T22:05:23(LocalDateTime),7(String)
<== Total: 1
//表达式更新
long rows = easyQuery.updatable(Topic.class)
.set(Topic::getStars, 12)
.where(o -> o.eq(Topic::getId, "2"))
.executeRows();
//rows为1
easyQuery.updatable(Topic.class)
.set(Topic::getStars, 12)
.where(o -> o.eq(Topic::getId, "2"))
.executeRows(1,"更新失败");
//判断受影响行数并且进行报错,如果当前操作不在事务内执行那么会自动开启事务!!!会自动开启事务!!!会自动开启事务!!!来实现并发更新控制,异常为:EasyQueryConcurrentException
//抛错后数据将不会被更新
==> Preparing: UPDATE t_topic SET `stars` = ? WHERE `id` = ?
==> Parameters: 12(Integer),2(String)
<== Total: 1

删除

long l = easyQuery.deletable(Topic.class)
.where(o->o.eq(Topic::getTitle,"title998"))
.executeRows();
==> Preparing: DELETE FROM t_topic WHERE `title` = ?
==> Parameters: title998(String)
<== Total: 1
Topic topic = easyQuery.queryable(Topic.class).whereId("997").firstNotNull("未找到当前主题数据");
long l = easyQuery.deletable(topic).executeRows();
==> Preparing: DELETE FROM t_topic WHERE `id` = ?
==> Parameters: 997(String)
<== Total: 1

联合查询

Queryable<Topic> q1 = easyQuery
.queryable(Topic.class);
Queryable<Topic> q2 = easyQuery
.queryable(Topic.class);
Queryable<Topic> q3 = easyQuery
.queryable(Topic.class);
List<Topic> list = q1.union(q2, q3).where(o -> o.eq(Topic::getId, "123321")).toList();

==> Preparing: SELECT t1.`id`,t1.`stars`,t1.`title`,t1.`create_time` FROM (SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic` t UNION SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic` t UNION SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic` t) t1 WHERE t1.`id` = ?
==> Parameters: 123321(String)
<== Time Elapsed: 19(ms)
<== Total: 0

子查询

in子查询

Queryable<String> idQueryable = easyQuery.queryable(BlogEntity.class)
.where(o -> o.eq(BlogEntity::getId, "1"))
.select(String.class,o->o.column(BlogEntity::getId));
List<Topic> list = easyQuery
.queryable(Topic.class, "x").where(o -> o.in(Topic::getId, idQueryable)).toList();
==> Preparing: SELECT x.`id`,x.`stars`,x.`title`,x.`create_time` FROM `t_topic` x WHERE x.`id` IN (SELECT t.`id` FROM `t_blog` t WHERE t.`deleted` = ? AND t.`id` = ?)
==> Parameters: false(Boolean),1(String)
<== Time Elapsed: 3(ms)
<== Total: 1

exists子查询

Queryable<BlogEntity> where1 = easyQuery.queryable(BlogEntity.class)
.where(o -> o.eq(BlogEntity::getId, "1"));
List<Topic> x = easyQuery
.queryable(Topic.class, "x").where(o -> o.exists(where1, q -> q.eq(o, BlogEntity::getId, Topic::getId))).toList();
==> Preparing: SELECT x.`id`,x.`stars`,x.`title`,x.`create_time` FROM `t_topic` x WHERE EXISTS (SELECT 1 FROM `t_blog` t WHERE t.`deleted` = ? AND t.`id` = ? AND t.`id` = x.`id`)
==> Parameters: false(Boolean),1(String)
<== Time Elapsed: 10(ms)
<== Total: 1

分片

easy-query支持分表、分库、分表+分库

分表

//创建分片对象
@Data
@Table(value = "t_topic_sharding_time",shardingInitializer = TopicShardingTimeShardingInitializer.class)
@ToString
public class TopicShardingTime { @Column(primaryKey = true)
private String id;
private Integer stars;
private String title;
@ShardingTableKey
private LocalDateTime createTime;
}
//分片初始化器很简单 假设我们是2020年1月到2023年5月也就是当前时间进行分片那么要生成对应的分片表每月一张
public class TopicShardingTimeShardingInitializer extends AbstractShardingMonthInitializer<TopicShardingTime> { @Override
protected LocalDateTime getBeginTime() {
return LocalDateTime.of(2020, 1, 1, 1, 1);
} @Override
protected LocalDateTime getEndTime() {
return LocalDateTime.of(2023, 5, 1, 0, 0);
} @Override
public void configure0(ShardingEntityBuilder<TopicShardingTime> builder) { ////以下条件可以选择配置也可以不配置用于优化分片性能
// builder.paginationReverse(0.5,100)
// .ascSequenceConfigure(new TableNameStringComparator())
// .addPropertyDefaultUseDesc(TopicShardingTime::getCreateTime)
// .defaultAffectedMethod(false, ExecuteMethodEnum.LIST,ExecuteMethodEnum.ANY,ExecuteMethodEnum.COUNT,ExecuteMethodEnum.FIRST)
// .useMaxShardingQueryLimit(2,ExecuteMethodEnum.LIST,ExecuteMethodEnum.ANY,ExecuteMethodEnum.FIRST); }
}
//分片时间路由规则按月然后bean分片属性就是LocalDateTime也可以自定义实现
public class TopicShardingTimeTableRule extends AbstractMonthTableRule<TopicShardingTime> { @Override
protected LocalDateTime convertLocalDateTime(Object shardingValue) {
return (LocalDateTime)shardingValue;
}
}

数据库脚本参考源码

其中shardingInitializer为分片初始化器用来初始化告诉框架有多少分片的表名(支持动态添加)

ShardingTableKey表示哪个字段作为分片键(分片键不等于主键)

执行sql

LocalDateTime beginTime = LocalDateTime.of(2021, 1, 1, 1, 1);
LocalDateTime endTime = LocalDateTime.of(2021, 5, 2, 1, 1);
Duration between = Duration.between(beginTime, endTime);
long days = between.toDays();
List<TopicShardingTime> list = easyQuery.queryable(TopicShardingTime.class)
.where(o->o.rangeClosed(TopicShardingTime::getCreateTime,beginTime,endTime))
.orderByAsc(o -> o.column(TopicShardingTime::getCreateTime))
.toList();


==> SHARDING_EXECUTOR_2, name:ds2020, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_time_202101` t WHERE t.`create_time` >= ? AND t.`create_time` <= ? ORDER BY t.`create_time` ASC
==> SHARDING_EXECUTOR_3, name:ds2020, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_time_202102` t WHERE t.`create_time` >= ? AND t.`create_time` <= ? ORDER BY t.`create_time` ASC
==> SHARDING_EXECUTOR_2, name:ds2020, Parameters: 2021-01-01T01:01(LocalDateTime),2021-05-02T01:01(LocalDateTime)
==> SHARDING_EXECUTOR_3, name:ds2020, Parameters: 2021-01-01T01:01(LocalDateTime),2021-05-02T01:01(LocalDateTime)
<== SHARDING_EXECUTOR_3, name:ds2020, Time Elapsed: 3(ms)
<== SHARDING_EXECUTOR_2, name:ds2020, Time Elapsed: 3(ms)
==> SHARDING_EXECUTOR_2, name:ds2020, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_time_202103` t WHERE t.`create_time` >= ? AND t.`create_time` <= ? ORDER BY t.`create_time` ASC
==> SHARDING_EXECUTOR_3, name:ds2020, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_time_202104` t WHERE t.`create_time` >= ? AND t.`create_time` <= ? ORDER BY t.`create_time` ASC
==> SHARDING_EXECUTOR_2, name:ds2020, Parameters: 2021-01-01T01:01(LocalDateTime),2021-05-02T01:01(LocalDateTime)
==> SHARDING_EXECUTOR_3, name:ds2020, Parameters: 2021-01-01T01:01(LocalDateTime),2021-05-02T01:01(LocalDateTime)
<== SHARDING_EXECUTOR_3, name:ds2020, Time Elapsed: 2(ms)
<== SHARDING_EXECUTOR_2, name:ds2020, Time Elapsed: 2(ms)
==> main, name:ds2020, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_time_202105` t WHERE t.`create_time` >= ? AND t.`create_time` <= ? ORDER BY t.`create_time` ASC
==> main, name:ds2020, Parameters: 2021-01-01T01:01(LocalDateTime),2021-05-02T01:01(LocalDateTime)
<== main, name:ds2020, Time Elapsed: 2(ms)
<== Total: 122

分库


@Data
@Table(value = "t_topic_sharding_ds",shardingInitializer = DataSourceAndTableShardingInitializer.class)
@ToString
public class TopicShardingDataSource { @Column(primaryKey = true)
private String id;
private Integer stars;
private String title;
@ShardingDataSourceKey
private LocalDateTime createTime;
}
public class DataSourceShardingInitializer implements EntityShardingInitializer<TopicShardingDataSource> {
@Override
public void configure(ShardingEntityBuilder<TopicShardingDataSource> builder) {
EntityMetadata entityMetadata = builder.getEntityMetadata();
String tableName = entityMetadata.getTableName();
List<String> tables = Collections.singletonList(tableName);
LinkedHashMap<String, Collection<String>> initTables = new LinkedHashMap<String, Collection<String>>() {{
put("ds2020", tables);
put("ds2021", tables);
put("ds2022", tables);
put("ds2023", tables);
}};
builder.actualTableNameInit(initTables); }
}
//分库数据源路由规则
public class TopicShardingDataSourceRule extends AbstractDataSourceRouteRule<TopicShardingDataSource> {
@Override
protected RouteFunction<String> getRouteFilter(TableAvailable table, Object shardingValue, ShardingOperatorEnum shardingOperator, boolean withEntity) {
LocalDateTime createTime = (LocalDateTime) shardingValue;
String dataSource = "ds" + createTime.getYear();
switch (shardingOperator){
case GREATER_THAN:
case GREATER_THAN_OR_EQUAL:
return ds-> dataSource.compareToIgnoreCase(ds)<=0;
case LESS_THAN:
{
//如果小于月初那么月初的表是不需要被查询的
LocalDateTime timeYearFirstDay = LocalDateTime.of(createTime.getYear(),1,1,0,0,0);
if(createTime.isEqual(timeYearFirstDay)){
return ds->dataSource.compareToIgnoreCase(ds)>0;
}
return ds->dataSource.compareToIgnoreCase(ds)>=0;
}
case LESS_THAN_OR_EQUAL:
return ds->dataSource.compareToIgnoreCase(ds)>=0; case EQUAL:
return ds->dataSource.compareToIgnoreCase(ds)==0;
default:return t->true;
}
}
}
LocalDateTime beginTime = LocalDateTime.of(2020, 1, 1, 1, 1);
LocalDateTime endTime = LocalDateTime.of(2023, 5, 1, 1, 1);
Duration between = Duration.between(beginTime, endTime);
long days = between.toDays();
EasyPageResult<TopicShardingDataSource> pageResult = easyQuery.queryable(TopicShardingDataSource.class)
.orderByAsc(o -> o.column(TopicShardingDataSource::getCreateTime))
.toPageResult(1, 33);

==> SHARDING_EXECUTOR_23, name:ds2022, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_ds` t ORDER BY t.`create_time` ASC LIMIT 33
==> SHARDING_EXECUTOR_11, name:ds2021, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_ds` t ORDER BY t.`create_time` ASC LIMIT 33
==> SHARDING_EXECUTOR_2, name:ds2020, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_ds` t ORDER BY t.`create_time` ASC LIMIT 33
==> SHARDING_EXECUTOR_4, name:ds2023, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_ds` t ORDER BY t.`create_time` ASC LIMIT 33
<== SHARDING_EXECUTOR_4, name:ds2023, Time Elapsed: 4(ms)
<== SHARDING_EXECUTOR_23, name:ds2022, Time Elapsed: 4(ms)
<== SHARDING_EXECUTOR_2, name:ds2020, Time Elapsed: 4(ms)
<== SHARDING_EXECUTOR_11, name:ds2021, Time Elapsed: 6(ms)
<== Total: 33

最后

希望看到这边的各位大佬给我点个star谢谢这对我很重要

献给转java的c#和java程序员的数据库orm框架的更多相关文章

  1. Java 进阶 hello world! - 中级程序员之路

    Java 进阶 hello world! - 中级程序员之路 Java是一种跨平台的语言,号称:"一次编写,到处运行",在世界编程语言排行榜中稳居第二名(TIOBE index). ...

  2. MySQL常用指令,java,php程序员,数据库工程师必备。程序员小冰常用资料整理

    MySQL常用指令,java,php程序员,数据库工程师必备.程序员小冰常用资料整理 MySQL常用指令(备查) 最常用的显示命令: 1.显示数据库列表. show databases; 2.显示库中 ...

  3. 话说C#程序员人手一个ORM

    话说C#程序员人手一个ORM,确实没有必要再写ORM了,不过我的ORM并不是新的,是从DBHelper演化过来的,算是DBHelper魔改版. 目前流行的ORM有EF.Dapper.SqlSugar. ...

  4. 前端程序员学习 Golang gin 框架实战笔记之一开始玩 gin

    原文链接 我是一名五六年经验的前端程序员,现在准备学习一下 Golang 的后端框架 gin. 以下是我的学习实战经验,记录下来,供大家参考. https://github.com/gin-gonic ...

  5. 大神为你分析 Go、Java、C 等主流编程语言(Go可以替代Java,而且最小化程序员的工作量,学习比较容易)

    本文主要分析 C.C++98.C++11.Java 与 Go,主要论述语言的关键能力.在论述的过程中会结合华为各语言编程专家和华为电信软件内部的骨干开发人员的交流,摒弃语言偏好或者语言教派之争,尽量以 ...

  6. Java开发者薪资最低?程序员只能干到30岁?国外真的没有996?Intellij真的比Eclipse受欢迎?

    Stack Overflow作为全球最大的程序设计领域的问答网站,每年都会出据一份开发者调查报告.近日,Stack Overflow公布了其第9次年度开发者调查报告(https://insights. ...

  7. Java程序员必备后台前端框架--Layui【从入门到实战】(一)

    layui入门使用及图标的使用 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] [编程工具:IDEA] 下载Layui与文件分析 下载直接去官网下载即可 文件分析 下载完成后,解压会 ...

  8. Java 9天入门(黑马程序员) 课程收尾 ------学生管理系统 (9.13)

    1 实现功能 2 结构概述 分为两个包,各自一个类 Student.java 为学生类,目的是储存学生信息 StudentManager.java 是主程序的代码 3 Student.java 的代码 ...

  9. IT观察】网络通信、图片显示、数据库操作……Android程序员如何利用开源框架

    每个Android 程序员都不是Android应用开发之路上孤军奋战的一个人,GitHub上浩如烟海的开源框架或类库就是前人为我们发明的轮子,有的轮子能提高软件性能,而有的轮子似乎是以牺牲性能为代价换 ...

  10. Oracle学习总结(8)—— 面向程序员的数据库访问性能优化法则

    特别说明: 1.  本文只是面对数据库应用开发的程序员,不适合专业DBA,DBA在数据库性能优化方面需要了解更多的知识: 2.  本文许多示例及概念是基于Oracle数据库描述,对于其它关系型数据库也 ...

随机推荐

  1. 【打怪升级】【rocketMq】rocket的持久化

    rocket持久化保证的思想有两点:1是刷盘保证大部分数据不丢失:2是持久化文件的处理,零拷贝技术和内存页,NIO模型保证处理能力 文件持久化目录 ├──abort:rocket broker启动检查 ...

  2. 加密脚本分析—evil.py

    加密脚本分析-evil.py 1.题目 源文件 一共两个文件 enc_flag.txt evil.py(原文件无注释) 1 # coding: utf-8 2 3 import base64 4 im ...

  3. P4555 最长双回文串 解题报告

    看到回文串,于是就想到了马拉车. 马拉车可以帮我们求出每个 \(i\) 的最大扩展距离,容易得出,双回文串就是两个回文串拼一起.当然,两个回文串必须要相交,不然形不成一个字符串. 有的小可爱就会想直接 ...

  4. FastDFS 使用流程

    一.什么是FastDFS? FastDFS 是用 C 语言编写的一款开源的分布式文件系统,对文件进行管理,主要功能包括:文件存储.文件同步.文件访问(文件上传.文件下载)等,解决了大容量存储和负载均衡 ...

  5. 在线调试工具 Arthas

    Arthas(阿尔萨斯) 是 Alibaba开源的一款 Java在线诊断工具,能够分析,诊断,定位Java应用问题,例如:JVM信息,线程信息,搜索类中的方法,跟踪代码执行,观测方法的入参和返回参数等 ...

  6. SpringBoot 项目使用 Sa-Token 完成登录认证

    一.设计思路 对于一些登录之后才能访问的接口(例如:查询我的账号资料),我们通常的做法是增加一层接口校验: 如果校验通过,则:正常返回数据. 如果校验未通过,则:抛出异常,告知其需要先进行登录. 那么 ...

  7. R语言网络数据爬虫之三个问题

    现在大家对爬虫的兴趣不断高涨,R和PYTHON是两个非常有力的爬虫工具.Python倾向于做大型爬虫,与R相比,语法相对复杂,因此Python爬虫的学习曲线会相对陡峭.对于那些时间宝贵,又想从网上获取 ...

  8. panda之series结构

    eries 结构,也称 Series 序列,是 Pandas 常用的数据结构之一,它是一种类似于一维数组的结构,由一组数据值(value)和一组标签组成,其中标签与数据值之间是一一对应的关系.Seri ...

  9. Linux环境变量及其配置

    为什么要说这个呢? 本人喜欢使用Linux开发(工作是个硬要求,有些时候不能使用Linux,比如我上一个工作.但是有些时候呢,工作环境比较开放,我可以选择我喜欢的系统进行工作:比如我现在的工作.红红火 ...

  10. CI框架内置分页代码

    Controller 控制器代码 <?php defined('BASEPATH') OR exit('No direct script access allowed'); class Welc ...