2017-04-05 30 views
2

發現,軌道5具有本地uuid integration,想嘗試一下,但我得到這個錯誤:Rails的5個MySQL UUID

== 20170330041631 EnableUuidExtension: migrating ============================== 
-- enable_extension("uuid-ossp") 
    -> 0.0000s 
== 20170330041631 EnableUuidExtension: migrated (0.0001s) ===================== 

== 20170331035925 CreateUsers: migrating ====================================== 
-- create_table(:users, {:id=>:uuid}) 
rake aborted! 
StandardError: An error has occurred, all later migrations canceled: 

Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'uuid PRIMARY KEY, `name` varchar(255), `username` varchar(255), `password_digest' at line 1: CREATE TABLE `users` (`id` uuid PRIMARY KEY, `name` varchar(255), `username` varchar(255), `password_digest` varchar(255), `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL) ENGINE=InnoDB 
/home/zetacu/projects/rails-5-test/db/migrate/20170331035925_create_users.rb:3:in `change' 
/home/zetacu/.rbenv/versions/2.4.0/bin/bundle:22:in `load' 
/home/zetacu/.rbenv/versions/2.4.0/bin/bundle:22:in `<main>' 
ActiveRecord::StatementInvalid: Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'uuid PRIMARY KEY, `name` varchar(255), `username` varchar(255), `password_digest' at line 1: CREATE TABLE `users` (`id` uuid PRIMARY KEY, `name` varchar(255), `username` varchar(255), `password_digest` varchar(255), `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL) ENGINE=InnoDB 
/home/zetacu/projects/rails-5-test/db/migrate/20170331035925_create_users.rb:3:in `change' 
/home/zetacu/.rbenv/versions/2.4.0/bin/bundle:22:in `load' 
/home/zetacu/.rbenv/versions/2.4.0/bin/bundle:22:in `<main>' 
Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'uuid PRIMARY KEY, `name` varchar(255), `username` varchar(255), `password_digest' at line 1 
/home/zetacu/projects/rails-5-test/db/migrate/20170331035925_create_users.rb:3:in `change' 
/home/zetacu/.rbenv/versions/2.4.0/bin/bundle:22:in `load' 
/home/zetacu/.rbenv/versions/2.4.0/bin/bundle:22:in `<main>' 
Tasks: TOP => db:migrate 

這是acoording到後期的遷移:

class EnableUuidExtension < ActiveRecord::Migration[5.0] 
    def change 
    enable_extension 'uuid-ossp' 
    end 
end 


class CreateUsers < ActiveRecord::Migration[5.0] 
    def change 
    create_table :users, id: :uuid do |t| 
     t.string :name 
     t.string :username 
     t.string :password_digest 

     t.timestamps 
    end 
    end 
end 

application.rb

config.generators do |g| 
    g.orm :active_record, primary_key_type: :uuid 
end 

什麼我失蹤?難道Rails的-5支持MySQL或必須像Rails-4一樣手動?

create_table :users, id: false do |t| 
    t.string :uuid, limit: 36, primary: true, null: false 
    ... 

寶石版本:

rails (~> 5.0.2) 
mysql2 (>= 0.3.18, < 0.5) 
+0

FWIW,uuid-ossp是postgresql擴展。因此,您的上述代碼只適用於使用postgresql後端。對於mysql,您需要一個字符串列,就像您在自己的答案中指出的一樣。 – philkry

+0

是的這就是爲什麼我問是否有''Rails 5-MySQL'有一個構建的方式來做到這一點,但我找不到一個解決方案。 – zetacu

回答

3

Din't找到關於MySQL /導軌-5 UUID集成任何文件,我結束了做這樣的:

## Migration 
class CreateUsers < ActiveRecord::Migration[5.0] 
    def change 
    create_table :users, id: false do |t| 
     t.string :id, limit: 36, primary_key: true, null: false 
     t.string :name 
     t.string :username 
     t.string :password_digest 
     t.timestamps 
    end 
    end 
end 


#user model 
class User < ApplicationRecord 
    before_create :set_uuid 

    def set_uuid 
    self.id = SecureRandom.uuid 
    end 
end 

它的工作原理和所有這些都在跳躍,以便使用「魔術欄」解決方案來自動處理uuid和關係。

3

我的回答是@zetacu回答的更新。它完美地工作,爲MySQL 在軌5.0.2

## Model 
    class Tip < ActiveRecord::Base 
    before_validation :set_uuid, on: :create 
    validates :id, presence: true 

    def set_uuid 
     self.id = SecureRandom.uuid 
    end 

    end 

    ## Migration 
    class CreateTip < ActiveRecord::Migration[5.0] 
     def change 
     create_table :tips, id: false, force: true do |t| 
      t.string :id, :limit => 36, :primary_key => true 
      t.string :title, :null => false, :default => "" 
      t.text :description 

      t.timestamps 
     end 
     end 
    end 
1

我的回答是@桑托斯的回答的更新。我在這裏結合所描述的所有最佳實踐:

我現在用的是simple_uuid寶石,因爲它可以產生 「V1」 的UUID。 Ruby的內置SecureRandom.uuid生成v4。我們需要v1,因爲這是合併時間戳作爲UUID的一部分。閱讀以上鍊接以獲得更深入的瞭解。 MySQL的UUID()函數生成v1 UUID。

應用程序/模型/顧慮/ binary_uuid_pk.rb

module BinaryUuidPk 
    extend ActiveSupport::Concern 

    included do 
    before_validation :set_id, on: :create 
    validates :id, presence: true 
    end 

    def set_id 
    uuid_object = SimpleUUID::UUID.new 
    uuid_string = ApplicationRecord.rearrange_time_of_uuid(uuid_object.to_guid) 
    uuid_binary = ApplicationRecord.id_binary(uuid_string) 
    self.id = uuid_binary 
    end 

    def uuid 
    self[:uuid] || (id.present? ? ApplicationRecord.format_uuid_with_hyphens(id.unpack('H*').first).upcase : nil) 
    end 


    module ClassMethods 
    def format_uuid_with_hyphens(uuid_string_without_hyphens) 
     uuid_string_without_hyphens.rjust(32, '0').gsub(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/, '\1-\2-\3-\4-\5') 
    end 

    def rearrange_time_of_uuid(uuid_string) 
     uuid_string_without_hyphens = "#{uuid_string[14, 4]}#{uuid_string[9, 4]}#{uuid_string[0, 8]}#{uuid_string[19, 4]}#{uuid_string[24..-1]}" 
     ApplicationRecord.format_uuid_with_hyphens(uuid_string_without_hyphens) 
    end 

    def id_binary(uuid_string) 
     # Alternate way: Array(uuid_string.downcase.gsub(/[^a-f0-9]/, '')).pack('H*') 
     SimpleUUID::UUID.new(uuid_string).to_s 
    end 

    def id_str(uuid_binary_string) 
     SimpleUUID::UUID.new(uuid_binary_string).to_guid 
    end 

    # Support both binary and text as IDs 
    def find(*ids) 
     ids = [ids] unless ids.is_a?(Array) 
     ids = ids.flatten 

     array_binary_ids = ids.each_with_object([]) do |id, array| 
     case id 
      when Integer 
      raise TypeError, 'Expecting only 36 character UUID strings as primary keys' 
      else 
      array << SimpleUUID::UUID.new(id).to_s 

     end 
     end 

     super(array_binary_ids) 
    end 
    end 
end 

應用程序/模型/ application_record.rb

## ApplicationRecord (new parent of all models in Rails 5) 
class ApplicationRecord < ActiveRecord::Base 
    self.abstract_class = true 

    include BinaryUuidPk 
end 

現在,所有車型將支持優化UUID主鍵。

樣品遷移

class CreateUserProfiles < ActiveRecord::Migration[5.0] 
    def change 
    create_table :user_profiles, id: false do |t| 
     t.binary :id, limit: 16, primary_key: true, null: false 
     t.virtual :uuid, type: :string, limit: 36, as: "insert(insert(insert(insert(hex(id),9,0,'-'), 14,0,'-'), 19,0,'-'), 24,0,'-')" 
     t.index :uuid, unique: true 

     t.string :name, null: false 
     t.string :gender, null: false 
     t.date :date_of_birth 
     t.timestamps null: false 
    end 

    execute <<-SQL 
     CREATE TRIGGER before_insert_user_profiles 
     BEFORE INSERT ON user_profiles 
     FOR EACH ROW 
     BEGIN 
      IF new.id IS NULL THEN 
      SET new.id = UUID_TO_BIN(uuid(), 1); 
      END IF; 
     END 
    SQL 
    end 
end 

添加UUID_TO_BIN()功能,MySQL數據庫

DELIMITER // 
CREATE FUNCTION UUID_TO_BIN(string_uuid BINARY(36), swap_flag INT) 
     RETURNS BINARY(16) 
     LANGUAGE SQL DETERMINISTIC CONTAINS SQL SQL SECURITY INVOKER 
     RETURN 
     UNHEX(CONCAT(
      SUBSTR(string_uuid, 15, 4), 
      SUBSTR(string_uuid, 10, 4), 
      SUBSTR(string_uuid, 1, 8), 
      SUBSTR(string_uuid, 20, 4), 
      SUBSTR(string_uuid, 25))); 
// 
DELIMITER ; 

上述功能是內置在上面的MySQL 8.0。在撰寫本文時,8.0還不是GA。所以,我現在添加了這個功能。但是我保持函數簽名與MySQL 8.0中的一樣。所以,當我們移動到8.0時,我們所有的遷移和觸發器仍然可以工作。