MySQL 使用 IF 判断是否执行 SQL

语法
if(condition, value_if_true, value_if_false)

上面的三个参数的意思是,如果 condition(表达式)为 true,则执行表达式 value_if_true,否则执行表达式 value_if_false

例如有下面这个 User 用户表:

1
2
3
4
5
6
7
8
9
CREATE TABLE User (
name VARCHAR(10) NOT NULL
COMMENT '姓名',
age INT NOT NULL
COMMENT '年龄',
sex BOOLEAN NOT NULL
COMMENT '性别'
)
COMMENT '用户表'

一个简单的根据属性查询列表的 SQL 语句大致如下:

1
2
3
4
5
6
SELECT *
FROM User
WHERE
name = ?
AND age = ?
AND sex = ?;

那么,现在问题来了。我们有一个需求就是如果性别为空,就查询全部的性别,否则就根据性别进行查询。这时就可以使用 if 来进行判断了(吾辈的某位同事就是碰到了这个坑。。。),如果 condition(sex = null) 为 true,就直接执行表达式 value_if_true(true),否则执行 value_if_false(sex = ?)。

1
2
3
4
5
6
SELECT *
FROM User
WHERE
name = ?
AND age = ?
AND if(sex = NULL, TRUE, sex = ?);

注:这里不要使用 1 = 1 而直接使用 true 就好了。
PS:这难道就是所谓的习惯成自然么。。。
附:诚然,在使用了 ORM 框架(例如 Mybatis 后),基本上都可以对传入的参数进行判空处理,然而有时候会出现一种情况:
某个属性经由其他属性统合计算而得到,并非是实体(或是其他的什么)传入进来的参数,那便是只能使用 SQL 的原生语法了呢

MySQL 字符串数字比较大小的问题以及解决方案

为什么要写这篇文章呢?因为吾辈碰到了 MySQL 中字符串数字比较起来的坑。

字符串数字:明明是数字却在数据库中使用 varchar 等类型进行存储的数字。

例如以下的表结构:

1
2
3
4
5
6
7
CREATE TABLE test_table (
id BIGINT NOT NULL
COMMENT 'id',
int_str VARCHAR(200) NOT NULL
COMMENT '使用 varchar 保存数字的列'
)
COMMENT '测试用的数据表';

当吾辈想要查询 big_int_column 大于某个值的数据时,最初是这样写的:

1
2
3
SELECT *
FROM test_table
WHERE int_str > ?;

但后来发现一个问题。。。

其实很简单,因为字符串的比较规则和数字并不一样,虽然很多时候看起来好像没什么不同。

例如下面的 SQL 语句:

1
2
SELECT 2 > 12; #结果为 0(false)
SELECT '2' > '12'; #结果为 1(true)

只是类型变化了,但结果却截然相反,因为字符串是按照字符依次进行对比,而数字则是按照大小直接比较。

按照字母依次进行对比的意思是:
例如上面的 ‘2’ 和 ‘12’,首先会比较字符 ‘2’ 和 ‘1’ 的大小,如果不是相等,就立刻得出结果,否则继续比较下一位,直到得到结果或没有可以继续比较的情况为止,所以就会得到看似荒谬的 1(true)。

所以后来去稍微查询了一下,发现了 MySQL 中的两个函数:

  • cast()
    标准语法:

    1
    cast(value as type)

    或者(不是重点)

    1
    cast(value AS CHAR CHARACTER SET encoding)

    使用示例:

    1
    2
    3
    4
    # 转换为整数(值区间 [-9223372036854775808 ~ 9223372036854775807])
    cast('12' AS SIGNED)
    # 或者转换为无符号整数(值区间 [0 ~ 18446744073709551615])
    cast('12' AS UNSIGNED)
  • convert()

    标准语法:

    1
    convert(value, type)

    或者(不是重点)

    1
    convert(value using charset)

    使用示例:

    1
    2
    3
    4
    # 转换为整数
    convert('12', SIGNED)
    # 或者转换为无符号整数
    convert('12', UNSIGNED)

所以吾辈将查询的 SQL 语句修改成如下样子:

1
2
3
SELECT *
FROM test_table
WHERE int_str > cast(? AS UNSIGNED);

算是解决了一些问题,然而如果 int_str 的值大于 UNSIGNED 类型的最大值又会发生问题。。。

然后从网络上看到了一种推荐的 SQL 的写法:

1
SELECT '12' + 0;

但经过了测试,甚至比上面的 cast()convert() 表现更糟。

例如下面的 SQL:

1
SELECT '9223372036854775807' + 0; #结果是:9223372036854776000

所以说,还是直接直接用数字存储数字最好啦!(_´ω `)

配置文件 yml

本文主要引用自:Spring Boot 配置文件详解,这里主要是为了进行记录以便吾辈进行查找。

yml 是 YAML(YAML Ain’t Markup Language)语言的文件,以数据为中心,比 json、xml 等更适合做配置文件。

对比:
yml 和 xml 相比,少了一些结构化的代码,使数据更直接,一目了然。
yml 和 json 呢?没有谁好谁坏,合适才是最好的。yml 的语法比 json 优雅,注释更标准,适合做配置文件。json 作为一种机器交换格式比 yml 强,更适合做 api 调用的数据交换。

语法

  • 以空格的缩进程度来控制层级关系。空格的个数并不重要,只要左边空格对齐则视为同一个层级。注意不能用 tab 代替 空格。且大小写敏感。支持字面值,对象,数组三种数据结构,也支持复合结构。
  • 字面值:字符串,布尔类型,数值,日期。字符串默认不加引号,单引号会转义特殊字符。日期格式支持 yyyy/MM/dd HH:mm:ss
  • 对象:由键值对组成,形如 key:(空格)value 的数据组成。冒号后面的空格是必须要有的,每组键值对占用一行,且缩进的程度要一致,也可以使用行内写法:{k1: v1, ….kn: vn}
  • 数组:由形如 -(空格)value 的数据组成。短横线后面的空格是必须要有的,每组数据占用一行,且缩进的程度要一致,也可以使用行内写法: [1,2,…n]
  • 复合结构:上面三种数据结构任意组合

使用

创建一个 Spring Boot 的全局配置文件 application.yml,配置属性参数。主要有字符串,带特殊字符的字符串,布尔类型,数值,集合,行内集合,行内对象,集合对象这几种常用的数据格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
yaml:
str: 字符串可以不加引号
specialStr: "双引号直接输出\n特殊字符"
specialStr2: '单引号可以转义\n特殊字符'
flag: false
num: 666
Dnum: 88.88
list:
- one
- two
- two
set: [1, 2, 2, 3]
map: { k1: v1, k2: v2 }
positions:
- name: ITDragon
salary: 15000.00
- name: ITDragonBlog
salary: 18888.88

注意

  • 字符串可以不加引号,若加双引号则输出特殊字符,若不加或加单引号则转义特殊字
  • 数组类型,短横线后面要有空格;对象类型,冒号后面要有空格。
  • YAML 是以空格缩进的程度来控制层级关系,但不能用 tab 键代替空格,大小写敏
  • 如何让一个程序员崩溃?在 yml 文件中加几个空格!(〃>皿<)

Linux Centos 上通过 RPM 安装 MySQL

此教程基本上参考自 MySQL 官方文档

1. 卸载 Centos Linux 自带的 MariaDB

官方参考文档链接
使用以下命令获取已安装的 MariaDB 软件包列表:

1
yum list installed mariadb\*

如果输出中有 MariaDB 的话,说明系统上已经有 MariaDB 了,需要先使用命令卸载。

1
yum remove MariaDB-common MariaDB-compat MariaDB-server

或者

1
yum remove mariadb-libs

如果没有的话可以直接跳到第 2 步

2. 添加 MySQL Yum 存储库

在 Centos Linux 上已经不能直接安装 MySQL 了, 所以要手动添加软件源。

1
2
wget https://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm
rpm -ivh mysql-community-release-el7-5.noarch.rpm

验证软件源是否按转成功

1
yum repolist enabled | grep "mysql.*-community.*"

3. 安装 MySQL

使用以下命令安装

1
yum install mysql-community-server

吾辈在安装的时候就遇到了错误:

1
2
3
4
Error: Package: mysql-community-server-5.7.22-1.el6.x86_64 (mysql57-community)
Requires: libsasl2.so.2()(64bit)
You could try using --skip-broken to work around the problem
You could try running: rpm -Va --nofiles --nodigest

去 Google 了一下找到了一个解决方案,修改 MySQL 安装源文件。

1
2
3
4
5
6
7
[mysql57-community]
name=MySQL 5.7 Community Server
## baseurl=http://repo.mysql.com/yum/mysql-5.7-community/el/6/$basearch/
baseurl=http://repo.mysql.com/yum/mysql-5.7-community/el/7/$basearch/
enabled=1
gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql

然后继续安装就好了

4. 测试 MySQL

  • 启动 MySQL 的服务 service mysqld start
  • 查看 MySQL 的状态 service mysqld status
  • 关闭 MySQL 的服务 service mysqld stop(执行后记得再重新开启,下面还需要用到)

    注:服务器启动时,如果服务器的数据目录为空,则会进行以下操作:

    • 服务器已初始化。
    • SSL 证书和密钥文件在数据目录中生成。
    • validate_password 已安装并启用。
    • MySQL root 用户‘root‘@’localhost 已创建。root 的密码被设置并存储在错误日志文件中。要显示它,请使用以下命令:
      grep "temporary password" /var/log/mysqld.log

5. 使用 mysql_secure_installation 执行一些重要的操作

执行以下命令

1
mysql_secure_installation

输入上面获得的默认的 root 密码,然后会询问一些重要的操作,列表如下:

  • 修改密码

    1
    2
    3
    4
    5
    6
    7
    The 'validate_password' plugin is installed on the server.
    The subsequent steps will run with the existing configuration
    of the plugin.
    Using existing password for root.

    Estimated strength of the password: 100
    Change the password for root ?

    如果是刚刚安装完成请务必修改一次密码,当然,MySQL 默认密码策略还是很严格的,所以要输入一个安全度较高的密码哦

  • 删除 MySQL 默认的匿名用户(便于测试使用)

    1
    2
    3
    4
    5
    6
    7
    By default, a MySQL installation has an anonymous user,
    allowing anyone to log into MySQL without having to have
    a user account created for them. This is intended only for
    testing, and to make the installation go a bit smoother.
    You should remove them before moving into a production
    environment.
    Remove anonymous users?

    生产环境删除

  • 是否允许远程连接 root 用户

    1
    2
    3
    4
    5
    Normally, root should only be allowed to connect from
    'localhost'. This ensures that someone cannot guess at
    the root password from the network.

    Disallow root login remotely?

    如果没有特殊需求还是尽量禁用了吧,再创建一个 MySQL 账户也不难,不是么?

  • 删除测试数据库 test

    1
    2
    3
    4
    5
    By default, MySQL comes with a database named 'test' that
    anyone can access. This is also intended only for testing,
    and should be removed before moving into a production
    environment.
    Remove test database and access to it?

    因为是任何人都可以访问的,所以没什么需求的话也删了就好

  • 是否重新加载权限表立即生效

    1
    2
    3
    4
    Reloading the privilege tables will ensure that all changes
    made so far will take effect immediately.

    Reload privilege tables now?

    没什么好说的,直接 y 就好了。

测试登录 MySQL

使用以下命令登录

1
mysql -u root -p

这里吾辈遇到过一个错误,死活就卡住了,也没有让输入密码,然后出现了一个错误:

1
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)

Google 了一下发现可能是默认的 localhost 没有映射到 127.0.0.1,所以尝试指定了 IP 地址

1
mysql -u root -h 127.0.0.1 -p

结果可以登陆了。。。
查询一下用户表发现只有 root@localhost 一个

user host
mysql.session localhost
mysql.sys localhost
root localhost

所以添加两个权限:

1
2
GRANT ALL PRIVILEGES ON mytimelinedb.* TO root@'127.0.0.1' IDENTIFIED BY 'yourRootPassword';
GRANT ALL PRIVILEGES ON mytimelinedb.* TO root@'yourServerIP' IDENTIFIED BY 'yourRootPassword';

好了,再查询一下用户表

user host
root 127.0.0.1
root yourServerIP
mysql.session localhost
mysql.sys localhost
root localhost

然后 exit 退出 MySQL 再使用 mysql -u root -p 重新登录,是不是就可以啦!

在 JavaScript 定义类

场景

在一个新的项目时,需要在 JavaScript 中编写与后端对应的实体类时,因为不想使用下面的方法定义类了,感觉实在不够灵活…

1
2
3
var User = {
//more field and function
}

或者

1
2
3
function User() {
//more field and function
}

解决

目前 ES6 已经完全普及开来,所以如果需要定义类的话,请使用 ES6 class 关键字 定义类而非下面这几种方式(需要支持 IE 浏览器的读者可以继续往下看)。

所以去 Google 了一下,大致发现了三种方法:

  1. 构造函数法
    经典方法,基本上是 JavaScript 书籍上都会说的方法。使用构造函数模拟 class,在其内部使用 this 关键字指代实例对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function User() {
    //注:这里所有的 property/function 都是对象级别(没错连方法都是。。。)。所以,toJSON() 方法在每个实例中都有一份,比较浪费内存,可以新建两 个 User 对象 user1,user2,然后使用 user1.toJSON === user2.toJSON 验证一下,你会发现为 false...(2333)
    this.name = 'rxliuli'
    this.toJSON = function() {
    return JSON.stringify(this)
    }
    }

    //想要定义所有对象都公用的 property/function,需要使用 Object.prototype 属性(原型),例如下面定义一个公用的 toJSON() 方法
    User.prototype.toJSON = function() {
    return JSON.stringify(this)
    }

    这种方法的缺陷很明显了,定义起来实在太过麻烦,使用 Object.prototype 让对象的定义实在很不直观。

  2. Object.create() 法
    为了解决 “构造函数法” 的缺点,更方便地生成对象,Javascript 的国际标准 ECMAScript 第五版(目前通行的是第五版),提出了一个新的方法 Object.create()。

    1
    2
    3
    4
    5
    6
    7
    var User = {
    //注:这里所有的 property/function 都是继承得到的(类似于上面的 Object.prototype),所以没有改变的 property/function 只会有一份
    name: 'rxliuli',
    toJSON: function() {
    return JSON.stringify(this)
    },
    }

    调用的话直接使用 Object.create() 生成实例,并不需要使用 new

    1
    2
    3
    var user = Object.create(User)
    user.name = '琉璃'
    console.log(user.toJSON())

    这种方法看起来简洁很多,但不能实现私有属性和私有方法,实例之间没有共享数据,对于模拟 class 而言并不是很好。

  3. 极简主义法
    据说是比 Object.create() 更好的方法,此方法不使用 thisproperty,代码部署起来也比较简单。

    • 封装
      js var User = { createNew: function () { var user = {}; user.name = 'rxliuli'; user.toJSON = function () { return JSON.stringify(this); }; return user; } }
      使用时调用 User.createNew() 即可。
      js var user = User.createNew(); user.name = '琉璃'; console.log(name);
      不知道你有没有发现,这里使用 createNew() 创建的对象的方法又是对象级别的了。但这种方法定义好处是,可以实现私有属性/私有方法,能够实现继承,能够在 class 之间共享数据,同时允许在构造对象时传入参数。

    • 继承
      通过第三种方法可以很容易实现,只要子类在创建对象时调用父类的 createNew() 方法即可!
      例如下面的 PersonStudent 类。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      var User = {
      createNew: function() {
      var user = {}
      user.name = 'rxliuli'
      user.toJSON = function() {
      return JSON.stringify(this)
      }
      return user
      },
      }

      var Student = {
      createNew: function() {
      var student = User.createNew()
      /*这里只定义 Student 特有的方法属性和方法即可*/
      student.grade = 1
      return student
      },
      }
    • 私有属性和私有方法

      createNew() 方法中,只要不是定义在 user 对象上的 property/function,就都是私有的。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      var User = {
      createNew: function () {
      var user = {};
      user.name = 'rxliuli';
      /*私有属性(最好不要问女人的年龄哦)*/
      var age = 17;
      /*这里的变量 age 对于外部而言就是无法读取的,只有通过 user 的公有方*/showAge() 读取
      user.showAge = function () {
      alert(age);
      }
      user.toJSON = function () {
      return JSON.stringify(this);
      };
      return user;
      }
      }
    • 数据共享
      有时候我们需要所有的实例对象都共享一项数据(其实就类似于静态变量/方法啦)。这种情况数据封装到类对象的里面,createNew() 方法的外面就好了。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      var User = {
      createNew: function() {
      var user = {}
      user.name = 'rxliuli'
      user.showAge = function() {
      alert(age)
      }

      return user
      },
      /**
      * 这个方法便是所有 User 对象公用的方法啦
      * @returns {string}
      */
      toJSON: function() {
      return JSON.stringify(this)
      },
      }

      然后生成两个实例对象,这两个实例对象的 toJSON() 方法是同一个,如果有个实例对象修改了,那其他所有的对象都会受到影响。

      1
      2
      3
      var user = User.createNew()
      var user2 = User.createNew()
      console.log(user.toJSON === user2.toJSON)

      还记得上面提出的发现方法 3 的一个问题么?

      不知道你有没有发现,这里使用 createNew() 创建的对象的方法又是对象级别的了

    这里其实可以解决一部分的,当然,前提是你的数据不需要修改,然而需要修改的数据也不该使用共享数据了。


其实也可以直接不使用类来构建对象的,使用 {} 也能直接进行构建对象

1
2
3
4
5
6
7
var user = {
name: 'rxliuli',
//注:这里所有的 property/function 都是对象级别(没错连方法都是。。。)。所以,toJSON() 方法在每个实例中都有一份,比较浪费内存
toJSON: function() {
return JSON.stringify(this)
},
}

好了,一个 user 对象就构建好了,可以立即使用了。

1
2
user.name = '琉璃'
console.log(name)

这种方法快速构建对象对于 function 参数而言很有帮助,然而同样缺陷极大,不适用于大量构造对象(因为每一次构建都要写这么多。。。),而且对象之间没有明显的关联。

那么,关于 JavaScript 定义类就到这里啦 (〜 ̄ ▽  ̄)〜

Spring 配置静态资源访问后结果发生 404 了?

场景

这两天使用 SpringMVC 时突然发现了一个问题,在 spring-mvc.xml 中配置静态资源访问以后,突然发现访问什么 Controller 控制的页面都 404 了。
如果把这个配置删除后,就能正常访问了,后来在与曾经的项目配置文件对比时发现是缺少了配置 <mvc:annotation-driven/>
然而,在没有添加静态资源访问时,没有这个配置也能够使用 Controller 控制器。吾辈好奇的去网络上查询了一下 <mvc:annotation-driven/> 相关的信息,在此记录到 blogger 中,以供日后查阅。

吾辈的 spring-mvc.xml 中的静态资源访问配置:

1
2
3
<!--扫描静态资源文件-->
<mvc:default-servlet-handler/>
<mvc:resources mapping="/statics/**" location="/statics/"/>

那么,下面就说一下 <mvc:annotation-driven/> 具体有什么作用好啦!
<mvc:annotation-driven> 相当于注册了 Spring MVC 分发请求到控制器所必须 DefaultAnnotationHandlerMappingAnnotationMethodHandlerAdapter 两个 Bean,配置一些 messageconvert。即解决了 @Controller 注解的使用前提配置。

配置中如果没有 <mvc:annotation-driven/>,那么所有的 Controller 可能就没有解析,所有当有请求时候都没有匹配的处理请求类,就都去 <mvc:default-servlet-handler/>default servlet 处理了。添加上 <mvc:annotation-driven/> 后,相应的请求被 Controller 处理,而静态资源因为没有相应的 Controller 就会被 default servlet 处理。总之没有相应的 Controller 就会被 default servlet 处理就 ok 了。

然后,<mvc:annotation-driven/> 标签配置的这 2 个 Bean 可以根据 classpath 中的内容默认提供以下功能:

  1. Support for Spring 3’s Type ConversionService in addition to JavaBeans PropertyEditors during Data Binding.A ConversionService instance produced by the org.springframework.format.support.FormattingConversionServiceFactoryBean is used by default.This can be overriden by setting the conversion-service attribute.
    支持 spring3 的 javaBeans 属性编辑器数据绑定时的类型转换服务。
    类型转换服务实例默认为 org.springframework.format.support.FormattingConversionServiceFactoryBean。
    可以覆盖 conversion-service 属性来指定类型转换服务实例类。
  2. Support for formatting Number fields using the @NumberFormat annotation.
    支持 @NumberFormat 注解格式化数字类型字段。
  3. Support for formatting Date, Calendar, Long, and Joda Time fields using the @DateTimeFormat annotation, if Joda Time 1.3 or higher is present on the classpath.
    @DateTimeFormat 注解格式化 Date, Calendar, Long 和 Joda Time(如 classpath 下存在 Joda Time 1.3 或更高版本)字段
  4. Support for validating @Controller inputs with @Valid, if a JSR-303 Provider is present on the classpath.The validation system can be explicitly configured by setting the validator attribute.
    支持 @Valid 注解验证控制器数据,classpath 中需 JSR-303 的 **。可以使用 setting 明确的配置
  5. Support for reading and writing XML, if JAXB is present on the classpath.
    支持读写 xml,classpath 中需 JAXB 。
  6. Support for reading and writing JSON, if Jackson is present on the classpath.
    支持读写 json,classpath 中需 Jackson 。

嗯,这篇 blogger 就这么多啦(其实大部分都是借鉴网络上诸位大佬的啦)

JavaScript 自定义解析字符串为 Date 对象

这两天在 JavaScript 遇到需要根据自定义的格式创建 Date 对象的情况,所以就没多想写个了解析方法。

基本思路是将非标准的日期字符串转换为标准的日期字符串,然后再创建对象。

具体的步骤如下:

  1. 接收两个字符串参数,分别代表要进行解析的字符串 dateStr,以及该字符串需要的自定义日期字符串格式 fmt
  2. 先解析 fmt,获取其中的代表 year, method, day, hour, minute, second, milliSecond 的部分得到一个数组(并且记录不同部分的 indexdateUnits,对 fmt 的代表日期的不同部分进行替换.
  3. 使用替换过的 fmt 创建正则验证 dateStr 是否合法,非法直接返回 null
  4. dateUnits 根据 index 进行排序,得到一个按照原本的 fmt 的不同日期部分的顺序的数组。
  5. 遍历 dateUnits 获取到不同的部分,然后拼接成一个标准的(yyyy-MM-ddThh:mm:ss.SSS)日期字符串 date
  6. 使用 new Date(date) 并且返回。

全部代码如下:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/**
* 解析字符串为 Date 对象
* @param dateStr 日期字符串
* @param fmt 日期字符串的格式
* 目前仅支持使用 y(年),M(月),d(日),h(时),m(分),s(秒),S(毫秒)
*/
Date.of = function(dateStr, fmt) {
if (!dateStr) {
throw new Error('传入的日期字符串不能为空!')
}
if (!fmt) {
throw new Error('传入的日期字符串的自定义格式不能为空!')
}

/**
* 日期格式化对象
* @param name 日期格式的名称
* @param format 日期的格式值
* @param value 格式化得到的值
* @constructor
*/
function DateFormat(name, format, value, index) {
this.name = name
this.format = format
this.value = value
this.index = index
}

//日期时间的正则表达式
const dateFormats = {
year: 'y{1,4}',
month: 'M{1,2}',
day: 'd{1,2}',
hour: 'h{1,2}',
minute: 'm{1,2}',
second: 's{1,2}',
milliSecond: 'S{1,3}',
}
//如果没有格式化某项的话则设置为默认时间
const defaultDateValues = {
year: '2001',
month: '01',
day: '01',
hour: '00',
minute: '00',
second: '00',
milliSecond: '000',
}
//保存对传入的日期字符串进行格式化的全部信息数组列表
const dateUnits = []
for (const fmtName in dateFormats) {
const regExp = new RegExp(dateFormats[fmtName])
if (regExp.test(fmt)) {
const matchStr = regExp.exec(fmt)[0]
const regexStr = String.fill('`', matchStr.length)
const index = fmt.indexOf(matchStr)
fmt = fmt.replaceAll(matchStr, regexStr)
dateUnits.push(
new DateFormat(
fmtName,
String.fill('\\d', matchStr.length),
null,
index,
),
)
} else {
dateUnits.push(
new DateFormat(fmtName, null, defaultDateValues[fmtName], -1),
)
}
}
//进行验证是否真的是符合传入格式的字符串
fmt = fmt.replaceAll('`', 'd')
if (!new RegExp(fmt).test(dateStr)) {
return null
}
//进行一次排序, 依次对字符串进行截取
dateUnits.sort(function(a, b) {
return a.index - b.index
})
for (var i = 0, length = dateUnits.length; i < length; i++) {
const format = dateUnits[i].format
if (format == null) {
continue
}
const matchDateUnit = new RegExp(format).exec(dateStr)
if (matchDateUnit !== null && matchDateUnit.length > 0) {
dateStr = dateStr.replace(matchDateUnit[0], '')
dateUnits[i].value = matchDateUnit[0]
}
}
//将截取完成的信息封装成对象并格式化标准的日期字符串
const obj = dateUnits.toObject(function(item) {
return {
key: item.name,
value: item.value,
}
})
const date = '{year}-{month}-{day}T{hour}:{minute}:{second}.{milliSecond}'.format(
obj,
)
try {
return new Date(date)
} catch (e) {
return null
}
}

//下面是上面的 Date.of() 使用的一些辅助方法

/**
* 替换所有匹配exp的字符串为指定字符串
* @param exp 被替换部分的正则
* @param newStr 替换成的字符串
*/
String.prototype.replaceAll = function(exp, newStr) {
return this.replace(new RegExp(exp, 'gm'), newStr)
}

/**
* 原型:字符串格式化
* @param args 格式化参数值
*/
String.prototype.format = function(args) {
var result = this
if (arguments.length < 1) {
return result
}

var data = arguments // 如果模板参数是数组
if (arguments.length === 1 && typeof args === 'object') {
// 如果模板参数是对象
data = args
}
for (var key in data) {
var value = data[key]
if (undefined !== value) {
result = result.replaceAll('\\{' + key + '\\}', value)
}
}
return result
}

/**
* 为 js 的 String 添加填充字符串的静态方法
* @param item 填充的元素
* @param length 填充的长度
* @returns {string} 填充得到的字符串
*/
String.fill = function(item, length) {
var result = ''
for (var i = 0; i < length; i++) {
result += item
}
return result
}

/**
* js 数组转换为一个 Object 对象
* @param fn 转换方法
* @returns {{}} 得到的 Object 对象
*/
Array.prototype.toObject = function(fn) {
const obj = {}
this.map(fn).forEach(function(item) {
obj[item.key] = item.value
})
return obj
}

Code Gist:https://gist.github.com/rxliuli/a81f058d03a99cbd08d6ca6095b2c7cb

那么,关于 JavaScript 中的字符串解析为 Date 对象就到这里啦,如果有什么错误/更好的建议都可以提出来呢

Linux Centos 6.5 下安装 JDK+Tomcat

下载 JDK/Tomcat

Oracle JDK 下载页面
首先,我们需要去 oracle 官网找到 JDK Linux 的下载链接(选择同意协议然后复制链接即可, 此处吾辈选择的是 1.8.7):
http://download.oracle.com/otn-pub/java/jdk/8u171-b11/512cd62ec5174c3487ac17c61aaa89e8/jdk-8u171-linux-x64.tar.gz

然后再 shell 中执行以下下载命令:

1
2
wget --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie;" htt p://download.oracle.com/otn-pub/java/jdk/8u171-b11/512cd62ec5174c3487ac17c61aaa89e8/jdk-8u171-linux-x64.tar.gz
--2018-06-10 01:08:27-- http://download.oracle.com/otn-pub/java/jdk/8u171-b11/512cd62ec5174c3487ac17c61aaa89e8/jdk-8u171-linux-x64.tar.gz

注: Oracle 限制了无法直接进行下载, 所以才要设置一下请求头…

然后找到 Tomcat 的下载链接(吾辈使用的是 8.5):
Apache Tomcat 下载页面

执行下载:

1
wget http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.31/bin/apache-tomcat-8.5.31.tar.gz

解压 JDK/Tomcat

1
2
tar -zxvf jdk-8u171-linux-x64.tar.gz -C /usr/java/
tar -zxvf apache-tomcat-8.5.29.tar.gz -C /usr/

设置环境变量

编辑环境变量配置:

1
vi /etc/profile

在最后加上:

1
2
3
4
5
# configurer Oracle JDK classpath
export JAVA_HOME=/opt/java/jdk1.8.0_171
export JRE_HOME=${JAVA_HOME}/jrea
export CLASSPATH=.:${JAVA_HOME}/lib/tools.jar:${JRE_HOME}/lib/dt.jar
export PATH=${JAVA_HOME}/bin:${JAVA_HOME}/jre/bin:$PATH

重新加载环境变量:

1
source /etc/profile

启动 Tomcat

1
/opt/apache-tomcat-8.5.31/bin/startup.sh

现在可以看到 Tomcat 确实启动了,然而每次都输入这么一大长串的值感觉有点麻烦,解决方案是把 Tomcat 也加入到环境变量中去。
再次修改环境变量配置文件:

1
vi /etc/profile
1
2
3
# configurer Apache Tomcat classpath
export TOMCAT_HOME=/opt/apache-tomcat-8.5.31
export PATH=${TOMCAT_HOME}/bin:$PATH

重新加载环境变量:

1
source /etc/profile

那么,JDK/Tomcat 的配置就到这里啦

Windows10 升级 Linux 的子系统 Ubuntu

先查看一下自己的系统版本,打开命令提示符输入 bash 回车然后键入如下命令回车

1
lsb_release -a

下面这样用 Ubuntu 的自动升级在 Bash on Windows 会失败的:

1
2
sudo aptitude install update-manager-core
sudo do-release-upgrade

因此需要用到 Debian 的升级方法

步骤:

  1. 获取全局 root 权限

    1
    2
    sudo -s
    # 然后输入密码
  2. 把所有包升级至 14.04 (trusty) 的最新版

    1
    2
    aptitude update
    aptitude full-upgrade -y
  3. 更改更新源为 16.04 (xenial)

    1
    2
    3
    # 方法一:修改初始的更新源文件
    # 备份初始的源文件为sources.list.ORIG,将sources.list里的"trusty"全替换为"xenial"。
    sed -i.ORIG 's/trusty/xenial/g' /etc/apt/sources.list
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 方法二(推荐):把更新源直接改为国内的阿里云Ubuntu(xenial)镜像,这样会很快
    # 编辑更新源文件
    vim /etc/apt/sources.list
    # 清空文件后粘贴如以下代码:
    deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse
    deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse
    deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse
    deb http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse
    deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
    deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse
    deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse
    deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse
    deb-src http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse
    deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
    # :wq 保存退出
  4. 备份并新建空文件夹 /etc/apt/sources.list.d/
    注:bash 刚装好时这个文件夹是空的,若如此可跳过这一步

    1
    2
    mv /etc/apt/sources.list.d/ /etc/apt/sources.list.d.ORIG/
    mkdir /etc/apt/sources.list.d/
  5. 升级至 16.04 (xenial)

    1
    2
    3
    4
    aptitude update
    aptitude safe-upgrade -y
    # 注1:升级过程中会提示你重启服务(restart services),选yes
    # 注2:还会出现文件冲突,保留当前版本(current version)即可,输入N
  6. 把所有包升级至 16.04 (xenial) 的最新版,并重装丢失的 aptitude 包,最后清理无用包

    1
    2
    3
    apt-get dist-upgrade
    apt-get install aptitude
    apt-get autoremove
  7. 至此已经顺利升级至 16.04 (xenial) 了,但你会发现像原来那样使用 sudo 命令会报错:

    1
    2
    3
    # 重启Bash,在非root权限下测试
    sudo apt-get update
    sudo: no tty present and no askpass program specified

    此后用 sudo 只能这么用:

    1
    sudo -S apt-get update

    或者,你可以设置 root 的密码:

    1
    2
    3
    4
    5
    sudo -S passwd root
    [sudo] password for rxliuli: # 输入你的密码
    Enter new UNIX password: # 输入 root 密码
    Retype new UNIX password: # 确认输入 root 密码
    passwd: password updated successfully # 大功告成!

    以后,我们就可以使用 root 账号了:

    1
    2
    3
    su
    Password: # 输入 root 密码
    # 进入 root 了

    如果你装错了,或者想退回 Ubuntu 14.04 (trusty),把 Linux 子系统卸载重装即可(在 Windows 的 cmd 而非 bash 下):

    1
    2
    > lxrun /uninstall /full /y
    > lxrun /install

最后,引用一句话:

So when will the newer Ubuntu 16.04 LTS release be available? In a recent comment, Microsoft’s Rich Turner explained: “We have to add some additional capabilities to make it work well, but we are looking at 16.04 support for a future release.”

微软自认为这个 Bash on Ubuntu on Windows 还不够稳定,所以暂不支持官方升级,这样强行升级会有什么后果,那就不好说了,大家凑合用吧。

参考解决方案: https://www.reddit.com/r/windowsinsiders/comments/4iy38n/upgrade_ubuntu_on_windows_from_1404_to_1604/?st=iq62jo5j&sh=7af74c79
https://help.ubuntu.com/lts/serverguide/installing-upgrading.html


此文转自知乎,有什么问题可以问原作者,在吾辈的电脑上是能够正常工作的呢

Xftp 工具连接到远程 linux 主机上传下载

该教程已经过时,如果需要 SSH/SFTP 工具请选择 MobaXterm

简介:

Xftp 是一款连接 linux 服务器的 ftp 传送工具,支持 FTP 和 SFTP 协议,支持多标签会话窗口。关键是免费,使用也很简单,支持拖放,类 Shell 管理模式,相当方便。

  1. 首先下载 xftp 工具,安装时选择:免费授权家庭 / 学校,不需要输入序列号。

  2. 安装完成后打开,选择文件——新建,弹出对话框,填写名称,主机,协议选择 SFTP,端口为 22,用户名和密码。
    附:使用 ftp 21 端口会提示无法连接;Xshell 登录默认用的是 22 端口,ssh 协议,也就是 sftp。

  3. 点击工具栏打开按钮,选择刚创建好的 ftp 站点,登录后远程主机在右侧,默认为 root 家目录,直接选择想要上传或下载的文件进行传输即可。

嗯,暂且也便是只有这些了,图片的话有时间再弄吧(markdown 使用图片不是很方便呢)