Mitt svar är en uppdatering av @santoshs svar. Jag införlivar alla de bästa metoderna som beskrivs här:
- https://www.percona .com/blog/2014/12/19/store-uuid-optimized-way/
- http://mysqlserverteam.com/storing-uuid-values -in-mysql-tables/
Jag använder simple_uuid
pärla eftersom det kan generera "v1" UUID. Rubys inbyggda SecureRandom.uuid
genererar v4. Vi behöver v1, eftersom det är det som innehåller tidsstämpel som en del av UUID. Läs länkarna ovan för att få en djupare förståelse. MySQL:s UUID()
funktionen genererar v1 UUID.
app/models/concerns/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
app/models/application_record.rb
## ApplicationRecord (new parent of all models in Rails 5)
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
include BinaryUuidPk
end
Nu kommer alla modeller att stödja optimerade UUID-primärnycklar.
Exempel på migrering
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
Lägg till UUID_TO_BIN()
funktion till MySQL DB :
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 ;
Ovanstående funktion är inbyggd i MySQL 8.0 och senare. I skrivande stund är 8.0 ännu inte GA. Så nu lägger jag till funktionen. Men jag har behållit funktionssignaturen densamma som den som finns i MySQL 8.0. Så när vi flyttar till 8.0, skulle alla våra migreringar och triggers fortfarande fungera.