Mysql
 sql >> Base de données >  >> RDS >> Mysql

Rails 5 Mysql UUID

Ma réponse est une mise à jour de la réponse de @santosh. J'intègre toutes les bonnes pratiques décrites ici :

J'utilise le simple_uuid gem car il peut générer des UUID "v1". SecureRandom.uuid intégré à Ruby génère v4. Nous avons besoin de v1, car c'est ce qui intègre l'horodatage dans le cadre de l'UUID. Lisez les liens ci-dessus pour mieux comprendre. UUID() de MySQL la fonction génère des UUID v1.

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

Désormais, tous les modèles prendront en charge les clés primaires UUID optimisées.

Exemple de migration

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

Ajouter UUID_TO_BIN() fonction à la base de données 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 ;

La fonction ci-dessus est intégrée à MySQL 8.0 et supérieur. Au moment de la rédaction, 8.0 n'est pas encore GA. Donc, j'ajoute la fonction pour l'instant. Mais j'ai gardé la même signature de fonction que ce qui existe dans MySQL 8.0 . Ainsi, lorsque nous passerons à la version 8.0, toutes nos migrations et tous nos déclencheurs fonctionneront toujours.