Multiple databases - wiele baz danych w RoR
Wysłane przez Krzysztof Kempiński dnia 12.11.2007
Ruby on Rails daje prosty sposób na łączenie się z wieloma bazami danych. Najczęściej można to wykorzystać, gdy chcemy aby różne modele zapisywały/odczytywały z różnych baz danych.
Aby nie zmieniać połączenia z bazą przy każdorazowej operacji na modelu można wykorzystać gotowe mechanizmy, które są dostępne w ActiveRecord.
W tym artykule chciałbym przedstawić i omówić kilka sposobów na łączenie się z wieloma bazami danych z naszej aplikacji Ruby on Rails.
1. Wstęp
Powodów, dla których chcielibyśmy się łączyć z wieloma bazami danych może być wiele. Możemy chcieć wykorzystywać dane z wielu baz/serwerów, mieć możliwość replikacji bazy przy zmianie danych, bądź też przełączania się na inną bazę w różnych porach dnia, dla różnych użytkowników. W zależności, jakie zastosowanie nas interesuje, możemy skorzystać z różnych sposobów podłączenia wielu baz (multiple databases).2. Przełączanie bazy w kontrolerze
Przełączanie bazy w kontrolerze stosujemy gdy chcemy odgórnie wybierać bazę dla całej aplikacji i uzależniać ją od ustalonych warunków. Przykład takiego przełączania podaję poniżej. W tym przypadku zmieniamy bazę po godzinie 13tej.class ApplicationController < ActionController::Base before_filter :change_db def change_db db_name = (Time.now.hour > 12) ? "db_1" : "db_2" # connect to database ActiveRecord::Base.establish_connection( :adapter => "mysql", :host => "localhost", :username => "db_user", :password => "db_pass", :database => db_name ) end end
Podany warunek przełączania bazy jest może mało "życiowy". Częściej można pewnie spotkać sytuację gdy takie przełączanie ma miejsce dla różnych subdomen itd.
Istnieje również możliwość ustalenia/zmiany połączenia dla poszczególnych modeli. Wystarczy użyć poniższy kod:
class ApplicationController < ActionController::Base require 'yaml' before_filter :set_extra_db_connection def set_extra_db_connection extra_coord = YAML.load(File.open(File.join(RAILS_ROOT,"config/database.yml"),"r"))["extradb"] Cat.establish_connection(extra_coord) Dog.establish_connection(extra_coord) end end
zakładając, że w pliku ./config/database.yml mamy zdefiniowane połączenie o nazwie "extradb":
extradb: adapter: mysql host: localhost username: root password: blabla database: extradb
3. Abstrakcyjne modele dla połączeń z różnymi danymi
W sytuacji gdy chcemy korzystać/zapisywać do różnych baz danych, możemy wykorzystać fakt, iż różne modele (mapujące tabele w bazie) mogą być podłączone do kilku baz danych. W aplikacji zatem operujemy na modelu a ten wie do której bazy skierować zapytanie.Żeby wykorzystać takie rozwiązanie należy w pliku ./config/database.yml zdefiniować dostęp do innej bazy, np. tak:
security_db: adapter: postgresql database: security host: localhost username: uuuu password: xxxxx
Następnym krokiem jest zdefiniowanie abstrakcyjnego modelu, z którego to będą dziedziczyć wszystkie modele korzystające z innej bazy. Tworzymy zatem plik ./app/models/security_base.rb :
class SecurityBase < ActiveRecord::Base self.abstract_class = true establish_connection "security_db" end
wystarczy wtedy, aby model dziedziczył z tej klasy, np. tak:
class SecurityUser < LegacyBase ... end
a wszelkie operacje na takim modelu będą się przekładały na zmianie w bazie "security_db".
4. Magic Multi-Connections
Jest to gem o którym poczytać i którego pobrać można pod adresem: http://magicmodels.rubyforge.org/magic_multi_connections/Gem instalujemy poleceniem:
sudo gem install magic_multi_connections
Następnie na końcu pliku environment.rb dodajemy
require 'magic_multi_connections'
a w kontrolerze na górze:
require 'rubygems' require 'magic_multi_connections'
Oprócz zdefiniowania nowej bazy w database.yml:
private: adapter: postgresql database: private host: localhost username: uuuu password: xxxxx
konieczne jest też utworzenie pliku :
cp config/environments/development.rb config/environments/private.rb
Trzeba pamiętać, że migrację w takim przypadku wywołujemy poprzez:
rake db:migrate RAILS_ENV=private
W pliku definiujemy nowe połączenie:
module Private establish_connection :private end
a wykorzystujemy je np. tak:
class PeopleController < ApplicationController before_filter :check_private def index @people = @mod::Person.find(:all) end private def check_private @mod = params[:private] ? Private : Object end end
zatem w zależności od parametru z URL system przełącza się na różne bazy. Jednakowoż w każdym z tych przypadków dostęp do odpowiedniego modelu następuje zawsze poprzez @mod::Person.
5. Active Delegate
ActiveDelegate to plugin, który możemy zainstalować komendą:script/plugin install http://svn.planetargon.org/rails/plugins/active_delegate
Następnie tradycyjnie w database.yml definiujemy połączenie z bazą:
master_database: adapter: postgresql database: master_database host: localhost username: uuuu password: xxxxx
Następnie tworzymy klasę obsługującą polecenia z nową bazą:
# app/models/master_database.rb class MasterDatabase < ActiveRecord::Base handles_connection_for :master_database # <-- this matches the key from our database.yml end
Pozostaje już stworzyć model, który będzie korzystał z nowego bazy oddelegowując połączenie z nią do powyżej stworzonego pliku:
# app/models/animal.rb class Animal < ActiveRecord::Base delegates_connection_to :master_database, :on => [:create, :save, :destroy] end
