献给转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. 教你如何用纯css代码实现太极阴阳鱼动画效果

    今天看到一个有意思的效果,闲来无事做一个: 把2d静态的太极图改成了3d,阴极和阳极分到了两个平面里实现旋转效果,这个好实现,重点是实现它的透明效果,平面太极图显示出两极是用另加的块元素挡住底面的颜色 ...

  2. Apinto Dashboad V2.0 发布:可视化控制台让配置更轻松!

    大家好, Eolink 旗下开源网关 Apinto 本次带来了 Apinto Dashboad V2.0 的版本发布. Dashboad 需要与 Apinto 主版本一起使用,目前 Dashboad ...

  3. vue之事件修饰符

    目录 修饰符 .stop事件 .self事件 .prevent事件 .once事件 修饰符 事件修饰服 释义 .stop 只处理自己的事件,子控件不再冒泡给父控件 .self 只处理自己的事件,子控件 ...

  4. docker方式实现minio数据持久化离线安装

    保存镜像 root@hello:~# docker pull minio/minio Using default tag: latest latest: Pulling from minio/mini ...

  5. QT实现可拖动自定义控件

    使用QT实现自定义类卡牌控件Card,使其能在父类窗口上使用鼠标进行拖动. 控件类头文件card.h #ifndef CARD_H #define CARD_H #include <QWidge ...

  6. pandas之样本操作

    随机抽样,是统计学中常用的一种方法,它可以帮助我们从大量的数据中快速地构建出一组数据分析模型.在 Pandas 中,如果想要对数据集进行随机抽样,需要使用 sample() 函数.sample() 函 ...

  7. 【Java SE】网络编程

    1. 网络编程概述 网络编程的目的:直接或者间接地通过网络协议与其他计算机实现数据交换,进行通讯. 网络编程两个主要的问题: ①如何精准地定位网络上的一台或多台主机,并定位主机上的特定应用 ②找到主机 ...

  8. C++11强制类型转换

    C++ 强制类型转换有四种关键字:static_cast.const_cast.reinterpret_cast和dynamic_cast.它们用于不同的情况和目的,比C语言的强制类型转换更清晰和安全 ...

  9. RDIFramework.NET开发框架用户字典助力Saas数据字典应用

    1.概述 在某些特殊应用(如:SaaS)中,系统内置的字典项有可能不能完全满足用户的需求,他们需要自己定义相应的数据项,我们框架完全支持这类应用,用户字典管理主界面如下图所示. 2.功能展示 需要说明 ...

  10. Optional避免频繁判空的神器

    1. 创建Optional //创建空的 Optional Optional<Object> optional = Optional.empty(); //创建非空的 Optional 如 ...