LoginSignup
8
3

More than 3 years have passed since last update.

ElixirでMySQLを使う #2(Ecto/マイグレーション〜スキーマ作成〜カラム修正)

Last updated at Posted at 2020-05-22

概要

の続きです。
前回、ElixirEctoを介してMySQLに接続し、データベースを作成しました。

  • 本記事では、マイグレーション〜スキーマ作成の実装から行っていきます。
  • また、Ectoで実際にレコードをMySQLに食わせていきます。
  • オマケ的に、Ectoによるテーブルカラム修正の実装も行っていきます。

なお、本記事シリーズにおける実行環境等については、前回の記事をご参照ください。

マイグレーション〜テーブル作成

Ectoでマイグレーションを実行していきます。

マイグレーションをつくることで、データベース内のテーブル及びインデックスの作成や更新をする仕組みを整えていきます。

terminal
  $ mix ecto.gen.migration create_people
  * creating priv/repo/migrations
  * creating priv/repo/migrations/20200521003013_create_people.exs
priv/repo/migrations/20200521003013_create_people.exs
  def change do
    create table(:people) do    --> add
      add :first_name, :string  --> add
      add :last_name, :string   --> add
      add :age, :integer        --> add
    end
  end

データ型の定義を行いました。
ちなみに、デフォルト値の設定やNOT NULL(NIL)などバリデーション関係は書いていませんが、一旦このまま進めます。
(NOT NULL制約の適用修正については後述します)

mix ecto.migrateを実行します。

terminal
  $ mix ecto.migrate

  09:32:21.356 [info]  == Running 20200521003013 Friendsmysql.Repo.Migrations.CreatePeople.change/0 forward

  09:32:21.358 [info]  create table people

  09:32:21.370 [info]  == Migrated 20200521003013 in 0.0s

テーブルpeopleが作成できました!

結果確認(MySQL)

この時点で、念のためデータベースの状況をMySQL側で確認してみます。

terminal(MySQL)
mysql> show tables;
+-----------------------------+
| Tables_in_friendsmysql_repo |
+-----------------------------+
| people                      |
| schema_migrations           |
+-----------------------------+
2 rows in set (0.00 sec)

mysql> desc people;
+------------+---------------------+------+-----+---------+----------------+
| Field      | Type                | Null | Key | Default | Extra          |
+------------+---------------------+------+-----+---------+----------------+
| id         | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| first_name | varchar(255)        | YES  |     | NULL    |                |
| last_name  | varchar(255)        | YES  |     | NULL    |                |
| age        | int(11)             | YES  |     | NULL    |                |
+------------+---------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

テーブルpeopleがデータベースに入ってます。

スキーマ作成

つづいて、Ecto.Schemeのセッティングをしていきます。

Scheme(スキーマ)は、データベースのデータをElixirで表現したものにあたり、データベースのテーブルに関連付けがされます。

実装は以下の通りです。

lib/friends/person.ex
  defmodule Friendsmysql.Person do
    use Ecto.Schema

    schema "people" do
      field :first_name, :string
      field :last_name, :string
      field :age, :integer
    end
  end

レコード作成(Create)〜MySQLにデータを渡す

それでは、以下の手順でElixir側からMySQL側へレコードを渡してみます。
IExで実行します。

terminal
  $ iex -S mix

  iex(1)> person = %Friendsmysql.Person{age: 28}
  %Friendsmysql.Person{
    __meta__: #Ecto.Schema.Metadata<:built, "people">,
    age: 28,
    first_name: nil,
    id: nil,
    last_name: nil
  }

  iex(2)> Friendsmysql.Repo.insert(person)

  09:39:55.834 [debug] QUERY OK db=0.9ms decode=0.8ms queue=1.8ms idle=321.1ms
  INSERT INTO `people` (`age`) VALUES (?) [28]
  {:ok,
  %Friendsmysql.Person{
    __meta__: #Ecto.Schema.Metadata<:loaded, "people">,
    age: 28,
    first_name: nil,
    id: 1,
    last_name: nil
  }}

person = %Friendsmysql.Person{age: 28}で、personという人のデータをつくり、insert()でデータをDBテーブルに渡しました。

つづけて、もう1レコード用意してみます。
先ほどのpersonさんはage: 28だけで、first_namelast_nameを持たない名無しの方だったので、こんどはfirst_namelast_nameを持ってる人のデータで実行します。

terminal
  iex(3)> im = %Friendsmysql.Person{first_name: "im", last_name: "miolab", age: 28}
  %Friendsmysql.Person{
    __meta__: #Ecto.Schema.Metadata<:built, "people">,
    age: 28,
    first_name: "im",
    id: nil,
    last_name: "miolab" 
  }

  iex(4)> Friendsmysql.Repo.insert(im)

  09:42:41.351 [debug] QUERY OK db=1.5ms queue=1.6ms idle=1841.3ms
  INSERT INTO `people` (`age`,`first_name`,`last_name`) VALUES (?,?,?) [28, "im", "miolab"]
  {:ok,
  %Friendsmysql.Person{
    __meta__: #Ecto.Schema.Metadata<:loaded, "people">,
    age: 28,
    first_name: "im",
    id: 2,
    last_name: "miolab"
  }}

結果確認(MySQL)

terminal(MySQL)
  mysql> select * from people;
  +----+------------+-----------+------+
  | id | first_name | last_name | age  |
  +----+------------+-----------+------+
  |  1 | NULL       | NULL      |   28 |
  |  2 | im         | miolab    |   28 |
  +----+------------+-----------+------+
  2 rows in set (0.00 sec)

first_namelast_nameage、すべて持っている人のデータが登録されました。

おまけ: テーブルカラムの修正(NOT NULL制約を追加)

ところで、名前がNULLの人をinsertできることに関して気持ちがよくないので、NOT NULL制約を適用しておきます。

apply_notnull_to_namesという名前で、マイグレーションファイルを新しく作ります。

terminal
$ mix ecto.gen.migration apply_notnull_to_names
* creating priv/repo/migrations/20200522004701_apply_notnull_to_names.exs

以下のようにコード記述します。

priv/repo/migrations/20200522004701_apply_notnull_to_names.exs
  def change do
    alter table(:people) do    --> add
      modify :first_name, :string, null: false    --> add
      modify :last_name, :string, null: false     --> add
    end
  end

なお、さきほど一番はじめにデータベースへ登録された「名前がNULLの人」は、MySQL側でdeleteしておきます。

terminal(MySQL)
mysql> delete from people where id="1";
Query OK, 1 row affected (0.00 sec)

ここまで準備が整ったら、mix ecto.migrateします。

terminal
$ mix ecto.migrate

09:54:06.756 [info]  == Running 20200522004701 Friendsmysql.Repo.Migrations.ApplyNotnullToNames.change/0 forward

09:54:06.758 [info]  alter table people

09:54:06.899 [info]  == Migrated 20200522004701 in 0.1s
impc:friendsmysql im$ iex -S mix
Erlang/OTP 22 [erts-10.5.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

結果確認(MySQL)

MySQL側で、first_namelast_nameへのNOT NULL適用変更が反映されているか見てみます。

terminal(MySQL)
mysql> desc people;
+------------+---------------------+------+-----+---------+----------------+
| Field      | Type                | Null | Key | Default | Extra          |
+------------+---------------------+------+-----+---------+----------------+
| id         | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| first_name | varchar(255)        | NO   |     | NULL    |                |
| last_name  | varchar(255)        | NO   |     | NULL    |                |
| age        | int(11)             | YES  |     | NULL    |                |
+------------+---------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

変更されていることを確認できました。

MySQLにデータを渡す(NOT NULL制約適用後)

ふたたびIExで、MySQLにデータを投げてみます。

まずは失敗パターン想定で、先ほどと同じ名無しのpersonさんで実行してみます。

terminal
iex(1)> person = %Friendsmysql.Person{age: 28}
%Friendsmysql.Person{
  __meta__: #Ecto.Schema.Metadata<:built, "people">,
  age: 28,
  first_name: nil,
  id: nil,
  last_name: nil
}

iex(2)> Friendsmysql.Repo.insert(person)

09:55:02.022 [debug] QUERY ERROR db=10.6ms queue=2.2ms idle=145.0ms
INSERT INTO `people` (`age`) VALUES (?) [28]
** (MyXQL.Error) (1364) (ER_NO_DEFAULT_FOR_FIELD) Field 'first_name' doesn't have a default value
    (ecto_sql) lib/ecto/adapters/myxql.ex:242: Ecto.Adapters.MyXQL.insert/6
    (ecto) lib/ecto/repo/schema.ex:661: Ecto.Repo.Schema.apply/4
    (ecto) lib/ecto/repo/schema.ex:263: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4

ぶじ失敗を確認できました。
first_nameを持たないため、errorで弾かれています。

つづいて、成功パターン想定でデータinsertを実行します。

terminal
iex(2)> ryan_bigg = %Friendsmysql.Person{first_name: "Ryan", last_name: "Bigg"}         
%Friendsmysql.Person{
  __meta__: #Ecto.Schema.Metadata<:built, "people">,
  age: nil,
  first_name: "Ryan",
  id: nil,
  last_name: "Bigg"
}

iex(3)> Friendsmysql.Repo.insert(ryan_bigg)                                    

09:55:11.713 [debug] QUERY OK db=1.3ms queue=0.7ms idle=1849.1ms
INSERT INTO `people` (`first_name`,`last_name`) VALUES (?,?) ["Ryan", "Bigg"]
{:ok,
 %Friendsmysql.Person{
   __meta__: #Ecto.Schema.Metadata<:loaded, "people">,
   age: nil,
   first_name: "Ryan",
   id: 3,
   last_name: "Bigg"
 }}

こんどは問題なく登録できました。

結果確認(MySQL)

terminal(MySQL)
  mysql> select * from people;
  +----+------------+-----------+------+
  | id | first_name | last_name | age  |
  +----+------------+-----------+------+
  |  2 | im         | miolab    |   28 |
  |  3 | Ryan       | Bigg      | NULL |
  +----+------------+-----------+------+
  2 rows in set (0.00 sec)

終わり & 次回

マイグレーション〜スキーマ作成を行って、Elixir側からデータをMySQLに渡すことができました。
また、Ectoを介してちょっとしたテーブルカラムの修正も行いました。

次回は、ElixirでのCRUD操作(データ作成・読み出し・更新・削除)を、Ectoのクエリを書いて実行していきます。

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3