可以将一份文本文档格式的市场营销报告与上面的 XML 客户数据关联起来。市场营销报告可以包含市场分析结果或者关于数据收集方式的细节。
在第 1 部分中,我们创建了一个 XML 类型的列来存储收集的市场营销数据。为了高效地管理 XML 数据,像传统的 SQL 数据类型一样,DB2 在内部使用 XML 数据模型作为逻辑数据模型,同时 也作为物理存储的基本单元。另外,当指定 XML 数据类型时,会向数据库用户公开这个数据模型。尽管这为管理 XML 数据提供了强大的功能和灵活性,尤其适合以 XML 为中心的开发人员,但是当前对于能够在 XML 列上执行的传统数据库管理活动类型还有一些限制。例如,包含 XML 列的表无法由 DB2 进行物理重组,这是因为 XML 具有层次化的存储结构。这实际上意味着,在包含 XML 列的表中,不能用 ALTER 操作删除列。尽管未来的 DB2 版本中可能会取消这个限制,但是现在要记住这个限制,因为这是 Ruby on Rails 迁移的一个基本操作。
为了继续利用 Ruby on Rails 迁移的灵活性,同时保持 XML 的强大功能,我们要创建一个单独的表来存储 XML 数据。我们将这个表命名为 XML_CONTENTS。它将存储 XML 文档,DOCUMENTS 表仍然存储其他所有相关信息。以后可以在 DOCUMENTS 表中添加或删除列,而不会受到 XML_CONTENTS 表中 XML 数据的影响,也不会影响这些 XML 数据。
关于当前使用 XML 数据类型的限制的更多细节,请参考 IBM DB2 Database for Linux, UNIX, and Windows Information Center 中的 “Restrictions on native XML data store” 部分。
为了执行这个任务,我们生成并运行以下迁移:
a) 运行 ruby script/generate migration create_xml_contents,这将创建 db/migrate/010_create_xml_contents.rb 文件。
b) 编辑 db/migrate/010_create_xml_contents.rb 文件:
清单 4. 编辑 010_create_xml_contents.rb
class CreateXmlContents < ActiveRecord::Migration def self.up drop_table :documents create_table :documents do |t| t.column :name, :string, :null => false t.column :size, :integer, :null => false t.column :data, :binary, :limit => 2.megabytes t.column :content_type, :string, :null => false t.column :created_at, :timestamp t.column :updated_at, :timestamp t.column :platform, :string, :limit =>10 t.column :subject_id, :integer t.column :user_id, :integer end create_table :xml_contents do |t| t.column :name, :string t.column :data, :xml, :null => false t.column :document_id, :integer end end
def self.down drop_table :documents drop_table :xml_contents create_table :documents do |t| t.column :name, :string, :null => false t.column :size, :integer, :null => false t.column :data, :binary, :limit => 2.megabytes t.column :content_type, :string, :null => false t.column :created_at, :timestamp t.column :updated_at, :timestamp t.column :platform, :string, :limit =>10 t.column :subject_id, :integer t.column :user_id, :integer t.column :xmldata, :xml, :null => false end end end |
c) 运行 rake db:migrate 以删除现有的 DOCUMENTS 表、创建只存储 XML 数据的新表 XML_CONTENTS 和新的 DOCUMENTS 表(不包含 XML 列)。
d) 在新的 DOCUMENTS 表和 XML_CONTENTS 表之间重新建立一个关系。
首先,在步骤 d 中生成的 /app/models/xml_content.rb 中添加 belongs_to :document 关联。
第二,在 /app/models/document.rb 文件中添加 has_one :xml_content 关联。
e) 上载功能与以前的文档模型(document.rb,现在变成了父模型)中的实现相似,并添加创建子模型(xml_content)的操作。
NAME 列中存储原来的文件名。
清单 5. 在 DOCUMENTS 记录中指定文件属性
self.name = File.basename(doc_field.original_filename).gsub(/[^\w._-]/, '') self.content_type = doc_field.content_type.chomp self.size = doc_field.size self.created_at = Time.now |
XML_CONTENTS.DATA 列存储指定的文件。
清单 6. 向 XML_CONTENTS.DATA 指定 XML 文件内容
unless self.content_type.include?('text/xml') self.data = doc_field.read else content = XmlContent.new content.name = self.name content.data = doc_field.read self.xml_content = content end |
最终的 /app/models/document.rb 应该像清单 7 这样。
清单 7. document.rb
class Document < ActiveRecord::Base belongs_to :user belongs_to :subject has_one :xml_content
# values displayed | stored PLATFORM_TYPES = [ ['Neutral', 'Any'], ['Windows', 'WinXP'], ['Mac OS X', 'MacOS'], ['Linux', 'Linux']]
def uploaded_doc=(doc_field) self.name = File.basename(doc_field.original_filename).gsub(/[^\w._-]/, '') self.content_type = doc_field.content_type.chomp self.size = doc_field.size self.created_at = Time.now unless self.content_type.include?('text/xml') self.data = doc_field.read else content = XmlContent.new content.name = self.name content.data = doc_field.read self.xml_content = content end end end |
既然定义了 XML_CONTENTS 和 DOCUMENTS 表的模型,就必须修改视图来处理上载和显示功能。更新了上载功能和列出文档的视图,允许显式地选择要显示的模型属性(列)。
/app/views/documents/list.rhtml 实现这种显式的列选择:
清单 8. /app/views/documents/list.rhtml
<table cellpadding="0" cellspacing="0"> <tr> <th>ID</th> <th>Document name</th> <th>Subject</th> <th>Shared by</th> <th>Size</th> <th>Update at</th> <th>Platform</th> <th></th> <th></th> <th></th> </tr> <% @documents.each_with_index do |document,i| %> <% row_class = i%2 ==0 ? "even" : "odd" %> <tr class="<%=row_class%>"> <td><%= document.id %></td> <td><%= document.name %></td> <% if document.subject %> <td><%= link_to "#{document.subject.name}", :controller => 'subjects', :action => 'list' %></td> <% else %> <td></td> <% end %> <% if document.user %> <td><%= link_to "#{document.user.userid}", :controller => 'users', :action => 'list' %></td> <% else %> <td></td> <% end %> <td><%= number_to_human_size( document.size ) %></td> <td><%= document.updated_at.strftime("%d/%m/%Y %I:%M%p") %></td> <td><%= document.platform %></td> <td><%= link_to 'Show', :action => 'show', :id => document %></td> <td><%= link_to 'Edit', :action => 'edit', :id => document %></td> <td><%= link_to 'Remove', { :action => 'destroy', :id => document }, :confirm => 'Are you sure?', :method => :post %></td> </tr> <% end %> </table> |
然后,对控制器做以下更新来处理 XML 数据的文档显示功能。这些代码添加在 /app/controllers/documents_controller.rb 中。
清单 9. documents_controller.rb
def show @document = Document.find(params[:id]) doc_type = @document.content_type unless doc_type.include?('text/xml') doc_content = @document.data else doc_content = @document.xml_content.data end send_data(doc_content, :filename => @document.name, :type => doc_type, :disposition => "inline") end |
还要做以下更新来支持 XML 文档上载,您可能注意到了主题和用户关系处理的文档实现。
清单 10. documents_controller.rb
def upload if params[:document][:uploaded_doc].to_s.empty? flash[:notice] = "Please provide a file for upload" redirect_to(:action => "new" ) else @document = Document.new(params[:document]) @subject = params[:subject_name] && params[:subject_name].empty? ? Subject.new : Subject.find_by_name(params[:subject_name])
Document.transaction do User.find(session[:user_id]).documents << @document @subject.documents << @document @subject.size = @subject.documents.size if @subject.new_record? @subject.name = params[:subject][:name] @subject.tag = params[:subject][:tag] @subject.description = params[:subject][:description] @subject.save end
if @document.save flash[:notice] = "Document #{@document.name} successfully created." if @document.subject.subscriptions SubscriptionMailer.deliver_notify(@document) end redirect_to :action => 'list' else render :action => 'new' end end end |
下面的 图 1 描述了在执行上面的迁移步骤之后,Team Room 中不同模型之间的关联。
图 1. 修改之后不同模型之间新的关联

附件:
您所在的用户组无法下载或查看附件 将 XML 市场营销数据上载到 Team Room
注:只能使用 IMPORT 命令导入格式良好的 XML 文档。
注:可以使用以下命令删除 XML 模式:
drop xsrobject teamroom.marketinfo