Ecto #
一、Ecto概述 #
1.1 什么是Ecto #
Ecto是Elixir的数据库包装器和查询语言,提供:
- Schema定义
- Changeset验证
- 查询DSL
- 迁移管理
1.2 配置 #
elixir
config :my_app, MyApp.Repo,
adapter: Ecto.Adapters.Postgres,
username: "postgres",
password: "postgres",
database: "my_app_dev",
hostname: "localhost",
pool_size: 10
二、Repo #
2.1 定义Repo #
elixir
defmodule MyApp.Repo do
use Ecto.Repo,
otp_app: :my_app,
adapter: Ecto.Adapters.Postgres
end
2.2 基本操作 #
elixir
MyApp.Repo.all(User)
MyApp.Repo.get(User, 1)
MyApp.Repo.get_by(User, email: "test@example.com")
MyApp.Repo.insert(%User{name: "Alice"})
MyApp.Repo.update(user)
MyApp.Repo.delete(user)
三、Schema #
3.1 定义Schema #
elixir
defmodule MyApp.Accounts.User do
use Ecto.Schema
schema "users" do
field :name, :string
field :email, :string
field :age, :integer
field :active, :boolean, default: true
timestamps()
end
end
3.2 关联关系 #
elixir
defmodule MyApp.Accounts.User do
use Ecto.Schema
schema "users" do
field :name, :string
has_many :posts, MyApp.Blog.Post
has_one :profile, MyApp.Accounts.Profile
belongs_to :organization, MyApp.Accounts.Organization
many_to_many :roles, MyApp.Accounts.Role, join_through: "user_roles"
timestamps()
end
end
3.3 预加载 #
elixir
user = MyApp.Repo.get(User, 1) |> MyApp.Repo.preload(:posts)
user.posts
四、Changeset #
4.1 定义Changeset #
elixir
defmodule MyApp.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
schema "users" do
field :name, :string
field :email, :string
field :age, :integer
timestamps()
end
def changeset(user, attrs) do
user
|> cast(attrs, [:name, :email, :age])
|> validate_required([:name, :email])
|> validate_format(:email, ~r/@/)
|> validate_number(:age, greater_than: 0)
|> unique_constraint(:email)
end
end
4.2 使用Changeset #
elixir
changeset = User.changeset(%User{}, %{name: "Alice", email: "alice@example.com"})
case MyApp.Repo.insert(changeset) do
{:ok, user} -> IO.puts("Created user: #{user.name}")
{:error, changeset} -> IO.inspect(changeset.errors)
end
4.3 验证函数 #
elixir
changeset
|> validate_required([:name, :email])
|> validate_length(:name, min: 2, max: 100)
|> validate_format(:email, ~r/@/)
|> validate_number(:age, greater_than: 0, less_than: 150)
|> validate_inclusion(:status, ["active", "inactive"])
|> validate_confirmation(:password)
|> unique_constraint(:email)
|> foreign_key_constraint(:organization_id)
五、查询 #
5.1 基本查询 #
elixir
import Ecto.Query
from u in User,
where: u.age > 18,
select: u.name
MyApp.Repo.all(from u in User, where: u.age > 18)
5.2 查询操作 #
elixir
from u in User,
where: u.age > 18 and u.active == true,
order_by: [desc: u.inserted_at],
limit: 10,
offset: 20,
select: %{name: u.name, email: u.email}
5.3 聚合查询 #
elixir
from u in User,
select: count(u.id)
from u in User,
select: avg(u.age)
from u in User,
group_by: u.organization_id,
select: {u.organization_id, count(u.id)}
5.4 关联查询 #
elixir
from u in User,
join: p in assoc(u, :posts),
where: p.published == true,
select: {u.name, p.title}
from p in Post,
join: u in User,
on: p.user_id == u.id,
select: {u.name, p.title}
5.5 子查询 #
elixir
active_users = from u in User, where: u.active == true
from p in Post,
join: u in subquery(active_users),
on: p.user_id == u.id,
select: p.title
六、迁移 #
6.1 创建迁移 #
bash
mix ecto.gen.migration create_users
6.2 迁移文件 #
elixir
defmodule MyApp.Repo.Migrations.CreateUsers do
use Ecto.Migration
def change do
create table(:users) do
add :name, :string
add :email, :string
add :age, :integer
timestamps()
end
create unique_index(:users, [:email])
end
end
6.3 运行迁移 #
bash
mix ecto.create
mix ecto.migrate
mix ecto.rollback
七、总结 #
本章学习了:
| 特性 | 用途 |
|---|---|
| Repo | 数据库连接 |
| Schema | 数据模型 |
| Changeset | 验证和转换 |
| Query | 查询DSL |
| Migration | 数据库迁移 |
准备好学习LiveView了吗?让我们进入下一章。
最后更新:2026-03-27