20210123のMySQLに関する記事は12件です。

MyBatis+SpringBootでCRUDアプリを作る ※余計なものはナシ※ 2/2

前回の記事の続きです。
次はクラスを作っていきます。
前回までの分はこちら
https://qiita.com/sumichan/items/bdc2a0e909416ae55162

3-1.Model.java

今回使うのはidとtask(やること)だけです。
@NotBlankはvalidationでblank入力を禁止します。
@Dataはlombokがセッティングさせていれば使用できますが、されていなければgetter,setterを自分で書いてください。

@Data //lombokが入っていればこれでgetter,setter生成完了
public class Todo {

    private Long id;

    @NotBlank
    private String task;
}

3-2.TodoMapper.java

個人的にmapperから書いていくとわかりやすいです。
必要なSQLを書いていきます。今回だとこの5つを使います。
mapperクラスにはinterfaceのクラスにして、@Mapperを付けます。

@Mapper
public interface TodoMapper {

    //全件取得
    @Select("select * from todo")
    List<Todo> selectAll();

    //idが一致した1件を取得
    @Select("select * from todo where id = #{id}")
    Todo selectOne(Long id);

    //登録
    @Options(useGeneratedKeys = true) //自動で連番のidを取得する
    @Insert("insert into todo (task) values (#{task})")
    void insert(Todo todo);

    //更新
    @Update("update todo set task = #{task} where id = #{id}")
    int update(Todo todo);

    //削除
    @Delete("delete from todo where id = #{id}")
    void delete(Long id);
}

3-3.TodoService.java

サービスクラスには@Serviceを付けます。
mapperへ何を取ってくるのか指示しています。

@Service
public class TodoService {

    @Autowired
    private TodoMapper todoMapper;

    //全件取得
    @Transactional
    public List<Todo> selectAll() {
        return todoMapper.selectAll();
    }

    //一件取得
    @Transactional
    public Todo selectOne(Long id) {
        return todoMapper.selectOne(id);
    }

    //登録
    @Transactional
    public void insert(Todo todo) {
        todoMapper.insert(todo);
    }

    //更新
    @Transactional
    public int update(Todo todo) {
        return todoMapper.update(todo);
    }

    //削除
    @Transactional
    public void delete(Long id) {
        todoMapper.delete(id);
    }


}

3-4.TodoController.java

controlleは実行した結果のSQLを画面に渡しています。
あとは画面遷移の指示をしています。

@Controller
@RequestMapping("/todos")
public class TodoController {

    @Autowired
    private TodoService todoService;

    //全件取得したtodoを、todoに入れておき、top.htmlで表示する。
    @GetMapping("")
    public String top(Model model) { //全件取得
        model.addAttribute("todo", todoService.selectAll());
        //todoにselectAll()の結果を入れて、top.htmlで表示する
        return "todos/top";//画面はtopへ移り、todoを表示する
    }

    @GetMapping("new") //top→newボタン
    public String newTodo(Model model, @ModelAttribute Todo todo) {
        //newボタンがtopで押されるとここを通る
        return "todos/new";
    }

    @PostMapping("new") //formから作成された画面
    public String create(@Validated @ModelAttribute Todo todo,BindingResult result) {

        if (result.hasErrors()) {
            return "todos/new";
        }

        todoService.insert(todo);//バリデーションに引っかからなければinsert実行
        return "redirect:/todos";//一覧表に戻る
    }

    @GetMapping("{id}") //1件分のデータの中身を確認する
    public String show(@PathVariable Long id, Model model) {
        model.addAttribute("todo", todoService.selectOne(id));//urlのidを使ってsql実行
        return "todos/show";
    }

    @GetMapping("{id}/change") //編集画面に行くまでの画面
    public String change(@PathVariable Long id, Model model) {
        model.addAttribute("todo", todoService.selectOne(id));
        return "todos/change";//取得したidを使って、change画面へ
    }

    @PutMapping("put/{id}") //更新画面
    public String update(Todo todo) {
        todoService.update(todo);
        return "redirect:/todos";
    }

    @DeleteMapping("{id}/delete") //消去画面
    public String dast(@PathVariable Long id) {
        todoService.delete(id);
        return "redirect:/todos";
    }
}

if (result.hasErrors()

バリデーションチェックに引っかかった場合(今回はtaskがブランクだった場合)は、new.htmlに戻るように指示されています。
バリデーションチェックを使用する場合は@Validatedを付けて、BindingResultを使います。

@PathVariableでURLの値を使うことができます。

4.http://localhost:8080/todosを実行する

設定を8080にしていなければ変えてください。
無事にCRUDができたら完成です!
お疲れ様でした!

ソースコード

https://github.com/IwaiSumire/trial/tree/main/Qiita210121

参考

https://qiita.com/ozaki25/items/fe5fc876bd55a9d7daa9
大変お世話になりました。
ありがとうございます。

最後に

このアプリができればMyBatisを使ったCRUDの流れが掴めると思います。
応用して色々試してみてください!

最後まで読んでいただきありがとうございました。

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

MyBatis+SpringBootでCRUDアプリを作る ※余計なものはナシ※ 1/2

MyBatisを使ったToDoリストを作成します。
目標物はこれ↓
ezgif.com-gif-maker.gif

  • やることを登録(blankは登録できない)
  • やることを見る
  • やることを変更
  • やることを消去
    以上ができる単純なアプリです。

作成した経緯

SpringBootのCRUDができる書籍はありますが、MyBatisを使ったものは少ないです。(ほぼJPAかJDBC)
また、BootstrapやJSが混ざったもの、HTMLが複雑なものも多く、私が理解しづらかったため、わかりやすくシンプルなものにしてみました。

対象者

  • Controller、Service、Repositoryが何をしている何となくわかる人。
  • CRUDが何かわかる人。
  • JPAやJDBCがなんとなくわかる人。
  • MyBatisを初めて使う人。

使っている環境

■OS : Windows10
■IDE : Eclipse
■Maven
■SpringBoot
■DB : MySQL8.0.22

MyBatisとは

簡単に言えばDBに接続してSQL文を実行してくれるものです。(O/Rマッパー)
JDBCより簡単で、JSPより細かいSQL文が実行できます。
今まではおそらくRepositoryクラスで作成されていたものが、今回はMapperクラスとなります。
XMLに書くこともできるのですが、今回は分かりやすいのでアノテーションに書いていきます。
(こっちの方が新しい書き方のようです)

1-1.pom.xml

今回DBはMySQL、O/RマッパーにMyBatis、バリデーション機能を使うので3つをapplicationに追加します。

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

1-2.application.properties

MySQLへの接続をします。

#MySQLのDBのURL
spring.datasource.url=jdbc:mysql://localhost:3306/xxxxxxx?serverTimezone=JST

#name、password指定。
spring.datasource.username=root
spring.datasource.password=xxxxxxxx

#SpringBootを起動したときに実行したいSQL文を記述したパス
#schemaにはテーブルを作るSQLを入れておく
spring.datasource.schema=classpath:schema.sql

#これがないとputやdeleteでエラーになる
spring.mvc.hiddenmethod.filter.enabled=true

ご自身のMySQLの環境に合わせてください。
spring.mvc.hiddenmethod.filter.enabled=true がないとエラーになります。
ここで私はハマりましたww
qiita.png

htmlのhttpメソッドでdeleteとputを使っているので必要になります。
getとpostで書けば不要となります。
その場合はControllerのアノテーションも@GetMappingに揃えてください。
↓こちらを参考にさせていただきました。
https://qiita.com/kazuhiro1982/items/b8b9965fddf9c5507517

1-3.MySQLでテーブルを作る

CREATE TABLE IF NOT EXISTS todo (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  task varchar(255) DEFAULT NULL,
  PRIMARY KEY (id)
);

特に難しいSQLではないですが
NOT EXISTSはもうテーブルが存在するなら、再度作らない。
AUTO_INCREMENTは連続した番号を付ける。

2-1.top.html(最初の画面)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
    layout:decorator="layout">
<meta charset="UTF-8">
<title>ToDOリスト</title>

</head>
<body>
    <h1>ToDoリスト</h1>
    <a th:href="@{/todos/new}">
        <button>ToDo作成【NEW】</button>
    </a>
    <div th:if="!${todo.size()}">
        <p>該当の検索結果がありません!</p>
    </div>
    <table th:if="${todo.size()}" border="1">
        <thead>
            <tr>
                <th>ID#</th>
                <th>やること</th>
                <th></th>
                <th></th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="todo:${todo}" th:object="${todo}">
                <!-- each;DBの情報をすべて出すまで繰り返す -->
                <td th:text="*{id}"></td>
                <td th:text="*{task}"></td>
                <td><a th:href="@{/todos/{id}(id=*{id})}">
                        <button>詳細</button>
                </a></td>
                <td><a th:href="@{/todos/{id}/change(id=*{id})}"><button>変更</button></a></td>
                <td>
                    <form th:action="@{/todos/{id}/delete(id=*{id})}"
                        th:method="delete">
                        <button>消去</button>
                    </form>
                </td>
            </tr>
        </tbody>

    </table>
</body>
</html>

th:if="${todo.size()

todoが何も登録していないときは「該当の検索結果がありません!」と表示

th:each="todo:${todo}" th:object="${todo}

todoが全部終わるまで、todoの中身を表示

th:text="{id}" th:text="{task}"

todoの中身のidとtaskを表示

th:method="delete"

Controllerの@DeleteMappingに対応します。
get、post以外にも出てくるんです。

2-2.new.html(登録画面)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
    layout:decorator="layout">
<meta charset="UTF-8">
<title>新規ToDo</title>
</head>
<body>

    <h2>新規ToDo作成</h2>

    <form method="post" th:action="@{/todos/new}" th:object="${todo}">
        <ul>
            <li><label>やること</label> <br> <input type="text" id="task"
                name="task" th:field="*{task}" /> 

            </li>

        </ul>

        <button >新規作成</button>
    </form>
</body>
</html>

th:action="@{/todos/new}" th:object="${todo}" th:field="*{task}"

objectのtodoの中に、taskを詰めて、controllerのtodos/newに渡します。

2-3.show.html(確認画面)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
    layout:decorator="layout">
<meta charset="UTF-8">
<title>todo</title>

</head>
<body>
    <h2>登録内容確認</h2>
    <div th:object="${todo}">
        <table border="1">

            <tr>
                <th>id</th>
                <td><div th:text="*{id}"></div></td>
            </tr>
            <tr>
                <th>やること</th>
                <td><div ><div th:text="*{task}"></div></div></td>
            </tr>

        </table>
    </div>

</body>
</html>

todoの中身のidとtaskを表示します。

2-4.change.html(変更画面)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
    layout:decorator="layout">
<meta charset="UTF-8">
<title>ToDo変更</title>

</head>
<body>

    <h2>ToDo変更</h2>

    <form th:action="@{/todos/put/{id}(id=*{id})}" th:method="put"
        th:object="${todo}">
        <!-- 入力したやつをtodoの中にいれて使える -->

        <ul>
            <li><label>やること</label> <br> <input type="text" id="todo"
                name="todo" th:field="*{task}" /> <!-- fieldはramen.shopとかにしてHTMLで使える -->
        </ul>
        <button>更新</button>

    </form>
</body>
</html>

todoの中身を入れ替えます。ほぼnew.htmlと同様です。

th:action="@{/todos/put/{id}(id=*{id})}" th:method="put" th:object="${todo}"

taskを詰め直したtodoをcontrollerの/todos/put/{id}(id={id})に渡します。
URLが該当のidになるようにしているので、{id}(id=
{id}となっています。
deleteと同じく、Controllerの@@PutMappingに対応します。

残りは次の記事へ
https://qiita.com/sumichan/items/a53e223c2a727cae26a4

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

MyBatis+SpringBootでCRUDアプリを作る ※一番単純※ 1/2

MyBatisを使ったToDoリストを作成します。
目標物はこれ↓
ezgif.com-gif-maker.gif

  • やることを登録(blankは登録できない)
  • やることを見る
  • やることを変更
  • やることを消去
    以上ができる単純なアプリです。

作成した経緯

SpringBootのCRUDができる書籍はありますが、MyBatisを使ったものは少ないです。(ほぼJPAかJDBC)
また、BootstrapやJSが混ざったもの、HTMLが複雑なものも多く、私が理解しづらかったため、わかりやすくシンプルなものにしてみました。

対象者

  • Controller、Service、Repositoryが何をしている何となくわかる人。
  • CRUDが何かわかる人。
  • JPAやJDBCがなんとなくわかる人。
  • MyBatisを初めて使う人。

使っている環境

■OS : Windows10
■IDE : Eclipse
■Maven
■SpringBoot
■DB : MySQL8.0.22

MyBatisとは

簡単に言えばDBに接続してSQL文を実行してくれるものです。(O/Rマッパー)
JDBCより簡単で、JSPより細かいSQL文が実行できます。
今まではおそらくrepositoryクラスで作成されていたものが、今回はMapperクラスとなります。
XMLに書くこともできるのですが、今回は分かりやすいのでアノテーションに書いていきます。
(こっちの方が新しい書き方のようです)

1-1.pom.xml

今回DBはMySQL、O/RマッパーにMyBatis、バリデーション機能を使うので3つをapplicationに追加します。

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

1-2.application.properties

MySQLへの接続をします。

#MySQLのDBのURL
spring.datasource.url=jdbc:mysql://localhost:3306/xxxxxxx?serverTimezone=JST

#name、password指定。
spring.datasource.username=root
spring.datasource.password=xxxxxxxx

#SpringBootを起動したときに実行したいSQL文を記述したパス
#schemaにはテーブルを作るSQLを入れておく
spring.datasource.schema=classpath:schema.sql

#これがないとputやdeleteでエラーになる
spring.mvc.hiddenmethod.filter.enabled=true

ご自身のMySQLの環境に合わせてください。
spring.mvc.hiddenmethod.filter.enabled=true がないとエラーになります。
ここで私はハマりましたww
qiita.png

1-3.MySQLでテーブルを作る

CREATE TABLE IF NOT EXISTS todo (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  task varchar(255) DEFAULT NULL,
  PRIMARY KEY (id)
);

特に難しいSQLではないですが
NOT EXISTSはもうテーブルが存在するなら、再度作らない。
AUTO_INCREMENTは連続した番号を付ける。

2-1.top.html(最初の画面)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
    layout:decorator="layout">
<meta charset="UTF-8">
<title>ToDOリスト</title>

</head>
<body>
    <h1>ToDoリスト</h1>
    <a th:href="@{/todos/new}">
        <button>ToDo作成【NEW】</button>
    </a>
    <div th:if="!${todo.size()}">
        <p>該当の検索結果がありません!</p>
    </div>
    <table th:if="${todo.size()}" border="1">
        <thead>
            <tr>
                <th>ID#</th>
                <th>やること</th>
                <th></th>
                <th></th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="todo:${todo}" th:object="${todo}">
                <!-- each;DBの情報をすべて出すまで繰り返す -->
                <td th:text="*{id}"></td>
                <td th:text="*{task}"></td>
                <td><a th:href="@{/todos/{id}(id=*{id})}">
                        <button>詳細</button>
                </a></td>
                <td><a th:href="@{/todos/{id}/change(id=*{id})}"><button>変更</button></a></td>
                <td>
                    <form th:action="@{/todos/{id}/delete(id=*{id})}"
                        th:method="delete">
                        <button>消去</button>
                    </form>
                </td>
            </tr>
        </tbody>

    </table>
</body>
</html>

th:if="${todo.size()

todoが何も登録していないときは「該当の検索結果がありません!」と表示

th:each="todo:${todo}" th:object="${todo}

todoが全部終わるまで、todoの中身を表示

th:text="{id}" th:text="{task}"

todoの中身のidとtaskを表示

th:method="delete"

Controllerの@DeleteMappingに対応します。
get、post以外にも出てくるんです。

2-2.new.html(登録画面)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
    layout:decorator="layout">
<meta charset="UTF-8">
<title>新規ToDo</title>
</head>
<body>

    <h2>新規ToDo作成</h2>

    <form method="post" th:action="@{/todos/new}" th:object="${todo}">
        <ul>
            <li><label>やること</label> <br> <input type="text" id="task"
                name="task" th:field="*{task}" /> 

            </li>

        </ul>

        <button >新規作成</button>
    </form>
</body>
</html>

th:action="@{/todos/new}" th:object="${todo}" th:field="*{task}"

objectのtodoの中に、taskを詰めて、controllerのtodos/newに渡します。

2-3.show.html(確認画面)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
    layout:decorator="layout">
<meta charset="UTF-8">
<title>todo</title>

</head>
<body>
    <h2>登録内容確認</h2>
    <div th:object="${todo}">
        <table border="1">

            <tr>
                <th>id</th>
                <td><div th:text="*{id}"></div></td>
            </tr>
            <tr>
                <th>やること</th>
                <td><div ><div th:text="*{task}"></div></div></td>
            </tr>

        </table>
    </div>

</body>
</html>

todoの中身のidとtaskを表示します。

2-4.change.html(変更画面)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
    layout:decorator="layout">
<meta charset="UTF-8">
<title>ToDo変更</title>

</head>
<body>

    <h2>ToDo変更</h2>

    <form th:action="@{/todos/put/{id}(id=*{id})}" th:method="put"
        th:object="${todo}">
        <!-- 入力したやつをtodoの中にいれて使える -->

        <ul>
            <li><label>やること</label> <br> <input type="text" id="todo"
                name="todo" th:field="*{task}" /> <!-- fieldはramen.shopとかにしてHTMLで使える -->
        </ul>
        <button>更新</button>

    </form>
</body>
</html>

todoの中身を入れ替えます。ほぼnew.htmlと同様です。

th:action="@{/todos/put/{id}(id=*{id})}" th:method="put" th:object="${todo}"

taskを詰め直したtodoをcontrollerの/todos/put/{id}(id={id})に渡します。
URLが該当のidになるようにしているので、{id}(id=
{id}となっています。
deleteと同じく、Controllerの@@PutMappingに対応します。

残りは次の記事へ
https://qiita.com/sumichan/items/a53e223c2a727cae26a4

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

CentOS8+MySQL 8.0のActive-Standby構成、HAProxy経由で

ちょっと、用語が変わったり、ややこしくなる前に現状のMySQL、Active-Standby構成の手順をまとめておこうと思った。

CentOS 8.3のインストールDVDの中に入っているMySQLはバージョン8.0.21で、これはギリギリ、アメリカのBLM活動から始まる用語改革の波に飲まれていないバージョンである。

最近はInnoDBクラスターじゃないのか?mysql-shellとmysql-routerがCentOS 8(≒RHEL 8)のインストールDVDの中に入っていないのでちょっと使い勝手が悪い。
今回説明する構成の良いところは、MySQL Workbench以外のサーバー側コンポーネントについて、RHEL 8のサブスクリプションを持っていればそのサポートのカバー範囲内のものしか使っていない点にある。

(1/25追記)OpenShift 4で似たような構成を試してみた。
https://qiita.com/rk05231977/items/de9680d9a2dc6eb640df

構成

手元のMySQL WorkbenchからMySQLサーバーに繋ぐ下図のような構成を作ってみる。
image.png

mysql1とmysql2はマニュアルの以下で説明される準同期レプリケーションを構成する。
https://dev.mysql.com/doc/refman/8.0/en/replication-howto.html

mysql1障害時はmysql2がレプリケーションを止めて、自分がActiveサーバーになる。
MySQL WorkbenchからDBへの接続は、途中にHAProxyが挟まってActive DB側にリクエストを振る構成である。

OSのインストールイメージからmysql-server、haproxy等が導入できれば、mysqlサーバー、haproxyサーバーはインターネット非接続でもOK。

本記事ではHAproxyの冗長化には触れないが、CentOS/RHELで、Keepalivedを使ってHAproxyを冗長化する方法は別途確立されている。
https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/load_balancer_administration/ch-lvs-overview-vsa

HAproxyでなくiptablesのport forwardingでもいいし、クラウドならNLBでもいいと思うが、KeepalivedをMySQLサーバー上で直接実行するのは手動切り替え、切り戻しに気を使うのでやめた方が良い。と思う。
HAproxyだと、statsの画面で現在のConnection数がWeb画面で見えるのが良い。後述する。

また、本記事内ではActive-Standby切り替えを自動化する事については触れないが、手順は十分に機械的なのでcron、あるいは秒単位で制御したいならsystemd、により切り替えを自動化するのはそれほど難しくないだろう(「フェールオーバーしてみる」の手順3~5が自動化できればいいのだ)。

mysql1サーバーを作る

とりあえず、OSにCentOS 8.3が導入済みで、IPアドレスが設定され、SSHで接続が可能なサーバーを作る。
IPアドレスは 192.168.0.201、ホスト名はmysql1とする。
また、OS導入に使ったDVDメディアが/dev/cdromでアクセスできるものとする。

1.mysql-serverをインストールする。

# mount /dev/cdrom /media
# rm -f /etc/yum.repos.d/*
# cat > /etc/yum.repos.d/media.repo << 'EOF'
[media-baseos]
name=CentOS Linux 8 - Media - BaseOS
baseurl=file:///media/BaseOS
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial

[media-appstream]
name=CentOS Linux $releasever - Media - AppStream
baseurl=file:///media/AppStream
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
EOF

# yum install -y mysql-server
# systemctl enable --now mysqld
# firewall-cmd --add-service=mysql --zone=public --permanent
# firewall-cmd --reload

2.何もデータが無いのもつまらないので、国データベースを初期データとしてロードしようか。
サーバーがインターネットに繋がっていない場合はworld.sql.gzを別途ダウンロードしてmysql1サーバーにコピーしておく。

# curl -k -O -L https://downloads.mysql.com/docs/world.sql.gz
# gunzip world.sql.gz
# mysql < world.sql

3.MySQL Workbenchから繋ぐ用のadminユーザーを作る。

# mysql -e "CREATE USER 'admin'@'%' IDENTIFIED BY 'password';"
# mysql -e "GRANT ALL ON *.* TO 'admin'@'%';"

4.ここからがレプリケーション用の構成。
https://dev.mysql.com/doc/refman/8.0/en/binlog-replication-configuration-overview.html

MySQLにserver-idを設定し、Standbyサーバーからレプリケーションで繋ぐためのユーザーを作る。
mysql1サーバーのIPアドレスが192.168.0.201なので、server-idには201を設定しようか。
Standbyサーバーmysql2には、IPアドレス192.168.0.202を使用する。

# cat > /etc/my.cnf.d/server-id.cnf << EOF
[mysqld]
server-id=201
EOF
# systemctl restart mysqld
# mysql -e "CREATE USER 'repl'@'192.168.0.202' IDENTIFIED BY 'password';"
# mysql -e "GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.0.202';"

5.レプリケーション用の初期データを用意し、レプリケーションの開始点を確認する。

# mysql -e "FLUSH TABLES WITH READ LOCK;"
# mysql -e "SHOW MASTER STATUS\G;" > master-status.txt
# mysqldump --all-databases --master-data > dbdump.db
# mysql -e "UNLOCK TABLES;"
# cat master-status.txt

(実行結果 - SHOW MASTER STATUS)
[root@mysql1 ~]# cat master-status.txt
*************************** 1. row ***************************
             File: binlog.000002
         Position: 683
     Binlog_Do_DB:
 Binlog_Ignore_DB:
Executed_Gtid_Set:

「binlog.~」と「Position」の値がStandby側でレプリケーションを開始するのに必要となる。

mysql2サーバーを作る

OSにCentOS 8.3が導入済みで、IPアドレスが設定され、SSHで接続が可能なサーバーを作る。
IPアドレスは 192.168.0.202、ホスト名はmysql2とする。

1.mysql-serverをインストールする。
手順割愛。mysql1の手順を参照。

2.MySQL Workbenchから繋ぐ用のadminユーザーを作る。
Standby状態のときは繋ぎに来ないが、Activeサーバーと同じユーザー名、パスワードの接続ユーザーを作る。

# mysql -e "CREATE USER 'admin'@'%' IDENTIFIED BY 'password';"
# mysql -e "GRANT ALL ON *.* TO 'admin'@'%';"

3.初期データをロードする。
mysql1のバックアップをロードする。

# scp 192.168.0.201:dbdump.db .
# mysql < dbdump.db

4.ここからがレプリケーション用の構成、準備編。
server-idを設定する。mysql2サーバーのIPアドレスが192.168.0.202なので、server-idは202。
また、mysql1サーバーがStandby落ちした時のレプリケーション用ユーザーも作る。

# cat > /etc/my.cnf.d/server-id.cnf << EOF
[mysqld]
server-id=202
EOF
# systemctl restart mysqld
# mysql -e "CREATE USER 'repl'@'192.168.0.201' IDENTIFIED BY 'password';"
# mysql -e "GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.0.201';"

5.レプリケーションを開始する。
MASTER_LOG_FILE、MASTER_LOG_POSは先のmaster1の手順で確認したレプリケーションの開始点である。

# MASTER_LOG_FILE=binlog.000002
# MASTER_LOG_POS=683
# mysql -e "CHANGE MASTER TO MASTER_HOST='192.168.0.201', \
MASTER_USER='repl', MASTER_PASSWORD='password', \
MASTER_LOG_FILE='${MASTER_LOG_FILE}', MASTER_LOG_POS=${MASTER_LOG_POS};"
# mysql -e "START SLAVE;"

レプリケーションの状態がどうなっているかはSHOW SLAVE STATUS\Gコマンドで確認するが、一旦それは置いておいて先に進もう。

HAProxyを作る

OSにCentOS 8.3が導入済みで、IPアドレスが設定され、SSHで接続が可能なサーバーを作る。
IPアドレスは 192.168.0.200、ホスト名はhaproxyとする。

1.haproxyをインストールする。

# mount /dev/cdrom /media
# rm -f /etc/yum.repos.d/*
# cat > /etc/yum.repos.d/media.repo << 'EOF'
[media-baseos]
name=CentOS Linux 8 - Media - BaseOS
baseurl=file:///media/BaseOS
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial

[media-appstream]
name=CentOS Linux $releasever - Media - AppStream
baseurl=file:///media/AppStream
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
EOF

# yum install -y haproxy

2.mysql1にmysqlの接続を振る設定を追加する。

# cat > /etc/haproxy/haproxy.cfg << EOF
global
    log         127.0.0.1 local2
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     10
    user        haproxy
    group       haproxy
    daemon
    stats socket /var/lib/haproxy/stats
defaults
    mode                    tcp
    log                     global
    retries                 3
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    maxconn     10

frontend mysql
    bind *:3306
    default_backend mysql
backend mysql
    server mysql1 192.168.0.201

EOF

# setsebool -P haproxy_connect_any=1
# systemctl enable --now haproxy
# firewall-cmd --add-service=mysql --zone=public --permanent
# firewall-cmd --reload

これで、手元の端末からHAproxy経由でmysql1サーバーのMySQLに繋がるようになったはずである。

MySQL WorkbenchからMySQLに繋ぐ

1.手元のPCにMySQL Workbenchをダウンロードしてインストールする。
https://www.mysql.com/jp/products/workbench/

2.HAproxyのIPアドレスに向けたConnectionを作って繋ぐ。
image.png

3.レコードを追加してみよう。
Schemas > world > Tables > countryのテーブルを開き、以下のSQL文を実行(⚡)する。

INSERT INTO `world`.`country` (`Code`, `Name`, `Continent`, `Region`, `SurfaceArea`, `Population`, `LocalName`, `GovernmentForm`, `Code2`) VALUES ('ASR', 'Asura Kingdom', 'Europe', 'Central Region', '100000.00', '1000000', 'Asura Kingdom', 'Monarchy', 'AA');

image.png

レコードが追加されただろうか。
image.png

フェールオーバーしてみる

それでは、おもむろにmysql1サーバーの電源を落として、mysql2サーバーにフェールオーバーしてみよう。

1.mysql1サーバーの電源を落とす。仮想マシンならPower Offで。

2.MySQL Workbenchで表がロードできなくなった事を確認する。
image.png
image.png

3.念のため、haproxyサーバーからmysqlへの振り分けを止めておく。
haproxyサーバーで以下を実行する。

# systemctl stop haproxy

4.mysql2サーバーで以下を実行する。
レプリケーションを停止し、Activeサーバーになっても良い様に準備する。

# mysql -e "SHOW SLAVE STATUS\G;"
 → 「Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates」となっているのを確認する。
# mysql -e "STOP SLAVE;"
# mysql -e "RESET SLAVE ALL;"

# mysql -e "FLUSH TABLES WITH READ LOCK;"
# mysql -e "SHOW MASTER STATUS\G;" > master-status.txt
# mysql -e "UNLOCK TABLES;"
# cat master-status.txt

(実行結果)
[root@mysql2 ~]# cat master-status.txt
*************************** 1. row ***************************
             File: binlog.000002
         Position: 1076
     Binlog_Do_DB:
 Binlog_Ignore_DB:
Executed_Gtid_Set:

「binlog.~」と「Position」の値は、mysql1がStandbyとしてレプリケーションを開始するのに必要となる。

この時点でmysql2は新しいActiveサーバーとして機能しているので、HAproxyの振り分けをmaster2に変更する。

5.haproxyサーバーで以下を実行する。

# sed -i -e 's/server mysql1 192.168.0.201/server mysql2 192.168.0.202/' /etc/haproxy/haproxy.cfg
# systemctl start haproxy

6.MySQL Workbenchで再度、表をロードする。
繋がるようになっているはずである。ついでに、先にmysql1で追加したレコードがmysql2にもデータ反映されている事を確認しよう。
image.png

7.mysql2がActiveサーバーになっている段階で、もう一つレコードを追加してみる。
MySQL Workbenchで以下の文を実行(⚡)する。

INSERT INTO `world`.`country` (`Code`, `Name`, `Continent`, `Region`, `SurfaceArea`, `Population`, `LocalName`, `GovernmentForm`, `Code2`) VALUES ('MLS', 'Holy Country of Millis', 'South America', 'Southern Millis', '100000', '1000000', 'Millis', 'Church State', 'MI');

image.png
image.png

元のActiveサーバーに切り戻す

mysql1サーバーに切り戻すところもやってみよう。

1.mysql1サーバーの電源を入れ、sshで接続する。

2.まずはmysql1サーバーを、mysql2サーバーのレプリカ(Standby)サーバーとして構成する。シャットダウン中のデータ更新に追いつかせるためである。
mysql1サーバーで以下を実行する。

# mysql -e "CHANGE MASTER TO MASTER_HOST='192.168.0.202', \
MASTER_USER='repl', MASTER_PASSWORD='password', \
MASTER_LOG_FILE='binlog.000002', MASTER_LOG_POS=1076;"
# mysql -e "START SLAVE;"

3.mysql1サーバーで以下のコマンドを実行し、レプリケーションが追いつくのを待つ。

# mysql -e "SHOW SLAVE STATUS\G;"
 → 「Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates」となるのを確認する。

レプリケーションが追いついたらmysql1をActiveサーバーとして復帰するための準備完了である。

4.念のため、haproxyサーバーからmysqlへの振り分けを止めておく。
haproxyサーバーで以下を実行する。

# systemctl stop haproxy

5.mysql2サーバーで以下を実行する。一回、mysqldを止める。

# systemctl stop mysqld

6.mysql1サーバーで以下を実行する。
レプリケーションを止め、Activeサーバーに復帰するための準備である。

# mysql -e "SHOW SLAVE STATUS\G;"
→ 「Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates」を待つ。

# mysql -e "STOP SLAVE;"
# mysql -e "RESET SLAVE ALL;"

# mysql -e "FLUSH TABLES WITH READ LOCK;"
# mysql -e "SHOW MASTER STATUS\G;" > master-status.txt
# mysql -e "UNLOCK TABLES;"
# cat master-status.txt

(実行結果)
[root@mysql1 ~]# cat master-status.txt
*************************** 1. row ***************************
             File: binlog.000003
         Position: 556
     Binlog_Do_DB:
 Binlog_Ignore_DB:
Executed_Gtid_Set:

「binlog.~」と「Position」の値は、mysql2が再度Standbyとしてレプリケーションを開始するのに必要となる。もういいか。

master1がActiveサーバーとして復帰する準備が整った。

7.HAproxyでリクエストをmaster1に振るよう設定し、開始する。
haproxyサーバーで以下を実行する。

# sed -i -e 's/server mysql2 192.168.0.202/server mysql1 192.168.0.201/' /etc/haproxy/haproxy.cfg
# systemctl start haproxy

8.MySQL Workbenchで再度、表をリロードする。表がリロードされ、master2側に追加したレコードがmaster1にも反映されていることを確認する。
image.png

9.最後に、mysql2サーバーが改めてStandbyとなるように再構成する。
mysql2サーバーで以下を実行する。

# systemctl start mysqld
# mysql -e "CHANGE MASTER TO MASTER_HOST='192.168.0.201', \
MASTER_USER='repl', MASTER_PASSWORD='password', \
MASTER_LOG_FILE='binlog.000003', MASTER_LOG_POS=556;"
# mysql -e "START SLAVE;"

(おまけ)HAproxyでMySQLのコネクション数を確認する

最初の方でちょろっと触れたHAproxyで現在のコネクション数を確認する件。

1.haproxyサーバーで、haproxyの設定ファイルを修正し、statsを有効にする。
admin:adminはユーザー名とパスワード。

/etc/haproxy/haproxy.cfg
(ファイルの最後に以下を追加して保存)

frontend stats
    bind *:8404
    mode http
    stats enable
    stats realm Haproxy\ Statistics
    stats uri /
    stats auth admin:admin

2.haproxyの設定をreloadする。

# systemctl reload haproxy
# firewall-cmd --add-port=8404/tcp

3.Webブラウザでhaproxyサーバーにアクセスする。
http://192.168.0.200:8404/

以下のような画面が表示される。字が小さく読めないかもしれないが、現在のセッション数などがGUIで確認できて便利。
image.png

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

【Mysqlエラー】Mysql2::Error: Specified key was too long; max key length is 767 bytes

はじめに

アプリ開発中に起こったMySQLのエラーについて忘備録も兼ねて記録しておこうと思います。

エラー文

rails g devise userでuserモデルを作成し、rails db:migrateを行うと下記のエラー文が表示。

Mysql2::Error: Specified key was too long; max key length is 767 bytes

直訳すると「指定したキーは長すぎます。キーの最大範囲は767バイトまでです」

意味は「Mysql2に格納できる最大文字データ量(767バイト)に対してオーバーしてしまったためエラーになった」ということのようです。

エラー解決に向けての予備知識

utf8

文字コードの中で世界で最も普及している文字コード。
通常は1~4バイトで文字を表現するが、MySQLでは3バイトの文字までしか扱えない。
1文字当たり3バイト。

utf8mb4

データベースMySQLで扱うための文字コード。
4バイトの文字を扱えるため、絵文字を扱いたい場合はこちらを使用する
→絵文字は4バイトなので、3バイトまでしか扱えないutf8では不可能なため。
1文字当たり4バイト。

VARCHAR型

・可変長(長さを変えられる)文字列のこと

・varchar(m)という形で指定する。(mはバイト数。0~65535まで。)

例) varchar(255) varchar(191)

・char型と異なり、末尾に空白は付かない。
・末尾に空白が付いた文字列はそのまま格納される。
↑の場合、取得時も空白が付いたままだが、WHERE句での比較時には削除された状態で比較が行われる。

エラー考察

前提条件として

MySQLのインデックスは最大767バイト

Railsのstringにおけるデフォルト値が255文字(MySQLで保存できるデータ量)。

  • 3バイトのutf8では

 3バイト × 255文字 = 765バイト < 767バイト

  • 4バイトのutf8mb4では

 4バイト × 255文字 = 1020バイト > 767バイト

つまり、上記のように文字コードutf8mb4では、
4 × 255 = 1020バイトとなって、MySQLのインデックスの最大数である767バイトを上回ってしまいエラーが発生してしまったということです。

解決方法

解決方法は大きく2つあるようで、

①767バイトを超えないように最大文字数を削る
②767バイトを超えてもOKにする

今回は簡単そうな①でエラー解決していきます。



結論 : 文字制限をかけるファイルを新規作成する

現在の入力できる最大文字数、255文字『varchar(255)』191文字『varchar(191)』変更できるファイルを新規作成し、767バイトを超えないようにします。



191文字『varchar(191)』の理由は

MySQLのインデックスは最大767バイト
utf8mb4は1文字当たり4バイト必要なので

767バイト ÷ 4バイト = 191.75文字 

実際のコード

mysql.rbconfig/initializer配下に新規作成しvarcharlimit191にしましょう。

このmysql.rbというファイルによってActiveRecord内のNATIVE_DATABASE_TYPESという設定を上書きすることができます。

ソースはこちら

config/initializer/mysql.rb
require 'active_record/connection_adapters/abstract_mysql_adapter'

module ActiveRecord
  module ConnectionAdapters
    class AbstractMysqlAdapter
      NATIVE_DATABASE_TYPES[:string] = { :name => "varchar", :limit => 191 } #注目
    end
  end
end

mysql.rb作成前

rails consoleにて確認することができます。

[1] pry(main)> ActiveRecord::Base.connection.native_database_types
=> {:primary_key=>"bigint auto_increment PRIMARY KEY",
 :string=>{:name=>"varchar", :limit=>255}, #limitが255になっている
#...以下略

mysql.rb作成後

:string=>{:name=>"varchar", :limit=>191}と最大文字数が191になっているのがわかりますね。

[2] pry(main)> ActiveRecord::Base.connection.native_database_types
=> {:primary_key=>"bigint auto_increment PRIMARY KEY",
 :string=>{:name=>"varchar", :limit=>191}, #ここがmysql.rbで設定した内容に変わる
#...以下略

これで文字のデータ量を767バイト以内に納めることができました。

この後rails db:migrateを入力すると上手く作動しエラーが解消されました

ちなみに

今回のように、最大文字数を255文字から191文字にする方法ではなく、
最初にconfig/database.ymlに記載した文字コードを途中から変更(今回であればutf8mb4からutf8に変更してマイグレーション)する方法を取ると、もっと面倒臭いエラーになるようです。

rails db:migrateをすると消えない謎のファイルが現れ、モデルの作り直しになってしまうとか。

database.ymlの記載された情報は、迂闊に手を出せないので慎重に取り扱う必要がありますね。

参考記事

Qiita記事【MySQL】Mysql2::Error: Specified key was too long; max key length is 767 bytes

Qiita記事 utf8mb4で巻き起こったトラブル

Qiita記事 【SQL】char型とvarchar型の違いについてまとめてみた

MySQL 5.5 / Redmine 3.4.3 / Ruby23-x64 --- Mysql2::Error: Specified key was too long; max key length is 767 bytes

MySQLでutf8とutfmb4の混在で起きること

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

[Oracle Cloud] MySQL Database Service で Inbound Replication をしてみた

はじめに

MySQL Database Service (略 MDS) には、ちょっと前に、Inbound Replication 機能が追加されました。これは、オンプレミスなどで稼働している MySQL を レプリケーションの Source(Master) にして、MDS へ非同期レプリケーションが出来る機能です。今回の記事では、Inbound Replication の設定手順を紹介します。

次の Document を参考にしています
https://docs.oracle.com/en-us/iaas/mysql-database/doc/replication.html

用語について

MySQL のレプリケーションで、データ同期元を Source と呼びます。データ同期先を Replica と呼びます。ちょっと前まで、Master, Slave と呼ばれていましたが、用語変更されました。今回の記事では、新しい定義で書いていきます。

https://mysqlhighavailability.com/mysql-terminology-updates/

制限事項

Document に書かれている通り、Inbound Replication は次の制限事項があるのでご注意ください

  • 行ベースレプリケーションのみサポート
  • GTIDベースのレプリケーションのみサポート
  • マルチソースレプリケーションは非サポート
  • レプリケーションフィルターは非サポート
  • Source(Master) の mysql スキーマはレプリケーション対象外。また、mysql スキーマで変更をしてしまうと、レプリケーションが停止してしまう

Source : MySQL のインストール

データ同期元の Source を CentOS 7 上で適当に作ります。この辺を参考に。
https://qiita.com/sugimount/items/bf44904db8201d377536

8.0.23 をインストール

========================================================================================================================
 Package                                   Arch              Version                 Repository                    Size
========================================================================================================================
Installing:
 mysql-community-libs                      x86_64            8.0.23-1.el7            mysql80-community            4.6 M
     replacing  mariadb-libs.x86_64 1:5.5.68-1.el7
 mysql-community-libs-compat               x86_64            8.0.23-1.el7            mysql80-community            1.2 M
     replacing  mariadb-libs.x86_64 1:5.5.68-1.el7
 mysql-community-server                    x86_64            8.0.23-1.el7            mysql80-community            518 M
Installing for dependencies:
 mysql-community-client                    x86_64            8.0.23-1.el7            mysql80-community             48 M
 mysql-community-client-plugins            x86_64            8.0.23-1.el7            mysql80-community            237 k
 mysql-community-common                    x86_64            8.0.23-1.el7            mysql80-community            621 k

Transaction Summary
========================================================================================================================

Source : テストデータ作成

作った Source の MySQL にテストデータを作成します
sugitest スキーマ作成

mysql> create database sugitest;
Query OK, 1 row affected (0.00 sec)

mysql>
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sugitest           |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

mysql>

sample テーブルの作成

use sugitest;

CREATE TABLE sample(
 id INT(11) NOT NULL AUTO_INCREMENT,
 value INT(5) NOT NULL DEFAULT 0,
 PRIMARY KEY (id)
);

INSERT INTO sample(value) VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);

Source : GTID Mode を有効

8.0.23 の Default では、gtid_mode が OFF になっている。Replication のために、ON へ変更

mysql> show variables like '%gtid%';
+----------------------------------+-----------+
| Variable_name                    | Value     |
+----------------------------------+-----------+
| binlog_gtid_simple_recovery      | ON        |
| enforce_gtid_consistency         | OFF       |
| gtid_executed                    |           |
| gtid_executed_compression_period | 0         |
| gtid_mode                        | OFF       |
| gtid_next                        | AUTOMATIC |
| gtid_owned                       |           |
| gtid_purged                      |           |
| session_track_gtids              | OFF       |
+----------------------------------+-----------+
9 rows in set (0.01 sec)

mysql>

編集

sudo vim /etc/my.cnf

追記

[mysqld]
...
省略
...
log-bin
server-id=100
log-slave-updates
gtid-mode=ON
enforce-gtid-consistency=true

再起動

sudo systemctl restart mysqld

有効化されている

mysql> show variables like '%gtid%';
+----------------------------------+-----------+
| Variable_name                    | Value     |
+----------------------------------+-----------+
| binlog_gtid_simple_recovery      | ON        |
| enforce_gtid_consistency         | ON        |
| gtid_executed                    |           |
| gtid_executed_compression_period | 0         |
| gtid_mode                        | ON        |
| gtid_next                        | AUTOMATIC |
| gtid_owned                       |           |
| gtid_purged                      |           |
| session_track_gtids              | OFF       |
+----------------------------------+-----------+
9 rows in set (0.00 sec)

mysql>

Source : レプリケーションユーザーの設定

ユーザー作成

CREATE USER rpluser001@'%' IDENTIFIED BY 'Rpl001#!' REQUIRE SSL;

REPLICATION SLAVE の権限付与

GRANT REPLICATION SLAVE on *.* to rpluser001@'%';

Source : Dump ファイルを Export

MySQL Shell を使って、Dump ファイルを作成します。
https://docs.oracle.com/en-us/iaas/mysql-database/doc/importing-and-exporting-databases.html#GUID-63396585-3ECA-4202-86D7-94C6DE08CCCD

このあたりを参考に、MySQL Shell を Insall
https://qiita.com/sugimount/items/bf44904db8201d377536#mysql-shell-install

Export用ユーザー作成

CREATE USER sugi01@'%' IDENTIFIED BY 'mIfoO8_fai12#-gai897fao';
grant all on *.* to sugi01@'%';

MySQL Shell で接続

mysqlsh sugi01@10.0.0.63

接続例

[opc@mysql-client01 ~]$ mysqlsh sugi01@10.0.0.63
Please provide the password for 'sugi01@10.0.0.63': ***********************
Save password for 'sugi01@10.0.0.63'? [Y]es/[N]o/Ne[v]er (default No):
MySQL Shell 8.0.23

Copyright (c) 2016, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
Other names may be trademarks of their respective owners.

Type '\help' or '\?' for help; '\quit' to exit.
Creating a session to 'sugi01@10.0.0.63'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 25 (X protocol)
Server version: 8.0.23 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
 MySQL  10.0.0.63:33060+ ssl  JS >

ここのバケットにDumpする

1611370271623.png

util.dumpInstance で、Source MySQL の全体の Dump を取得

util.dumpInstance("SourceInstanceDump", {osBucketName: "mysqldump", osNamespace: "orasejapan", threads: 4, ocimds: true, compatibility: ["strip_restricted_grants"]})

ちなみに、自分の環境ではハマりポイントがありました。次のエラーが発生ました。

 JS > util.dumpInstance("SourceInstanceDump", {osBucketName: "mysqldump", osNamespace: "orasejapan", threads: 4, ocimds: true, compatibility: ["strip_restricted_grants"]})
Util.dumpInstance: Cannot load "~/.oci/oci_api_key.pem" private key associated with OCI configuration profile named "DEFAULT". (RuntimeError)

原因は /home/opc/.oci/config ファイルの中の書き方でした。key_file の書き方がチルダ(~) が含まれているとエラーでした。

[opc@mysql-client01 .oci]$ cat config
[DEFAULT]
key_file=~/.oci/oci_api_key.pem
region=ap-tokyo-1

絶対パスで指定すると正常に動作しました。もし同じエラーの場合は注意してください。

[opc@mysql-client01 .oci]$ cat config
[DEFAULT]
key_file=/home/opc/.oci/oci_api_key.pem
region=ap-tokyo-1

実行例

 MySQL  10.0.0.63:33060+ ssl  JS > util.dumpInstance("SourceInstanceDump", {osBucketName: "mysqldump", osNamespace: "orasejapan", threads: 4, ocimds: true, compatibility: ["strip_restricted_grants"]})
Acquiring global read lock
Global read lock acquired
Gathering information - done
All transactions have been started
Locking instance for backup
Global read lock has been released
Checking for compatibility with MySQL Database Service 8.0.23
NOTE: User 'root'@'localhost' had restricted privileges (AUDIT_ADMIN, BACKUP_ADMIN, BINLOG_ADMIN, BINLOG_ENCRYPTION_ADMIN, CLONE_ADMIN, CREATE TABLESPACE, ENCRYPTION_KEY_ADMIN, FILE, FLUSH_OPTIMIZER_COSTS, FLUSH_STATUS, FLUSH_TABLES, FLUSH_USER_RESOURCES, GROUP_REPLICATION_ADMIN, INNODB_REDO_LOG_ARCHIVE, INNODB_REDO_LOG_ENABLE, PERSIST_RO_VARIABLES_ADMIN, PROXY, RELOAD, REPLICATION_SLAVE_ADMIN, ROLE_ADMIN, SERVICE_CONNECTION_ADMIN, SESSION_VARIABLES_ADMIN, SET_USER_ID, SHOW_ROUTINE, SHUTDOWN, SUPER, SYSTEM_USER, SYSTEM_VARIABLES_ADMIN, TABLE_ENCRYPTION_ADMIN) removed
NOTE: User 'sugi01'@'%' had restricted privileges (AUDIT_ADMIN, BACKUP_ADMIN, BINLOG_ADMIN, BINLOG_ENCRYPTION_ADMIN, CLONE_ADMIN, CREATE TABLESPACE, ENCRYPTION_KEY_ADMIN, FILE, FLUSH_OPTIMIZER_COSTS, FLUSH_STATUS, FLUSH_TABLES, FLUSH_USER_RESOURCES, GROUP_REPLICATION_ADMIN, INNODB_REDO_LOG_ARCHIVE, INNODB_REDO_LOG_ENABLE, PERSIST_RO_VARIABLES_ADMIN, RELOAD, REPLICATION_SLAVE_ADMIN, ROLE_ADMIN, SERVICE_CONNECTION_ADMIN, SESSION_VARIABLES_ADMIN, SET_USER_ID, SHOW_ROUTINE, SHUTDOWN, SUPER, SYSTEM_USER, SYSTEM_VARIABLES_ADMIN, TABLE_ENCRYPTION_ADMIN) removed
NOTE: Database sugitest had unsupported ENCRYPTION option commented out
Compatibility issues with MySQL Database Service 8.0.23 were found and repaired. Please review the changes made before loading them.
Writing global DDL files
Writing users DDL
Preparing data dump for table `sugitest`.`sample`
Writing DDL for schema `sugitest`
Data dump for table `sugitest`.`sample` will be chunked using column `id`
Running data dump using 4 threads.
NOTE: Progress information uses estimated values and may not be accurate.
Writing DDL for table `sugitest`.`sample`
Data dump for table `sugitest`.`sample` will be written to 1 file
1 thds dumping - 100% (10 rows / ~10 rows), 7.00 rows/s, 32.00 B/s uncompressed, 0.00 B/s compressed
Duration: 00:00:01s
Schemas dumped: 1
Tables dumped: 1
Uncompressed data size: 42 bytes
Compressed data size: 0 bytes
Compression ratio: 42.0
Rows written: 10
Bytes written: 0 bytes
Average uncompressed throughput: 32.59 B/s
Average compressed throughput: 0.00 B/s
 MySQL  10.0.0.63:33060+ ssl  JS >

こんな具合に Bucket に DumpFile が出力されている

1611386021375.png

Replica : Dumpfile を Import

Replica である MDS に Dumpfile を Import します

MySQL Shell で接続

mysqlsh admin@replica01.mysqlsubnet01.vcn.oraclevcn.com

実行例

[opc@mysql-client01 ~]$ mysqlsh admin@replica01.mysqlsubnet01.vcn.oraclevcn.com
Please provide the password for 'admin@replica01.mysqlsubnet01.vcn.oraclevcn.com': ***********************
Save password for 'admin@replica01.mysqlsubnet01.vcn.oraclevcn.com'? [Y]es/[N]o/Ne[v]er (default No):
MySQL Shell 8.0.23

Copyright (c) 2016, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
Other names may be trademarks of their respective owners.

Type '\help' or '\?' for help; '\quit' to exit.
Creating a session to 'admin@replica01.mysqlsubnet01.vcn.oraclevcn.com'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 275
Server version: 8.0.23-cloud MySQL Enterprise - Cloud
No default schema selected; type \use <schema> to set one.
 MySQL  replica01.mysqlsubnet01.vcn.oraclevcn.com:3306 ssl  JS >

Dumpfile を Import

util.loadDump("SourceInstanceDump", {osBucketName: "mysqldump", osNamespace: "orasejapan", threads: 4})

実行例

 MySQL  replica01.mysqlsubnet01.vcn.oraclevcn.com:3306 ssl  JS > util.loadDump("SourceInstanceDump", {osBucketName: "mysqldump", osNamespace: "orasejapan", threads: 4})
Loading DDL and Data from OCI ObjectStorage bucket=mysqldump, prefix='SourceInstanceDump' using 4 threads.
Opening dump...
Target is MySQL 8.0.23-cloud (MySQL Database Service). Dump was produced from MySQL 8.0.23
Fetching dump data from remote location...
Fetching 1 table metadata files for schema `sugitest`...
Checking for pre-existing objects...
Executing common preamble SQL
Executing DDL script for schema `sugitest`
[Worker000] Executing DDL script for `sugitest`.`sample`
[Worker001] sugitest@sample@@0.tsv.zst: Records: 10  Deleted: 0  Skipped: 0  Warnings: 0
Executing common postamble SQL

1 chunks (10 rows, 42 bytes) for 1 tables in 1 schemas were loaded in 1 sec (avg throughput 42.00 B/s)
0 warnings were reported during the load.
 MySQL  replica01.mysqlsubnet01.vcn.oraclevcn.com:3306 ssl  JS >

GTID ベースのレプリケーションでは、どこまでトランザクションをレプリケーションしたかを、GTID で管理しています。MySQL Shell のユーティリティでは、Replica 側で、GTID 関連の設定が必要です。
Object Storage 内の json メタデータから、gtidExecutedを取得してメモします。

Object Storage の@.json ファイルをダウンロードします。

1611387373790.png

この @.json ファイルの中身は以下。この中の gtidExecuted をメモっておく

{
    "dumper": "mysqlsh Ver 8.0.23 for Linux on x86_64 - for MySQL 8.0.23 (MySQL Community Server (GPL))",
    "version": "1.0.2",
    "origin": "dumpInstance",
    "schemas": [
        "sugitest"
    ],
    "basenames": {
        "sugitest": "sugitest"
    },
    "users": [
        "'root'@'localhost'",
        "'rpluser001'@'%'",
        "'rpluser002'@'%'",
        "'sugi01'@'%'",
        "'test2'@'%'"
    ],
    "defaultCharacterSet": "utf8mb4",
    "tzUtc": true,
    "bytesPerChunk": 64000000,
    "user": "sugi01",
    "hostname": "mysql-client01",
    "server": "mysql-source01",
    "serverVersion": "8.0.23",
    "gtidExecuted": "0e1a6a53-5cac-11eb-8d54-02001701e97e:1-3",
    "gtidExecutedInconsistent": false,
    "consistent": true,
    "mdsCompatibility": true,
    "begin": "2021-01-23 07:10:44"
}

メモった値を使って、gtid_purged を行います。Replica に MySQL Client (MySQL Shell じゃないよ) を使って接続します。次にメモった値を使ってコマンドを発行します

mysql> call sys.set_gtid_purged("0e1a6a53-5cac-11eb-8d54-02001701e97e:1-3");
Query OK, 0 rows affected (0.00 sec)

mysql>

確認

mysql> select @@gtid_purged;
+------------------------------------------+
| @@gtid_purged                            |
+------------------------------------------+
| 0e1a6a53-5cac-11eb-8d54-02001701e97e:1-3 |
+------------------------------------------+
1 row in set (0.00 sec)

mysql>

memo : エラーになったとき

mysql> call sys.set_gtid_purged("0e1a6a53-5cac-11eb-8d54-02001701e97e:1-10");
ERROR 3546 (HY000): @@GLOBAL.GTID_PURGED cannot be changed: the added gtid set must not overlap with @@GLOBAL.GTID_EXECUTED

memo : 次の gtid_executed が空白じゃないとエラーになる

mysql> show global variables like 'GTID%';
+----------------------------------+------------------------------------------------------------------------------------+
| Variable_name                    | Value                                                                              |
+----------------------------------+------------------------------------------------------------------------------------+
| gtid_executed                    | 0e1a6a53-5cac-11eb-8d54-02001701e97e:1-5,
cbcefcc6-5cb0-11eb-8761-020017004a1e:1-7 |
| gtid_executed_compression_period | 0                                                                                  |
| gtid_mode                        | ON                                                                                 |
| gtid_owned                       |                                                                                    |
| gtid_purged                      | 0e1a6a53-5cac-11eb-8d54-02001701e97e:1-3                                           |
+----------------------------------+------------------------------------------------------------------------------------+
5 rows in set (0.01 sec)

mysql>

Replica : Channel の作成

OCI Console 上で、Create Channel を行います

1611327494852.png

各種パラメータを入れます。Source で作成したレプリケーション用のユーザー名とパスワードを指定します

rpluser001
Rpl001#!

1611327913895.png

Create

1611327953254.png

Creating

1611327971359.png

ステータスが NEEDS ATTENTION になった場合、レプリケーションに何らかの問題が発生しています
次の URL で「NEEDS ATTENTION」で検索すると、詳細情報が見えます
https://docs.oracle.com/en-us/iaas/mysql-database/doc/replication.html#GUID-CB1AC60B-AA87-4D7C-9D75-191449F89663

1611328818226.png

一個上の画面に戻ると、エラーメッセージが確認出来ます。自分の環境の場合は、Source のセキュリティーグループで 3306 を開けていなかったため、次のエラーが出ていました。

1611328850292.png

エラー原因を改善したあとに、Resume を押すと再開できます。

1611329277254.png

ACTIVE になります。これで Inbound Replication を使った非同期レプリケーションが完成です。

1611330497665.png

レプリケーション確認

レプリケーションされているか確認するために、Source にテストデータを入れてみます

mysql -h 10.0.0.63 -u sugi01 -p

現在のデータ

mysql> select * from sugitest.sample;
+----+-------+
| id | value |
+----+-------+
|  1 |     1 |
|  2 |     2 |
|  3 |     3 |
|  4 |     4 |
|  5 |     5 |
|  6 |     6 |
|  7 |     7 |
|  8 |     8 |
|  9 |     9 |
| 10 |    10 |
+----+-------+
10 rows in set (0.00 sec)

mysql>

Source にデータを Insert します

INSERT INTO sugitest.sample(value) VALUES (11);

replica 先

mysql -h replica01.mysqlsubnet01.vcn.oraclevcn.com -u admin -p

レプリケートされている

mysql> select * from sugitest.sample;
+----+-------+
| id | value |
+----+-------+
|  1 |     1 |
|  2 |     2 |
|  3 |     3 |
|  4 |     4 |
|  5 |     5 |
|  6 |     6 |
|  7 |     7 |
|  8 |     8 |
|  9 |     9 |
| 10 |    10 |
| 11 |    11 |
+----+-------+
11 rows in set (0.00 sec)

mysql>

memo : わかったこと

  • MDS1個につき、Channel は1個まで (マルチソースレプリケーションが現状出来ないので、問題なし)
  • - mysql スキーマ内で CREATE TABLE しても、レプリケーションされない。また、これが影響してレプリケーションが中断されるので注意 (Document で書かれているので、mysql スキーマ内で作業するのは止めよう)

参考URL

Inbound Replication
https://docs.oracle.com/en-us/iaas/mysql-database/doc/replication.html

https://dev.mysql.com/doc/refman/8.0/en/replication.html

MySQL Database Serviceへ移行してみよう
https://www.s-style.co.jp/blog/2021/01/7076/

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

LaradockでLaravel8の開発環境を構築する

前提

  • Mac を使っている
  • Git インストール済
  • Docker インストール済

できること

  • Laravel8の開発環境が構築できる
  • phpMyAdminでDBを参照できる

手順

  1. Laradockをローカル環境に複製し、環境設定ファイルを編集する
  2. Dockerコンテナを起動し、コンテナに入る
  3. Laravel8をローカル環境にインストールし、環境設定ファイルを編集する
  4. Laradockの環境設定ファイルを編集し、Dockerコンテナ再起動
  5. Laravel8phpMyAdminの表示を確認する

※コンテナはnginxPHP-FPMMysqlそしてphpMyAdminを用意します

構築する

1. Laradockをローカル環境に複製し、環境設定ファイルを編集する

Laradockを複製(複製場所は"/Users/任意の名前")

git clone https://github.com/LaraDock/laradock.git

環境設定ファイル(.env)を編集

cp env-example .env
vim .env

編集箇所と内容

DATA_PATH_HOST=.laradock/data
COMPOSE_PROJECT_NAME=project_name
MYSQL_VERSION=5.7.31

ファイル末尾に追記

DB_HOST=mysql

2. Dockerコンテナを起動し、コンテナに入る

Dockerコンテナを起動

docker-compose up -d nginx mysql phpmyadmin
docker-compose ps

Dockerコンテナに入る

docker exec -it river_web_workspace_1 bash
3. Laravel8をローカル環境にインストールし、環境設定ファイルを編集する

Laravel8をローカル環境にインストール(ディレクトリ名はsrc)

composer create-project laravel/laravel src

環境設定ファイル(.env)を編集

vim .env

編集箇所と内容

DB_HOST=mysql
DB_DATABASE=default
DB_USERNAME=default
DB_PASSWORD=secret

4. Laradockの環境設定ファイルを編集し、Dockerコンテナ再起動
exit
vim .env

編集箇所と内容

APP_CODE_PATH_HOST=../src

docker-compose up -d nginx
5. Laravel8phpMyAdminの表示を確認する

http://localhost/
http://localhost:8081/

データベース名:mysql
ユーザー:default
パスワード:secret

※DB編集する際は、先ずはルートではいり、上記ユーザーに編集権限を付与します

データベース名:mysql
ユーザー:root
パスワード:root

以上になります。:laughing:

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

ポートフォリオ ー店舗管理システム作ってみた

初めに

バックエンドをLaravel,フロントエンドをVue.jsを用いて実装しています。
この記事ではアプリ開発の説明や工夫した点を記載したいと思います。

アプリの概要

実際に飲食店舗の従業員は活用することをイメージしながら作成した店舗管理システムです。

・店長の事務業務負荷
・実際に生じている店舗の問題点

を解決できるようにすることをコンセプトとして作成しました。
「Cafe de Drip」という喫茶店で活用されることを想定しています。

アプリURL:https://cafe-de-drip.herokuapp.com/
※ユーザー認証が必要なので閲覧できません....。

GitHub:https://github.com/Tanaka-Kizuki/Store-management-system

↓ユーザー認証後のホーム画面
Cafe de Drip ホーム画面.png

実際に店舗で起きている問題点

◉勤怠管理システム(打刻式)がなくタイムカードもしくはシステム上に手打ち
 →店長がシステム上に勤怠入力もしくは実績と入力に相違がないか確認・承認作業

◉ノートを使った共有事項の伝達
 →ノートを手書きで書く作業負荷

◉日々の発注業務
 →発注が出来る能力者が限られており、休みの日でも出勤もしくは遠隔発注

上記問題点解決策(具体的なアプリ実装・機能)

◉勤怠管理システムの導入
スクリーンショット 2021-01-23 18.03.23.png

◉掲示板形式の連絡ノート
スクリーンショット 2021-01-23 18.36.22.png

◉店舗在庫を入力することで必要数が発注される発注システム
スクリーンショット 2021-01-23 18.38.04.png

機能一覧

◉ユーザー管理登録
 ○管理者権限
 ○新規登録(管理者のみ有効)
 ○ログイン、ログアウト機能
◉勤怠管理システム
 ○出退勤
 ○休憩開始、終了
 ○出退勤時刻、休憩時間より勤務時間の算出(15分繰り上げ)
 ○ユーザーの勤怠実積照合(ログイン従業員の指定月の勤務実積表示)
 ○日次勤怠照合(指定日に勤務していた従業員、勤務時間等表示)
◉コミュニケーションノート
 ○新規作成
 ○編集
 ○削除
◉発注システム
 ○アイテムの追加、編集
 ○発注(データベース登録)
 ○発注履歴の表示

使用技術

◉フロントエンド
 ○HTML/CSS
 ○JavaScript
 ○Vue.js 4.3.1
◉バックエンド
 ○PHP 7.4.9
 ○Laravel 7.27.0
◉インフラ
 ○mysql 8.0.21

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

MySQLのrootユーザーにパスワードを設定する

MySQLのrootユーザーにパスワードが設定されていなかったので割り当てました。

MySQLのrootユーザーにパスワードが割り当てられていない人向けです。

パスワードなしで、rootユーザーとしてサーバーに接続
$ mysql -u root(--skip-password)
登録されているユーザーとそのユーザーにパスワードが付いているか確認する。
mysql> SELECT User, Host, Password FROM mysql.user;

・それぞれの MySQL root アカウントにパスワードを割り当てるべきです。
・クライアントが匿名ユーザーとしてパスワードなしで接続することを防ぐには、それぞれの匿名のアカウントにパスワードを割り当てるか、あるいはそれらのアカウントを削除するとよいでしょう。
引用:最初のMySQLアカウントのセキュリティー設定

すべてのrootユーザーにパスワードを割り当てる
mysql> UPDATE mysql.user SET Password = PASSWORD('パスワード') WHERE User = 'root';
反映させるために必要
mysql> FLUSH PRIVILEGES;
パスワード設定後の接続
$ mysql -u root -p
Enter password: (パスワード)
匿名ユーザーを削除する場合
mysql> DROP USER ''@'localhost';
匿名ユーザーにパスワードを割り当てる場合
mysql> SET PASSWORD FOR ''@'localhost' = PASSWORD('パスワード');

これでrootユーザーにパスワード割り当てられました。

参考
MySQL5.6
最初のMySQLアカウントのセキュリティー設定
MySQL8.0
Securing the Initial MySQL Account

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

【超初心者編】PHP7×MySQL、PDOを使用してDBに接続する方法

データベースとの接続は、初心者にとって「最初の壁」

こんにちは、自分はPHPを勉強して2ヶ月目の初心者です。
今回は自分用のメモと記憶整理としてデーターベース(以降:DB)との接続方法を超簡単に説明します。

環境は以下の通り
・MAMP6.2
・PHP7.4.9
・MySQL 5.7.30

【注意】初心者の解説なので、専門的に学習したい人は他の学習用のサイト・ページなどを参考にしてください!

MySQLとPDOについて

MySQLはオープンソースのDB(データベース)管理システムです。

(オープンソースとは商用、非商用を問わず配布されてるもの)
・無償で利用できる
・大容量のデーターを高速で動作させられる
・幅広いOSに対応している(Windows,mac,Linuxなど)

上記は特徴の一部に過ぎません。
優秀なシステムで実際に世界中の企業でも使用されています。

PDOとは?

PDO(PHP Data Objects)とは、
PHPからDBへアクセスする機能を持った拡張モジュールです。

なにやら難しそうですが、とりあえずDBへアクセする為の便利な機能だと思ってもらえれば、今回は大丈夫です!

接続する方法

それでは、接続方法を解説していきます。
先づはDBに接続するSQL文を用意します。

~phpMyAdmin上の表記以下と仮定~
Sever:localhost8889
ユーザー名:root
パスワード:root

DB接続用のSQL文を作成

PHP7
$sql = 'mysql:dbname=test;host=localhost;port=8889;charset=utf8';
$user = 'root';
$pass = 'root';

変数sqlにDB接続用のsql文を代入して、
dbname、host、portの各所にDBの情報を入力します。

最後のcharasetの所は、文字コードをUTF-8に指定しています。

変数userとpassにはユーザー名とパスワードを入力してください。

PDOを使いSQL文を$dbhに代入する

$dbh = new PDO($sql,$user, $id);

$dbhにPDOを使い、先程の変数を組み合わせる。

例外処理を追加

もしDBとの接続が出来なかった場合、エラー文を表示する様に
try(例外処理)を追加します。

try {
 $dbh = new PDO($sql,$user,$id);
} catch(PDOException $e) {
  echo('DB接続エラー:' . $e->getMessage());
}

これによりDBと接続出来なかった時、cath内の'DB接続エラー'とエラー文が表示されるようになります。

まとめ

MySQLなどDBの世界は本当に奥が深く、覚える事が多く難しいです。
そんな中、少しでも分かりやすく記録できたらと思い投稿させていただきました。

今回はDBの接続方法のみですが、今後SELECTやINSERT、内部結合についても取り上げられたらと思います。
少しでもお役に立てたら幸いです!

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

【超初心者編】PHP7×MySQL、PDOを使用してDB(データーベース)に接続する方法

データベースとの接続は、初心者にとって「最初の壁」

こんにちは、自分はPHPを勉強して2ヶ月目の初心者です。
今回は自分用のメモと記憶整理としてデーターベース(以降:DB)との接続方法を超簡単に説明します。

環境は以下の通り
・MAMP6.2
・PHP7.4.9
・MySQL 5.7.30

【注意】初心者の解説なので、専門的に学習したい人は他の学習用のサイト・ページなどを参考にしてください!

MySQLとPDOについて

MySQLはオープンソースのDB(データベース)管理システムです。

(オープンソースとは商用、非商用を問わず配布されてるもの)
・無償で利用できる
・大容量のデーターを高速で動作させられる
・幅広いOSに対応している(Windows,mac,Linuxなど)

上記は特徴の一部に過ぎません。
優秀なシステムで実際に世界中の企業でも使用されています。

PDOとは?

PDO(PHP Data Objects)とは、
PHPからDBへアクセスする機能を持った拡張モジュールです。

なにやら難しそうですが、とりあえずDBへアクセする為の便利な機能だと思ってもらえれば、今回は大丈夫です!

接続する方法

それでは、接続方法を解説していきます。
先づはDBに接続するSQL文を用意します。

~phpMyAdmin上の表記以下と仮定~
Sever:localhost8889
ユーザー名:root
パスワード:root

DB接続用のSQL文を作成

PHP7
$sql = 'mysql:dbname=test;host=localhost;port=8889;charset=utf8';
$user = 'root';
$pass = 'root';

変数sqlにDB接続用のsql文を代入して、
dbname、host、portの各所にDBの情報を入力します。

最後のcharasetの所は、文字コードをUTF-8に指定しています。

変数userとpassにはユーザー名とパスワードを入力してください。

PDOを使いSQL文を$dbhに代入する

$dbh = new PDO($sql,$user, $id);

$dbhにPDOを使い、先程の変数を組み合わせる。

例外処理を追加

もしDBとの接続が出来なかった場合、エラー文を表示する様に
try(例外処理)を追加します。

try {
 $dbh = new PDO($sql,$user,$id);
} catch(PDOException $e) {
  echo('DB接続エラー:' . $e->getMessage());
}

これによりDBと接続出来なかった時、cath内の'DB接続エラー'とエラー文が表示されるようになります。

まとめ

MySQLなどDBの世界は本当に奥が深く、覚える事が多く難しいです。
そんな中、少しでも分かりやすく記録できたらと思い投稿させていただきました。

今回はDBの接続方法のみですが、今後SELECTやINSERT、内部結合についても取り上げられたらと思います。
少しでもお役に立てたら幸いです!

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

【Django】ローカル環境-MySQL設定からマイグレーションまで

Djangoでアプリ開発する前に必要なのは、DBの設定です。
ローカル環境でのdbの設定からマイグレーションまでの手順をメモ
使うDBはMySQL

DB追加

mysqlのインストールしたての初期状態はこんな感じ

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

まずDBを追加。今回のDB名はengineer_app

mysql> create database engineer_app;
Query OK, 1 row affected (0.05 sec)

正しく追加されているか確認して

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| engineer_app       |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

追加されていますね!!OKです。

当然まだテーブルは作られていません。

mysql> use engineer_app;
Database changed
mysql> show tables;
Empty set (0.00 sec)

ドライバのインストール

DjangoとMySQLをつなぐためにドライバーが必要です。
今回はmysqlclientを使おうと思います。

pip install mysqlclient

setting.pyの変更

ドライバーをインストールできたら次は、setting.pyの変更が必要です。
デフォルトではsqliteについて書かれているので、それをMySQLに変更します。

setting.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'engineer_app',
        'USER': 'root',
        'PASSWORD': 'password',
        'HOST': '127.0.0.1',
        'PORT': '3306',
    }
}

マイグレーションファイル作成

普通はモデルを作成してからマイグレーションファイルを作成しますが
今回は管理画面用のテーブルだけ作成するので、モデルは作成せずに以下のコマンドを実行します。

python manage.py makemigrations

マイグレーション実行

マイグレーションファイルを作成した次は、マイグレーションを実行します。

python manage.py migrate

実行すると以下のようなログが出力されて、成功です。

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK

DBのテーブル確認

DBにテーブルが作られているか確認してみます。

mysql> use engineer_app;
Database changed
mysql> show tables;
+----------------------------+
| Tables_in_engineer_app     |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
+----------------------------+
10 rows in set (0.01 sec)

ちゃんと管理画面用のテーブルが作られています!

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