概要
の続きです。
前回、__Elixir__でEcto
を介して__MySQL__に接続し、データベースを作成しました。
- 本記事では、マイグレーション〜スキーマ作成の実装から行っていきます。
- また、Ectoで実際にレコードをMySQLに食わせていきます。
- オマケ的に、Ectoによるテーブルカラム修正の実装も行っていきます。
なお、本記事シリーズにおける__実行環境__等については、前回の記事をご参照ください。
マイグレーション〜テーブル作成
Ectoでマイグレーションを実行していきます。
マイグレーションをつくることで、データベース内のテーブル及びインデックスの作成や更新をする仕組みを整えていきます。
$ mix ecto.gen.migration create_people
* creating priv/repo/migrations
* creating 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
を実行します。
$ 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側で確認してみます。
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で表現したものにあたり、データベースのテーブルに関連付けがされます。
実装は以下の通りです。
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
で実行します。
$ 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_name
とlast_name
を持たない名無しの方だったので、こんどはfirst_name
とlast_name
を持ってる人のデータで実行します。
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)
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_name
、last_name
、age
、すべて持っている人のデータが登録されました。
おまけ: テーブルカラムの修正(NOT NULL制約を追加)
ところで、名前がNULL
の人をinsertできることに関して気持ちがよくないので、NOT NULL
制約を適用しておきます。
apply_notnull_to_names
という名前で、マイグレーションファイルを新しく作ります。
$ mix ecto.gen.migration apply_notnull_to_names
* creating 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しておきます。
mysql> delete from people where id="1";
Query OK, 1 row affected (0.00 sec)
ここまで準備が整ったら、mix ecto.migrate
します。
$ 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_name
とlast_name
へのNOT NULL適用変更が反映されているか見てみます。
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さん_で実行してみます。
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を実行します。
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)
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のクエリを書いて実行していきます。