8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-05-22

概要

の続きです。
前回、__Elixir__でEctoを介して__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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?