星低级格式化工具_Elixir 数据库查询工具 Ecto 讲解

本文介绍了Ecto在Elixir中的使用,包括其作为数据映射和查询工具的角色,以及如何避免ORM的面向对象特性。Ecto通过类似SQL的DSL提供直观的查询,并通过Changeset处理数据验证和变更记录。文中详细阐述了Changeset的工作逻辑,以及如何配置Ecto环境,创建数据库表,并展示了增删查改操作的示例。此外,还提及了防止SQL注入的安全措施。
摘要由CSDN通过智能技术生成

也不知道今天写不写得完,写多少算多少吧。

Elixir 是一门怎样的语言,此处不表,可以看一篇被转载了无数次的翻译,我顺带也整合进了专栏:

小亦:Comparing Elixir & Go 译文​zhuanlan.zhihu.com
ac2069dac1579a8e2dd9f7b1480c0977.png

今天介绍一下 Ecto 的使用,和一些容易踩坑的地方。这篇文章也会持续记录我踩到的关于 Ecto 的坑,给大家在缺乏足够文档的时候做做参考。


Ecto is a toolkit for data mapping and language integrated query for Elixir.
—— https:// github.com/elixir-ecto/ ecto

Ecto 团队(其实感觉就 josevalim 一个人主力在撸。。)如是说道:Ecto 是一个数据映射和程序语言查询的工具。

Ecto 不是一个 ORMORM 是面向对象的专属产物,而 Elixir 里面是没有对象的(嗯。写多了会单身?)。但 Ecto 做的事情和 JavaMyBatis 很类似,就是用程式语言去生成 SQL,执行查询并将数据匹配为程式语言的数据结构。得益于 Elixir 支持秀出天际的宏(具体后面文章会讲),Ecto 完美地定义了自己的 DSL,写法和写 SQL 类似,所以还是非常直观。

废话不多说,直接开始走代码!


Ecto 环境配置

创建一个项目(这里用 Phoenix 项目模版举例)

$shell> mix phx.new zhihu_demo

首先我们需要在 mix.exs 文件添加我们的依赖,我们使用 Postgres 进行演示,需要的同学可以自行换成 MySQL

{:postgrex, ">= 0.0.0"}
{:ecto_sql, "~> 3.1"}

然后在 config/dev.exs 中配置好数据库信息:

# Configure your database
config :zhihu_demo, ZhihuDemo.Repo,
  username: System.get_env("DATABASE_USERNAME") || "root",
  password: System.get_env("DATABASE_PASSWORD") || "123456",
  database: System.get_env("DATABASE_DATABASE") || "zhihu_demo_dev",
  hostname: System.get_env("DATABASE_HOSTNAME") || "localhost",
  port: System.get_env("DATABASE_PORT") || "5432",
  show_sensitive_data_on_connection_error: true,
  pool_size: 10

这里的 System.get_env/1 可以获取系统环境变量,方便部署的时候配置和代码隔离(想想真省心)。上面只配置了 dev 环境,其余环境配置方法是一样的,修改数据库配置为正式/测试/开发环境的数据库就行。

然后我们创建一个 ZhihuDemo.Repo 用于数据库的读取操作:

defmodule ZhihuDemo.Repo do
  use Ecto.Repo,
    otp_app: :zhihu_demo,
    adapter: Ecto.Adapters.Postgres
end

因为数据库是需要保持连接的,所以 Ecto 需要在项目启动的时候拥有一个自己的进程。我们需要在 Application 里面添加 ZhihuDemo.Repo

defmodule ZhihuDemo.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  def start(_type, _args) do
    # List all child processes to be supervised
    children = [
      # Start the Ecto repository
      ZhihuDemo.Repo
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: ZhihuDemo.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

至此,Ecto 的环境就算搭建完毕,没有任何花里胡哨的操作和依赖,就这么干净存粹(此处点名批评 Java Spring,配置就得让一个新手 debug 一整天)


Ecto Changeset

在讲 Ecto 的数据库操作前,我们先捋一下 Ecto 对于一个查询语句的处理逻辑。

Ecto 会将所有的信息(用户输入的 sql 语句、数据校验结果、约束信息等)都封装成一个 %Ecto.Changeset{},这个 changset名副其实得记录了所有得变更记录。我们看一下这个 module 的描述:

Changesets allow filtering, casting, validation and definition of constraints when manipulating structs.

简单翻译一下:changeset允许过滤、cast(这咋翻译??cast也能翻译?额。。大概就是把一类别数据转化成另一类别数据的一种操作吧,比如 Java 里面的 int number = (int)1.0 就是把 float castintEctocast 类似,主要是根据数据类型做匹配) 、验证数据合法性、校验数据约束关系这些个操作。

我们看一个实际的例子:

#Ecto.Changeset<
   action: :insert,
   changes: %{
     avatar: "https://forktea.com/path_of_image.jpg",
     capacity: 1500,
     inserted_at: ~N[2020-05-06 19:43:23],
     name: "超大杯",
     origin_price: 100,
     sale_price: 80,
     updated_at: ~N[2020-05-06 19:43:23]
   },
   errors: [
     size: {"is invalid", [type: ZhihuDemo.Cup.CupSize, validation: :cast]}
   ],
   data: #ZhihuDemo.Cup<>,
   valid?: false 
 >

这个 changeset 就记录了一个 insert 操作的变更信息,在校验数据的时候发现字段 size 不合法,所以 errors 里面就标示出了这样的错误信息。当 Ecto.Repo需要执行数据库操作的时候发现 valid?falseerrors 不为空是会把里面的错误信息当作异常抛出去的,Phoenix 这类 mvc 框架对 Ecto 有支持后就自动会把 Ecto 抛出的错误信息格式化为 HttpResponse 传给用户(当然你也可以接住这些异常然后自定义处理方式)。

正常的 changeset 大概长这样:

#Ecto.Changeset<
  action: nil,
  changes: %{
    avatar: "https://forktea.com/path_of_image.jpg",
    capacity: 1500,
    name: "超大杯",
    origin_price: 100,
    sale_price: 80,
    size: :huge
  },
  errors: [],
  data: #ZhihuDemo.Cup<>,
  valid?: true
>

Ecto 拿到这样一个 changeset 后就就知道该干啥了,如果是要执行 insert 操作,就会根据 #ZhihuDemo.Cup<> 这个结构来匹配出对应的 sql 语句进行 insert 操作:

INSERT INTO "cups" ("avatar","capacity","count_like","count_share","inserted_at","name","order_seq","origin_price","sale_price","size","status","updated_at") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13) RETURNING "id" ["https://forktea.com/path_of_image.jpg", 1500, 0, 0, ~N[2020-05-06 19:50:40], "超大杯", 0, 100, 80, 13, 1, ~N[2020-05-06 19:50:40]]

聪明的宝宝们肯定已经看出来了,Ecto 自个儿就把传入进来的数据参数化了,确保绝壁不会在 Ecto 手里有 sql 注入这种低级错误。

总结一句话:Ecto 将用户想干的事情都写到 changeset 这个小本本里面,然后处理这个 changeset,将它转化为对应数据库支持的 sql 进行执行。


Ecto 增删查改

我们先建立一个表。我们有一个用户表 user,字段有 nicknameavataremailpassword 等。如果是 Phoenix 框架下,我们可以用这样的命令创建一个 ecto context 信息,会自动生成相关的代码文件:

#格式:mix phx.gen.context <模块名称> <数据表模块名称> <数据表名> [<字段名:类型>]...
$shell> mix phx.gen.context Accounts User users nickname:string avatar:string email:string password:string

然后我们就有了这样一个 schema 信息:

defmodule Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset

  schema "users" do
    field :nickname, :string
    field :avatar, :string
    field :email, :string
    field :password, :string

    timestamps()
  end

  @doc false
  def changeset(user, attrs) do
    user
    |> cast(attrs, [:nickname, :avatar, :email, :password])
    |> validate_required([:nickname, :avatar])
  end
end

同时附赠 migration 记录(数据库结构变更记录),通常在目录 priv/repo/migrations 下:

defmodule ZhihuDemo.Repo.Migrations.CreateUsers do
  use Ecto.Migration

  def change do
    create table(:users) do
      add :nickname, :string
      add :avatar, :string
      add :email, :string
      add :password, :string

      timestamps()
    end
  end
end

(很多项目其实是不带数据库结构变更记录的,这样的话做版本回滚的时候必然凉凉,Ecto 自带变更记录和回滚操作,比起很多框架自个儿开发的用起来必然舒服得多)

同时生成的文件里面还附赠了一个 lib/zhiu_demo/accounts.ex 文件,里面定义了几个简单的增删查改操作。因为我们要仔细讲讲如何使用 Ecto 的增删查改操作,所以这个文件我们就略过不讲。

Ecto INSERT/UPDATE

我等会继续写。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值