20200531のMySQLに関する記事は6件です。

SQLで時間差を算出する

データが作成された日と更新日との差を取得するために、エラーメッセージが出たときの備忘録。

失敗作

時間差を算出するにはTIMEDIFFがよいということで、下記のコードで実行。

SELECT DATEDIFF(DAY, createTimestamp, updateTimestamp) AS DiffDate

このときのデータの型はDATETIMEで長さは6と定義したもので、値は2019-08-02 12:53:33.172000になります。
しかし、上記コードではError Code: 1582 Incorrect parameter count in the call to native function 'DATEDIFF'のエラー発生。

解決策

こちらのサイトでも同じ様な悩みを持った人がいたようです。
DATEDIFFでは2つのパラーメターしか許可されず、日数で返却されるようです。

下記のコードで無事解決出来ました。

SELECT DATEDIFF(createTimestamp, updateTimestamp) AS DiffDate; 

他サイトで、上記3つのパラメータがとれるときと取れない時の違いが分からない点に不安を残しつつ、そのうち解決していきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MySQLでよく使う便利なコマンド20選

データベースの一覧

mysql> SHOW DATABASES;
+-------------------------+
| Database                |
+-------------------------+
| information_schema      |
| mysql                   |
| mysql_rails_development |
| mysql_rails_test        |
| performance_schema      |
| sys                     |
| tabechoku               |
| tabechoku_test          |
+-------------------------+
8 rows in set (0.00 sec)

ログインしているユーザーを確認

mysql> SELECT USER();
+----------------+
| user()         |
+----------------+
| root@localhost |
+----------------+
1 row in set (0.00 sec)

ユーザーの一覧

mysql> SELECT user,host FROM mysql.user;
+---------------+-----------+
| user          | host      |
+---------------+-----------+
| hatsu         | localhost |
| mysql.session | localhost |
| mysql.sys     | localhost |
| root          | localhost |
+---------------+-----------+
4 rows in set (0.00 sec)

TIPS

SQL文の末尾を ;から \Gに変えると縦にデータを並べて表示できる

mysql> SELECT user,host FROM mysql.user\G
*************************** 1. row ***************************
user: hatsu
host: localhost
*************************** 2. row ***************************
user: mysql.session
host: localhost
*************************** 3. row ***************************
user: mysql.sys
host: localhost
*************************** 4. row ***************************
user: root
host: localhost
4 rows in set (0.01 sec)

現在接続しているクライアントを表示

mysql> SHOW FULL PROCESSLIST\G
*************************** 1. row ***************************
     Id: 68
   User: root
   Host: localhost
     db: mysql_rails_development
Command: Query
   Time: 0
  State: starting
   Info: SHOW FULL PROCESSLIST
1 row in set (0.00 sec)

特定のユーザーの権限を確認

mysql> SHOW GRANTS FOR 'root'@'localhost';
+---------------------------------------------------------------------+
| Grants for root@localhost                                           |
+---------------------------------------------------------------------+
| GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION |
| GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION        |
+---------------------------------------------------------------------+
2 rows in set (0.00 sec)

Statusを確認

mysql> STATUS;
--------------
mysql  Ver 14.14 Distrib 5.7.29, for osx10.15 (x86_64) using  EditLine wrapper

Connection id:      68
Current database:
Current user:       root@localhost
SSL:            Not in use
Current pager:      stdout
Using outfile:      ''
Using delimiter:    ;
Server version:     5.7.29 Homebrew
Protocol version:   10
Connection:     Localhost via UNIX socket
Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    utf8
Conn.  characterset:    utf8
UNIX socket:        /tmp/mysql.sock
Uptime:         1 day 2 hours 10 min 56 sec

Threads: 1  Questions: 2580  Slow queries: 0  Opens: 1585  Flush tables: 1  Open tables: 1050  Queries per second avg: 0.027
--------------

MySQLのバージョンを確認

mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 5.7.29    |
+-----------+
1 row in set (0.00 sec)

使うデータベースを切り替える

mysql> USE mysql_rails_development;

テーブル一覧

mysql> SHOW TABLES;
+-----------------------------------+
| Tables_in_mysql_rails_development |
+-----------------------------------+
| ar_internal_metadata              |
| articles                          |
| schema_migrations                 |
+-----------------------------------+
3 rows in set (0.01 sec)

テーブル一覧の詳細

ysql> SHOW TABLE STATUS\G
*************************** 1. row ***************************
           Name: ar_internal_metadata
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 1
 Avg_row_length: 16384
    Data_length: 16384
Max_data_length: 0
   Index_length: 0
      Data_free: 0
 Auto_increment: NULL
    Create_time: 2020-05-31 15:02:22
    Update_time: 2020-05-31 15:10:26
     Check_time: NULL
      Collation: utf8mb4_general_ci
       Checksum: NULL
 Create_options:
        Comment:
*************************** 2. row ***************************
           Name: articles
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 0
 Avg_row_length: 0
    Data_length: 16384
Max_data_length: 0
   Index_length: 0
      Data_free: 0
 Auto_increment: 1
    Create_time: 2020-05-31 15:10:26
    Update_time: NULL
     Check_time: NULL
      Collation: utf8mb4_general_ci
       Checksum: NULL
 Create_options:
        Comment: 記事
*************************** 3. row ***************************
           Name: schema_migrations
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 1
 Avg_row_length: 16384
    Data_length: 16384
Max_data_length: 0
   Index_length: 0
      Data_free: 0
 Auto_increment: NULL
    Create_time: 2020-05-31 15:02:22
    Update_time: 2020-05-31 15:10:26
     Check_time: NULL
      Collation: utf8mb4_general_ci
       Checksum: NULL
 Create_options:
        Comment:
3 rows in set (0.00 sec)

テーブル定義の確認

mysql> SHOW CREATE TABLE articles \G
*************************** 1. row ***************************
       Table: articles
Create Table: CREATE TABLE `articles` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL COMMENT 'タイトル',
  `detail` text COMMENT '本文',
  `created_at` datetime(6) NOT NULL,
  `updated_at` datetime(6) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='記事'
1 row in set (0.00 sec)

テーブルの各種情報を確認

mysql> SHOW TABLE STATUS LIKE 'articles'\G
*************************** 1. row ***************************
           Name: articles
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 0
 Avg_row_length: 0
    Data_length: 16384
Max_data_length: 0
   Index_length: 0
      Data_free: 0
 Auto_increment: 1
    Create_time: 2020-05-31 15:10:26
    Update_time: NULL
     Check_time: NULL
      Collation: utf8mb4_general_ci
       Checksum: NULL
 Create_options:
        Comment: 記事
1 row in set (0.00 sec)

テーブルのカラム一覧

mysql> DESC articles;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| title      | varchar(255) | NO   |     | NULL    |                |
| detail     | text         | YES  |     | NULL    |                |
| created_at | datetime(6)  | NO   |     | NULL    |                |
| updated_at | datetime(6)  | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

テーブルのIndexを確認

mysql> SHOW INDEX FROM users \G
*************************** 1. row ***************************
        Table: users
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: id
    Collation: A
  Cardinality: 101200
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:
*************************** 2. row ***************************
        Table: users
   Non_unique: 0
     Key_name: index_users_on_email
 Seq_in_index: 1
  Column_name: email
    Collation: A
  Cardinality: 101200
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:

SELECTの結果をファイルに書き出す

mysql> SELECT * FROM USERS INTO OUTFILE 'users.txt';

Auto Commitモードにする/しない

  • SQL文を実行した後にすぐにコミットするモード
  • デフォルトでは、AutoCommitモードになっている

AutoCommitモードにする

mysql> SET AUTOCOMMIT=1;
Query OK, 0 rows affected (0.00 sec)

AutoCommitモードを取り消す

mysql> SET AUTOCOMMIT=0;
Query OK, 0 rows affected (0.00 sec)

テーブルをロックする

  • READロック:誰もがテーブルを読み込めるが、誰も書き込みはできない
  • WRITEロック:自分だけがそのテーブルを読み書きできる。他のユーザーは読むこともできない
  • Lockを外すときは、UNLOCK TABLES
mysql> LOCK TABLES users READ, items WRITE;
mysql> UNLOCK TABLES;

前のエラー内容を見る

mysql> SHOW WARNINGS;
ERROR 1193 (HY000): Unknown system variable 'AUTOCOMIT'

テーブルをコピーする

  • souceテーブルの構造、データをコピーしてcopyテーブルを作る
mysql> CREATE TABLE copy SELECT * FROM souce;
  • テーブル構造のみをコピー
mysql> CREATE TABLE copy LIKE souce;

テーブルが存在したら/しなかったら

テーブルが存在しなかったら作成する

mysql> CREATE TABLE IF NOT EXISTS foo_table;

テーブルが存在したら作成する

mysql> CREATE TABLE IF NOT EXISTS foo_table;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Capistrano自動デプロイ設定時(初回)に出たエラー「Mysql2::Error: Table 'users' already exists」の解決方法

アプリを本番環境に上げる際にCapistranoを導入しようとしたら、下記のエラーが出ました。

Mysql2::Error: Table 'users' already exists: CREATE TABLE 'users'

「ユーザーズテープルすでにあるよ」と怒られているようです。

状況を整理

・手動デプロイは上手くいっている。
・Capistranoの【初回デプロイ】(つまりまだ一回も成功していない)でエラーが出た。
・MySQLやusersテーブルについて言及されているので、マイグレーション系のエラーである。

どうやら、本番環境下のマイグレーション関係で問題が起きている模様。


解決法

結論、
ローカル環境で修正したマイグレーションが本番環境で反映されてませんでした。

[ec2-user@ip <リポジトリ名>] 以下で下記コマンドを打って解決

①DBを削除する

ターミナル
RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bundle exec rake db:drop 

(Railsバージョンによって異なる模様。参考記事参照)

②DBをcreateする

ターミナル
rails db:create RAILS_ENV=production



③DBをmigrateする

ターミナル
rails db:migrate RAILS_ENV=production



■ちなみに、①DBを削除するCapistranoの自動デプロイが成功した後であれば、データベースが「current」に入っていると思われるので「current」で削除を行う模様です(試していません)

■参考記事
Rails5以降の本番環境でデータベースをresetする方法!



デプロイは難しいですね。ですがコーディングと同様にエラーのたびに理解が深まるので知識が積み重なってく感じがすきです。頑張りましょ〜

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker mysql sqlファイル実行されない

はじめに

おっちょこちょい事件でした、、、

参考dockerhub_mysql

結論から言うと、コンテナを最初に作成するときにsqlファイル実行されるので、
コンテナ削除までしましょうってことでした!!
コンテナストップまでではなくコンテナ削除まで!
ちなみに、イメージの削除までは行わなくて良いっぽいですね!

なので行う処理は以下みたいな感じです!

:older_man_tone3:sqlコンテナ削除で再度コンテナ構築まで

a725b2d62fceは自分のものを

$ docker ps
$ docker stop a725b2d62fce
$ docker rm a725b2d62fce
$ docker-compose up --build -d

:older_man_tone3:sqlコンテナ入る

602a4868b08bは自分のものを

$ docker exec -i -t 602a4868b08b bash

:older_man_tone3:sqlログインする
$ mysql -u root -p
パスワード以下のものならexample

:older_man_tone3:コンテナ入っての確認

$ SHOW DATABASES;
$ use mydb01;
$ SHOW TABLES;

本文引用

When a container is started for the first time, a new database with the specified name will be created and initialized with the provided configuration variables. Furthermore, it will execute files with extensions .sh, .sql and .sql.gz that are found in /docker-entrypoint-initdb.d. Files will be executed in alphabetical order. You can easily populate your mysql services by mounting a SQL dump into that directory and provide custom images with contributed data. SQL files will be imported by default to the database specified by the MYSQL_DATABASE variable.

google翻訳

コンテナが初めて起動されると、指定された名前の新しいデータベースが作成され、提供された構成変数で初期化されます。 さらに、/ docker-entrypoint-initdb.dにある拡張子.sh、.sql、.sql.gzのファイルを実行します。 ファイルはアルファベット順に実行されます。 SQLダンプをそのディレクトリにマウントすることでmysqlサービスを簡単に作成し、提供されたデータを含むカスタムイメージを提供できます。 SQLファイルは、デフォルトでMYSQL_DATABASE変数で指定されたデータベースにインポートされます。

大事なところ

コンテナが初めて起動されると、指定された名前の新しいデータベースが作成され、提供された構成変数で初期化されます。さらに、/ docker-entrypoint-initdb.dにある拡張子.sh、.sql、.sql.gzのファイルを実行します。

構成ファイル(必要部分のみ)

docker-compose.ymlの記述。以下ピンポイントのみ

docker-compose.yml
version: '3'
services:
  mysql:
    container_name: sample-mysql1 #作成されていると出来ないので新しく作成する場合ここの名前を変更
    build: ./mysql/ #mysqlのDockerfileを使用してコンテナ
    volumes:
      - ./mysql/initdb.d:/docker-entrypoint-initdb.d # ./mysql/initdb.dは入れたいフォルダ名パス。
    environment:
      MYSQL_DATABASE: mydb01 #はじめに作成します!データベース
      MYSQL_ROOT_PASSWORD: example # これでmysqlのrootユーザー exampleパスで入れる
    restart: always

mysql/initdb.d/Sample.sqlの中 Sample.sqlだけ書いているが複数あっても良い

Sample.sql
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    email VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL,
    created DATETIME,
    modified DATETIME
);

INSERT INTO users (email, password, created, modified)
VALUES
('cakephp@example.com', 'secret', NOW(), NOW());
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「Golang」 ×「Gorm」でシンプルに「Mysql」を操作する

REST APIサーバから受け取ったリクエストパラメータの内容を元にリレーショナルデータベース(Mysql)を更新する」という流れで、説明を進めています。

以下の記事の続編という形で投稿を行っておりますので、仕様などの前提条件はこちらで確認していただけるとありがたいです。

「Golang」 ×「gorilla/mux router」でシンプルなREST APIサーバーを開発する

データベース(Mysql)準備

コードを実装する前に、Mysql上でデータベースの作成を行います。
Mysql自体導入していない場合は、インストールが必要です。詳細は他のサイトで解説されているのでここでは割愛します。

# ルートユーザーでログイン
mysql -uroot

# mysql shell上
CREATE DATABASE { データベース名 };

# ログインユーザー作成
GRANT ALL PRIVILEGES ON { データベース名 }.* TO 'username'@'localhost' IDENTIFIED BY 'password';

# 一度mysqlからログアウトして、再度作成したユーザーでログイン
mysql -u username -p

# 念の為、データベースが作成されているか確認
SHOW DATABASES;

コード実装

バージョン情報

ORMマッピング

SQL文でデータベースを操作することも可能ですが、今回は「コードを簡素化する」・「単純なDB操作のみ行う」ためにORMでマッピングします。
データベースの複雑な操作が必要な場合はSQLをそのまま書いたほうが、メンテナンスしやすいと思います。
Gormを採用した理由としては「スター数が多い」「ドキュメントが充実している」からです。

root上でgoモジュールインストール
go get github.com/jinzhu/gorm

フォルダ構成

フォルダ構成
merchandise_control_system
├── config
│   └── config.go
├── controllers
│   └── webserver.go
├── models
│   └── middleware.go
├── config.ini
├── go.mod
├── go.sum
└── main.go

サーバポート情報・データベース接続情報をconfigに記載する

APIサーバのポート番号、DBの接続情報は一箇所にまとめ読み込むようにすることで可読性が向上します。

root上でgoモジュールインストール
go get gopkg.in/ini.v1
config.ini
[db]
db_driver_name = mysql
db_name = { データベース名 }
db_user_name = { データベースユーザー名 }
db_user_password = { データベースパスワード }
db_host = 127.0.0.1
db_port = 3306

[api]
server_port = 8080
config/config.go
package config

import (
    "gopkg.in/ini.v1"
    "log"
    "os"
)

type ConfigList struct {
    DbDriverName   string
    DbName         string
    DbUserName     string
    DbUserPassword string
    DbHost         string
    DbPort         string
    ServerPort     int
}

var Config ConfigList

func init() {
    cfg, err := ini.Load("config.ini")
    if err != nil {
        log.Printf("Failed to read file: %v", err)
        os.Exit(1)
    }

    Config = ConfigList{
        DbDriverName:   cfg.Section("db").Key("db_driver_name").String(),
        DbName:         cfg.Section("db").Key("db_name").String(),
        DbUserName:     cfg.Section("db").Key("db_user_name").String(),
        DbUserPassword: cfg.Section("db").Key("db_user_password").String(),
        DbHost:         cfg.Section("db").Key("db_host").String(),
        DbPort:         cfg.Section("db").Key("db_port").String(),
        ServerPort:     cfg.Section("api").Key("server_port").MustInt(),
    }
}

コード全体

イメージとしては、main.goからコントローラを呼び出し、APIサーバを起動。
APIリクエストが行われると、コントローラ内からモデルが呼び出され、データベースの値を参照・更新。
最終的にレスポンスが返されるといった流れになります。

main.go
package main

import (
    "merchandise_control_system/controllers"
)

func main() {
    controllers.StartWebServer()
}
controllers/webserver.go
package controllers

import (
    "encoding/json"
    "fmt"
    "github.com/gorilla/mux"
    "io/ioutil"
    "log"
    "merchandise_control_system/config"
    "merchandise_control_system/models"
    "net/http"
    "strconv"
)

type DeleteResponse struct {
    Id string `json:"id"`
}

func rootPage(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Welcome to the Go Api Server")
    fmt.Println("Root endpoint is hooked!")
}

func fetchAllItems(w http.ResponseWriter, r *http.Request) {
    var items []models.Item
    // modelの呼び出し
    models.GetAllItems(&items)
    responseBody, err := json.Marshal(items)
    if err != nil {
        log.Fatal(err)
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(responseBody)
}

func fetchSingleItem(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]

    var item models.Item
    // modelの呼び出し
    models.GetSingleItem(&item, id)
    responseBody, err := json.Marshal(item)
    if err != nil {
        log.Fatal(err)
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(responseBody)
}

func createItem(w http.ResponseWriter, r *http.Request) {
    reqBody, _ := ioutil.ReadAll(r.Body)

    var item models.Item
    if err := json.Unmarshal(reqBody, &item); err != nil {
        log.Fatal(err)
    }
    // modelの呼び出し
    models.InsertItem(&item)
    responseBody, err := json.Marshal(item)
    if err != nil {
        log.Fatal(err)
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(responseBody)
}

func deleteItem(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]

    // modelの呼び出し
    models.DeleteItem(id)
    responseBody, err := json.Marshal(DeleteResponse{Id: id})
    if err != nil {
        log.Fatal(err)
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(responseBody)
}

func updateItem(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]
    reqBody, _ := ioutil.ReadAll(r.Body)

    var updateItem models.Item
    if err := json.Unmarshal(reqBody, &updateItem); err != nil {
        log.Fatal(err)
    }
    // modelの呼び出し
    models.UpdateItem(&updateItem, id)
    convertUintId, _ := strconv.ParseUint(id, 10, 64)
    updateItem.Model.ID = uint(convertUintId)
    responseBody, err := json.Marshal(updateItem)
    if err != nil {
        log.Fatal(err)
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(responseBody)
}

func StartWebServer() error {
    fmt.Println("Rest API with Mux Routers")
    router := mux.NewRouter().StrictSlash(true)

    router.HandleFunc("/", rootPage)
    router.HandleFunc("/items", fetchAllItems).Methods("GET")
    router.HandleFunc("/item/{id}", fetchSingleItem).Methods("GET")

    router.HandleFunc("/item", createItem).Methods("POST")
    router.HandleFunc("/item/{id}", deleteItem).Methods("DELETE")
    router.HandleFunc("/item/{id}", updateItem).Methods("PUT")

    return http.ListenAndServe(fmt.Sprintf(":%d", config.Config.ServerPort), router)
}

models/middleware.go
package models

import (
    "fmt"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
    "log"
    "merchandise_control_system/config"
    "time"
)

type Model struct {
    ID        uint        `gorm:"primary_key" json:"id"`
    CreatedAt *time.Time  `json:"created_at"`
    UpdatedAt *time.Time  `json:"updated_at"`
    DeletedAt *time.Time  `json:"deleted_at"`
}

type Item struct {
    Model
    JanCode      string     `gorm:"size:255" json:"jan_code,omitempty"`
    ItemName     string     `gorm:"size:255" json:"item_name,omitempty"`
    Price        int        `json:"price,omitempty"`
    CategoryId   int        `json:"category_id,omitempty"`
    SeriesId     int        `json:"series_id,omitempty"`
    Stock        int        `json:"stock,omitempty"`
    Discontinued bool       `json:"discontinued"`
    ReleaseDate  *time.Time `json:"release_date,omitempty"`
}

var Db *gorm.DB

func GetAllItems(items *[]Item) {
    Db.Find(&items)
}

func GetSingleItem(item *Item, key string) {
    Db.First(&item, key)
}

func InsertItem(item *Item) {
    Db.NewRecord(item)
    Db.Create(&item)
}

func DeleteItem(key string) {
    Db.Where("id = ?", key).Delete(&Item{})
}

func UpdateItem(item *Item, key string) {
    Db.Model(&item).Where("id = ?", key).Updates(
        map[string]interface{}{
            "jan_code":     item.JanCode,
            "item_name":    item.ItemName,
            "price":        item.Price,
            "category_id":  item.CategoryId,
            "series_id":    item.SeriesId,
            "stock":        item.Stock,
            "discontinued": item.Discontinued,
            "release_date": item.ReleaseDate,
        })
}

// データベースの初期化
func init() {
    var err error
    dbConnectInfo := fmt.Sprintf(
        `%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local`,
        config.Config.DbUserName,
        config.Config.DbUserPassword,
        config.Config.DbHost,
        config.Config.DbPort,
        config.Config.DbName,
    )

    // configから読み込んだ情報を元に、データベースに接続します
    Db, err = gorm.Open(config.Config.DbDriverName, dbConnectInfo)
    if err != nil {
        log.Fatalln(err)
    } else {
        fmt.Println("Successfully connect database..")
    }

    // 接続したデータベースにitemsテーブルを作成します
    Db.Set("gorm:table_options", "ENGINE = InnoDB").AutoMigrate(&Item{})
    if err != nil {
        fmt.Println(err.Error())
    } else {
        fmt.Println("Successfully created table..")
    }
}

デモ

APIサーバを起動します。と同時にデータベース接続、テーブル作成が行われます。

go run main.go

Postmanからリクエストを送ってみます。(curlコマンドでもOKです)。

【POST】 /item

最初はデータベースが空のため、データを作成します。

リクエストパラメータ
{
    "jan_code": "32739028488888",
    "item_name": "item_1",
    "price": 1000,
    "category_id": 1,
    "series_id": 1,
    "stock": 1000,
    "discontinued": true,
    "release_date": "2020-05-31T07:00:00.660666+09:00"
}
レスポンスパラメータ
{
    "id": 1,
    "created_at": "2020-05-31T13:46:09.932124+09:00",
    "updated_at": "2020-05-31T13:46:09.932124+09:00",
    "deleted_at": null,
    "jan_code": "32739028488888",
    "item_name": "item_1",
    "price": 1000,
    "category_id": 1,
    "series_id": 1,
    "stock": 1000,
    "discontinued": true,
    "release_date": "2020-05-31T07:00:00.660666+09:00"
}
DB(Mysql)
select * from items;

+----+---------------------+---------------------+------------+----------------+-----------+-------+-------------+-----------+-------+--------------+---------------------+
| id | created_at          | updated_at          | deleted_at | jan_code       | item_name | price | category_id | series_id | stock | discontinued | release_date        |
+----+---------------------+---------------------+------------+----------------+-----------+-------+-------------+-----------+-------+--------------+---------------------+
|  1 | 2020-05-31 13:46:10 | 2020-05-31 13:46:10 | NULL       | 32739028488888 | item_1    |  1000 |           1 |         1 |  1000 |            1 | 2020-05-31 07:00:01 |
+----+---------------------+---------------------+------------+----------------+-----------+-------+-------------+-----------+-------+--------------+---------------------+
1 row in set (0.00 sec)

【GET】 /item/1

レスポンスパラメータ
{
    "id": 1,
    "created_at": "2020-05-31T13:46:10+09:00",
    "updated_at": "2020-05-31T13:46:10+09:00",
    "deleted_at": null,
    "jan_code": "32739028488888",
    "item_name": "item_1",
    "price": 1000,
    "category_id": 1,
    "series_id": 1,
    "stock": 1000,
    "discontinued": true,
    "release_date": "2020-05-31T07:00:01+09:00"
}

【GET】 /items

レスポンスパラメータ
[
    {
        "id": 1,
        "created_at": "2020-05-31T13:46:10+09:00",
        "updated_at": "2020-05-31T13:46:10+09:00",
        "deleted_at": null,
        "jan_code": "32739028488888",
        "item_name": "item_1",
        "price": 1000,
        "category_id": 1,
        "series_id": 1,
        "stock": 1000,
        "discontinued": true,
        "release_date": "2020-05-31T07:00:01+09:00"
    }
]

【PUT】 /item/1

リクエストパラメータ
{
    "jan_code": "3273902878656",
    "item_name": "item_1_update",
    "price": 12000,
    "category_id": 1,
    "series_id": 1,
    "stock": 10,
    "discontinued": false,
    "release_date": "2020-05-30T10:30:19.978603+09:00"
}
レスポンスパラメータ
{
    "id": 1,
    "created_at": null,
    "updated_at": "2020-05-31T14:26:04.165559+09:00",
    "deleted_at": null,
    "jan_code": "3273902878656",
    "item_name": "item_1_update",
    "price": 12000,
    "category_id": 1,
    "series_id": 1,
    "stock": 10,
    "discontinued": false,
    "release_date": "2020-05-30T10:30:19.978603+09:00"
}
DB(Mysql)
select * from items;

+----+---------------------+---------------------+------------+---------------+---------------+-------+-------------+-----------+-------+--------------+---------------------+
| id | created_at          | updated_at          | deleted_at | jan_code      | item_name     | price | category_id | series_id | stock | discontinued | release_date        |
+----+---------------------+---------------------+------------+---------------+---------------+-------+-------------+-----------+-------+--------------+---------------------+
|  1 | 2020-05-31 14:25:15 | 2020-05-31 14:26:04 | NULL       | 3273902878656 | item_1_update | 12000 |           1 |         1 |    10 |            0 | 2020-05-30 10:30:20 |
+----+---------------------+---------------------+------------+---------------+---------------+-------+-------------+-----------+-------+--------------+---------------------+
1 row in set (0.00 sec)

【DELETE】 /item/1

レスポンスパラメータ
{
    "id": "1"
}

まとめ

Gormを使うと、比較的簡単にデータベース接続、テーブル作成、テーブル操作を行うことができました。
また、ORMの特徴として、DBを他のDB(例えばPostgreSQL)に変更したとしてもコードの変更が少なくて済むのもメリットかと思います。

細かいコードメンテナンスは必要ですが、一旦最低限の「APIサーバ起動」と「DB接続」が可能になりました。
今後こちらのサーバサイドコードを成長させながら、「ビュー」部分をReactNativeで実装していきたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

さくらVPSでLAMP + Wordpress環境を構築する方法まとめ

さくらVPSでLAMP + Wordpress環境を構築する方法まとめ

学習用にさくらVPSでLAMP環境を構築したので、手順をまとめます.

目次

動作環境

OS : macOS Mojave 10.14.6
VPS OS : centOS

手順

さくらVPSの申し込み

  1. さくらのwebサイト>VPS>2週間無料でお試し にアクセス
    サーバをカートに入れ申し込み.

  2. メールに記載されているIDとパスワードでログインし,VPSコントロールパネルにログイン

  3. 各種設定>OSインストール
    *OSをインストールする際にVPSにログインするためのパスワードを入力するので忘れずにメモします.
    image.png

これでさくらVPSの申し込みからOSのインストールまでが完了です.

ssh接続

つぎにローカルPCからインターネット上のVPSに接続するためのSSH接続の設定手順をまとめます.

1. Terminal起動とroot権限でログイン

手元のMACでTerminalを起動します.

そして以下のsshコマンドで接続します.
パスワードはOSをインストールした際に入力したものです.

$ ssh root@VPSのIPアドレス

2. ソフトウエアのupdate

$ yum update

3. 一般ユーザの作成

$ adduser jun1 #user追加
$ passwd jun1 #password設定

sudoが使えるように設定

$ visudo
wheel ALL=(ALL) ALL #コメントアウトを外す

jun1ユーザをwheelグループに追加

$ usermod -aG wheel jun1
$ groups jun1
jun1 : jun1 wheel

jun1でログインできるか確認

$ ssh jun1@VPSのIPアドレス
$ パスワード入力 

4. ssh鍵認証の設定

ローカルのPCのターミナルを起動して実行

MAC$ ssh-keygen -t rsa -v
MAC$ ls ~/.ssh
config      id_rsa      id_rsa.pub  known_hosts

sshでVPSに接続

VPS$ mkdir .ssh
VPS$ chmod 700 .ssh

ローカルPCからVPSへ公開鍵を転送

ローカルPC
MAC$ scp ~/.ssh/id_rsa.pub jun1@VPSのIPアドレス:~/.ssh/authorized_keys
MAC$ VPSのパスワード入力

鍵を利用してサーバにログイン

ローカルPC
MAC$ ssh -i ~/.ssh/id_rsa jun1@VPSのIPアドレス

ssh接続のセキュリティ設定

1.ポート番号の設定

VPS
$ sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.ort #設定ファイルのバックアップ
$ sudo vim /etc/ssh/sshd_config

Portを検索し、#を消して, 22 -> 56789(任意の番号で可)

2.パスワード認証の無効化

PasswordAuthenticationを検索し、
noに変更

3.rootログインの無効化

PermitRootLoginで検索し,noに変更

4.設定ファイルの確認

VPS
$ sudo sshd -t #設定ファイルに構文ミスがないか確認.何も出なければOK
$ sudo systemctl restart sshd #sshdの再起動

5.ファイアウォールの設定

VPS
$ sudo cp /usr/lib/firewalld/services/ssh.xml /etc/firewalld/services/ssh-56789.xml

$ sudo vim /etc/firewalld/services/ssh-56789.xml

port番号を22 -> 56789に変更

VPS
$ sudo firewall-cmd --reload
>success
$ sudo firewall-cmd --permanent --add-service=ssh-56789
>success
$ sudo firewall-cmd --reload
>success
$ sudo firewall-cmd --list-all
>ssh-56789

6.SSH動作確認

上記で開いていたターミナルを閉じずに別タブを開く

MAC
$ ssh -p 56789 -i ~/.ssh/id_rsa jun1@VPSのIPアドレス

7. sshをファイアウォールから削除

VPS
$ sudo firewall-cmd --permanent --remove-service=ssh
$ sudo firewall-cmd --reload

最後に念の為、ssh接続できるか確認

MAC
$ ssh -p 56789 -i ~/.ssh/id_rsa jun1@VPSのIPアドレス

apacheのインストール

VPS
$ sudo yum install httpd 

Apacheを起動

VPS
$ sudo systemctl start httpd
$ systemctl status httpd
#Activeと表示されているか確認

Firewallの設定

VPS
$ sudo firewall-cmd --add-service=http --zone=public --permanent
$ sudo firewall-cmd --add-service=https --zone=public --permanent
$ sudo systemctl restart firewalld

さくらVPSのパケットフィルタの設定

さくらVPSのコントロールパネル>設定>パケットフィルタ>WEBを許可

ブラウザからApacheの動作確認

ブラウザにVPSのIPアドレスを入力し,以下のテストページが表示されればOK

image.png

Apacheの自動起動の設定

VPS
$sudo systemctl enable httpd

権限の設定

ドキュメントルートの所有権をapacheに変更します.

VPS
$sudo groupadd web #グループの作成
$sudo usermod -aG web jun1 #グループにユーザの追加
VPS
$sudo chown apache:web /va/www/html/
$sudo chmod -R 775 /var/www/html/

htmlファイルを作成しApacheの動作確認

VPS
$ vim /var/www/html/index.html
#適当に編集して保存

ブラウザから開いて確認

ドメインの設定

ドメイン取得サイトから購入

今回はバリュードメインに会員登録し、購入します。

DNS設定

バリュードメインにログインし、DNSを設定
詳細は各ドメインサイトから確認します.

バリュードメインの設定画面
a VPSのIPアドレス

ブラウザにドメインを入力し、表示されるか確認
*DNSの反映には1日かかることもあります.

リポジトリの設定

2つのリポジトリをインストールします.

epelリポジトリ(Linuxのリポジトリ)
remiリポジトリ(PHPのリポジトリ)

epelリポジトリ

VPS
$sudo yum repolist
#epelリポジトリがない場合は以下実行
$sudo yum install epel

remiリポジトリ

ブラウザからremiリポジトリのURLをコピー

image.png

VPS
$sudo yum localinstall remiのurl

phpのインストール

インストールできるバージョンを確認

VPS
$ yum list available | grep php-

php71-common.x86_64を確認

VPS
$ sudo yum --enablerepo=remi-php71 install php php-devel php-mysql php-gd php-mbstring

Apaceh再起動

VPS
$ sudo systemctl restart httpd

PHPの設定

VPS
$ sudo cp /etc/php.ini /etc/php.ini.org #設定ファイルのバックアップ
$ sudo vim /etc/php.ini

ファイルのアップロード上限を変更

php.ini
post_max_size = 128M #8M -> 128M
upload_max_filesize = 128M #2M -> 128M

Apaceh再起動

VPS
$ sudo systemctl restart httpd

PHPの動作確認

/var/www/html/index.php
<?php 
echo 'Hello World';

ブラウザにドメイン名を入力し,index.phpを確認する

mysqlのインストール

MariaDBの削除

VPS
$ sudo yum remove mariadb-libs
$ sudo rm -rf /var/lib/mysql

MySQLインストール

MySQL 5.7をインストールする

VPS
$ sudo yum localinstall https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm 
$ sudo yum install mysql-community-server

MySQLの設定

VPS
$ sudo systemctl start mysqld
$ sudo cat /var/log/mysqld.log | grep 'temporary password' #初期接続パスワードを確認
VPS
$ mysql_secure_installation
#以下、指示どおり設定していく

VPS
$ mysql -u root -p
#ログインできるか確認

文字コードをUTFに変更

VPS
$ sudo vim /etc/my.conf
my.conf
character-set-server=utf8
VPS
$ sudo systemctl restart mysqld

自動起動の設定

VPS
$ sudo systemctl enable mysqld

sslの設定

certbotを利用するためバーチャルホストを設定

VPS
$ sudo vim /etc/httpd/conf.d/ドメイン名.conf
ドメイン名.conf
<VirtualHost *:80>
DocumentRoot /var/www/html
ServerName ドメイン名
</VirtualHost>

certbotをインストール

certbotのwebサイトからソフトウエアとOSを選択

VPS
$ sudo vim install certbot-apache

証明書を取得

VPS
$ sudo certbot --apache

動作確認

ブラウザにドメイン名を入力し、httpsになっているか確認

wordpressのインストール

DBの準備

VPS
$ mysql -u root -p 
mysql> create database myblog;
mysql> create user 'myblog_user'@'localhost' identified with mysql_native_password by '任意のpassword'
mysql> grant all privileges on myblog.* to 'myblog_user'@'localhost';
mysql> flush privileges;

wordpressのダウンロード

VPS
$ wget https://ja.wordpress.org/latest-ja.tar.gz

$ sudo tart -zxvf latest-ja.tar.gz
-C /var/www/
#圧縮ファイルを展開

$sudo chown -R apache:web wordpress/

$sudo vim /etc/httpd/conf/httpd.conf
httpd.conf
DocumentRoot "/var/www/wordpress"

<Directory "/var/www/wordpress">
ドメイン名.conf
DocumentRoot /var/www/wordpress
ドメイン名-le-ssl.conf
DocumentRoot /var/www/wordpress

wordpressの確認

ドメインにアクセスして以下のページが表示されるか確認
image.png

終わりに

今回はさくらVPSにLAMP環境を構築する学習の内容をまとめました。

Linuxコマンドの勉強になりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む