侧边栏壁纸
博主头像
极客日记 博主等级

行动起来,活在当下

  • 累计撰写 93 篇文章
  • 累计创建 17 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

【Ruby on Rails】Model CRUD

Jack.Jia
2022-03-21 / 0 评论 / 0 点赞 / 3 阅读 / 0 字

数据持久层

数据持久层的缩写: persistant layer. 大家往往把它简称为: 持久层.

我们获取到一些数据的时候, 往往是这样:

# 假设 这个变量,来自于 网络的其他服务器
String content = SomeTool.query_from_remote ....

# 如何把它保存到本地?
经典方法: 保存到数据库

# 不怕断电了。 数据就持久了。

所以,在实际项目中, 持久层有两个实现方式:

  • 保存到数据库. (这个是 99% 的选择)
  • 保存到文件.

所以,持久层的架构是:

本地代码  < === >  持久层  < === > 数据库/文件

从例子来看持久层

例如: 希望读取所有的 book 记录。

  • 浏览器发起一个请求给 Rails. 例如 http://localhost:3000/books
  • Rails 调起持久层。 在 books_controller#index_action 中处理这个 /books 请求。
class BooksController < ApplicationController
  def index
    @books = Book.all
  end
end

持久层查询数据库。并且把数据加载过来。

Book.all
  • Book 它是个 model. 它映射到了 books 表。
  • Book.all, 就会被 Rails 的持久层的机制,转换成一段 SQL 语句:
select * from books;

然后, Rails 的持久层,再把上面的结果,从 " 数据库结果集”,转换成:“ruby 代码” 的格式。

数据库与持久层

数据库:

  • sqlite: 是一个 .sqlite3 文件(例如 Rails 中,默认就会创建:db/development.sqlite3)
  • mysql: 也是一个文件。 对于 ubuntu, 默认放在: /var/lib/mysql/ 数据库名下面,例如: /var/lib/mysql/me/students.frm, .ibd
  • 文件放在硬盘上。

持久层:
以代码的形式来存在的。

可以认为, 持久层, 是对数据库的一种封装。

把原始的数据库操作, 封装成 ruby 代码。

例子:

  • 对于 select * from books
Book.all
  • 对于 select * from books where author = '大刘‘
Book.where(name: '大刘')
  • 对于 insert into books(title, author) values (“«十万个为什么»“, “李博士”) :
book = Book.new
book.title = "《十万个为什么》"
book.author = "李博士"
book.save

上面四行代码,可以简写为:

Book.create({ :title => '十万个为什么', :authro => '李博士'})

你会发现一个巨大的区别:

  • SQL 语句的形式: 特别原始。 不好操作。不好维护。特别容易出错。
  • 持久层的形式: 特别贴近于自然语言。 好维护。 好学习。 而且:菜鸟写的持久层操作, 都会被自动转换成高手写的 SQL 语句。

例如:

分页:

如果用 MYSQL:

select ... from ... order by id limit(100) offset(2000)

oracle:
写法就变了…

select ... from ... order by id limit(100) top(2000)

所以,想兼容数据库,那就是一场噩梦。(例如: mysql 支持 select … from (select … from …) 这样的嵌套 select, 其他好多数据库就不支持)

所以:持久层最大的卖点:

学好一个持久层, 可以操作所有数据库。

例如:
学好 Rails ActiveRecord, 可以在所有数据库上操作。 而且持久层生成的代码,就是专家级别的。(持久层在生成代码时会自动作优化。)

下面是持久层把 代码 转换成 SQL 语句的例子:

City.first:

SELECT  `cities`.* FROM `cities`   ORDER BY `cities`.`id` ASC LIMIT 1

City.first.airport_managers:

SELECT  `cities`.* FROM `cities`   ORDER BY `cities`.`id` ASC LIMIT 1
SELECT `airport_managers`.* FROM `airport_managers`  WHERE `airport_managers`.`city_id` = 1

City.first.airport_managers.count

SELECT  `cities`.* FROM `cities`   ORDER BY `cities`.`id` ASC LIMIT 1
SELECT COUNT(*) FROM `airport_managers`  WHERE `airport_managers`.`city_id` = 1

CRUD

先创建一个表: books

  1. 新建一个 migration
$ rails g migration create_books
  1. 为它增加内容:
def change
  create_table :books do |t|
    t.string :author
    t.string :title
    t.timestamp
  end
end
  1. 运行 migrate:
$ rails db:migrate

== 20210926221205 CreateBooks: migrating ======================================
-- create_table(:books)
   -> 0.0030s
== 20210926221205 CreateBooks: migrated (0.0031s) =============================
  1. 创建 model:
# app/models/book.rb
class Book < ActiveRecord::Base
end
  1. 进入到控制台
$ rails c

接下来, 我可以创建一个 book, author 为 王博士

> book = Book.create :author => '王博士'
```sql
sqlite> select * from books;
1|十万个为什么|王博士
```ruby
>book = Book.first
>book.title = '二十万个为什么'
>book.save

可以看到, 上面的代码, 被转换成了下面的 SQL 语句:

 (0.2ms)  begin transaction
 SQL (0.5ms)  UPDATE "books" SET "title" = ? WHERE "books"."id" = 1  [["title", "二十万个为什么"]]
 (173.7ms)  commit transaction
```ruby
> book.delete
SQL (52.4ms)  DELETE FROM "books" WHERE "books"."id" = 1
=> #<Book id: 1, title: "二十万个为什么", author: "王博士">

插入 100 条语句:

> (1..100).each  { |e|  Book.create :title => "#{e}个为什么" }

会有 100 条 SQL 被转换出来, 并且被执行.

查询:
> Book.where('title like "5%"')
  Book Load (0.6ms)  SELECT "books".* FROM "books"  WHERE (title like "5%")
```ruby
Book.where('title like "5%"').order('title desc') # 根据title倒序

总结: 如何在 Rails 中实现 数据库的映射呢?

Rails 中声明持久层极其简单:

如果你的表,名字叫: teachers, 那么,就在 app/models 目录下,新建一个 rb 文件: teacher.rb

class Teacher < ActiveRecord::Base
end

然后就可以在 Rails console 中调用这个 teacher 了。

0

评论区