编写此文的时候使用的是
MySQL
数据库, 现已经切换到PostgreSQL
,Github
上的仓库已经更新到PostgreSQL
简介
Ecto
分为4个主要组件
组件 | 说明 |
---|---|
Ecto.Repo |
数据库包装器, 通过它可以执行创建,更新,删除和查询等数据库操作, 它需要一个适配器和一个URL与数据库通信 |
Ecto.Schema |
允许开发者定义映射到底层存储的数据结构 |
Ecto.Changeset |
为开发者提供了一个过滤和转换外部参数的方法, 以及在发送到数据库之前追踪和验证变更的机制. |
Ecto.Query |
以Elixir语法编写查询, 从数据库检索信息. 在Ecto中查询是安全的, 避免了类似SQL注入, 等常见的问题. 并提供类型安全. 通过Ecto.Queryable 协议, 查询是可组合的 . |
关于可组合的查询
你可以通过Ecto.Query.from/2
创建一个查询, 这个查询可以作为另一个查询的输入. 类似于 Elixir 的|>
操作符的功能, 可以通过管道合并多个查询条件.例如:
q = (from u in User) |> where([u], u.username == "developerworks") |> where([u], u.status == "verified")
配置Ecto
本节包含详细的创建, 配置, 开发一个Ecto项目的完整过程. 本文基于 Ecto 2.0.0-rc
版本, 演示下面的过程.
创建项目
➜ /tmp> mix new ecto_test
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/ecto_test.ex
* creating test
* creating test/test_helper.exs
* creating test/ecto_test_test.exs
Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:
cd ecto_test
mix test
Run "mix help" for more commands.
添加依赖
defp deps do
[
{:mariaex, "~> 0.7.4"},
{:ecto, "~> 2.0.0-rc.3"}
]
end
设置OTP应用
def application do
[applications: [:logger, :mariaex, :ecto]]
end
获取依赖
➜ ecto_test> mix deps.get
A new Hex version is available (0.11.5), please update with `mix local.hex`
Running dependency resolution
Dependency resolution completed
connection: 1.0.2
db_connection: 0.2.5
decimal: 1.1.2
ecto: 2.0.0-rc.3
mariaex: 0.7.4
poolboy: 1.5.1
* Getting mariaex (Hex package)
Checking package (https://s3.amazonaws.com/s3.hex.pm/tarballs/mariaex-0.7.4.tar)
Fetched package
* Getting ecto (Hex package)
Checking package (https://s3.amazonaws.com/s3.hex.pm/tarballs/ecto-2.0.0-rc.3.tar)
Fetched package
* Getting poolboy (Hex package)
Checking package (https://s3.amazonaws.com/s3.hex.pm/tarballs/poolboy-1.5.1.tar)
Using locally cached package
* Getting decimal (Hex package)
Checking package (https://s3.amazonaws.com/s3.hex.pm/tarballs/decimal-1.1.2.tar)
Using locally cached package
* Getting db_connection (Hex package)
Checking package (https://s3.amazonaws.com/s3.hex.pm/tarballs/db_connection-0.2.5.tar)
Using locally cached package
* Getting connection (Hex package)
Checking package (https://s3.amazonaws.com/s3.hex.pm/tarballs/connection-1.0.2.tar)
Using locally cached package
配置数据库
在config/config.exs
配置文件中添加如下配置:
config :ecto_test, EctoTest.Repo,
adapter: Ecto.Adapters.MySQL,
database: "ecto_test",
username: "root",
password: "root",
hostname: "192.168.212.129"
# URL 配置方式
config :ecto_test, ecto_repos: [EctoTest.Repo]
url: "mysql://root:[email protected]/ecto_test"
关于数据库配置的详细参数, 参考 https://hexdocs.pm/ecto/Ecto.Adapters.MySQL.html 或 https://hexdocs.pm/ecto/Ecto.Adapters.Postgres.html
编写Repo
模块
创建lib/ecto_test
目录, 并在其中创建文件repo.ex
, 内容如下:
defmodule EctoTest.Repo do
use Ecto.Repo, otp_app: :ecto_test
end
这个文件也可以通过下面的命令生成, mix ecto.gen.repo
还会修改你的config/config.exs
配置文件以增加数据库的配置信息.
➜ ecto_test git:(master) ✗ mix ecto.gen.repo -r EctoTest.Repo
* creating lib/ecto_test
* creating lib/ecto_test/repo.ex
lib/ecto_test/repo.ex already exists, overwrite? [Yn]
编译
➜ ecto_test> mix compile
==> connection
Compiled lib/connection.ex
Generated connection app
==> poolboy (compile)
Compiled src/poolboy_sup.erl
Compiled src/poolboy_worker.erl
Compiled src/poolboy.erl
==> decimal
Compiled lib/decimal.ex
Generated decimal app
==> db_connection
Compiled lib/db_connection/app.ex
Compiled lib/db_connection/error.ex
Compiled lib/db_connection/log_entry.ex
...
...
...
创建数据库
➜ ecto_test> mix ecto.create -r EctoTest.Repo
The database for EctoTest.Repo has been created
上面的输出提示, 底层的数据库已经创建完成.
创建模型和迁移(Migration)
这一节正式开始说明如何创建模型模块, 和迁移模块
创建模型
defmodule EctoTest.Domain do
use Ecto.Schema
schema "domain" do
field :url
timestamps
end
end
创建移植脚本
创建一个空的移植脚本
➜ ecto_test> mix ecto.gen.migration add_domain_table -r EctoTest.Repo
* creating priv/repo/migrations
* creating priv/repo/migrations/20160427032452_add_domain_table.exs
修改移植脚本priv/repo/migrations/20160427032452_add_domain_table.exs
, 为如下内容, 移植脚本默认会创建在项目根目录的priv
子目录中. 该目录的位置可以在数据库配置中, 通过指定:priv
键设置.
defmodule EctoTest.Repo.Migrations.AddDomainTable do
use Ecto.Migration
def change do
create table(:domain) do
add :url, :string
timestamps
end
end
end
2016-07-15更新
上述数据库迁移脚本可以修改为:
defmodule EctoTest.Repo.Migrations.AddDomainTable do
use Ecto.Migration
def up do
create table(:domain) do
add :url, :string
timestamps
end
end
def down do
drop table(:domain)
end
end
mix ecto.migrate
执行 up
函数创建底层数据库表mix ecto.rollback
执行 down
函数删除底层数据库表
执行表的创建
➜ ecto_test> mix ecto.migrate -r EctoTest.Repo
11:29:30.396 [info] == Running EctoTest.Repo.Migrations.AddDomainTable.change/0 forward
11:29:30.396 [info] create table domain
11:29:30.406 [info] == Migrated in 0.0s
在IEx中测试
iex(3)> domain = %EctoTest.Domain{url: "https://segmentfault.com/u/developerworks"}
%EctoTest.Domain{__meta__: #Ecto.Schema.Metadata<:built>, id: nil, inserted_at: nil, updated_at: nil, url: "https://segmentfault.com/u/developerworks"}
iex(4)> domain |> EctoTest.Repo.insert
12:04:36.455 [debug] QUERY OK db=10.0ms queue=0.1ms
INSERT INTO `domain` (`inserted_at`,`updated_at`,`url`) VALUES (?,?,?) [{{2016, 4, 27}, {4, 4, 36, 0}}, {{2016, 4, 27}, {4, 4, 36, 0}}, "https://segmentfault.com/u/developerworks"]
{:ok, %EctoTest.Domain{__meta__: #Ecto.Schema.Metadata<:loaded>, id: 2, inserted_at: #Ecto.DateTime<2016-04-27 04:04:36>, updated_at: #Ecto.DateTime<2016-04-27 04:04:36>, url: "https://segmentfault.com/u/developerworks"}}
iex(5)>
代码
https://github.com/developerworks/ecto_test