BigBing 技术博客

使用 Rails Migration转换 MySQL数据库和表的字符集总结

MySQL Character Set基础知识

对于 MySQL 数据库你可以在不同的 Level 设置Character Set 和 Collation,包括:Server Level,Database Level,Table Level,Column Level 还有 Application Level.

Server Level:

可以通过命令行设置,也可以通过配置文件设置

默认: --character-set-server=latin1

latin1_swedish_ci is the default collation for latin1

还可以通过重新编译时指定参数实现:use the DEFAULT_CHARSET and DEFAULT_COLLATION

作用范围:如果创建数据库时不指定,那么就使用 Server Level 的设置

查看当前的设定,可以查看系统变量:character_set_server and collation_server

Database Level:

可以在创建数据库时设置:

CREATE DATABASE db_name CHARACTER SET latin1 COLLATE latin1_swedish_ci;

默认值:可以由 character_set_database and collation_database 系统变量决定.

可以通过下面的命令查看当前的设置:

USE db_name;

SELECT @@character_set_database, @@collation_database;

或者

SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = 'db_name';

作用:如果建表时没有指定,那么会作为表的默认值,同时也是作为 LOAD DATA 的默认值

修改数据库level 的 character set 和 collation

ALTER DATABASE db_name CHARACTER SET utf8 COLLATE utf8_unicode_ci;

或者单独修改

ALTER DATABASE my_database DEFAULT COLLATE utf8_unicode_ci;

ALTER DATABASE my_database DEFAULT CHARACTER SET utf8;

Table Level:

可以在建表的语句中进行设置

作用:如果字段没有具体制定,那么会作为字段的默认值 note:该功能是 mysql 的一个扩展,不是标准的SQL

使用下面语句可以同时修改 table 和 table 中字段的设置

ALTER TABLE tbl_name CONVERT TO CHARACTER SET charset_name [COLLATE collation_name];

查看某个数据库中的所有表的一些设置信息的语句

SHOW TABLE STATUS FROM db_name;

Column Level:

N/A

Application Connection Level:

对于 Rails 应用,在 database.yml的数据库连接设置中加上 ?reconnect=true&encoding=utf8&collation=utf8_unicode_ci

查看数据库不同级别的元数据的设置的语句,比如:character set

SELECT DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA S WHERE schema_name = 'db_name' AND DEFAULT_COLLATION_NAME != 'utf8_unicode_ci';

SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES WHERE table_schema = 'db_name' AND table_collation != 'utf8_unicode_ci';

SELECT * FROM information_schema.COLUMNS WHERE table_schema = 'db_name' AND collation_name != 'utf8_unicode_ci';

ConvertDatabaseCharacterSetAndCollationToUtf8 Migration

class ConvertDatabaseCharacterSetAndCollationToUtf8 < ActiveRecord::Migration
  def up
    execute <<~SQL
      ALTER DATABASE #{ActiveRecord::Base.connection.current_database} CHARACTER SET utf8 COLLATE utf8_unicode_ci;
    SQL
  end

  def down
    execute <<~SQL
      ALTER DATABASE #{ActiveRecord::Base.connection.current_database} CHARACTER SET latin1 COLLATE latin1_swedish_ci;
    SQL
  end
end

ConvertTablesCharacterSetAndCollationToUtf8 Migration

class ConvertTablesCharacterSetAndCollationToUtf8 < ActiveRecord::Migration
  def up
    execute("SET foreign_key_checks = 0")

    latin_tables_sql = <<~SQL
      SELECT TABLE_NAME, TABLE_COLLATION
      FROM information_schema.TABLES
      WHERE table_schema = '#{ActiveRecord::Base.connection.current_database}'
      AND table_collation != 'utf8_unicode_ci';
    SQL

    results = ActiveRecord::Base.connection.execute(latin_tables_sql)
    say "Total: #{results.count}"

    results.each do |result|
      alter_table_sql = "ALTER TABLE #{result[0]} CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;"
      execute alter_table_sql
    end

    execute("SET foreign_key_checks = 1")
  end
end

Rails Migration tips

在调试该功能的时候,学到的一些 tips:

rake db:migrate:status 查看当前migration 的状态,包括版本信息等

rake db:migrate VERSION=33333333 migrate指定 version

rake db:rollback STEP=n 通过 STEP 参数指定回滚的范围

User.connection 可以用来检查当前数据库设置和连接的信息

ActiveRecord::Base.connection.current_database 获取当前连接的数据库

ActiveRecord::Migrator.current_version 查看当前的版本

在 Rails Console 或者 Runner 中执行 SQL语句,可以使用 ActiveRecord::Migration.execute("SQL")

参考文章: How to change all columns’ and tables’ collation to ‘utf8_bin’ in MySQL

ActiveRecord Enum实战总结

基本使用方法

案例说明: 给已经存在的 Company 增加一个 size 属性, 属性包括 large, medium, small 三个选项

从 Rails4.1 开始,可以通过 ActiveRecord::Enum 来快速实现这样的功能

class Company < ActiveRecord::Base
  enum size: [:large, :medium, :small]
end

定义了 enum 后,Rails会自动产生下面这些方法

Company.sizes
# => {"large"=>0, "medium"=>1, "small"=>2}

# Scope methods
Company.large
Company.medium
Company.small

# Query methods
company.large?
company.medium?

# Action methods
company.large!
company.small!

Migration

enum 的实现是基于该字段是一个 integer 类型的。所以添加这个新的字段我们需要下面的 migration

class AddSizeToCompanies < ActiveRecord::Migration
  def change
    add_column :companies, :size, :integer, null: false, default: 0
    add_index  :companies, :size
  end
end

赋值操作

通过上面 Company.sizes 方法,我们看到 Rails 默认是从0开始对应的。所以上面的 migration 中默认值是0。当新建一个Company时,默认 company 的 size 是 large

company = Company.new
company.size # => "large"

# 多种赋值操作
company.size = :medium
company.medium? # => true

company.size = 2
company.small? # => true

company.large = 'large'
company.large? # => true

enum 会自动添加一些验证,如果给 size 属性赋错误的值,Rails会抛出异常

company.size = 5
# => ArgumentError: 5 is not a valid billing_category
company.size = :bala
# => ArgumentError: 'bala' is not a valid billing_category

Form下拉菜单的填充

当在前端的Form中填充到下拉列表中,可以这样做

f.input :size, collection: Company.sizes.keys.map {|s| [s.titleize, s]}, prompt: "Select a size"

需要注意的地方:

由于存到数据库的仍然是 number,所以如果有别的应用也使用同样的数据库,那么该应用需要知道对应关系。

Rails4.1 及之后的版本中,由于 enum 会自动产生一些方法,所以要特别注意选项的命名问题,尽量用明确的命名。 另外如果你还需要不同的属性,拥有相同的选项,那么你可以考虑这个 gem activerecord-enum-without-methods

同时在 Rails5 中,就可以使用_prefix_postfix选项来避免相应的问题。·

更好的Migration方法

上面的migration方法对于产品环境已经有数据的情况下,可能会产生问题。

对于Mysql 数据库,新加字段的时候对于已有的记录,mysql 会自动设置 NOT NULL 的已有记录为 default 但是对于 postgreSQL 就会出现下面的错误:

PG::NotNullViolation: ERROR: column “size” contains null values

这时可以先add_column添加字段,不要加 NOT NULL 的限制,然后更新已有数据,然后再通过change_column_null来添加 NOT NULL 限制。

用CSS隐藏元素的五种方法

1. opacity: 0

.hide {
  opacity: 0;
}      

相当于设置元素为透明 屏幕阅读器可读, 可以继续交互

2. visibility: hidden

.hide {
  visibility: hidden;
}      

屏幕阅读器不可读.

重新成visible后就可以重新进行交互了 后续的子节点可以设置为visible而变得可见

3. display: none

.hide {
  display: none;
}

该元素不会被渲染, 所有后续的子节点也都是不会显示的 只可以通过dom api进行操作 屏幕阅读器不可读.

4. move out the viewport

.hide {
   position: absolute;
   top: -9999px;
   left: -9999px;
}

该方法的目的是想保留交互的功能,同时不要影响布局. 屏幕阅读器可读.

5. clip-path

.hide {
  clip-path: polygon(0px 0px,0px 0px,0px 0px,0px 0px);
}

通过裁剪的方法来隐藏.之前大家会用clip属性,现在clip属性已经废弃了,可以使用新的clip-path属性 想深入了解的可以看: 介绍clip-path属性

不过现在IE对clip-path还不支持

总结

用一张表来总结各种方法的区别:

方法 Occupy its position User interaction Screen Reader Transition Animation IE Support
opacity: 0 yes yes yes yes yes
visibility: hidden yes no no no yes
display: none no no no no yes
move out the viewport no yes yes yes yes
clip-path yes no yes yes no

Make Parallel Test Fast

顺利通过Code Test的Checklist

最近在找新的工作,澳洲这里几乎所有招Ruby工程师的公司都会要求你先做一个Code Test。就是给你一个需求,用代码实现。 Code Test通过后,才会进入面对面的面试,这其中还会有Pair Programming,所以一定要自己好好实践。下面是我总结的一些经验:

需求

  • 要很好的理解需求,不要忽略一些细节
  • 不要忘记一些非功能性的需求(比如要提供github地址,或者发送zip包)

面向对象设计

  • 要符合一些基本的OOD的原则, 比如Single Responsibility Principle, Open Close Principle
  • 充分利用语言的特性,设计好类,接口以及抽象和继承的层次
  • 使用常用的设计模式:Factory PatternTemplate PatternObserver PatternCommand PatternNull Ojbect
  • 设想一两个需求变更的场景,验证自己的代码能够很容易的扩展

系统和架构的设计

  • 尽量采用MVC分层架构模式,有Data ModelBusiness ModelControllerDisplay/Runner Class

异常的处理

  • 自定义异常,然后做好异常的处理
  • 提供友好的错误的提示

Test:

  • 尽可能采用TDD,有清楚的commits,体现代码的更新过程
  • 每个测试尽量保证一个assertion
  • 保证足够高的测试覆盖率,100%是可以有的,可以使用simplecov来帮助
  • 要有集成测试,有正常的流程测试和非正常的流程测试
  • 一定要测试好各种edge cases
  • 提供易用的rake tasks,方便运行相关的测试,比如单元测试,集成测试等

良好的接口设计

  • 非常容易安装和执行,减少需要的依赖(比如尽量少的gems,Ruby版本)
  • 有很好的命令行接口和帮助说明
  • 要有一份README,说明如何安装和使用

注意代码质量和规范:

  • 按照产品级别的代码要求自己
  • 采用Rubocop进行质量检查
  • 不要遗留任何的TODO
  • 方法尽可能短小,不要超过5行

最后祝所有找工作的朋友好运!