献给转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查询)、数据追踪差异更新、乐观锁、多租户、自动分库、自动分表、读写分离,支持框架全功能外部扩展定制,拥有强类型表达式。

  • GITHUB github地址

  • GITEE gitee地址

? 文档

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.where(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谢谢这对我很重要

  • GITHUB github地址

  • GITEE gitee地址

本文转载于网络 如有侵权请联系删除

相关文章

  • uniapp 使用本地数据库

    大家好,又见面了,我是你们的朋友全栈君。//我这个封装通过promise返回出去!!! //我这个封装通过promise返回出去!!! //创建数据库或者有该数据库就打开,这一步必须要! exportfunctionopenSqlite(cb){ //创建数据库或者打开 //这plus.sqlite只在手机上运行 plus.sqlite.openDatabase({ name:‘wallet’,//数据库名称 path:‘_doc/wallet.db’,//数据库地址,uniapp推荐以下划线为开头,这到底存在哪里去了,我也不清楚,哈哈 success(e){ cb(e) }, fail(e){ cb(e) } }) }//在该数据库里创建表格,这一步也必须要! //下面注释里说的都是说sql:’createtableifnotexists….这里 //userInfo是表格名,你也可以写其他的名,不能用数字作为表格名的开头!!! //括号里是表格的结构,列,这里我写了四列,list,id,gender,avatar这四列 //list后面大写的英文是自动增加的意思,因为表格里

  • 实锤!大基金总经理丁文武涉嫌严重违纪违法被调查!旗下子基金合伙人王文忠也被调查!

    2022年7月30日消息,日前传闻的国家集成电路产业投资基金(以下简称“大基金”)总裁丁文武被调查的消息,今日得到了官方的进一步确认。据中央纪委国家监委驻工业和信息化部纪检监察组、北京市监察委员会消息:国家集成电路产业投资基金股份有限公司总经理丁文武涉嫌严重违纪违法,经中央纪委国家监委指定管辖,目前正接受中央纪委国家监委驻工业和信息化部纪检监察组纪律审查、北京市监委监察调查。不久之前的7月15日,根据中央纪委国家监委驻国家开发银行纪检监察组、吉林省纪委监委的通报,国家开发银行国开发展基金管理部原副主任路军涉嫌严重违纪违法,目前正接受中央纪委国家监委驻国家开发银行纪检监察组纪律审查和吉林省监委监察调查。而路军同时也是大基金管理公司——华芯投资管理有限责任公司(以下简称“华芯投资”)原总裁。更早之前的2021年11月,中央纪委国家监委网站发布消息称,大基金管理人——华芯投资管理有限责任公司原副总裁高松涛涉嫌严重违法,正在接受监察调查。 从目前官方公告来看,仅9个月不到的时间,就有三位大基金相关的高管或原高管被调查,且均是因为“涉嫌严重违法违纪”,显然,大基金内部的管理可能确实存在问题,正面

  • CentOS安装OpenLDAP

    1、安装OpenLDAP yuminstallopenldapopenldap-* serviceslapdstart2、下载 wgethttp://nchc.dl.sourceforge.net/project/phpldapadmin/phpldapadmin-php5/1.2.3/phpldapadmin-1.2.3.tgztar-zxvfphpldapadmin-1.2.3.tgz mvphpldapadmin/var/www/html/3、配置服务vi/etc/sysconfig/ldap,确保SLAPD_LDAPI=yes cp/usr/share/openldap-servers/slapd.conf.obsolete/etc/openldap/slapd.conf cp/usr/share/openldap-servers/DB_CONFIG.example/var/lib/ldap/DB_CONFIGmv/etc/openldap/slapd.d{,.bak} 生成密码: slappasswd [root@LAMP~]#slappasswd Newpassword:

  • Flutter 上的一个 Bug 带你了解键盘与路由的另类知识点

    事情是这样的,由于近期Flutter发布了1.17的稳定版,按照“惯例”开始着手把生产项目升级到1.12.13+hotfix.9版本,在升级适配完成之后,一个突如其来的Bug让我陷入了沉思。image如上图所示,可以看到在键盘B页面打开后,退回上一个页面A时键盘已经收起,但是原先键盘所在的区域在A页面变成了空白,而A页面内容也被resize成了键盘弹出后的大小。1、Scaffold针对这个问题,首先想到的Scaffold的resizeToAvoidBottomInset属性。在Flutter中Scaffold默认情况下resizeToAvoidBottomInset为true,当resizeToAvoidBottomInset为true时,Scaffold内部会将mediaQuery.viewInsets.bottom参与到BoxConstraints的大小计算,也就是键盘弹起时调整了内部的bottom位置来迎合键盘。但是问题发送在A界面,这时候键盘已经收起,mediaQuery.viewInsets.bottom应该更新为0,那为何界面没有产生应有的更新呢?2、MediaQuery那

  • PageAdmin自助建站系统之站点的添加和管理

    PageAdminCms支持多站点,很适合制作大型站群网站,在站点管理中可以对每个站点进行管理,站点绑定域名,访问目录,站点模板等操作都在此界面进行操作,下面小编介绍一下PageAdmin建站系统站点的添加和管理1、打开站点管理,如下图2、如果需要添加新站点,点击左上角菜单中有一个添加按钮,出现如下界面 下面说一下几个重要参数:2.1、访问目录:必填项,如果没有绑定域名,则网站用:http://系统主域名/my 的形式访问当前站点。2.2、绑定域名:如果填写了此项,则网站用http://my.domain.cn来访问域名,需要注意是,你的服务器站点需要先绑定http://my.domain.cn域名 如果后期网站域名更改了,站点会出现打不开的情况,只需要进入当前管理界面,修改一下绑定域名即可。2.3、模板目录:点击右侧选择目录按钮选择即可。2.4、Gzip压缩:一般选择开启即可,如果确认服务器已经开启了,这里可以选择关闭,主要为了压缩html输出,减少http传输大小,以达到节省网络带宽,提高网站速度的作用。3、如果添加了多个站点,如何在多个站点之间切换管理比如站点栏目,网站信息数据呢

  • 业界 | 通过引入 ML,谷歌 ARCore 实时自拍 AR 的逼真效果更进一步

    AI科技评论按:ARCore是谷歌于去年2月份正式推出的增强现实(AR)软件开发工具包,给开发者提供了一个开发ARAPP的平台。不到一个月前,谷歌正式推出ARCore1.7版本,为其添加了前置摄像头AR自拍能力以及动画效果支持;而日前,谷歌又为其引入了机器学习技术,使其实时自拍AR的逼真效果更进一步,该技术相关的文章发布在谷歌AI的官方博客上,AI科技评论编译如下。增强现实(AR)通过将数字内容与信息叠加到物质世界的真实场景中,来让人类实现超越现实的感官体验。例如,谷歌地图的AR功能,能够在现实场景中叠加方向信息来为你指路。借助于Pixel相机中的Playground模式,你可以使用AR从不同的视角看世界。并且通过借助于最新发布的「YouTubeStories」以及ARCore全新的面部增强(AugmentedFaces)API,你可以在自拍上添加动画面具、眼镜、帽子、皮肤等特效。实现这种AR功能的关键挑战之一,就是以合适的方式将虚拟内容锚定到现实世界:这一过程需要一套能够追踪到每一次微笑、皱眉或假笑的高动态表面几何结构的独特的感知技术。ARCore的3D网格以及它能实现的一些特效为此

  • 仿人人网侧边栏滑动效果

    这次的侧边栏通过scrollview和animation两种方法实现通过scrollview实现mysrollview.xml<?xmlversion="1.0"encoding="utf-8"?> <HorizontalScrollViewxmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/mySrollView1" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:id="@+id/ll_child" android:layout_width="match_parent" android:layout_height="match_parent&q

  • CentOS下如何更改默认的启动方式

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011415782/article/details/78708355 此处主要介绍较为普遍应用的centos6.5和centos7两种版本的默认启动方式修改前提是系统已经安装了GUI,一般默认的官方iso镜像文件都能支持图形界面如果没有安装图形界面,可以运行如下命令进行安装:yumgroupinstall"GNOMEDesktop""GraphicalAdministrationTools"复制【声明】本文参考了网上资源,经过实测可用,所以在此整理完善,希望能帮到你…centos6.5下的操作指导(1).解释Linux有7种运行级别,其实根据文档中的解释就能明白,在此中文说明一下:#Defaultrunlevel.Therunlevelsusedare: #0-halt(DoNOTsetinitdefaulttothis)*关机 #1-Singleusermode*单用户字符界面; #2-Multiuser,withoutN

  • 如何在电脑上多开微信?(windows)

    新媒体管家在电脑上多开微信,在工作中很常见,今天来介绍一种简单的方法。(windows下)这个问题在百度和知乎上都有许多回答,很多都是:长按Enter电脑上怎么登录几个微信(微信多开)?这种方法体验并不好,下面介绍一种超级简单的。。首先在开始中搜索“微信”,右键“打开文件位置”,例如这是我电脑上微信安装的文件目录下一步,复制应用程序的全名WeChat.exe按住Shift,鼠标右键单击空白区域,“在此处打开命令窗口”输入:startWeChat.exe&WeChat.exe回车发现,现在已经出现两个可以登录的微信了、其实方法很简单,就是在微信安装文件的目录下启用start命令,使用&表示多次,三次就是startWeChat.exe&WeChat.exe&WeChat.exe这应该是非常简单的方法了吧,如果你还知道更简单的,欢迎评论交流~

  • 黑客的七夕礼物:大量IoT设备Telnet密码列表遭泄露

    一份包含几千条telnet密码的列表自7月11日起被贴到了Pastebin上,这些密码可以被黑客用来扩大僵尸网络。来自NewSkySecurity的安全研究员AnkitAnubhav发现,这份列表中包含了IP地址、设备名称和telnet密码,列表中大部分的用户名密码组合都是”admin:admin”或者”root:root”等。这整份列表中包含143种密码组合,其中60种密码组合都来自于MiraiTelnet扫描器。这份列表简直就是给黑客们的七夕礼物,通过telnet,黑客可以轻松攻占主机,进而扩大僵尸网络,如果要进行DDoS攻击,其影响也会更大。列表中共有33,138个条目,知名安全专家在推特上转发了它的链接之后火了起来。这份文件已经有22,000+的浏览量。截至目前,这份文件已经被Pastebin官方删除。据统计,前五大密码分别是:root:[blank]—782条 admin:admin—634条 root:root—320条 admin:default—21条 default:[blank]—18条GDI基金会主席VictorGevers分析了这份列表,他发现:有1,775个条

  • idea导入项目出现Unable to import maven project: See logs for details提示

    摘要:从git上面check多工程项目后,maven不能正常下载相应的依赖,最后查询国外网站,找出错误原因。按照此步骤,可以自动配置好每个工程的module。 删除项目根目录下.idea文件夹 关闭idea工具,重新打开选择File->New->Projectfromtheexistingsource,选择刚才的项目根目,下一步:打勾此选项 一直点next,最后点击主工程的pom.xml完成! 勇气,信念,坚持

  • 20220927今晚试试

    https://www.cnblogs.com/qiantao/p/9832021.html https://bbs.csdn.net/topics/392345001 https://blog.csdn.net/qq_26621859/article/details/103402217 https://blog.csdn.net/the_future_way/article/details/107966718 https://www.cnblogs.com/xielong/p/4686261.html https://www.codedown123.com/37311.html 还可参考filestream https://www.codedown123.com/37311.html 橘子Jane

  • 四种内存泄漏方法总结和比较

      关注:QStockView,获取股票智能分析报警软件 目录 1     简介...1 2     检测内存泄漏的方法...1 2.1    VS2015性能探测器...1 2.2    VLD嵌入式分析...4 2.3    windbg工具udmh解决定位内存泄漏方法...9 2.4     LeakDiag内存泄漏分析LDGrapher查看...13 3     四种方法比较...17       1   简介 内存泄漏一直是比较头疼的问题,一个内存泄漏往往要排查,定位,修改,测试,再排查、定位,修改,测试,直到找到所有的内存泄漏位置,费时费力,如果通过查找new或者malloc函数名称,然后再

  • CF1163F: Indecisive Taxi Fee

    1IndecisiveTaxiFee 题目链接:https://codeforces.com/problemset/problem/1163/f 2题目描述 时间限制\(2s\)|空间限制\(512M\) 给你一个\(n\)个点,\(m\)条边的无向图,每条边连接点\(u,v\),并且有个长度\(w\)。 有\(q\)次询问,每次询问给你一对\(t,x\),表示仅当前询问下,将\(t\)这条边的长度修改为\(x\),请你输出当前\(1\)到\(n\)的最短路长度。 数据范围: \(2≤?≤2\times10^5,1≤?,?≤2\times10^5\) \(1≤?_?,?_?≤?,1≤?_?≤10^9,?_?≠?_?\) \(1≤?_?≤?,1≤?_?≤10^9\) 3题解 容易发现一共有四种情况:改变的边不在最短路上且变大,改变的边不在最短路上且变小,改变的边在最短路上且变小,改变的边在最短路上且变大。 第一种情况非常简单,容易发现不在最短路上的边变大并不会影响答案,因此直接输出\(dist_{1,n}\)即可。 第二种情况稍微复杂一点点。发现此时除了原最短路之外还有一条强制经过改

  • flask强大的第三方组件 flask-script

    Flask-Script从字面意思上来看就是Flask的脚本 是的,熟悉Django的同学是否还记得Django的启动命令呢?pythonmanager.pyrunserver大概是这样对吧 其实Flask也可以做到,基于Flask-Script就可以了-但是你还是得有一个项目就是第十四章的项目点击下载 1.安装Flask-Script pipinstallFlask-Script复制 2.将Flask-Script加入到Flask项目中 1importMyApp 2#导入Flask-Script中的Manager 3fromflask_scriptimportManager 4 5app=MyApp.create_app() 6#让app支持Manager 7manager=Manager(app) 8 9if__name__=='__main__': 10#app.run() 11#替换原有的app.run(),然后大功告成了 12manager.run()复制 MyApp/manager.py 3.使用命令启动Flask项目 pythonmanager.pyrunse

  • 「联赛模拟测试35」题解

    T1:组合 很容易发现,这题需要建个图 建图方法是把每个串的首尾相连,能反过来就建双向边 由于每个串都要用上,所以第一问就是判断是否存在欧拉路,第二问就是欧拉路径 考场前80打了个\(O(n^2)\)带回溯的暴搜,后20口胡了一个\(O(nlogm)\)的最长路路径记录 然后本地得了45分,联考oj得了25,原因: 1. 2. 其实一遍DFS就能找出欧拉路径(没写过。。) 判是否存在欧拉路的以前考过两次,不写了,贴个欧拉路的 voidDFS1(intu,intid){ // for(intx=now[u];x;x=e[x].next){ while(now[u]){ intx=now[u];now[u]=e[x].next; intv=e[x].to; if(black[x])continue; black[x]=1; if(opt==1)black[x^1]=1; DFS1(v,e[x].id); //sta[++top]=e[x].id; } if(id)sta[++top]=id; } 复制 T2:小W的魔术 沙雕题,十几分钟就推出来了 先

  • json-server 是什么 ?

    基于Node.js,为前端人员提供一个模拟服务端接口数据,一般用在前后端分离测试 安装json-server npminstall-gjson-server 复制 通过查看版本号检查是否安装成功 json-server-V 复制 创建db.json 在任意文件夹下创建db.json文件内容如下 { "posts":[ {"id":1,"title":"json-server","author":"typicode"} ], "comments":[ {"id":1,"body":"somecomment","postId":1} ], "profile":{"name":"typicode"} } 复制 启动json-server cd进入db.json存在的文件夹后运行如下代码 json-server--watchdb.json 复制

  • ASP.NET Core教程-认证配置(token-based authentication)

    更新记录 转载请注明出处: 2022年11月2日发布。 2022年11月1日从笔记迁移到博客。 认证配置(token-basedauthentication) 基于Token方式的认证介绍 基于Token方式的认证和Session对比 Token方式,服务器端可伸缩性更好。 Token方式,状态记录更加方便。 认证生成Token和使用Token流程 Theclientistheapplicationthatistryingtoaccessourresources. Theidentityprovideristheservicethat,givenausernameandapassword,providesanencryptedauthenticationtoken. Theresourceproviderisanotherservicecalledbytheclient.Furthermore,theresourceproviderwillaccepttheencryptedauthenticationtoken,anditwillprovidetheinformationr

  • python 列表生成式、生成器及迭代器介绍

    一、列表生成式   列表生成式即ListComprehensions,是python内置的非常强大的创建列表的方式。   比如有一个要求,列表a=[0,1,2,3,4,5],要求把列表里的每个值增加1,实现方式有以下几种: #方式一通过for循环 a=[0,1,2,3,4,5] b=[] foriina: b.append(i+1) a=b print(a) #方式二通过enumerate函数 a=[0,1,2,3,4,5] forindex,iinenumerate(a): a[index]+=1 print(a) #方式三通过lambda函数 a=[0,1,2,3,4,5] a=map(lambdax:x+1,a) foriina: print(i) #方式四通过列表生成器 a=[0,1,2,3,4,5] a=[i+1foriina] print(a)复制 ViewCode   列表生成式中,for循环后边还可以加上if判断语句: a=[0,1,2,3,4,5] a=[i+1foriinaifi<3] print(a) 结果:[1,2,3]复制   还可以使用

  • android 圆角item shape

    先上效果图:   就是一个类似微信qq那种item的shape。   即时上代码,少废话。 以下文件均为drawable文件夹下 文件:shape_general_item_top_bg_up.xml <?xmlversion="1.0"encoding="UTF-8"?> <layer-listxmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape> <solidandroid:color="@android:color/white"/> <!--圆角--> <corners android:topLeftRadius="5dp" android:topRightRadius="5dp" android:bottomLeftRadius="0dp" android:bottomRightRadius="0dp" /><!--设置圆角半径--> <strokeandroi

  • mysql 中文数据库

    mysql中文复制 安装数据库的时候选择utf-8或者是gbk就行。 或者: mysql安装目录下的my.ini文件以下部分: [mysql] //改成你要的编码,比如gbk之类 default-character-set=utf8 [mysqld] #TheTCP/IPPorttheMySQLServerwilllistenon port=3306 //改成你要的编码,比如gbk之类注意要和上面修改的一样 default-character-set=utf8复制

相关推荐

推荐阅读