ActiveRecord(Ruby).doc_第1页
ActiveRecord(Ruby).doc_第2页
ActiveRecord(Ruby).doc_第3页
ActiveRecord(Ruby).doc_第4页
ActiveRecord(Ruby).doc_第5页
已阅读5页,还剩20页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

ActiveRecord是Rails提供的一个对象关系映射(ORM)层,从这篇开始,我们来了解Active Record的一些基础内容,连接数据库,映射表,访问数据等。Active Record使用基本的ORM模式:表映射成类,行映射成为对象,列映射成对象的属性。与很多大量使用配置的ORM库不同,Active Record最小化了配置。想象一下,有一个使用Active Record的程序把Mysql数据库中的orders表转换到类,通过制定的ID查找到order,设定order的名称,然后保存回数据库:require rubygemsrequire active_recordActiveRecord:Base.establish_connection(:adapter = mysql,:host = localhost, :database = railsdb)class Order ActiveRecord:Baseendorder = Order.find(123) = Dave Thomasorder.save在上面的例子里不需要任何配置,Active Record为我们做了这些事情,下面我们来看看ActiveRecord是怎样工作的。表和类当你创建了一个ActiveRecord:Base类的子类,Active Record假定表名是复数的,而类名是单数的,当类名包括多个单词时,表名被假定为单词间带有下划线,复数形式不规则,例如:类名表名类名 表名Order ordersLineItem line_itemsTaxAgency tax_agenciesPerson peopleDiagnosis diagnoses Quantity quantitiesBatch batchesDatumdata默认的,Active Record的表名是复数的,类名是单数的,如果你不太习惯,可以通过设置一个全局标记来禁用它,在config目录的environment.rb文件中设置:ActiveRecord:Base.pluralize_table_names = false单复数规则可以对付大部分情况,对于一些特殊情况,Active Record允许我们覆盖默认的生成的表名,使用set_table_name命令,例如:class Sheep ActiveRecord:Baseset_table_name sheep # Not sheepsendclass Order ActiveRecord:Baseset_table_name ord_rev99_x # Wrap a legacy table.endActiveRecord中的一个对象相当于数据库中表的一行,对象的属性对应于表的列,也许你会注意到我们的Order类没有提及关于orders表的任何东西,这是因为ActiveRecord在运行时来确定这些对应关系,Active Record将数据库中的模式反应到类中。我们的orders表可能使用下面的sql来创建:create table orders (id int not null auto_increment,name varchar(100) not null,email varchar(255) not null,address text not null,pay_type char(10) not null,shipped_at datetime null,primary key (id);我们可以创建一个类来转换这个表:require rubygemsrequire_gem activerecord# Connection code omitted.class Order ActiveRecord:Baseend当我们创建了Order类,就可以访问它的属性来获取信息,下面的代码使用columns()方法,来返回一个Columns对象的数组,在这里,我们显示了orders表中的每个列,并且显示指定字段的详细信息。require pppp Order.columns.map |col| pp Order.columns_hashshipped_at运行代码,会得到下面的输出:id, name, email, address, pay_type, shipped_at#注意,Active Record决定了每个列的类型,在这个例子里,将shipped_at列作为datetime类型,该列的值被保存在一个ruby的Time类型的对象中,我们可以写些代码来验证该列的类型及其内容:order = Order.neworder.shipped_at = 2005-03-04 12:34pp order.shipped_at.classpp order.shipped_at输出为:TimeFri Mar 04 12:34:00 CST 2005下面的列表展示了sql和ruby间的数据类型对应关系:SQLTypeRuby ClassSQLTypeRuby Classint, integer Fixnum float, double Floatdecimal, numeric Floatchar, varchar, string Stringclob, blob, text String datetime, timeTimeinterval, date Date Boolean 后面详细介绍有一个潜在的可能是关于decimal的,在数据库里,使用decimal的列来存储number和fix number型,Active Record将decimal映射成Float类的对象,尽管这样可以应用于大多数应用,浮点数是不精确的,在对这一类型的属性进行一系列操作的时候,可能会发生舍入的错误,你也许可以使用integer类型来作为替代方案,例如,存储货币型的时候可以将元,角,分,分别存入不同的字段。做为一种选择,你可以使用聚合(aggregations),使用多个分开的字段来构建货币类型。如果在一个model对象中有一个名为balance的属性,你可以通过索引操作符来获取该属性的值,你可以使用一个字符串或者标记,在这里我们使用标记,例如: account:balance #= 获取值 account:balance = 0.0 #= 设置值 但是这种常见的代码是不提倡的,更好的是这样,利用ruby的访问方法: account.balance #= 获取值 account.balance = 0.0 #=设置值 在这里,我们使用了两种方法来获取属性的值,Active Record会进行适当的类型转换,比如,如果数据库中的列是时间戳(TimeStamp),那么,我们将会得到一个Time对象,如果你想得到属性的原始的值,添加_before_type_cast到访问方法的最后,例如: account.balance_before_type_cast #= 123.4, a string account.release_date_before_type_cast #= 20050301 最后,也可以使用Model自己的私有方法read_attribute和write_attribute,这两个方法使用属性名作为参数。一些数据库支持boolean类型,而另一些则不支持,这使得Active Record要抽象boolean类型变得困难。例如,如果数据库不支持boolean类型,有的开发者使用char(1)来替代,而内容使用“t”和“f”来表示true和false,而另外一些开发者使用integer类型,0是false,1是true。即使数据库支持boolean类型,在内部也许还是使用0和1来存储。在Ruby里,在条件判断中,数字0和字符f都被认为是true值,这就意味着如果你直接使用属性的值,你的代码会被认为该列的值是true,而不是你认为的false,例如:# 不要这样使用user = Users.find_by_name(Dave)if user.superusergrant_privilegesend当在查询条件中使用属性时,你必须在列名后添加一个问号:# 这样是正确的user = Users.find_by_name(Dave)if user.superuser?grant_privilegesend当使用访问操作符来获取属性的值时,当值为数字0,或者字符“0”,“f”,“false”,或“”(空字符串),或nil,或一个常量false时,都被认为是false,否则,就会被认为是true。如果你在一个遗留系统上或者非英语系统上开发,上面对true的定义也许会无法工作,在这种情况下,你可以override内建的谓词方法的定义,例如,荷兰语情况下,字段也许包含J或者N,这种情况下,你可以像下面这样:class User ActiveRecord:Basedef superuser?self.superuser = Jend# . . .end一些数据库支持boolean类型,而另一些则不支持,这使得Active Record要抽象boolean类型变得困难。例如,如果数据库不支持boolean类型,有的开发者使用char(1)来替代,而内容使用“t”和“f”来表示true和false,而另外一些开发者使用integer类型,0是false,1是true。即使数据库支持boolean类型,在内部也许还是使用0和1来存储。在Ruby里,在条件判断中,数字0和字符f都被认为是true值,这就意味着如果你直接使用属性的值,你的代码会被认为该列的值是true,而不是你认为的false,例如:# 不要这样使用user = Users.find_by_name(Dave)if user.superusergrant_privilegesend当在查询条件中使用属性时,你必须在列名后添加一个问号:# 这样是正确的user = Users.find_by_name(Dave)if user.superuser?grant_privilegesend当使用访问操作符来获取属性的值时,当值为数字0,或者字符“0”,“f”,“false”,或“”(空字符串),或nil,或一个常量false时,都被认为是false,否则,就会被认为是true。如果你在一个遗留系统上或者非英语系统上开发,上面对true的定义也许会无法工作,在这种情况下,你可以override内建的谓词方法的定义,例如,荷兰语情况下,字段也许包含J或者N,这种情况下,你可以像下面这样:class User ActiveRecord:Basedef superuser?self.superuser = Jend# . . .end也许你已经注意到了,在我们前面的代码中,数据库定义里都使用了一个integer型的字段id作为主键,这是Active Record的一个约定。或许你要问,为什么不用订单编号或者某个有意义的列来作为主键呢?使用id作为主键有一个很重要的原因,就是如果使用具有内在格式的主键的话,随着时间推移,有可能其中的规则也会变化。例如,使用ISBN号码来给book表做主键,毕竟ISBN号码是唯一的,但是,有可能当一本书写完后,美国的出版业已经发展了并且在所有的ISBN号码后又附加了一位数字。如果我们使用了ISBN作为book表的主键,我们就要更新所有book表的记录来反映这个变化,而且还有一个问题,还有其他表引用了book表的主键,我们就要更新所有的引用,这还牵涉到要删除外键,所有的这一切都是非常痛苦的。如果使用有意义的值作为主键,那么我们将收到外界业务规则的影响,如果使用id,我们可以自己完全控制,而且如果象ISBN等一些东西改变的话,将不会影响到数据库结构。如果你从一个新的数据库结构开始,可能会遵循约定,给所有的表都使用id作为主键,但是,当你使用的是一个既存的数据库开始的时候,Active Record提供了简单的方法来让你重新给表指定主键,例如:class BadBook My Great American Novelp book.attributes #= isbn =0-12345-6789,title=My Great American Novel也就是说,在设置主键的时候,使用id属性,其他时候,使用真实的列名。Active Record抽象了数据库连接的概念,帮助应用程序来处理底层的数据库链接的细节,作为替代,Active Record使用通用的调用,将细节委托给一组数据库适配器。可以使用establish_connection( )方法来制定连接,下面的例子创建了一个mysql数据库连接,数据库的名字是railsdb,服务器的Host名为,用户名为railsuser,密码为railspw。ActiveRecord:Base.establish_connection(:adapter = mysql,:host = ,:database = railsdb,:username = railsuser,:password = railspw)Active Record支持DB2,MySql,Oracle,Postgres,SqlServer,以及SqlLite,每一种数据库适配器在链接的参数上都有一些细小的差别,下表列出了常用的参数:注意Oracle适配器的名字为oci。数据库连接和Model类是关联的,每个类都从父类那里继承了链接,ActiveRecord:Base作为所有的Active Record类的父类,设置这里的数据库连接就给所有的活动记录类设置了链接,当然,如果需要的话,你也可以复写(override)链接配置。下面的例子里,我们的大多数表都在MySql数据库中,库名为online,由于一些历史原因,customers表在名为backend的数据库中,ActiveRecord:Base.establish_connection(:adapter = mysql,:host = ,:database = online,:username = groucho,:password = swordfish)class LineItem ActiveRecord:Base# .endclass Order ActiveRecord:Base# .endclass Product ActiveRecord:Base# .endclass Customer mysql,:host = ,:database = backend,:username = chicho,:password = piano)在我们前面所写的depot程序中,我们没有使用establish_connection方法,而是在config/database.yaml文件中指定了数据库连接的参数信息,对于大多数rails程序来说,这是首选的方式,不仅因为将配置信息和代码分离,而且在测试和部署时也能带来方便,上面的表格里列出的参数都可以应用在YAML文件中,这一点我们在前面的配置文件一节已经有介绍。最后,如果你通过一个标记访问establish_connection(),Rails会在database.yaml文件中查找名字对应的配置节,来获取链接的参数,这样就可以将所有的数据库连接配置从代码中分离出来。Active Record使得实现CRUD的数据库基本操作变得简单,在下面的几节里我们使用Mysql数据库中的orders表来进行CRUD的操作,这次先看创建(Create)。我们假想有一个Model,名为Order:class Order Dave Thomas,:email = ,:address = 123 Main St,:pay_type = check)an_order.save注意到现在为止,我们还没有任何关于id的设置,这是因为我们使用Active Record的默认约定,将orders表的主键为一个integer类型的列。在存入数据库的时候,Active Record自动给新建的对象生成一个唯一的值,并且设置到id属性上,我们可以在save()之后查询id的值:an_order = Order.newan_ = Dave Thomas# .an_order.saveputs The ID of this order is #an_order.idnew()构造函数在内存中创建了一个Order类的对象,你需要在某个时候调用save()方法来保存到数据库。Active Record还有一个约定的方法create(),下面的例子说明这个方法的用法,同时展示了创建对象和存储到数据库:an_order = Order.create(:name = Dave Thomas,:email = ,:address = 123 Main St,:pay_type = check)也可以给create()方法传递哈希(hash)的数组,在数据库中创建多条记录,并且返回对应的对象数组。orders = Order.create( :name = Dave Thomas,:email = ,:address = 123 Main St,:pay_type = check, :name = Andy Hunt,:email = ,:address = 456 Gentle Drive,:pay_type = po )方法new()和create()的真正目的就是让我们可以通过一组参数就能够创建Model对象:order = Order.create(params)读取记录包括指定那些特定的数据是你感兴趣的,你给Active Record指定标准,Active Record再返回给你一些对象,其中包含了符合条件的记录的数据。在一个表中检索数据的最简单的办法就是指定主键,任何一个Model都支持find()方法,该方法支持一个或多个主键值,如果只指定了一个主键,将会返回对应的对象,如果指定了多个主键给find方法,该方法一组相应的对象。注意,当没有任何符合条件的数据的时候,将会抛出一个RecordNotFound异常,所以如果find方法没有抛出这个异常的话,返回的数组中的对象个数就等于给find方法指定的id数目。an_order = Order.find(27) # find the order with id = 27# Get a list of order ids from a form, then# sum the total valueorder_list = params:order_idsorders = Order.find(order_list)count = orders.size通常,在查询的时候都要用到除过id以外的值,Active Record提供了一组设置来执行这些查询,我们会介绍find使用方法,从基本的查询,再到高阶些的动态查询。到现在我们只是了解了find方法的最基本的内容,通过指定id来获取一个或一组对象。另外,我们还可以使用一些标记比如:first,:all来作为find方法的参数。:first将返回符合条件的第一条记录,:all将返回所有符合条件的记录,下一篇我们来看看Active Record是如何处理sql的。想象一下Active Record是如何处理SQL的,我们来看看find方法的:conditions参数,调用的时候像这样:find(:all,:conditions=),这里的:conditions参数决定了find方法将返回哪些记录,它相当于Sql语句的where部分,例如,要获取所有的名字为Dave,pay_type为po的订单,我们这样写:pos = Order.find(:all,:conditions = name = dave and pay_type = po)find方法将返回所有符合条件的记录,并且都已经被转换成Order类的对象,如果没有符合条件的订单,find方法将返回一个长度为零的数组。如果你的条件是预定的,那么上面的写法就很好了,但是如果我们要指定条件中的值呢,我们这样写:# get the limit amount from the formname = params:name# DONT DO THIS!pos = Order.find(:all,:conditions = name = #name and pay_type = po)但是,这并不是一个好主意,因为如果你的程序要发布在网络上的话,这样给SQL注入攻击留下了方便(后面我们在关于安全的主题里会讨论SQL注入的话题),更好的办法是,动态的生成SQL(注1),让Active Record来处理它,我们可以在SQL语句中加入占位符,然后这些占位符将会在运行期被替换成指定的值,指定占位符的方法就是在SQL中使用问号,在运行时,第一个问号将被替换为集合的第二个元素的值,以此类推,例如,我们把上面的查询重写一次:name = params:namepos = Order.find(:all,:conditions = name = ? and pay_type = po, name)我们也可以使用带名字的占位符,名字前带冒号,例如下面这样:name = params:namepay_type = params:pay_typepos = Order.find(:all,:conditions = name = :name and pay_type = :pay_type,:pay_type = pay_type, :name = name)我也可以更进一步,因为params是一个hash,我们可以让conditions部分更简单pos = Order.find(:all,:conditions = name = :name and pay_type = :pay_type, params)不管使用哪种占位符,Active Record都会很小心地处理SQL中的引号和escape。使用动态SQL,Active Record会保护我们不受SQL注入攻击。注1:这里的动态sql不同于oracle等数据库中所指的动态sql,其含义类似于ADO.net中不拼接sql字符串,而是传参数来防止sql注入攻击。现在我们知道如何指定条件,现在我们来看看find方法支持的一些其他设置。首先,理解find(:first,)方法是非常重要的,该方法在同样的conditions设置下,和find(:all,)方法生成同样的sql语句,不同之处仅在于只返回一条记录。在调用的时候,一种的参数为:first,另一种为:all。find方法执行一个select from这样的sql语句,:all标记指定返回表中所有的记录,:first返回第一条记录。但是现在:first还不能保证你得到的就是表中的第一条记录,原因是什么?我们继续往下看。:conditions参数指定了SQL语句的where部分,可以包含Sql语句或者包含可以替换的参数的名字,值,上一篇我们已经做了了解。daves_orders = Order.find(:all, :conditions = name = Dave)name = params:nameother_orders = Order.find(:all, :conditions = name = ?, name)yet_more = Order.find(:all,:conditions = name = :name and pay_type = :pay_type,params)上面的find方法并不能保证按照特定的顺序返回记录,除非指定查询的排序(order by)部分。:order参数就是用来指定SQL的排序条件的,下面的例子演示了查询名字为Dave的订单,并且按照pay_type,shipped_at字段进行降序排列。orders = Order.find(:all,:conditions = name = Dave,:order = pay_type, shipped_at DESC)我们还可以设置:limit参数来限制返回的记录数,如果你使用:limit参数,或许还想指定排序条件,下面的例子返回10条记录,并且按照指定条件排序:orders = Order.find(:all,:conditions = name = Dave,:order = pay_type, shipped_at DESC,:limit = 10)参数:offset经常与:limit参数一同出现,用来指定从第一条记录起,返回指定的偏移量,下面代码演示了:offset参数的使用:def Order.find_on_page(page_num, page_size)find(:all,:order = id,:limit = page_size,:offset = page_num*page_size)end从上面的代码可以看到,这样使用find的场景就是分页显示数据,用pagesize指定每页的记录数,然后由pagenum*page_size指定从第几条开始提取数据。参数:join用来指定主表和哪些表进行关联查询,:join参数指定的部分会插入到SQL中,下面的代码演示取得一个所有名为“Programing Ruby”的条目的列表:LineItem.find(:all,:conditions = pr.title = Programming Ruby,:joins = as li inner join products as pr on duct_id = pr.id)在后面的内容里,我们还会了解更多的进行表关联查询的方法。现在,我们在来回头看看:all和:first参数,实际上在使用:first参数时,默认的带有参数:limit,只不过:limit参数的值为1。如果你要取得最后一条记录,只需要改变:order里的排序方向为降序。find方法为我们构建了完整的Sql查询,而方法find_by_sql方法则允许我们对Sql有完整的控制,该方法只有一个参数,就是你想要使用的完整的sql语句,下面是示例代码:orders = LineItem.find_by_sql(select line_items.* from line_items, orders + where order_id = orders.id + and = Dave Thomas )现在有一个问题了,就是返回的Model对象中都包含有哪些属性呢?我们使用attributes( ), attribute_names( ), and attribute_present?( )方法来确定在Model对象中都包含有哪些属性,第一个返回一个hash,里面是键值对,第二个返回属性名的数组,第三个判断Model对象中是否含有某个属性,例如:orders = Order.find_by_sql(select name, pay_type from orders)first = orders0p first.attributesp first.attribute_namesp first.attribute_present?(address)程序的结果:name=Dave Thomas, pay_type=checkname, pay_typefalsefind_by_sql方法也可以用来创建包含有派生(derived)(注1)的Model对象,如果你使用了as XXX 这样的sql来给派生字段一个别名,这个别名会作为Model中的属性名,例如:items = LineItem.find_by_sql(select *, + quantity*unit_price as total_price, + products.title as title + from line_items, products + where line_duct_id = products.id )li = items0puts #li.title: #li.quantityx#li.unit_price = #li.total_price在find_by_sql方法中,我们一样可以使用占位符来给Sql语句传递参数,例如:Order.find_by_sql(select * from orders where amount ?,params:amount)数据库上最常运行的查询莫过于根据指定条件返回符合的结果集,查询可能是返回所有名字为dave的订单,或者是某个博客上所有标题含Rails的post,在很多其他的框架和程序设计语言中,你需要创建sql来执行查询,Active Record利用了ruby语言所包含的动态能力来做这些事。例如,我们的Order Model包含了诸如name,email,address这样的属性,我们可以使用这些名字对应的find方法来查询,例如:order = Order.find_by_name(Dave Thomas)orders = Order.find_all_by_name(Dave Thomas)order = Order.find_all_by_email(paramsemail)如果你调用了一个Model类的find_by_或者find_all_by_这样字符串打头的格式的方法,Active Record将它们转换成一个查询器(finder),而将方法后面的字符串作为字段名转换find方法的参数,例如:order = Order.find_by_name(Dave Thomas, other args.)上面的调用等效的转换为:order = Order.find(:first,:conditions = name = ?, Dave Thomas,other_args.)类似的,调用一个find_all_by_xxx方法相当于调用find(:all, )方法。到这里魔术还没有停止,Active Record还可以对多个列创建查询器(finder),例如,你可以写:user = User.find_by_name_and_password(name, pw)相当于:user = User.find(:first,:conditions = name = ? and password = ?, name, pw)为了确定要检查哪些字段,Active Record简单的分割find_by_和find_all_by_后面的字符串,这在大多数情况下是够用的,除非你的根本就没有某个在方法名中包含的字段。注意,Active Record并不提供在find_by_或find_all_by后面的两个字段名中含有_or_。Active Record提供了两个方法来获取符合条件的记录的条数:count()和count_by_sql()。例如:c1 = Order.countc2 = Order.count(name = ?, Dave Thomas)c3 = LineItem.count_by_sql(select count(*) + from line_items, orders + where line_items.order_id = orders.id + and = Dave Thomas )puts Dave has #c3 line items in #c2 orders (#c1 orders in all)在一个程序中,数据库有可能被多个进程或多个程序访问,随时都有可能获取最新的Model对象,这些对象有可能刚刚被编辑过。从某种程度上讲,这主要应用在事务中,不管怎么说,当你需要手动刷新Model对象时,Active Record可以帮助你,只需调用reload()方法,Model对象属性的值就会被数据库中的值更新。stock = Market.find_by_ticker(RUBY)loop doputs Price = #stock.pricesleep 60stock.reloadend前面了解了检索的方法,这次来看看Active Record怎样更新数据库中的记录。如果你有一个Active Record对象(或许对应于order表),你可以通过调用save方法将它写道数据库中去,如果这个对象是先前从数据库中读取出来的,save方法将会更新既有的记录,否则将会新建一条记录。如果一条既有记录被更新,Active Record将会用它的主键和来匹配内存中的对象,Active Record对象中的属性被更新到对应的列,即使一个列中的值没有变化也会被更新,在下面的例子中,id为123的订单所有的内容都会被更新:order = Order.find(123) = Fredorder.save不管怎样,在下面的例子里,Active Record对象只包含id,name,paytype,当对象被保存的时候仅仅只有这些字段被更新,注意如果你想要把对象保存到数据库,那么在使用find_by_sql方法时,一定要包含id字段。orders = Order.find_by_sql(select id, name, pay_type from orders where id=123)first = = Wilmafirst.save另外,Active Record还提供了update_attribute()方法,该方法可以将Model对象的某个属性保存到数据库。order = Order.find(123)order.update_attribute(:name, Barney)order = Order.find(321)order.update_attributes(:name = Barney,:email = )我们可以把读取和更新结合在一起,使用update()方法或update_all(),update()方法使用一个id和一组属性,如果在数据库中对应的记录,就更新指定的属性,然后返回model对象。order = Order.update(12, :name = Barney, :email = )也可以传递一组id或者属性和值的hash给update()方法,这样会更新所有匹配的记录,并且返回一组model对象。最后,update_all()方法允许你指定给update语句指定Where条件,下面的例子给所有标题中含有java的商品涨价10%:result = Product.update_all(price = 1.1*price, title like %Java%)这里的返回值依赖于具体的数据库适配器,很多数据库都返回被更新的记录数目。下面我们看看save()和save!()这两个方法。简单的save()方法在Model对象存在并且可以的保存的情况下返回true:if order.save# all OKelse# validation failedend这样会导致你在所有调用save方法的地方都要加上检查,但是Active Record假定save方法是在Controler的Action的上下文中的,并且视图里的代码不进行这些检查。(这部分书上看不明白,不能确定)。不管怎样,如果你需要在上下文环境中保存Model对象,并且想确定是否所有的错误都被处理了,你可以使用save!()方法,如果Model对象不能保存,那么这个方法会抛出一个RecordInvailid异常:beginorder.save!rescue RecordInvalid = error# validation failedend在学习Rails中的并发处理的处理前,我们先简单了解下并发处理的概念。在有多个处理同时访问同一个数据库的应用程序中,可能会出现这样的情况,因为一个处理更新了数

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论