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