Content Table

常用 SQL

创建数据库

1
CREATE DATABASE ebag DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci

增加新列

1
ALTER TABLE table_name ADD COLUMN column_name varchar(64)

分页查询

MySQL 中使用 LIMIT 进行分页,第一个参数是起始位置 offset,从 0 开始,第二个参数是要取多少条记录

1
SELECT * FROM question WHERE subject_code='XXX' LIMIT 0, 30

数据库规范

标准化和规范化

数据的标准化有助于消除数据库中的数据冗余。标准化有好几种形式,但 Third Normal Form (3NF) 通常被认为在性能、扩展性和数据完整性方面达到了最好平衡。简单来说,遵守 3NF 标准的数据库的表设计原则是:

  • 第一范式 (1NF) 无重复的列

    所谓第一范式 (1NF) 是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如果出现重复的属性,就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原实体之间为一对多关系。在第一范式 (1NF) 中表的每一行只包含一个实例的信息。简而言之,第一范式就是无重复的列。说明:在任何一个关系数据库中,第一范式 (1NF) 是对关系模式的基本要求,不满足第一范式 (1NF) 的数据库就不是关系数据库。

  • 第二范式 (2NF) 属性完全依赖于主键 (每个表要定义主键,如无意义自增长 id)

    第二范式 (2NF) 是在第一范式 (1NF) 的基础上建立起来的,即满足第二范式 (2NF) 必须先满足第一范式 (1NF)。第二范式 (2NF) 要求数据库表中的每个实例或行必须可以被唯一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。例如员工信息表中加上了员工编号 (emp_id) 列,因为每个员工的员工编号是惟一的,因此每个员工可以被惟一区分。这个惟一属性列被称为主关键字或主键、主码。

    第二范式 (2NF) 要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。简而言之,第二范式就是属性完全依赖于主键。

  • 第三范式(3NF)属性不依赖于其它非主属性 (表中最多包含其他表中的主键,即外键)

    满足第三范式 (3NF) 必须先满足第二范式 (2NF)。简而言之,第三范式 (3NF) 要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。例如,存在一个部门信息表,其中每个部门有部门编号 (dept_id)、部门名称、部门简介等信息。那么在的员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。如果不存在部门信息表,则根据第三范式 (3NF) 也应该构建它,否则就会有大量的数据冗余。简而言之,第三范式就是属性不依赖于其它非主属性。

拓扑排序在并行任务调度中的运用

如下图所示的一个任务设计:

  • 任务 1 和任务 2 可以同时执行,等他们都结束后才能执行任务 3
  • 任务 4 执行结束后可以同时执行任务 5 和任务 6
  • 任务 8 执行结束后则任务执行完成

如果用程序来实现这个任务的调度,触发任务的执行,应该怎么做呢?

通过观察,这是一个有向无环图,要访问某个节点 (执行任务),需要先访问它的所有前驱节点,而且每个节点只能访问一次,这是一个典型的可以使用图的拓扑排序解决的问题。

Stream 笔记

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。

Stream 中的方法单独拿出来使用的话体现不出其优点,在复杂的操作情况下,Stream 的优势才明显,例如下面这样生成不重复有序随机数的例子,如果用传统的写法代码会很长,还需要 if else 判断,使用临时集合等截断,去重等复杂操作,使用 Stream 的方式的话,逻辑看上去就清晰很多:

1
2
3
4
5
6
7
Stream<Double> stream = Stream.generate(Math::random) // double 无限流
.limit(20) // 截断,取前 20 个
.filter(x -> x > 0.3) // 过滤,取大于 0.3 的元素
.skip(1) // 忽略,丢弃第一个元素
.distinct() // 去重
.map(x -> x * 10) // 映射,将每个元素扩大 10 倍
.sorted(); // 对 double 流进行排序

Lambda 笔记

Lambda 是什么 ?

简单的说,Lambda 就是匿名函数。由于 Java 是完全面向对象的,不能像 C 语言那样独立的定义函数以及函数变量,Java 中的方法 (函数) 只能定义在类或者接口中,所以 Java 采取了一种折衷的方案实现 Lambda: 通过有且只有一个未实现的方法的接口来实现 Lambda,所以这样的接口又叫做 Functional Interface

在 Java 中,Lambda 的本质是匿名内部类的语法糖,用于简化匿名内部类的编码。也许您要问,匿名内部类在编译的时候都会成功 class 文件,但 Lambda 却没有看到对应的 class 文件啊。其实这也好理解,class 文件最后会被 JVM 加载到 byte[] 中,用于生成 Class 对象,Lambda 实现的匿名内部类在运行时生成对应的 bytes[] 来生成 Class 对象就可以了,class 文件只是一个载体,其内容也可以通过 URL 远程加载呢,这样本地是不是也一样看不到 class 文件了呢?

Lambda comes from the Lambda Calculus and refers to anonymous functions in programming.

Fabric 笔记

Fabric 是基于 HTML5 canvas 的图形库,通过操作对象的属性来操作 canvas (叫 object model),已经提供了一些基本的图元,如 Rect, Circle, Ellipse, Line, Polygon, Image, SVG, Text, Textbox, 阴影等,支持动画、拖动放大缩小旋转等,还支持自由绘制,能够序列化为字符串方便保存,需要的时候再把字符串反序列化为 fabric.Canvas 对象。

Fabric.js is a powerful and simple Javascript HTML5 canvas library. Fabric provides interactive object model on top of canvas element. Fabric also has SVG-to-canvas (and canvas-to-SVG) parser.

下面列举一些可能会常用到的知识点:

  • 全局设置默认值: fabric.Object.prototype.transparentCorners = false (cornerSize: 7)
  • 导出时包含指定 / 自定义属性: canvas.toJSON(['name', 'lockScalingY'],默认很多属性都没有到处,根据自己的需要在这里指定
  • 设置选中 item 的属性: canvas.getActiveObject().set("fontFamily", 'Monaco')
  • 修改整个 canvas 的鼠标: canvas.defaultCursor = 'move'
  • <canvas width="100" height="100"> 的大小被 Canvas 中指定的值覆盖: new fabric.Canvas('c', { width: 600, height: 600 })
  • 控制点 controls 指的是选中图形后在图形边框上出现的 8 个用来控制图形大小的小方框,名字为 tl, tr, br, bl, ml, mt, mr, mb
  • canvas.renderAll() 会立即重绘,canvas.requestRenderAll() 则是把重绘的命令放到事件队列里,如果准备放入时已经存在则丢弃,不重复放入队列中
  • 常用属性: Well, as you would expect there are those related to
    • positioning: left, top
    • dimension: width, height
    • rendering: fill, opacity, stroke, strokeWidth
    • scaling and rotation: scaleX, scaleY, angle
    • flipping: flipX, flipY
    • skewing: skewX, skewY

Fabric Demos 里有很多例子,对学习 Fabric 非常有帮助。

微信网页中精确定位

支持 H5 的浏览器提供了获取 GPS 的接口 navigator.geolocation.getCurrentPosition(success, error),由于各种原因,国内得到的定位都是不准确的,百度地图 API 也提供了定位接口 new BMap.Geolocation().getCurrentPosition(callback),不过也不准确,有时候会偏离几十公里,为了能够在浏览器里精确的定位,可以在微信浏览器中打开网页,使用微信的 JS SDK 中的 wx.getLocation() 进行定位得到大地坐标系 WGS84 的坐标,然后转换为百度 BD09 坐标,使用百度 API 获取此坐标对应的中文地址。

动态切换数据源

动态切换数据源的原理为使用 AbstractRoutingDataSource,根据数据源的名字查找数据源:

  1. 在 Spring IoC 容器中创建一个 AbstractRoutingDataSource 实现的对象 routingDataSource (Spring 默认没有使用此数据源)
  2. 创建一个数据源的 Map dataSourceMap,key 为 DataSource 的名字,value 为 DataSource 的对象
  3. 调用 routingDataSource.setTargetDataSources(dataSourceMap) 设置可供使用的数据源
  4. 在需要使用数据源的时候,Spring JDBC 会调用 routingDataSource.determineTargetDataSource() 获取数据源,而要返回哪个数据源,由 routingDataSource.determineCurrentLookupKey() 返回的数据源的名字决定

下面我们从最简单的方式实现数据源切换,然后一步一步的深入优化基于 AbstractRoutingDataSource 的数据源。

Promise 知识点

可以访问 Promise 对象基础 对 Promise 进行了解,这里就不再赘述了。

Promises 承诺,你将会得到延期或长期运行任务的未来结果。承诺有两个渠道:第一个为结果,第二个为潜在的错误。要获取结果,您将回调函数作为 then 函数参数,要处理错误,您将回调函数提供为 catch 函数参数。Promise 的状态的改变是单向的,一次性的,一旦改变,状态就会凝固了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
</head>

<body>
<script type="text/javascript">
let p = new Promise((resolve, reject) => {
setTimeout(function() {
const result = Math.random();
result > 0.5 ? resolve(result) : reject('Lower than 0.5');
}, 2000);
});

p.then(result => {
console.log('success', result);
}).catch(result => {
console.log('fail', result);
});
</script>
</body>

</html>

Html 转为 Pdf

使用 html2pdf 把 Html 转为 Pdf,实际项目中可以先借助 Freemarker 模板生成 Html,然后再转换为 Pdf。

缺点: html2pdf 不支持 CSS 的 flex 和 grid 布局。

依赖

1
implementation "com.itextpdf:html2pdf:2.1.7"

转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.layout.font.FontProvider;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Html2Pdf {
public static void main(String[] args) throws IOException {
File htmlSrc = new File("/Users/Biao/Desktop/x.html");
File pdfDest = new File("/Users/Biao/Desktop/x.pdf");

// 使用中文字体解决不显示中文问题: 字体文件可以从系统中找,也可以从网上下载
FontProvider fontProvider = new FontProvider();
fontProvider.addStandardPdfFonts();
fontProvider.addFont("/Users/Biao/Desktop/Yahei.ttf");
// fontProvider.addDirectory("..."); // 添加文件夹下的所有字体

ConverterProperties converterProperties = new ConverterProperties();
converterProperties.setCharset("UTF-8");
converterProperties.setFontProvider(fontProvider);

// HTML 中本地图片要使用 file:// 的格式,如 file:///Users/Biao/Desktop/shot.png
HtmlConverter.convertToPdf(htmlSrc, pdfDest, converterProperties);
}
}