JWorld@TW the best professional Java site in Taiwan
      註冊 | 登入 | 全文檢索 | 排行榜  

» JWorld@TW » Languages on JVM » JRuby  

按列印兼容模式列印這個話題 列印話題    把這個話題寄給朋友 寄給朋友    訂閱主題
reply to topicthreaded modego to previous topicgo to next topic
己加入精華區
by koji at 2007-08-24 16:47
本主題所含的標籤
無標籤
作者 如何連結到舊系統的資料庫 [精華]
weijenlu





發文: 30
積分: 6
於 2007-08-24 00:41 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
如何連結到舊系統的資料庫

這是我新案子所遇到的問題,我的客戶原本就有一個舊的系統,那他希望作一些功能的新增,我新的系統將會在新的伺服器中執行,而舊系統就照舊,唯一會要共用的,就是舊系統的會員資料。系統的規劃會是從舊的系統登入,當使用這要用新系統的功能時,再連結到新系統來。

而舊系統的會員還有層級的分別,而層級間的關係其實是act_as_tree的方式,可是很可惜,他們舊系統不是用RoR寫的,他們把不同的層級寫在不同的table中,一共有3層,我把sql放在下面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
CREATE TABLE  `lagacy`.`tb_manage` (
 
  `manage_id` int(11) NOT NULL auto_increment,
 
  `login_name` varchar(20) NOT NULL,
 
  `password` varchar(20) NOT NULL,
 
  `alias_name` varchar(20) NOT NULL,
 
  PRIMARY KEY  (`manage_id`)
 
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
 
CREATE TABLE  `lagacy`.`tb_b_stocker` (
 
  `b_stocker_id` int(11) NOT NULL auto_increment,
 
  `manage_id` int(11) NOT NULL,
 
  `login_name` varchar(20) NOT NULL,
 
  `password` varchar(20) NOT NULL,
 
 `alias_name` varchar(20) NOT NULL,
 
  PRIMARY KEY  (`b_stocker_id`)
 
) ENGINE=MyISAM DEFAULT CHARSET=latin1
 
CREATE TABLE  `lagacy`.`tb_stocker` (
 
  `stocker_id` int(11) NOT NULL auto_increment,
 
  `b_stocker_id` int(11) NOT NULL,
 
  `manage_id` int(11) NOT NULL,
 
  `login_name` varchar(20) NOT NULL,
 
  `password` varchar(20) NOT NULL,
 
  `alias_name` varchar(20) NOT NULL,
 
  PRIMARY KEY  (`stocker_id`)
 
) ENGINE=MyISAM DEFAULT CHARSET=latin1


那基本上就是越上面的越高層,越下面的就越低層。

連到舊系統的資料庫

第一件事情當然是連接到舊系統的資料庫啦,各位在使用RoR時,一定有去修改過config/database.yml吧,在這裡面預設會有3個連結資料庫的方式:development、test、production,那我們在啟動系統的時候,系統會讀入config/environment.rb這個檔案,那這個檔案會去呼叫Rails的Initializer(定義在/usr/lib/ruby/gems/1.8/gems/rails1.2.3/lib/initializer.rb,適合非常有空的人看一下),那這個Initializer會作很多事,其中一件事就是設定資料庫的連結。
Initializer會呼叫ActiveRecord::Base.establish_connection(),如果你沒有給參數的話,他會檢查RAILS_ENV來決定要使用哪一個參數來連結資料庫,而這個RAILS_ENV就是你啟動系統的模式(development、test、production mode)。
如果你給ActiveRecord::Base.establish_connection()參數的話,那他會去看在config/database.yml中有沒有相對應的資料庫連結設定(這其中還有關yaml file的讀入,並轉成object的問題,等我很有空的時候再告訴大家)。如果有的話就會讀入,如果沒有就會產生下列的Exception:
1
2
3
ActiveRecord::AdapterNotSpecified: none database is not configured
  from /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/abstract/connection_specification.rb:204:in `establish_connection'
  from (irb):9

回頭看我的case,首先呢,我在config/database.yml中增加了一個lagacy的資料庫連結參數:
1
2
3
4
5
6
7
lagacy:
  adapter: mysql
  socket: /var/lib/mysql/mysql.sock
  database: lagacy
  username: myname
  password: mypassword
  host: db.lagacy.com.tw

有好幾個table都是連結到舊系統中,所以我先有一個model,我定名為User(app/model/user.rb),我定義它是abstract class,且連結到舊系統,程式如下列:
1
2
3
4
5
6
class User < ActiveRecord::Base
  self.abstract_class=true
  establish_connection :lagacy
  
  self.pluralize_table_names=false
end

其中self.pluralize_table_names=false這一行,是要取消table的名稱是複數的這個規定。

然後我再新增TbManage/TbBStocker/TbStocker三個model。最重要的是,原本是繼承ActiveRecord::Base,改成User,所以TbManage(app/model/tb_manage.rb)的程式就會像這樣:
1
2
class TbManage < User
end

另外兩個都是一樣的。

現在我們可以來試試看了喔,我們開啟script/console
1
2
3
4
5
Loading development environment.
>> TbManage.new
TbManage.new
=> #<TbManage:0xb7a7f564 @attributes={"login_name"=>"", "alias_name"=>"", "password"=>""}, @new_record=true>
>> 

ㄟ,連上了耶。那我們來看看能不能新增。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Loading development environment.
>> a=TbManage.new
a=TbManage.new
=> #<TbManage:0xb7b0fc04 @attributes={"manage_id"=>nil, "login_name"=>"","alias_name"=>"", "password"=>""}, @new_record=true>
>> a.login_name="aa001"
a.login_name="aa001"
=> "aa001"
>> a.password="aa001"
a.password="aa001"
=> "aa001"
>> a.alias_name="aa001"
a.alias_name="aa001"
=> "aa001"
>> a.save
a.save
=> true
>> 

也是成功的,那我們來用我們最常用的find吧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
>> a=TbManage.find(1)
a=TbManage.find(1)
ActiveRecord::StatementInvalid: Mysql::Error: #42S22Unknown column 'tb_manage.id' in 'where clause': SELECT * FROM tb_manage WHERE (tb_manage.`id` = 1) 
  from /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/abstract_adapter.rb:128:in `log'
  from /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/mysql_adapter.rb:243:in `execute'
  from /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/mysql_adapter.rb:399:in `select'
  from /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/abstract/database_statements.rb:7:in `select_all'
  from /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/base.rb:427:in `find_by_sql'
  from /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/base.rb:997:in `find_every'
  from /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/base.rb:1028:in `find_one'
  from /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/base.rb:1014:in `find_from_ids'
  from /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/base.rb:419:in `find'
  from (irb):9
>> 

原來在RoR的世界中,primary key都是id,可是在舊系統中並不是這樣,還好ActiveRecord已經有想到這點了,它提供了set_primary_key這個函數讓我們可以指定我們的primary key,所以我的TbManage現在看起來是這樣:
1
2
3
class TbManage < User
  set_primary_key :manage_id
end

我們再試一次find的功能:
1
2
3
4
5
Loading development environment.
>> a=TbManage.find(1)
a=TbManage.find(1)
=> #<TbManage:0xb75f3a7c @attributes={"manage_id"=>"1", "login_name"=>"aa001", “alias_name"=>"", "password"=>"aa001"}>
>> 

這次就ok了。那剩下的兩個table也是一樣,我就不再贅述了。
就這樣,我們針對單一個表格的CRUD就完成了,是不是很簡單。但是可惜這個世界不是只針對一個表格作存取而已,表格跟表格之間還會有關係,接下來,我們就來看看怎麼讓表格發生關係。

讓表格發生關係

首先解釋一下這個案子中會員系統的表格間的關係。為了方便起見,我們就用ActiveRecord的DSL來解釋:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class TbManage < User
  set_primary_key :manage_id
  
  #relationship
  has_many :tb_b_stockers
  has_many :tb_stockers
end
 
class TbBStocker < User
  set_primary_key :b_stocker_id
  
  #relationship
  belongs_to :tb_manage
  has_many :tb_stockers
end
 
class TbStocker < User
  set_primary_key :stocker_id
  
  #relationship
  belongs_to :tb_manage
  belongs_to :tb_b_stockers
end

如果在正常的RoR中,這樣就可以了,可是對於我的案子卻是不行的,因為就系統中的foreign key的定義方式不是依據RoR的習俗。要解決這個問題也很簡單,只需要在has_many與belogns_to中增加參數,定義對方的class_name與foreign_key就可以了,所以我的TbManage與TbBStocker看起來就像這樣:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class TbManage < User
  set_primary_key :manage_id
  
  #relationship
  has_many :tb_b_stockers,
    :class_name => "TbBStocker",
    :foreign_key => "manage_id"
  has_many :tb_stockers
end
 
class TbBStocker < User
  set_primary_key :b_stocker_id
  
  #relationship
  belongs_to :tb_manage,
    :class_name => "TbManage",
    :foreign_key => "manage_id"
  has_many :tb_stockers
end

讓我們來試試看:
首先我們建立一個TbBStocker的record,並讓這個record是屬於TbManage中第一個record的屬下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>> b=TbBStocker.new
b=TbBStocker.new
=> #<TbBStocker:0xb7694918 @attributes={"manage_id"=>nil, "login_name"=>"","password"=>""}, @new_record=true>
>> b.login_name="ab001"
b.login_name="ab001"
=> "ab001"
>> b.password="ab001"
b.password="ab001"
=> "ab001"
>> b.tb_manage=TbManage.find(1)
b.tb_manage=TbManage.find(1)
=> #<TbManage:0xb768a01c @attributes={"manage_id"=>"1", "login_name"=>"aa001", "alias_name"=>"", "password"=>"0000"}>
>> b.save
b.save
=> true
>> 

然後我們來看看是不是可以利用b.tb_manage來叫出上司:
1
2
3
4
>> b.tb_manage
b.tb_manage
=> #<TbManage:0xb768a01c @attributes={"manage_id"=>"1", "login_name"=>"aa001", "alias_name"=>"", "password"=>"0000"}>
>> 

沒有問題,那反過來,我們定義a=TbManage.find(1),那能否利用tb_b_stockers來看看他所有的下屬呢:
1
2
3
>> a.tb_b_stockers
a.tb_b_stockers
=> [#<TbBStocker:0xb7695cf0 @attributes={"b_stocker_id"=>"1", "manage_id"=>"1", "login_name"=>"ab001", “password"=>"ab001"}>]

就這樣,大功告成了。

基本上,我也是希望直接作一個全新的案子,不用跟舊系統連接,那我們就可以完全依照RoR的慣例(convention)來做事,多方便呀,但是很可惜,世界不是這麼運作的,為了能夠順利的把客戶的錢騙到自己的口袋裡面,一些些的不方便是難免的,但是感謝RoR有提供這麼多的方式,讓我們在跟舊系統連接時,能把不方便減到最小。

Reference:
  <<Rails Recipes>>,Chad Fowler著,Copyright 2006 The Pragmatic Programmers LLC.,ISBN 0-9776166-0-6


reply to postreply to post
» JWorld@TW »  Languages on JVM » JRuby

reply to topicthreaded modego to previous topicgo to next topic
  已讀文章
  新的文章
  被刪除的文章
Jump to the top of page

JWorld@TW 本站商標資訊

Powered by Powerful JuteForum® Version Jute 1.5.8