Antdesign Pro中ProTable和EditableProTable使用上的区别

Antdesign Pro中提供了很多高级控件,其中ProTable和EditableProTable非常实用,但是他们之间到底区别在哪里呢?怎么选择呢?

  1. ProTable是标准的表格,增删改查等各种操作按钮比较标准化,而EditableProTable更重交互性,直接可以在表格内做编辑操作;
  2. ProTable有翻页功能,EditableProTable没有,所以EditableProTable适合记录条数比较少的情况;

用markdown格式写博客怎么让图片上传更方便

类似于hugo这类通过编译成静态文件的方式来写博客,对于博主来说是非常方便的,而且内容都是用markdown语法来书写,很快速就能实现排版。但是文章中的图片就不那么好处理了,存在代码仓库里,时间久了就会非常臃肿,放到图床上又很麻烦,每次都要截图,上传,然后拿到地址,在贴回文章里。

今天发现了一个非常好用的工具,叫做PicGo,他的功能就是,设定一个快捷键(如:Shift+Alt+W),先屏幕截图(这个快捷键相信你已经用的很熟练了),然后按Shift+Alt+W(PicGo后台会自动把截图上传到阿里云oss),然后回到你的博客编辑页,按Ctrl+V,直接就能把图片地址加上markdown语法贴上去。整个过程不到5s终,相当方便。

PicGo的下载地址可以去github里找:https://github.com/Molunerfinn/PicGo/releases

那么PicGo如何设置阿里云OSS呢,首先去阿里云注册一个OSS的bucket,然后在PicGo中设置好即可,如下:

快捷键的设置方法:

用PicGo配合着markdown编辑工具atom(下载地址:https://atom.io/)真是相当方便。

mybatis使用疑难杂症:多表关联、字段重复

mybatis通过mapperLocations: classpath:mapper/*.xml所自定的xml文件能轻松的实现数据库表和完全对应的entity做关联的dao接口,比如下面的例子:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.blublublu.dao.MyDao">
   <select id="myInterface" resultType="com.blublublu.entities.MyStruct">
      SELECT * FROM my_table
   </select>
</mapper>

注意,为了避免java驼峰风格和数据库下划线风格的命名之间的自动关联,一般我们要开启驼峰开关:

mybatis:
  configuration:
    map-underscore-to-camel-case: true # 驼峰开关

但是一旦一个返回对象需要关联多个库表的时候,就比较麻烦了,所以我们以实现一个值班列表为例,做一些实战演练。

首先设计我们的数据结构,定义如下entity:

ShiftsPerson.java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ShiftsPerson {
    private Long id;
    private String mailId; // 邮箱id
    private String name; // 姓名
    private String phone; // 电话
}

=======================================================

ShiftsSchedule.java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ShiftsSchedule {
    private Long id;
    private Date beginDate;
    private ShiftsPerson mainPerson; // 主值班人
    private ShiftsPerson vicePerson; // 副值班人
}

然后依次定义我们的dao、service、controller,如下:

ShiftsDao.java

@Mapper
public interface ShiftsDao {
    List<ShiftsSchedule> getShiftsScheduleWithPerson();
}

=======================================================

ShiftsService.java

public interface ShiftsService {
    List<ShiftsSchedule> getShiftsScheduleWithPerson();
}

=======================================================

ShiftsServiceImpl.java

@Service
@Slf4j
public class ShiftsServiceImpl implements ShiftsService {
    @Resource
    private ShiftsDao shiftsDao;
    @Override
    public List<ShiftsSchedule> getShiftsScheduleWithPerson() {
        return shiftsDao.getShiftsScheduleWithPerson();
    }
}

=======================================================

ShiftsController.java

@RestController
@Slf4j
@RequestMapping("/api/shifts")
public class ShiftsController {
    @Resource
    private ShiftsService shiftsService;
    @GetMapping(value = "list")
    public BaseModel<List<ShiftsSchedule>> getShiftsList() {
        return BaseModel.getInstance("list", shiftsService.getShiftsScheduleWithPerson());
    }
}

然后就到最重要的mapper设计了:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.blublublu.dao.ShiftsDao">
    <resultMap id="ShiftsScheduleResultMap" type="com.blublublu.entities.ShiftsSchedule">
        <id column="id" property="id" />
        <result column="begin_date" property="beginDate" />
    </resultMap>
&lt;select id="getShiftsScheduleList" resultMap="ShiftsScheduleResultMap"&gt;
    select * from shifts_schedule
&lt;/select&gt;

&lt;resultMap id="ShiftsScheduleWithPersonResultMap"
           type="com.blublublu.entities.ShiftsSchedule"
           extends="ShiftsScheduleResultMap"&gt;
    &lt;association property="mainPerson" javaType="com.blublublu.entities.ShiftsPerson" autoMapping="true"&gt;
        &lt;id column="main_id" property="id" /&gt;
        &lt;result column="main_mail_id" property="mailId" /&gt;
        &lt;result column="main_name" property="name" /&gt;
        &lt;result column="main_phone" property="phone" /&gt;
    &lt;/association&gt;
    &lt;association property="vicePerson" javaType="com.blublublu.entities.ShiftsPerson" autoMapping="true"&gt;
        &lt;id column="vice_id" property="id" /&gt;
        &lt;result column="vice_mail_id" property="mailId" /&gt;
        &lt;result column="vice_name" property="name" /&gt;
        &lt;result column="vice_phone" property="phone" /&gt;
    &lt;/association&gt;
&lt;/resultMap&gt;
&lt;select id="getShiftsScheduleWithPerson" resultMap="ShiftsScheduleWithPersonResultMap"&gt;
    SELECT s.*,
        p1.id AS main_id,
        p1.name AS main_name,
        p1.mail_id AS main_mail_id,
        p1.phone AS main_phone,
        p2.id AS vice_id,
        p2.name AS vice_name,
        p2.mail_id AS vice_mail_id,
        p2.phone AS vice_phone
    FROM shifts_schedule AS s
    LEFT JOIN shifts_person AS p1 ON s.main_person_id=p1.id
    LEFT JOIN shifts_person AS p2 ON s.vice_person_id=p2.id
&lt;/select&gt;

</mapper>

这里有一些重要的细节需要说明一下,首先extends="ShiftsScheduleResultMap"可以用来继承,相同的字段就不用重复写了,然后就是这里的association是用来实现关联表逻辑的,最后就是因为这里同时join了两次person表,所以返回字段相同情况下是有问题的,那么就用AS重命名,然后修改association里的result里的column来匹配。

大数据专家养成记 一-当今OLAP引擎选型的一点思考

大家好,我依然投身在大数据方向,最近一段时间因为工作需要做了很多olap引擎的探索和应用,积累了一些自己的心得,希望和大家做一些分享,也非常希望有不同见解的朋友一起讨论。

一、什么是OLAP

OLAP(On-Line Analytical Processing)联机分析处理,也称为面向交易的处理过程,其基本特征是前台接收的用户数据可以立即传送到计算中心进行处理,并在很短的时间内给出处理结果,是对用户操作快速响应的方式之一。应用在数据仓库,使用对象是决策者。OLAP系统强调的是数据分析,响应速度要求没那么高。

OLTP(On-Line Transaction Processing)联机事务处理,它使分析人员能够迅速、一致、交互地从各个方面观察信息,以达到深入理解数据的目的。它具有FASMI(Fast Analysis of Shared Multidimensional Information),即共享多维信息的快速分析的特征。主要应用是传统关系型数据库。OLTP系统强调的是内存效率,实时性比较高。

二、一些OLAP引擎的特点研究

  1. impala。这是一个相对老牌一点的OLAP引擎,底层强依赖于kudu或hive,其他的一概不支持,有很多大厂使用,比如滴滴,在前几年和性能不占优势的hadoop系计算引擎相比,确实十几秒钟能把上千亿、TB级数据的sql查出来让人眼前一亮,但是他的局限性在于是用C++写的,这让很多java系的伙伴望而生畏,毕竟在大数据领域java独领风骚,所以impala的发展自然就被自己限制住了。
  1. presto。这是facebook开发的一款为了替代impala的引擎,这次比较好的就是改成java语言开发了,而且改进了impala只能支持kudu和hive的弱点,他通过一种connector机制,想支持谁就支持谁,写一个对应的connector就行了,官方提供了应有尽有的connector,所以你能想到的基本都能用。presto就是一个纯计算层,没有存储,配置对应的catalog指定引擎,都可以连,比如你让后端存储是mysql、hive、及所有能通过jdbc访问的存储都可以,甚至excel表格也可以。然而这种MPP架构的引擎有一个弱点,就是如果内存扛不住了就会挂掉,所以稳定性是个问题,那么就有很多人基于稳定性做文章,在其上包装各种稳定性保障的外壳,也使得presto有了很大的应用。京东就出了一本有名的讲presto的书《presto技术内幕》,还是不错的。
  1. clickhouse。简称CH,是战斗民族(俄罗斯)发明的产品,俄罗斯人民发明的开源软件最有名的有两个,一个是nginx,一个就是这个clickhouse了。clickhouse可以说是一个超级无敌的瓤子,是个单机引擎,把DBMS优化到了极致。但是现在互联网行业最大的部署方式还是分布式集群,所以需要对clickhouse做各种包装,俄罗斯人确实也比较彪悍,官方提供了分布式解决方案,可惜用了一套zookeeper,几乎把clickhouse的瓶颈全都限制在了zookeeper里,一个邪恶的想法:是不是这家伙为了做商业化特意让分布式的clickhouse性能变得极低,好让我们买他的更高性能的商业化产品。不过还好,最近发问说明年就要在分布式方案上做去zk的工作了,让我见到了一丝曙光。社区里因为很多人青睐他的单机性能,所以也做了很多分布式方案改造的尝试,初见成效。
  1. kylin。是一个应用在T+1场景的olap引擎,提前建各个维度的cube,也就是相当于把你要查的东西全部提前跑出来,到时候直接拿就行了,所以预计算量很大,查询速度很快,这也是他的特点,也是在有限的应用场景管用,比如多维分析,非他莫属了,极快。但是如果说找一个通用性强点的olap引擎,他一定是最不适合的那个。

三、引擎选择的考虑

以上几个OLAP引擎各有优缺点,在没有场景限定的前提下说不上谁好谁坏,所以讲几个方面的考虑来如何选择。如果是T+1场景,也就是没有实时更新的需求,那么presto+hive(数仓)是一个不错的选择,因为一般公司离线数仓都是建在hive上的。如果有实时更新的诉求,而且更新量很大,那么首选impala+kudu,因为kudu的更新性能是很高的,其他的不能匹敌。如果实时更新量不是很大,那么可以用clickhouse,他更新能做但性能一般,但是查询性能很高,但是如果你有大量的join,那么clickhouse又不太合适了,因为他对单表查询是优化到极致的,但是join的性能就不敢恭维了。如果你没有实时更新诉求,但是需要多维查询性能极高,那么就用kylin吧,虽然他配起来很难,改配置也比较麻烦,但是查询快啊。

总之,以上就是我近期对OLAP引擎的调研和应用的一些收获,希望对大家有帮助,有不同观点欢迎留言讨论。

实操演示springcloud怎么用ribbon做负载均衡

点击下方链接观看:

https://www.bilibili.com/video/BV1Ji4y1g7pD/

 

speed code第二弹:springcloud里怎么用eureka

真实编码录制回放课,演示在springcloud里怎么使用eureka,继上一篇(12分钟视频演示springcloud微服务模块的开发过程)之后又新录制一篇,请点击观看:

点击下方链接观看:

https://www.bilibili.com/video/BV1QD4y1U7bc/

12分钟视频演示springcloud微服务模块的开发过程

点击下方链接观看:

https://www.bilibili.com/video/BV1Pi4y1G79t/

其中的关键内容:

一、父模块pom.xml内容如下,供大家直接拷贝

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.shareditor.springcloud</groupId>
    <artifactId>cloud2020</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>cloud-provider-payment</module>
    </modules>
    <packaging>pom</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <lombok.version>1.16.18</lombok.version>
        <mysql.version>5.1.47</mysql.version>
        <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>Hoxton.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

二、payment模块pom.xml内容如下,供大家直接拷贝

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020</artifactId>
        <groupId>com.shareditor.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>cloud-provider-payment</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
&lt;/dependencies&gt;

</project>

三、注意:java版本选择1.8

四、关键点:controller调用service,service调用dao

智能游戏AI从基础到实战教程 二-游戏人工智能中的数学与物理学

笛卡尔坐标系

这个名字你可能不太熟,但是看到下面这个图你应该很熟悉

 

这其实就是笛卡尔坐标系,一个x轴,一个y轴,都在一个平面上,这个平面一般叫做xy平面,所有二维的笛卡尔坐标系中的点都可以画在这个平面上,比如点:(x, y)

如果想表示三维空间,还需要增加一个z轴

 

毕达哥拉斯定理与三角函数

毕达哥拉斯定理又叫做勾股定理: h^2 = a^2 + b^2

在直角三角形中sin(θ) = 对边/斜边, cos(θ) = 邻边/斜边, tan(θ) = 对边/邻边

 

矢量

标量是没有方向的,矢量是有方向的,比如v = (x, y)这个矢量表示从原点出发沿x轴移动x个单位,沿y轴移动y个单位的一个即有大小又有方向的一个数学表达

矢量的计算有加减,但是乘法比较特殊,点乘是一种乘法,用符号·表示,点乘可以计算出两个矢量的夹角,公式为:u·v = |u| |v| cos(θ),所以cos(θ) = u·v / (|u| |v|)

使用点乘能够很容易地判断一个对象是位于智能体前面还是后面,如果对象在智能体前面则点乘为正,如果在后面则为负

 

局部空间和世界空间

这对应着游戏开发中常见的局部坐标系和世界坐标系,世界坐标系是相对于屏幕而言的全局坐标系,局部坐标是是相对于某一个物体而言的坐标系,比如在屏幕底部10%高度上绘制了一个状态栏,在状态栏左上角画一个小图标,那么这个小图标的世界坐标是相对于屏幕而言的,而局部坐标是相对于状态栏而言的,他们的数值不同,当然这两个坐标可以通过状态栏的世界坐标来做相互转换

 

加速度和牛顿第二定律

加速度表示速度变化的快慢,v = at + u

牛顿第二定律:a = F / m,即物体受力大小除以质量等于加速度,这常被用在游戏中的物体的运动情况的仿真,另外如果游戏中有模拟重力的物体,那么也是一样的规律,只不过这里的a就是重力加速度g

 

总结

游戏中要用到的数学和物理知识还有很多,上面仅仅列举了一些最基础的,后续我们再随着开发随着复习。

智能游戏AI从基础到实战教程 一-发动集体智慧开发游戏AI

《头号玩家》的启发

看过《头号玩家》的朋友们应该都记得在2045年虚拟现实技术和人工智能技术多么的强大,然而虚拟世界对现实世界的影响和冲击也是不容小觑的,万一哪天人工智能控制了人类就像智子控制了基础科学一样那该怎么办呢?当然这有些开脑洞了,言归正传,个人觉得第一个真正的人工智能应该会出现在虚拟世界里,那么最直接的虚拟世界就是游戏世界,而且是网络游戏。

 

这个系列的设计

最终目标:做一款简单有趣的小游戏,在游戏里加入人工智能算法,不断收集玩家的操作数据作为训练样本,利用集体智慧优化游戏中的AI,最终产生一个可以比拟真人的AI角色,这个AI角色作为玩家的伙伴,帮助玩家更容易获胜。

目标分解一——游戏部分:这款游戏需要两个人来共同操作,一个是你自己,另一个可以是另外一个人也可以是AI,只有两个人默契地合作才有可能获胜,游戏是关卡制,每一关都会增加难度,游戏越简单越好,因为我们是专业搞AI的不是搞游戏的,但是可玩性要足够强,便于传播。

目标分解二——AI部分:最初的AI角色因为没有经过训练会比较弱智,但是随着大量样本的录入,通过深度学习技术,它会越来越智能,最终甚至会比真人更厉害,和真人合作过不了的关卡,你和AI合作也许能通过。

针对以上目标,每有一点进展我都会以博客的形式分享出来,欢迎大家持续关注。

 

游戏玩法和题材募集

本人才疏学浅,针对上面所述的游戏需求没有太好的想法,希望发动集体的智慧,收集大家的点子,相信最终一定能有一个最佳方案,为了方便收集大家的想法,特地建了一个微信群,大家扫下方二维码加入,欢迎交流(二维码7天有效期,过期后我会再更新,请随时关注此博客)。

 

自己动手做聊天机器人 四十三-继续从理论到实践开发自己的聊天机器人

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

上篇回顾

上篇文章《自己动手做聊天机器人 四十二-(重量级长文)从理论到实践开发自己的聊天机器人》使用带attention的seq2seq模型实现一般聊天机器人,经过10个小时对1000条样本的训练,达到了比较好的效果,代码分享在https://github.com/lcdevelop/ChatBotCourse/tree/master/chatbotv5

但是存在一个问题,当把样本量加大的时候内存随之增长,如果样本量达到万级别,内存占用已经达到了10G,样本量如果到几十万几百万,内存已经不知道能到多少了,这个主要问题是每次迭代都是把样本全量加载到内存并一次性训练完再更新模型,另外还有一个问题就是词表是基于样本生成的,没有做任何限制,导致样本大词表就大,那么模型就很大,所以占据内存也更大,所以我做了一版优化,在自己机器上尝试训练20w的样本内存占用不到1G,希望大家能找到更大量的样本来帮我充分测试,我这里有三千万的聊天语料可以使用,欢迎大家尝试,获取方式请见《自己动手做聊天机器人 二十九-重磅:近1GB的三千万聊天语料供出》。

 

优化方案

首先我们把全量加载样本改成批量加载,修改原来的train()函数,核心部分如下:

        # 训练很多次迭代,每隔10次打印一次loss,可以看情况直接ctrl+c停止
        previous_losses = []
        for step in xrange(20000):
            sample_encoder_inputs, sample_decoder_inputs, sample_target_weights = get_samples(train_set, 1000)
            input_feed = {}
            for l in xrange(input_seq_len):
                input_feed[encoder_inputs[l].name] = sample_encoder_inputs[l]
            for l in xrange(output_seq_len):
                input_feed[decoder_inputs[l].name] = sample_decoder_inputs[l]
                input_feed[target_weights[l].name] = sample_target_weights[l]
            input_feed[decoder_inputs[output_seq_len].name] = np.zeros([len(sample_decoder_inputs[0])], dtype=np.int32)
            [loss_ret, _] = sess.run([loss, update], input_feed)
            if step % 10 == 0:
                print 'step=', step, 'loss=', loss_ret, 'learning_rate=', learning_rate.eval()
                if len(previous_losses) > 5 and loss_ret > max(previous_losses[-5:]):
                    sess.run(learning_rate_decay_op)
                previous_losses.append(loss_ret)
                # 模型持久化
                saver.save(sess, './model/demo')

这里的get_samples(train_set, 1000)是批量获取样本,其中1000是每次获取的样本量,这个函数增加了如下逻辑:

    if batch_num >= len(train_set):
        batch_train_set = train_set
    else:
        random_start = random.randint(0, len(train_set)-batch_num)
        batch_train_set = train_set[random_start:random_start+batch_num]
    for sample in batch_train_set:
        raw_encoder_input.append([PAD_ID] * (input_seq_len - len(sample[0])) + sample[0])
        raw_decoder_input.append([GO_ID] + sample[1] + [PAD_ID] * (output_seq_len - len(sample[1]) - 1))

也就是说每次都在全量样本中随机位置抽取连续的1000条样本

另外,在加载样本词表时做了词的最小频率的限制,如下:

    def load_file_list(self, file_list, min_freq):
    ......
        for index, item in enumerate(sorted_list):
            word = item[1]
            if item[0] < min_freq:
                break
            self.word2id_dict[word] = self.START_ID + index
            self.id2word_dict[self.START_ID + index] = word
        return index

 

试验效果

经过如上改造,我把样本量扩展到21w,运行起来内存占用不到1G,最新的代码请见最新更新的:https://github.com/lcdevelop/ChatBotCourse/tree/master/chatbotv5