时隔好几年,记录一下吧
elixir的增删改查跟rails的几乎一样。唯一不同的是elixir喜欢用postgres
参考:https://hexdocs.pm/ecto/getting-started.html#adding-ecto-to-an-application
前期准备
1. 创建一个mix项目
mix new test_ecto --sup
$ mix new test_ecto --sup
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/test_ecto.ex
* creating lib/test_ecto/application.ex
* creating test
* creating test/test_helper.exs
* creating test/test_ecto_test.exs
Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:
cd test_ecto
mix test
Run "mix help" for more commands.
2. 下载依赖
mix deps.get
$ mix deps.get Resolving Hex dependencies... Dependency resolution completed: New: connection 1.1.0 db_connection 2.4.1 decimal 2.0.0 ecto 3.7.1 ecto_sql 3.7.2 postgrex 0.16.1 telemetry 1.0.0 * Getting ecto_sql (Hex package) * Getting postgrex (Hex package) * Getting connection (Hex package) * Getting db_connection (Hex package) * Getting decimal (Hex package) * Getting telemetry (Hex package) * Getting ecto (Hex package)
3. 创建配置文件。
mix ecto.gen.repo -r TestEcto.Repo
==> connection
Compiling 1 file (.ex)
Generated connection app
==> test_ecto
Could not find "rebar3", which is needed to build dependency :telemetry
I can install a local copy which is just used by Mix
Shall I install rebar3? (if running non-interactively, use "mix local.rebar --force") [Yn]
* creating /home/siwei/.asdf/installs/elixir/1.12.3-otp-24/.mix/rebar
* creating /home/siwei/.asdf/installs/elixir/1.12.3-otp-24/.mix/rebar3
===> Analyzing applications...
===> Compiling telemetry
==> decimal
Compiling 4 files (.ex)
Generated decimal app
==> db_connection
Compiling 14 files (.ex)
Generated db_connection app
==> ecto
Compiling 56 files (.ex)
Generated ecto app
==> postgrex
Compiling 64 files (.ex)
Generated postgrex app
==> ecto_sql
Compiling 26 files (.ex)
Generated ecto_sql app
==> test_ecto
* creating lib/test_ecto
* creating lib/test_ecto/repo.ex
* creating config/config.exs
Don't forget to add your new repo to your supervision tree
(typically in lib/test_ecto/application.ex):
{TestEcto.Repo, []}
And to add it to the list of Ecto repositories in your
configuration files (so Ecto tasks work as expected):
config :test_ecto,
ecto_repos: [TestEcto.Repo]
修改这个 文件的内容:
vim config/config.exs
import Config config :test_ecto, TestEcto.Repo, database: "test_ecto_repo", username: "myroot", password: "123456", hostname: "localhost"
(不用修改,了解即可)
上面的命令会为lib/test_ecto/repo.ex 增加一行代码:
defmodule Friends.Repo do
use Ecto.Repo,
otp_app: :friends, # 就是增加了这一行
adapter: Ecto.Adapters.Postgres
end
4. 修改 lib/test_ecto/application.ex
def start(_type, _args) do
children = [
Friends.Repo, # 增加这一行,就可以让对应的worker随着application的启动而启动了
]
5. 修改 config/config.exs
# 增加这一句 config :test_ecto, ecto_repos: [TestEcto.Repo]
6. 创建数据库:mix ecto.create
Compiling 3 files (.ex) Generated test_ecto app The database for TestEcto.Repo has been created
7. 创建一个migration : mix ecto.gen.migration create_books
* creating priv/repo/migrations * creating priv/repo/migrations/20220129071854_create_books.exs
修改 这个migration的内容如下;
defmodule TestEcto.Repo.Migrations.CreateBooks do
use Ecto.Migration
def change do
create table(:books) do
add :title, :string
add :author, :string
add :year, :integer
end
end
end
8. 运行migration : mix ecto.migrate
15:22:08.661 [info] == Running 20220129071854 TestEcto.Repo.Migrations.CreateBooks.change/0 forward 15:22:08.664 [info] create table books 15:22:08.751 [info] == Migrated 20220129071854 in 0.0s
9. 在console中运行试试: iex -S mix
新增记录
iex(2)> book = %TestEcto.Book{ title: "三体", author: "刘慈欣", year: 2006}
%TestEcto.Book{
__meta__: #Ecto.Schema.Metadata<:built, "books">,
author: "刘慈欣",
id: nil,
title: "三体",
year: 2006
}
iex(4)> TestEcto.Repo.insert(book)
15:37:35.758 [debug] QUERY OK db=0.7ms queue=0.7ms idle=1836.2ms
INSERT INTO "books" ("author","title","year") VALUES ($1,$2,$3) RETURNING "id" ["刘慈欣", "三体", 2006]
{:ok,
%TestEcto.Book{
__meta__: #Ecto.Schema.Metadata<:loaded, "books">,
author: "刘慈欣",
id: 2,
title: "三体",
year: 2006
}}
查询
1 基本查询 Ecto.Query
temp = Ecto.Query.first(TestEcto.Book)
book = TestEcto.Repo.one(temp)
# 上面2句也可以写成:
book = TestEcto.Book |> Ecto.Query.first |> TestEcto.Repo.one
15:47:31.089 [debug] QUERY OK source="books" db=0.9ms idle=1401.0ms
SELECT b0."id", b0."title", b0."author", b0."year" FROM "books" AS b0 ORDER BY b0."id" LIMIT 1 []
%TestEcto.Book{
__meta__: #Ecto.Schema.Metadata<:loaded, "books">,
author: "刘慈欣",
id: 2,
title: "三体",
year: 2006
}
2. 查询第一条
Ecto.Query.first(TestEcto.Book) |> TestEcto.Repo.one 16:15:17.868 [debug] QUERY OK source="books" db=1.2ms idle=1587.8ms SELECT b0."id", b0."title", b0."author", b0."year" FROM "books" AS b0 ORDER BY b0."id" LIMIT 1 [] nil
3. 查询所有
TestEcto.Repo.all(TestEcto.Book) 16:17:18.707 [debug] QUERY OK source="books" db=0.3ms queue=0.5ms idle=1970.0ms SELECT b0."id", b0."title", b0."author", b0."year" FROM "books" AS b0 [] []
4. 根据条件查询
TestEcto.Repo.all TestEcto.Book
16:43:17.966 [debug] QUERY OK source="books" db=0.6ms idle=1762.1ms
SELECT b0."id", b0."title", b0."author", b0."year" FROM "books" AS b0 []
[
%TestEcto.Book{
__meta__: #Ecto.Schema.Metadata<:loaded, "books">,
author: "刘慈欣",
id: 3,
title: "三体",
year: 2004
},
%TestEcto.Book{
__meta__: #Ecto.Schema.Metadata<:loaded, "books">,
author: "刘慈欣",
id: 4,
title: "三体2",
year: 2006
},
%TestEcto.Book{
__meta__: #Ecto.Schema.Metadata<:loaded, "books">,
author: "刘慈欣",
id: 5,
title: "三体3",
year: 2008
}
]
5. 根据ID查询
TestEcto.Repo.get(TestEcto.Book, 5)
16:47:15.855 [debug] QUERY OK source="books" db=0.7ms idle=1884.6ms
SELECT b0."id", b0."title", b0."author", b0."year" FROM "books" AS b0 WHERE (b0."id" = $1) [5]
%TestEcto.Book{
__meta__: #Ecto.Schema.Metadata<:loaded, "books">,
author: "刘慈欣",
id: 5,
title: "三体3",
year: 2008
}
6. where查询
# 1. 传入字符串
TestEcto.Book |> Ecto.Query.where(title: "三体") |> TestEcto.Repo.all
17:54:15.356 [debug] QUERY OK source="books" db=0.3ms queue=0.7ms idle=1598.7ms
SELECT b0."id", b0."title", b0."author", b0."year" FROM "books" AS b0 WHERE (b0."title" = '三体') []
[
%TestEcto.Book{
__meta__: #Ecto.Schema.Metadata<:loaded, "books">,
author: "刘慈欣",
id: 3,
title: "三体",
year: 2004
}
]
# 2. 使用变量
TestEcto.Book |> Ecto.Query.where(title: ^book_title) |> TestEcto.Repo.all
17:56:10.968 [debug] QUERY OK source="books" db=0.3ms queue=0.7ms idle=1166.4ms
SELECT b0."id", b0."title", b0."author", b0."year" FROM "books" AS b0 WHERE (b0."title" = $1) ["三体2"]
[
%TestEcto.Book{
__meta__: #Ecto.Schema.Metadata<:loaded, "books">,
author: "刘慈欣",
id: 4,
title: "三体2",
year: 2006
}
]
修改
修改需要使用changeset, 这里需要import一个新的内容。
(changeset API : https://hexdocs.pm/ecto/Ecto.Changeset.html )
修改model文件如下:
defmodule TestEcto.Book do
use Ecto.Schema
# 增加这一行, 引入ChangeSet
import Ecto.Changeset
schema "books" do
field :title, :string
field :author, :string
field :year, :integer
end
# 实现这个方法。
def changeset book, params \\ %{} do
book
|> cast(params, [:title, :author, :year])
|> unique_constraint(:title)
end
end
然后就可以更新了
iex> changeset = TestEcto.Book.changeset(book, %{ year: 2007, title: "三体x"})
#Ecto.Changeset<
action: nil,
changes: %{title: "三体x", year: 2007},
errors: [],
data: #TestEcto.Book<>,
valid?: true
>
iex> TestEcto.Repo.update(changeset)
16:01:12.238 [debug] QUERY OK db=0.7ms queue=0.6ms idle=497.9ms
UPDATE "books" SET "title" = $1, "year" = $2 WHERE "id" = $3 ["三体x", 2007, 2]
{:ok,
%TestEcto.Book{
__meta__: #Ecto.Schema.Metadata<:loaded, "books">,
author: "刘慈欣",
id: 2,
title: "三体x",
year: 2007
}}
每次修改之前都需要查询它。。
删除记录 Repo.delete
iex > TestEcto.Repo.delete(book)
16:04:53.865 [debug] QUERY OK db=0.8ms queue=0.5ms idle=1074.6ms
DELETE FROM "books" WHERE "id" = $1 [2]
{:ok,
%TestEcto.Book{
__meta__: #Ecto.Schema.Metadata<:deleted, "books">,
author: "刘慈欣",
id: 2,
title: "三体x",
year: 2007
}}