- 投稿日:2019-10-28T23:53:55+09:00
【Laravel】複数のテーブルをJOIN→GROUP BYでグループ化→特定のレコードを取得
前提条件
親テーブル:classes
id name 1 A 2 B 3 C 子テーブル:students
id number name class_id 1 1 AAA 1 2 2 BBB 1 3 1 CCC 2 4 2 DDD 2 5 1 EEE 3 6 3 FFF 2 やりたいこと:各クラスの中で出席番号(number)が一番最後の生徒のデータを取得
MySQLSQL
SELECT * FROM classes LEFT JOIN students ON classes.id = students.class_id WHERE students.number IN( SELECT MAX(students.number) FROM students GROUP BY students.class_id );Laravel SQL直書き
$sql = 'students.number IN( SELECT MAX(students.number) FROM students GROUP BY students.class_id )'; $student = DB::table('classes') ->leftJoin('students', 'classes.id','=','students.class_id') ->whereRaw($sql);Laravel サブクエリ
$student = DB::table('classes') ->leftJoin('students', 'classes.id','=','students.class_id') ->whereIn('students.number', function($sub) { $sub->from('students') ->select(DB::raw('max(students.number)')) ->groupBy('students.class_id'); });
- 投稿日:2019-10-28T22:24:27+09:00
勉強ログ_day4_データベース入門③(テーブル属性・レコード抽出)
はじめに
非エンジニアの勉強ログ。
超初心者プレイのため、内容は自分の勉強メモです。今日のゴール
- MySQLとやらにもっと触ってみる
環境
- ホストOS:Windows10 Pro 1903
- こちらで構築した環境を利用する
- MySQL:5.7.27
参考資料・引用元など
- ドットインストール:MySQL入門 ←勉強はこれを基軸に進める
- Qiita:よく使うMySQLコマンド集
テーブルの属性(情報)を変える
- 基本はalter
- add, drop, change でフィールド属性(情報)を追加変更策個
- addではafterをあとにつけてフィールド追加位置指定可能
mysqlalter table testtable01 add user_name varchar(255); alter table testtable01 add person varchar(255) after user_name; alter table testtable01 drop person; alter table testtable01 change user_name varchar(80) default 'nobody';テーブル自体の名前もあとからalterで変えられるよ
mysqlalter table testtable01 rename users;レコードを条件つけて抽出する
whereを使います
mysqlselect * from users where age >= 20;等号不等号
条件 書き方 ~より多い > ~ ~より少ない < ~ ~以上 >= ~ ~以下 <= ~ ~と同じ = ~ ~と同じじゃない(~以外) <> ~ または != ~ nullだ is null nullじゃない is not null 記号以外(and, not, between, in)
andやnotもつかえるよ
betweenで代用したりも出来るmysqlselect * from users where age >= 0 and age <= 19; select * from users where age between 0 and 19;等号(=)はinと代用できたりする
mysqlselect * from users where user_name = 'sato', or name = 'Mary'; select * from users where user_name in ('sato', 'Mary');文字列検索
文字列検索も可能。
あいまい検索時は「%」(ワイルドカード)
大文字小文字区別したかったら「like binary」
文字数は「_」mysqlselect * from users where user_name like 'sato'; select * from users where user_name like 's%'; select * from users where user_name like binary 'S%'; select * from users where user_name like '____';並び替え
** order by ** をつかいます
降順にしたい場合は descをあとにつけるmysqlselect * from users order by age desc;絞り込み
上位3名、とか絞り込みたい場合は「limit」
mysqlselect * from users order by age desc limit 5;あとがき
- 業務でやりたいことには程遠いんだよな もっとがんばる
- 今回はじめてMarkdownでtableを書いた 書きづらいな
- 投稿日:2019-10-28T22:24:27+09:00
勉強ログ_day4_MySQL入門③(テーブル属性・レコード抽出)
はじめに
非エンジニアの勉強ログ。
超初心者プレイのため、内容は自分の勉強メモです。今日のゴール
- MySQLとやらにもっと触ってみる
環境
- ホストOS:Windows10 Pro 1903
- こちらで構築した環境を利用する
- MySQL:5.7.27
参考資料・引用元など
- ドットインストール:MySQL入門 ←勉強はこれを基軸に進める
- Qiita:よく使うMySQLコマンド集
テーブルの属性(情報)を変える
- 基本はalter
- add, drop, change でフィールド属性(情報)を追加変更策個
- addではafterをあとにつけてフィールド追加位置指定可能
mysqlalter table testtable01 add user_name varchar(255); alter table testtable01 add person varchar(255) after user_name; alter table testtable01 drop person; alter table testtable01 change user_name varchar(80) default 'nobody';テーブル自体の名前もあとからalterで変えられるよ
mysqlalter table testtable01 rename users;レコードを条件つけて抽出する
whereを使います
mysqlselect * from users where age >= 20;等号不等号
条件 書き方 ~より多い > ~ ~より少ない < ~ ~以上 >= ~ ~以下 <= ~ ~と同じ = ~ ~と同じじゃない(~以外) <> ~ または != ~ nullだ is null nullじゃない is not null 記号以外(and, not, between, in)
andやnotもつかえるよ
betweenで代用したりも出来るmysqlselect * from users where age >= 0 and age <= 19; select * from users where age between 0 and 19;等号(=)はinと代用できたりする
mysqlselect * from users where user_name = 'sato', or name = 'Mary'; select * from users where user_name in ('sato', 'Mary');文字列検索
文字列検索も可能。
あいまい検索時は「%」(ワイルドカード)
大文字小文字区別したかったら「like binary」
文字数は「_」mysqlselect * from users where user_name like 'sato'; select * from users where user_name like 's%'; select * from users where user_name like binary 'S%'; select * from users where user_name like '____';並び替え
** order by ** をつかいます
降順にしたい場合は descをあとにつけるmysqlselect * from users order by age desc;絞り込み
上位3名、とか絞り込みたい場合は「limit」
mysqlselect * from users order by age desc limit 5;あとがき
- 業務でやりたいことには程遠いんだよな もっとがんばる
- 今回はじめてMarkdownでtableを書いた 書きづらいな
- 投稿日:2019-10-28T21:06:10+09:00
【2019年10月版】TypeGraphQL なら ORM は TypeORM でしょう。
せっかく TypeGraphQL で TypeScript Ready な GraphQL にしたのに Sequelize はもったいない。
先週の記事(【2019年10月版】Apollo + Sequelize で GraphQL やるなら TypeScript バッチリの TypeGraphQL がおすすめ!、【2019年10月版】Cloud Functions + Cloud SQL で TypeGraphQL + Sequelize の Apollo Server を動かす) にて Cloud Functions 上で Apollo + TypeGraphQL を使って GraphQL の API エンドポイントを作成してみましたが、その際に使った ORM は Sequelize でありまして、以下のような不満がありました。
Classの宣言にアノテーションでRDB用のマッピングも書けたらなぁ。。。
はい、まさにその通りでありまして、Sequelize の init 処理は無駄無(ry なわけでございます。
この点を劇的に改善する
TypeORMを使用して、TypeScript Native な GraphQL サーバーを Cloud Functions に立て、Cloud SQL 上の MySQL へ ORM させてみたいと思います!参考サイト
今回も情報量多めなので参考サイトのご紹介から参ります。
まずは TypeORM の本家サイト。
TypeORM の本家サイトはチュートリアルというか使い方メインで記載されているので、しっかりリファレンスを参照したい場合は githubの /doc 内を読んだ方がよいですね。
TypeORM もフルボリュームの ORM ですので一通りのチュートリアルだけでも異常なボリュームがあります。そして、実は、GraphQL の本家リポジトリには実装サンプルがありまして、その中で "TypeORM" を使っているサンプルがあります!!
(あらやだ、すごい・・・)
- https://github.com/MichalLytek/type-graphql/tree/master/examples/typeorm-basic-usage
- https://github.com/MichalLytek/type-graphql/tree/master/examples/typeorm-lazy-relations
このようなサンプルを示していただけると TypeGraphQL と TypeORMの組み合わせに関しては基本的には大丈夫なんだという安心感があってありがたいですね。
また、TypeORMの使い方に関して Qiita の記事がありましたのでご紹介いたします。ありがとうございます!
それでは上記のページなどを参考に、前回までの記事で作成した Sequelize 用のプロジェクトを修正していきたいと思います。
1. 依存関係の修正
まずは依存パッケージのインストールですが、前回の記事から Sequelize 関連の構成が変わっているので
package.jsonは以下のようになりました。package.json... "dependencies": { "@types/bluebird": "^3.5.28", "apollo-server": "^2.9.7", "apollo-server-cloud-functions": "^2.9.7", "apollo-server-core": "^2.9.7", "firebase-admin": "^8.0.0", "firebase-functions": "^3.0.0", "graphql": "^14.5.8", "merge-graphql-schemas": "^1.7.0", "mysql2": "^1.7.0", "reflect-metadata": "^0.1.13", "type-graphql": "^0.17.5", "typeorm": "^0.2.20" }, "devDependencies": { "@types/node": "^12.11.6", "tslint": "^5.12.0", "typescript": "^3.2.2" }, ...TypeORM はアノテーション使うので本来であれば、tsconfig.json に修正が必要("emitDecoratorMetadata": true,"experimentalDecorators": trueの追加)ですが、TypeGraphQL で設定済みのため、ここでは割愛させていただきます。
2. 接続設定の切り替え
TypeORM は接続設定にいくつかの形式を提供してくれていますが、今回は外部ファイルの
ormconfig.jsを使用しました。
例によって、Cloud SQL の接続先情報をfunctions.config()から取ってくるためです。ということで接続の設定は以下のようになります。
ormconfig.jsconst functions = require('firebase-functions'); module.exports = [ { name: "docker", type: "mysql", host: "localhost", port: 3306, username: "example", password: "example", database: "example", entities: ["lib/models/*.js"], synchronize: true, logging: ["query", "error"] }, { name: "cloud-sql", type: "mysql", username: functions.config().db.user, password: functions.config().db.pw, database: functions.config().db.name, entities: ["lib/models/*.js"], synchronize: true, extra: { socketPath: `/cloudsql/${functions.config().db.conn}`, } } ]ここではローカル開発のDockerへの接続設定に"docker"という名前を付け、GCP 向けには "cloud-sql" という名前を付けています。
また、この接続で使用するスキーマ定義を
entitiesで明示的に指定しています。接続先ごとにモデルを分けたい、なんて時も利用できます。それと今回、使用している
synchronizeオプションはプロダクション環境では使用するな、とのことです。基本的に変更のあったモデルは "Drop & Create" しているようです。(common-connection-options)接続設定の "extra" の項目は各ドライバへ直接渡すパラメタを記載します。Cloud SQL への接続には UNIXドメインソケットが推奨ですので、Sequelize 同様に mysql.js にソケット経由で接続させるために "socketPath" を指定しております。
で、このファイルは自動的に TypeORM が読み取ってくれますがどちらの接続を使うかは
createConnectionメソッドの引数で指定します。database.tsimport { createConnection } from "typeorm"; export const connection = createConnection("cloud-sql");この
connectionを利用する方法は後ほどご紹介いたします。3. スキーマ定義
今回の TypeORM を採用して可能になったのがアノテーションによるDBスキーマ定義です。TypeGraphQLとTypeORMでスキーマ定義だけで同じモデルのコードが共有できるなんて、素敵すぎます。
早速ですが前回のユーザーモデルは以下のようになりました。
model/user.tsimport { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToMany, BaseEntity } from "typeorm"; import { ObjectType, Field, ID } from 'type-graphql' import { Todo } from "./todo"; @Entity() @ObjectType() export class User extends BaseEntity { @PrimaryGeneratedColumn() @Field(type => ID) public id!: number; @Column() @Field() public name!: string; @OneToMany(type => Todo, todo => todo.user) @Field(type => [Todo]) public todos?: Todo[]; @CreateDateColumn({ type: "timestamp" }) public readonly createdAt!: Date; @UpdateDateColumn({ type: "timestamp" }) public readonly updatedAt!: Date; }RDBのフィールド設定、リレーションの設定と GraphQLのスキーマ設定が一回のモデル定義で完了しています。
前回の Sequelize の定義と比べるまでもなく、タイプの量が圧倒的に減っています。
@ColumnでDBのフィールドであることと、フィールドの型などを宣言しますが、省略した場合はvarchar(255)となるようです。(MySQLの場合)自動生成のID項目ですが、TypeORMでも
@PrimaryGeneratedColumn('uuid)'はサポートされているはずですが、MySQLでは対応が不十分なようです・・・Closeされているけどうまく動かなかったですね。。。
また自動生成モノでは@CreateDateColumn、@UpdateDateColumnなどがあります。リレーションの設定では
@OneToManyの定義がありますが、逆の@MenyToOne側は以下のように書いています。models/todo.tsimport { Entity, PrimaryGeneratedColumn, Column, ManyToOne, CreateDateColumn, UpdateDateColumn, BaseEntity } from "typeorm"; import { ObjectType, Field, ID } from 'type-graphql' import { User } from "./user"; @Entity() @ObjectType() export class Todo extends BaseEntity { @PrimaryGeneratedColumn() @Field(type => ID) public id!: number; @ManyToOne(type => User, user => user.todos) @Field(type => User) public user!: User; @Column() @Field() public title!: string; @Column("text") @Field() public content?: string; @CreateDateColumn({ type: "timestamp" }) @Field() public readonly createdAt!: Date; @UpdateDateColumn({ type: "timestamp" }) @Field() public readonly updatedAt!: Date; }こちらもこのフィールド定義だけでリレーションが出来てしまうので便利です。
4. リゾルバの修正
実は、上記の手順でモデルのClass宣言に
extends BaseEntityしていたのをあえて黙っておりました。TypeORM では BaseEntity を extend しないすっぴんの Class でも Repository を使うことで同じことが可能です。
まず BaseEntity を継承するとどんなことができるようになるの?という点は以下のページをご覧ください。
リンクのタイトルでもうお分かりかと思いますが、要は ActiveRecord パターンで書きたかったのです。
このリゾルバの処理で実際にレコードの操作を行いますが、Data Mapper pattern + Repository では実現できることが同じなのに記述量が増えてしまったので ActiveRecord パターンを採用しています。特にサンプル実装は簡易なCRUD操作だけだしね・・・ということもあります。複雑なビジネスロジックには Data Mapper + Repository の方が適している場合もあるかもしれません。
実際に、先ほど挙げた TypeGraphQL のサンプル実装では Data Mapper + Repository を使用した実装を行っています。
どちらのパターンでもTypeORMは対応可能ですので、仕様とコーディングのコストでどちらかを採用すればよいかと思います。というわけで、今回の Active Record パターンでのリゾルバ実装は以下のようになりました。
resolver.tsimport { Resolver, Query, InputType, Field, Arg, Ctx, Mutation } from 'type-graphql'; import { User } from "../models"; import { Context } from "apollo-server-core"; @InputType({ description: "New User Argument" }) class AddUserInput implements Partial<User> { @Field() name!: string; } @InputType({ description: "Update User Argument" }) class UpdateUserInput implements Partial<User> { @Field() id!: number; @Field() name!: string; } @Resolver() export class TodoAppResolver { @Query(returns => User, { nullable: true }) async user(@Arg("id") id: number): Promise<User | undefined> { return User.findOne(id); } @Mutation(returns => User) async addUser(@Arg("data") newUser: AddUserInput, @Ctx() ctx: Context): Promise<User> { const user = User.create(newUser); return user.save(); } @Mutation(returns => User, { nullable: true }) async updateUser(@Arg("data") updateUser: UpdateUserInput, @Ctx() ctx: Context): Promise<User | null> { const user = await User.findOne(updateUser.id); if (user !== undefined) { user.name = updateUser.name; return await user.save(); } return null; } }実はここは Sequelize よりコード量が増えてます。。。いや、なのでRepositoryパターンをやめたのですが、コード量が減る Active Record パターンを使っても、やっぱり Sequelize よりコード量増えてます。。。
ここは DeepPartial でそのまま Create、Updateできるメソッドがないのが辛いっすね~。Active Record パターンがダメかと思ったのですが、Repository 側にもそのようなメソッドがないので諦めました。
5. Apollo Server の用意
ここは前回のものとほとんど変わらないですが、接続設定については TypeORM 用のコードを加える必要があります。
index.tsasync function init() { const conn = await db.connection; BaseEntity.useConnection(conn); } ... exports.gqltest = functions.https.onRequest(server.createHandler({ cors: { origin: true, credentials: true, }, })); init().catch(console.log);
BaseEntityはデフォルトでdefaultという名前の接続を参照しに行きます。今回は名前指定でコネクションを作成しているために、BaseEntity.useConnection()で作成した接続を使用するように指定しています。サーバー起動時の修正はこの部分だけでOKです。
6. Functions の Node.js ランタイムは 8 じゃないとダメ。
ちょっとショッキングなのですが、functionsで利用する node.js ランタイムは前回、
10を指定しておりましたが、今回は8じゃないとダメです。
package.json を以下のように修正してください。pakcage.json... "engines": { "node": "8" }, ...Cloud Functions 起動時の接続でMySQLとの接続が切断されてしまうので、色々試したところ、node.js "8" で落ち着きました。こういうところが"β版"なのかなぁ。。。
7. デプロイ
では、以下のコマンドでデプロイしましょう!Apollo、行きまーす!
$ firebase deploy --only functions:gqltest例によって Functions のログ監視は必須です。こちらの環境では
Node.js 8のインスタンスで一応は安定していますが、Cloud Functionsの起動時に他サービスとの接続が不安定なのはあるあるなので、ダメなら再デプロイが必須です。(困ったもんだ。。。)Functions の Httpsエンドポイントにアクセスしたら GraphQL の Playgroud が立ち上がるはずです!
8. Cloud Shell から MySQL を確認。
GraphQLのPlayground画面での確認は割愛いたしますが、Cloud SQLでの確認結果は載せておきましょう。
前回同様、Cloud Shell から MySQL に接続いたします。
$ gcloud sql connect graphql-sample-xxxx --user=example --quiet Whitelisting your IP for incoming connection for 5 minutes...done. Connecting to database with SQL user [example].Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 25401 Server version: 5.7.14-google-log (Google) Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> use example; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> select * from user; +----+-------+----------------------------+----------------------------+ | id | name | createdAt | updatedAt | +----+-------+----------------------------+----------------------------+ | 1 | user1 | 2019-10-28 10:15:02.958659 | 2019-10-28 10:15:02.958659 | | 2 | user2 | 2019-10-28 10:17:30.685594 | 2019-10-28 10:17:30.685594 | | 3 | user3 | 2019-10-28 10:17:40.487853 | 2019-10-28 10:17:40.487853 | | 4 | user4 | 2019-10-28 10:22:54.050144 | 2019-10-28 10:22:54.050144 | | 5 | user5 | 2019-10-28 10:27:47.665271 | 2019-10-28 10:27:47.665271 | | 6 | user5 | 2019-10-28 10:30:17.408823 | 2019-10-28 10:30:17.408823 | +----+-------+----------------------------+----------------------------+ 6 rows in set (0.15 sec) mysql>quit;MySQL 側にレコードがバンバン追加できているのが確認できました!
ここでちょっと注意が必要な点は、Sequelize で作成されたテーブルは
usersでしたが、TypeORMはuserです。モデル名がそのままテーブル名になっております。
というわけで、TypeORMが生成するDBのテーブル名やフィールド名などがちょっと気に食わない・・・という場合は先ほどご紹介した記事 で修正できるようです。本日は以上といたします。
- 投稿日:2019-10-28T19:12:50+09:00
MySQLで月の差分をみる
date_timeの差分をdate_diffで見るのはいいんだけど、
何ヶ月経ってるのかを計算するとき無造作に30で割るってやったらダメかな...って思ってたけど
こいつがいたとは
PERIOD_DIFF(date_format(now(),"%Y%m"), date_format(contract_date,"%Y%m"))
- 投稿日:2019-10-28T16:57:29+09:00
【Java】JDBCをインストールして、servlet + MySQL で接続してみた。( DAO / Bean を使用バージョンあり)
◆ 目次
◇ ディレクトリ構成
◇ 【画像】出力内容 & DB内容
- JDBC ドライバ導入
- context.xml を作成する
- 実行(複数パターン)
- 【実行】servlet(サーブレット)
- 【実行】DAO ~Beanを使わない~
- 【実行】DAO ~Beanを使用~
◇ ディレクトリ構成
book
┣━src
┃ ┣━sample
┃ ┃ ┣━All.java
┃ ┃ ┣━ProductList.java
┃ ┃ ┗━ProductList2.java
┃ ┃
┃ ┣━dao
┃ ┃ ┣━DAO.java
┃ ┃ ┗━ProductDAO.java
┃ ┃
┃ ┗━bean
┃ ┗━Product.java
┃
┗━WebContent
┣━META-INF
┃ ┗━context.xml
┃
┗━WEB-INF
┗━lib
┗━mysql-connector-java-5.1.48-bin.jar
◇ 【画像】出力内容 & DB内容
1. JDBC ドライバ導入
- 公式サイトから
mysql-connector-java-8.0.18.zipをダウンロード- 解凍するして
mysql-connector-java-5.1.48-bin.jarを取り出す/WebContent/WEB-INF/lib/mysql-connector-java-5.1.48-bin.jarに入れる2. context.xml を作成する
/WebContent/META-INF/context.xmlに作成するcontext.xml<?xml version="1.0" encoding="UTF-8" ?> <Context> <Resource name = "jdbc/book" auth = "Container" type = "javax.sql.DataSource" driverClassName = "com.mysql.jdbc.Driver" url = "jdbc:mysql://localhost/book" username = "username" password = "password"> </Resource> </Context>3. 実行(複数パターン)
- 【実行】servlet(サーブレット)
- 【実行】DAO ~Beanを使わない~
- 【実行】DAO ~Beanを使用~
1. 【実行】servlet(サーブレット)
/src/sample/All.javaに作成するAll.javapackage sample; import java.io.*; import java.sql.*; import javax.naming.InitialContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; import javax.sql.*; /** * Servlet implementation class All */ @WebServlet("/All") public class All extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); try { // コネクション取得 InitialContext ic = new InitialContext(); DataSource ds = (DataSource) ic.lookup("java:/comp/env/jdbc/book"); Connection con = ds.getConnection(); // SQL文送信 PreparedStatement st = con.prepareStatement("select * from product"); // 実行&結果受け取り ResultSet rs = st.executeQuery(); // データの表示 while (rs.next()) { out.println( rs.getInt("id") + ":" + rs.getString("name") + ":" + rs.getInt("price") ); } // データベース切断 st.close(); con.close(); } catch (Exception e) { // 接続・SQL文エラー e.printStackTrace(out); } // try } }2. 【実行】DAO ~Beanを使わない~
DAO(Data Access Object)を作成!!!!( ^ω^ )
上記、1. 【実行】servlet(サーブレット)の内容をライブラリ化する1 )
DAO.javaを作成する(コネクション取得)
2 )ProductList.javaを作成する(DAO取得 & 処理 & 表示)1 ) DAO.java を作成する(コネクション取得)
DAO.javapackage dao; import java.sql.Connection; import javax.naming.InitialContext; import javax.sql.DataSource; public class DAO { static DataSource ds; public Connection getConnection() throws Exception { // 初期コンテキスト構築 InitialContext ic = new InitialContext(); // DBの接続先情報を取得(context.xmlの内容) ds = (DataSource) ic.lookup("java:/comp/env/jdbc/book"); return ds.getConnection(); } }2 ) ProductList.java を作成する(DAO取得 & 処理 & 表示)
ProductList.javapackage sample; import dao.DAO; import java.io.*; import java.sql.*; import javax.servlet.*; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; @WebServlet("/sample/productList") public class ProductList extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); try { // DB接続 DAO dao = new DAO(); Connection con = dao.getConnection(); // SQL作成 PreparedStatement st = con.prepareStatement("SELECT * FROM product"); // SQL実行 ResultSet rs = st.executeQuery(); // データをセット while (rs.next()) { out.println( rs.getInt("id") + ":" + rs.getString("name") + ":" + rs.getInt("price") + "<br>" ); } // 接続解除 st.close(); con.close(); } catch (Exception e) { e.printStackTrace(out); } } }3. 【実行】DAO ~Beanを使用~
1 )
DAO.javaを作成する(コネクション取得)
2 )Product.javaを作成する(Bean)
3 )ProductDAO.javaを作成する(DAO取得 & 処理)
4 )ProductList2.javaを作成する(ProductDAO取得 & 表示)1 ) DAO.java を作成する(コネクション取得)
上記、
2 ) 【実行】DAO ~Beanを使わない~と同じ為
!!!!! 割愛 !!!!!2 ) Product.java を作成する(Bean)
Product.javapackage bean; public class Product implements java.io.Serializable { private int id; private String name; private int price; public int getId() { return id; } public String getName() { return name; } public int getPrice() { return price; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } public void setPrice(int price) { this.price = price; } }3 ) ProductDAO.java を作成する(DAO取得 & 処理)
ProductDAO.javapackage dao; import java.sql.*; import java.util.*; import bean.Product; public class ProductDAO extends DAO { public List<Product> listAll() throws Exception { List<Product> list = new ArrayList<>(); // DB接続 Connection con = getConnection(); // SQL作成 PreparedStatement st = con.prepareStatement("SELECT * FROM product"); // SQL実行 ResultSet rs = st.executeQuery(); // データをセット while (rs.next()) { Product p = new Product(); p.setId(rs.getInt("id")); p.setName(rs.getString("name")); p.setPrice(rs.getInt("price")); list.add(p); } // 接続解除 st.close(); con.close(); return list; } }4 ) ProductList2.java を作成する(ProductDAO取得 & 表示)
ProductList2.javapackage sample; import java.io.*; import java.sql.*; import java.util.List; import javax.servlet.*; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; import javax.sql.*; import bean.Product; import dao.ProductDAO; @WebServlet("/sample/productList2") public class Search2 extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); try { ProductDAO dao = new ProductDAO(); List<Product> list = dao.listAll(); for (Product p : list) { out.println( p.getId() + ":" + p.getName() + ":" + p.getPrice() + "<br>" ); } } catch (Exception e) { e.printStackTrace(out); } } }参考サイト
※ めちゃくちゃ参考になった!!!
Java で MySQL に 接続する 方法
https://garafu.blogspot.com/2016/05/how-to-connect-mysql.html#con-svlt1
- 投稿日:2019-10-28T09:00:59+09:00
初心者でもDockerでAmazonLinux+Go(Echo)+Mysqlの環境構築しつつ簡単なAPIを作って動かす
DockerでAmazonLinux+Go+Mysqlの環境構築する
PHPerだったけど今度はGoやっていくので環境をDockerで作ってそこで簡単なAPIをGoのフレームワーク「Echo」で作っていく
環境
- Docker Desktop on Windows:19.03.1
- AmazonLinux(イメージ:https://hub.docker.com/_/amazonlinux)
- MySQL:8.0:(イメージ:https://hub.docker.com/_/mysql)
- Go:1.13.3
- labstack/echo
- go-sql-driver/mysql
ちなみに作業環境はWindows10 Proのi7メモリ32GBノートPCです
ディレクトリ構成
|-Dockerfile |-docker-compose.yml |-./src |-main.goDockerfile
まずAmazonLinuxにGoをインストールしたイメージを作成する
FROM amazonlinux:latest ENV GOPATH /go ENV PATH /usr/local/go/bin:/go/bin:$PATH WORKDIR /usr/src # go install RUN yum update -y \ && yum install -y tar \ && yum install -y gzip \ && curl -OL https://dl.google.com/go/go1.13.3.linux-amd64.tar.gz \ && tar -C /usr/local -xzf ./go1.13.3.linux-amd64.tar.gz \ # git install for go get command RUN yum install -y git # go library RUN go get github.com/go-sql-driver/mysql \ && go get github.com/labstack/echo CMD ["go", "run", "main.go"]イメージ生成(docker build -t )
イメージ名は何でもいいですけどここでは
test-aws_go-imgとします(.は同ディレクトリを指す)docker build -t test-aws_go-img .docker-compose.yml
上記でビルドして作成したイメージ(
test-aws_go-img)をdocker-compose.ymlで指定docker-compose.ymlversion: '3.7' services: test-aws_go-img: build: . container_name: test_app # わかりやすいコンテナ名にしておく(任意) ports: - 8080:8080 db: container_name: test_db # わかりやすいコンテナ名にしておく(任意) image: mysql:8.0 command: --default-authentication-plugin=mysql_native_password # 従来の認証方式にしておく volumes: - db-store:/var/lib/mysql - ./logs:/var/log/mysql environment: - MYSQL_DATABASE=test_todo - MYSQL_ROOT_PASSWORD=password - TZ=Asia/Tokyo ports: - 3307:3306 volumes: # データの永続化 db-store:これでDocker周りは完了
main.go
アクセスするとテーブルに登録されているデータをJSONで返すようなかんたんなAPIを作る
main.gopackage main import ( "database/sql" "fmt" "net/http" _ "github.com/go-sql-driver/mysql" "github.com/labstack/echo" ) // JSON用の構造体を定義 type Task struct { ID int `json:"id"` Name string `json:"name"` } func main() { e := echo.New() e.GET("/tasks", getTasks) e.Start(":8080") } func dbConnect() *sql.DB { // DB接続処理 db, err := sql.Open("mysql", "root:password@tcp(test_db)/test_todo") if err != nil { panic(err.Error()) } return db } func getRows(db *sql.DB) *sql.Rows { rows, err := db.Query("SELECT id,name FROM tasks") if err != nil { panic(err.Error()) } return rows } func getTasks(c echo.Context) error { // DB接続 db := dbConnect() defer db.Close() rows := getRows(db) defer rows.Close() task := Task{} var results []Task for rows.Next() { err := rows.Scan(&task.ID, &task.Name) if err != nil { panic(err.Error()) } else { results = append(results, task) } } return c.JSON(http.StatusOK, results) }Docker起動
動くか確認するためにバックグラウンドのオプションはつけないで普通に起動
docker-compose up無事起動したら次に確認用のサンプルデータを入れていく
DBへ接続してデータを入れる
別の画面を開いて(Windowsならコマンドプロンプトとか)MySQLのコンテナの中に入る
docker exec -it test_db bashMySQLへログイン(パスワードは上記で設定した
password)# mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 9 Server version: 8.0.18 MySQL Community Server - GPL Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> use test_todo; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed以下のSQLを流す(コピペで可)
SET NAMES utf8; SET time_zone = '+00:00'; SET foreign_key_checks = 0; SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO'; SET NAMES utf8mb4; CREATE DATABASE `test_todo` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */; USE `test_todo`; DROP TABLE IF EXISTS `tasks`; CREATE TABLE `tasks` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; INSERT INTO `tasks` (`id`, `name`) VALUES (1,"go"), (2, "php"), (3, "java"), (4, "html"), (5, "css");念の為入ったか確認しておく
mysql> select * from tasks;URLを叩いてJSONが帰ってくるか確認
準備ができたのでターミナルやらで
curlコマンドを叩くcurl -s http://localhost:8080/tasksすると以下のようにJSONでデータが取得出来る
[{"id":1,"name":"go"},{"id":2,"name":"php"},{"id":3,"name":"java"},{"id":4,"name":"html"},{"id":5,"name":"css"}]以下はChromeからアクセスした際の様子
おわり
- 80%くらいの確率で動くと思うんですが動かなかったらおしえてほしいです
- それ以外でもこうしたほうがいい等あればコメント下さい
参考URL
- https://docs.docker.com/compose/compose-file/
- https://stackoverflow.com/questions/57566060/panic-dial-tcp-127-0-0-13306-connect-connection-refused
- https://tech.speee.jp/entry/2017/08/22/110000
- https://qiita.com/K-juju/items/f6d0eedb9aee5714cb73
- https://shinter8823.hatenablog.com/entry/2019/07/18/015051
- https://qiita.com/zembutsu/items/9e9d80e05e36e882caaa
- https://omohikane.com/golang_on_dokcer/
- https://designsupply-web.com/knowledgeside/3325/
- https://qiita.com/lkeix_/items/b291eb9cebc76deeb916
- https://qiita.com/ShinyaIshikawa/items/fede44cee7c71721247a
などなど
- 投稿日:2019-10-28T00:11:04+09:00
RSpec導入方法
環境
- Mac OS
- Ruby 2.6.3
- Rails 5.2.3
- MySQL 8.0
RSpecについて
TDD(テスト駆動開発)で使用される、
Rubyの代表的なテスティングフレームワーク。TDD(テスト駆動開発)とは
- 先にテストコードを書き実行、テストを失敗させる(レッド)
- テストコードが成功するようにコードを書く(グリーン)
- テストコードを実行し、テストを成功させる(リファクタリング)
を繰り返し、ある程度のところでリファクタリングをする
というように開発を進めていく手法。
- バグを開発段階で発見、修正できる
- テストを繰り返すことで実装するシステムへの理解が深まる
- 有効なリファクタリングかどうかの確認がしやすい
などのメリットがある。
Capybaraについて
WebアプリケーションのE2Eテスト
(単体テストとは違い、システム全体を実稼働時に近い状況で動かしつつテストする手法)
用のテストフレームワーク。FactoryBotについて
テスト用のデータの作成をサポートするgem
テスト用のデータを簡単に作成し、テストから呼び出して使用できる。【1】 gemをインストールする
Gemfilegroup :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] endGemfileの上記の部分に
gem 'rspec-rails', '~> 3.7' gem 'factory_bot_rails', '~> 4.11'を追加して下記のようにする。
Gemfilegroup :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'rspec-rails', '~> 3.7' gem 'factory_bot_rails', '~> 4.11' endその後bundle installを実行する。
$ bundle【2】 RSpecの設定ファイルを作成する
$ rails g rspec:install上記のコマンドを実行しRSpecの設定ファイルを作成する。
その後、元々あったtestディレクトリ(Minitestで使うもの)を削除する。
$ rm -r ./test【3】 Capybaraを使う準備をする
spec_helper.rb# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| # rspec-expectations config goes here. You can use an alternate # assertion/expectation library such as wrong or the stdlib/minitest # assertions if you prefer.作成されたspecディレクトリにある
上記のようなspec_helper.rbに対して必要なコードを加え、下記のようにする。spec_helper.rb# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration require 'capybara/rspec' RSpec.configure do |config| config.before(:each, type: :system) do driven_by :selenium_chrome_headless end # rspec-expectations config goes here. You can use an alternate # assertion/expectation library such as wrong or the stdlib/minitest # assertions if you prefer.感じたこと
RSpecはどうやって導入するんだっけ?
と忘れてしまう事もあるので自分用のメモとしてさらっとまとめました!(笑)
まだまだ勉強中の身なのでしっかり学習していきます!!
- 投稿日:2019-10-28T00:10:01+09:00
【エラー】MySQLが起動できない
環境
- Mac OS
- MySQL 8.0
エラー内容
$ sudo mysql.server startを実行しMySQLを起動しようとすると、
Starting MySQL .Logging to '/usr/local/var/mysql/***.err'. ERROR! The server quit without updating PID file (/usr/local/var/mysql/***.pid).という「PIDファイルを更新せずにサーバーが終了した」的な内容が表示され、
MySQLが起動できないというエラーが発生しました。対処方法
$ sudo rm -rf /usr/local/var/mysql上記のコードを実行し、
MySQLのデータファイルなどがあるディレクトリを削除する。その後MySQLを一旦アンインストールし、再インストールする。
$ brew uninstall mysql $ brew install mysql❯ mysql.server start Starting MySQL .. SUCCESS!...無事MySQLが起動しました!
感じたこと
注意点として、/usr/local/var/mysqlディレクトリを削除するので、
MySQLで作成したデータベースなどがが全て削除されます!!僕は今回特に残しておきたいデータなどなかったのでこの方法で解決しましたが
全てのデータを削除したくない場合はこの方法は避けた方が良さそうです!(汗)追記(2019/10/30)
同じエラーがまた発生しました。
Macを再起動した時に発生したので
おそらくですがMySQLサーバーの停止を忘れていたから発生したのかと推測しています。詳しく調べてみるとMySQLの権限のエラーらしく、
下記のコマンドで権限を正しく直すとエラーが解決できました!!$ sudo chown -R _mysql:_mysql /usr/local/var/mysql今後このエラーが発生する事があればこちらの方法で解決した方が良さそうです!
参考


