献给转java的c#和java程序员的数据库orm框架
献给转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查询)、数据追踪差异更新、乐观锁、多租户、自动分库、自动分表、读写分离,支持框架全功能外部扩展定制,拥有强类型表达式。
文档
缺点
先说一下缺点,目前只适配了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框架的更多相关文章
- Java 进阶 hello world! - 中级程序员之路
Java 进阶 hello world! - 中级程序员之路 Java是一种跨平台的语言,号称:"一次编写,到处运行",在世界编程语言排行榜中稳居第二名(TIOBE index). ...
- MySQL常用指令,java,php程序员,数据库工程师必备。程序员小冰常用资料整理
MySQL常用指令,java,php程序员,数据库工程师必备.程序员小冰常用资料整理 MySQL常用指令(备查) 最常用的显示命令: 1.显示数据库列表. show databases; 2.显示库中 ...
- 话说C#程序员人手一个ORM
话说C#程序员人手一个ORM,确实没有必要再写ORM了,不过我的ORM并不是新的,是从DBHelper演化过来的,算是DBHelper魔改版. 目前流行的ORM有EF.Dapper.SqlSugar. ...
- 前端程序员学习 Golang gin 框架实战笔记之一开始玩 gin
原文链接 我是一名五六年经验的前端程序员,现在准备学习一下 Golang 的后端框架 gin. 以下是我的学习实战经验,记录下来,供大家参考. https://github.com/gin-gonic ...
- 大神为你分析 Go、Java、C 等主流编程语言(Go可以替代Java,而且最小化程序员的工作量,学习比较容易)
本文主要分析 C.C++98.C++11.Java 与 Go,主要论述语言的关键能力.在论述的过程中会结合华为各语言编程专家和华为电信软件内部的骨干开发人员的交流,摒弃语言偏好或者语言教派之争,尽量以 ...
- Java开发者薪资最低?程序员只能干到30岁?国外真的没有996?Intellij真的比Eclipse受欢迎?
Stack Overflow作为全球最大的程序设计领域的问答网站,每年都会出据一份开发者调查报告.近日,Stack Overflow公布了其第9次年度开发者调查报告(https://insights. ...
- Java程序员必备后台前端框架--Layui【从入门到实战】(一)
layui入门使用及图标的使用 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] [编程工具:IDEA] 下载Layui与文件分析 下载直接去官网下载即可 文件分析 下载完成后,解压会 ...
- Java 9天入门(黑马程序员) 课程收尾 ------学生管理系统 (9.13)
1 实现功能 2 结构概述 分为两个包,各自一个类 Student.java 为学生类,目的是储存学生信息 StudentManager.java 是主程序的代码 3 Student.java 的代码 ...
- IT观察】网络通信、图片显示、数据库操作……Android程序员如何利用开源框架
每个Android 程序员都不是Android应用开发之路上孤军奋战的一个人,GitHub上浩如烟海的开源框架或类库就是前人为我们发明的轮子,有的轮子能提高软件性能,而有的轮子似乎是以牺牲性能为代价换 ...
- Oracle学习总结(8)—— 面向程序员的数据库访问性能优化法则
特别说明: 1. 本文只是面对数据库应用开发的程序员,不适合专业DBA,DBA在数据库性能优化方面需要了解更多的知识: 2. 本文许多示例及概念是基于Oracle数据库描述,对于其它关系型数据库也 ...
随机推荐
- 非侵入式入侵 —— Web缓存污染与请求走私
作者:vivo 互联网安全团队- Gui Mingcheng 本文介绍了两种攻击者无需直接接触服务端即可攻击和影响用户行为的安全漏洞 -- Web缓存污染与请求走私.Web缓存污染旨在通过攻击者向缓存 ...
- Linux & 标准C语言学习 <DAY14>
一.头文件 头文件可能会被任意源文件包含,意味着头文件中的内容可能会在多个目标文件中存在,要保证合并时不要冲突 重点:头文件只编写声明语句,不能有定义语句 1.头文件应 ...
- 22.this指针
1.this指针工作原理 我们知道,c++的数据和操作也是分开存储,并且每一个非内联成员函数(non-inline member function)只会诞生一份函数实例,也就是说多个同类型的对象会共用 ...
- 【读书笔记】排列研究-模式避免-基础Pattern Avoidance
目录 模式避免的定义 避免Pattern q 的n-排列计数\(S_n(q)\) q长度是2 q长度是3 对一些模式q,做\(S_n(q)\)的阶估计 Backelin, West, and Xin给 ...
- Spring AOP面向切面编程案例 (注解驱动开发)
AOP(动态代理):指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式:[1]导入 aop 模块:Spring AOP:(spring-aspects):[2]定义一个业务逻辑类 ...
- 基于swiper.js的异型轮播
基于原生swiper.js的异型轮播 <div class="swiper-container" > <div class="swiper-wrappe ...
- 使用golang+antlr4构建一个自己的语言解析器(二)
Antlr4文件解析流程 该图展示了一个语言应用程序中的基本流动过程 输入一个字符流,首先经过词法分析,获取各个Token 然后经过语法分析,组成语法分析树 Antlr4语法书写规范 语法关键字和使用 ...
- CoordConv:给你的卷积加上坐标
摘要:本文主要对CoordConv的理论进行了介绍,对其进行了复现,并展示了其在网络结构中的用法. 本文分享自华为云社区<CoordConv:给你的卷积加上坐标>,作者: 李长安. 一.理 ...
- do { } while(){ }
1 var j=10; 2 do{ 3 console.log("十八岁以下禁止观看") 4 console.log(j); 5 j++; //j为18之前都不执行while 6 ...
- 六位一体Serverless化应用,帮你摆脱服务器的烦恼
随着互联网技术的飞速发展,越来越多的应用横空出世,是以不可避免带来了大量的服务器需求.大部分的开发者都选择购买或者租用服务器,然而这样也带来了诸多的烦恼. 1.硬件成本高昂 购买服务器费用昂贵,除 ...