20210129のJavaに関する記事は6件です。

【SpringBoot:Maven】Elastic Beanstalkでデプロイするまでの流れ

前提

  • MySQLに接続したアプリケーションが既にある。
  • awsのアカウントを発行済である。

対象者

  • デプロイをとりあえずしたい人。
  • Elastic Beanstalkでデプロイする流れをサクッと知りたい人。
  • AWSについて詳しくない人。

注意

  • くれぐれもくるくるしている間に触らない。1つ1つ読み込み長いです。
  • インフラ系は戻るボタンを使わない。

ざっくりElastic Beanstalkって何?

AWSでデプロイに必要な

  • Elastic Load Balancing (ロードバランサー)
    • アプリケーションのデータ量を自動で分散してくれる。
  • Amazon Elastic Compute Cloud (Amazon EC2)
    • 仮想サーバーを構築してくれる。これですばやくデプロイできる。
  • Amazon Relational Database Service (Amazon RDS)
    • クラウド上に作る性能の高いDB。今回はデータベースエンジンにMySQLを選択。

をセットで構築してくれて、簡単にデプロイできるようにするためのサービス。
(簡単に、と言っていますが初心者の筆者は1週間悩みましたw)

Elastic Beanstalkの準備

  • Elastic Beanstalkにログイン
  • リージョンを東京にしておく。
  • いますぐ始めるを押す
  • 各項目を入力してアプリケーションの作成

    • アプリケーション名→好きな名前(URLになる)
    • プラットフォーム→Java qiitaaws.png
    • アプリケーションコード→サンプルアプリケーション
      qiitaaws1.png

作成まで結構時間がかかります。

qiitaaws2.png
ヘルスチェックが緑になれば無事に作成完了です。
qiitaaws3.png

DBの作成

qiitaaws4.png

編集を押したら、

  • エンジン→mysql
  • ユーザー名→好きな名前(今回はadmin)
  • パスワード→好きなパスワード
    そのほかは触らない。
  • 適用を押す

DBの作成も時間がかかります。

qiitaaws5.png
作成されたDBのエンドポイントを確認します。

RDSを見てみると、DBが作成されているのが分かります。
qiitaaws15.png

application.propertiesをYAML形式に変更

MySQLに接続していたこの状態のapplication.propertiesを

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

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

#SpringBootを起動したときに実行したいSQL文を記述したパス
spring.datasource.schema=classpath:schema.sql
spring.datasource.data=classpath:data.sql

RDSに接続できるようにapplication.ymlに変更

spring:
  profiles:
    active: dev
  datasource:
    initialization-mode: always
    url: jdbc:mysql://確認したエンドポイント/ebdb
    username: admin
    password: 設定したパスワード
server: 
  port: 5000
  • プロファイル名dev(今回は開発と本番環境を分ける前提でdevと名前を付ける)
  • MySQLでschema.sqlやdatasqlを実行したい場合はinitialization-mode: alwaysにする
  • YAMLは:の後に設定がある場合1マスブランクを入れます。
  • YAMLは一段下がると、前に2マスブランクを入れます。(必ず偶数になる)
  • ebdbは標準で作成するDB名
  • port: 5000は重要です。(いつも8080だったのであまり重要視していませんでした。)

application.propertiesではまったこと

dev書き方

参考にしていた書籍では

 config: 
  activate:
   on-profile: dev

となっていたのですが、これだとRDSと接続エラーとなりました。

Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).

公式リファレンスを見てみると書き方が違ったため、

  profiles:
    active: dev

に変更したらいけました~!
リファレンスURL

ポート番号

最初はポート番号を8080にしており、localhostでは正常な挙動なのにawsのURLだと「502 Bad Gateway」になるという現象に悩まされました。
Elastic Beanstalkのデフォルトのポートは5000です。aws公式サイト
nginxがTomcatの5000番を参照しに行くので、5000以外だとRDSに接続できなくなります。

ビルド

pom.xmlのversionを指定しておく。

今後アプリケーションがバージョンアップして、デプロイし直す度にここを変えていく。
qiitaaws11.png
アプリを選択して実行→Mavenビルド
qiitaaws6.png

  • ゴール→package
  • プロファイル→ブランク qiitaaws7.png

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.22.2:test (default-test) on project Qiita210129: There are test failures.

※テストができていないと上記のエラーがでるので、最初からあるcom.example.demoパッケージ内も含めて一旦すべて消してください。

ビルドがうまくできたら、jarファイルがtargetの中にできるので、格納場所を確認しておく。
qiitaaws8.png

デプロイ

Elastic Beanstalk
qiitaaws9.png

qiitaaws10.png

このバージョンはpomで指定したversionと同じにしておく。
デプロイはとても時間がかかります。

Elastic Beanstalkにdevを設定する

Elastic BeanstalkでYAMLに書いたdevの設定をする。
設定→ソフトウエア(一番上)編集
qiitaaws12.png

画面が自動転送されるとき(loginなど)

今回のアプリケーションの仕様にはないですが、最初のページに自動転送されるという仕様があるときに設定が必要になります。
(例えば、/hogeをurlに入力しても、ログインしていないので/loginにリダイレクトされるようなとき)

qiitaaws13.png
設定→ロードバランサー→ヘルスチェックパスを最初の画面(/login)にする。
デフォルトでは/になっている。

※何故設定するかというと、ロードバランサーがHTTPのステータスコードが成功だと思って「200」が返ってくると思っているのに、リダイレクトの「301」「302」「303」が返ってきてくるからです。

こちらを参考にしました。
基礎からのネットワーク&サーバー構築

デプロイできているか確認

Elastic Beanstalk
環境に移動
qiitaaws16.png
awsのページに飛ぶので、最初のページ(/todos)を末尾に付け足します。(今回はawsのURL/todosが最初のページに指定されているため)
qiitaaws17.png

controllerの指定が最初から/なのであれば「環境に移動」を選択すればすぐに表示されるので付け足し不要です。

Inkedqiitaaws14_LI.jpg

最後に

お疲れさまでした!!
こんなに色々やってくれるElastic Beanstalkすごいですね!
RDSは課金されるので、必要でないときは消すのがよいかと思います。

Elastic Beanstalkは簡単にデプロイできるらしい、便利じゃないかと思いやってみましたが、難しかったです。
これをきっかけにインフラ・ネットワークの仕組みを学習していこうと思います。

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

Ubuntu 20.04 で Java をインストール

Ubuntu において Java を使おうとするといろいろ選択肢がある。
一番安直にしようとするとどうしたらいいかな・・・

環境

  • Ubuntu Linux 20.04
  • openjdk-11 (デフォルト設定)

何があるかな?

$ apt-cache search openjdk | grep jre

とすると、以下が得られた。

default-jre - 標準的な Java または Java 互換のランタイム
default-jre-headless - 標準的な Java または Java 互換のランタイム (ヘッドレス)
openjdk-11-jre - OpenJDK Java ランタイム - Hotspot JIT 版
openjdk-11-jre-headless - OpenJDK Java ランタイム - Hotspot JIT 版 (ヘッドレス)
openjdk-11-jre-zero - Alternative JVM for OpenJDK, using Zero
openjdk-8-jre - OpenJDK Java ランタイム - Hotspot JIT 版
openjdk-8-jre-headless - OpenJDK Java ランタイム - Hotspot JIT 版 (ヘッドレス)
openjdk-8-jre-zero - Zero/Shark を用いた OpenJDK 用の代替 JVM
openjdk-11-jre-dcevm - Alternative VM for OpenJDK 11 with enhanced class redefinition
openjdk-13-jre - OpenJDK Java runtime, using Hotspot JIT
openjdk-13-jre-headless - OpenJDK Java runtime, using Hotspot JIT (headless)
openjdk-13-jre-zero - Alternative JVM for OpenJDK, using Zero
openjdk-14-jre - OpenJDK Java runtime, using Hotspot JIT
openjdk-14-jre-headless - OpenJDK Java runtime, using Hotspot JIT (headless)
openjdk-14-jre-zero - Alternative JVM for OpenJDK, using Zero

Java をインストール

jre でいい場合

# sudo apt install default-jdk

jdk が必要な場合

# sudo apt install default-jdk

今回は、jdk が必要だったため後者を使いました。

$ sudo apt install default-jdk
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
・
・
・
以下のパッケージが新たにインストールされます:
  default-jdk default-jdk-headless default-jre libice-dev libpthread-stubs0-dev libsm-dev libx11-dev
  libxau-dev libxcb1-dev libxdmcp-dev libxt-dev openjdk-11-jdk openjdk-11-jdk-headless x11proto-core-dev
  x11proto-dev xorg-sgml-doctools xtrans-dev
アップグレード: 0 個、新規インストール: 17 個、削除: 0 個、保留: 66 個。
235 MB のアーカイブを取得する必要があります。
この操作後に追加で 251 MB のディスク容量が消費されます。
続行しますか? [Y/n] y

openjdk-11 が入った。かなりダウンロードする必要があるので、時間を十分に見積もろう。

確認

$ java --version
openjdk 11.0.9.1 2020-11-04
OpenJDK Runtime Environment (build 11.0.9.1+1-Ubuntu-0ubuntu1.20.04)
OpenJDK 64-Bit Server VM (build 11.0.9.1+1-Ubuntu-0ubuntu1.20.04, mixed mode, sharing)

となりました。(2020/01/29現在)

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

DB上でTimestamp型のカラムはSqlResultSetMappingするとjava.util.Date型にマッピングされる件

DB検索結果がオブジェクトにマッピングされない

SpringbootでNativeQueryでDBにアクセスする時、Entityと素直にマッピングされない結果が欲しかったため、以下の方法で独自クラスにマッピング定義をして結果を格納するようにしました。

  • 結果セット格納用POJO
ForMapping.java
@Data
@AllArgsConstructor
public class ForMapping {
    private Long id,
    private String name,
    private Timestamp created
}

※コンストラクタやsetter/getterはlombokでやってます。

  • Entityクラス
SomeEntity.java
@SqlResultSetMapping(name = "MappingDef", classes =
{
    @ConstructorResult(targetClass = ForMapping.class, columns =
    {
        @ColumnResult(name = "id", type = Long.class),
        @ColumnResult(name = "name", type = String.class),
        @ColumnResult(name = "created", type = Timestamp.class),
    })
})

テーブル上は、idはlong、nameはTEXT、createdはTIMESTAMP型だったので、それぞれ対応するJavaのクラスを型に指定してマッピングを試みます。

これでSQLを実行して結果を取得しようとした所、結果が1件も取得できませんでした。地道にデバッグしてみると、SQLの実行自体は成功していましたが、結果セットのPOJOへのマッピングのあたりで失敗しているようでした。こんなエラーメッセージが。

Could not locate appropriate constructor on class

エラーメッセージはHibernateの以下の部分で出ていることが分かりました。やはり、SQLの結果セットをPOJOにマッピングするところのようです。

ConstructorResultColumnProcessor.java
private static Constructor resolveConstructor(Class targetClass, List<Type> types) {
    for ( Constructor constructor : targetClass.getConstructors() ) {
        final Class[] argumentTypes = constructor.getParameterTypes();
        if ( argumentTypes.length != types.size() ) {
            continue;
        }

        boolean allMatched = true;
        for ( int i = 0; i < argumentTypes.length; i++ ) {
            if ( ! areAssignmentCompatible( argumentTypes[i], types.get( i ).getReturnedClass() ) ) {
                allMatched = false;
                break;
            }
        }
        if ( !allMatched ) {
            continue;
        }

        return constructor;
    }

    throw new IllegalArgumentException( "Could not locate appropriate constructor on class : " + targetClass.getName() );
}

ソース
https://github.com/hibernate/hibernate-orm/blob/5881b88173d5d8727f8e4aec064751107f430da4/hibernate-core/src/main/java/org/hibernate/loader/custom/ConstructorResultColumnProcessor.java#L71

原因はマッピング定義の型誤り

デバッグで分かりましたが原因は単純で、createdという項目をjava.sql.Timestamp型で指定しているのがNGだったようです。これにより、マッピングしようとしてもマッチするコンストラクタが見つからん!ということで、先のエラーメッセージでした。

公式の情報が見つけられなかったのですが、ネイティブクエリの結果セットをマッピングする時にDB上のTIMESTAMP型をjava.util.Dateにマッピングする、という情報を以下URL内で見つけました。

(参考)
https://stackoverflow.com/questions/24160817/getting-error-could-not-locate-appropriate-constructor-on-class

マッピングの定義部分とPOJOの型をjava.util.Dateに変更したところ、無事結果を取得できるようになりました。

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

関連エンティティがあるときのJPQLの書き方

環境

Spring boot

参考にしたサイト

Spring DATA JPAでデータ検索
JPQLの内部テーブル結合を試してみる

エラーメッセージ

MySQL server version for the right syntax to use near '' at line 1

もうこし長かったけど、構文エラーがあるよってエラー。

原因

JPQLの書き方が間違っていた!というか、MySQLのNativeQueryとはちょっと書き方がちがうんだなぁ~くらいの認識でしたけど、結構違う感じでしたね。

特に@OneToOneとかの関連エンティティがある場合の扱い方が間違っていました。

外部キーで結合するので、LEFT JOINとか書いていたけど、それが原因でした。

application.propertiesに次のように書いて、SQLを参考にしました。コンソールに大量に表示されるようになります。

application.properties
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.show-sql=true

けど、MySQLのSQL文とはなんかが違うみたいですね。

Hibernateってなに?

wikiによると

Java のためのオブジェクト関係マッピング (ORM) ライブラリであり、オブジェクト指向のドメインモデルを関係データベースにマッピングするためのフレームワークを提供する。

初心者には難しいですけど、多分の解釈でいうと、
データベースから取得してきたデータを、オブジェクトとかインスタンスのフィールドに代入して、以降はオブジェクトとして扱えるようにするためにHibernateっていうのがよしなにやってくれる

って認識で良さそうです。

PHPの学習をしているときには、
1. DB接続
2. レコード取得
3. カラムを取得
4. 配列に格納
5. DB切断

って言う流れをそのまま書いてましたね。オブジェクトとかクラスをまだ知らないときです。

Javaをやりだして、早速クラスを勉強して、便利さもわかってきたところでなんとデーブルをそのままクラスと対応させることができるということもできることを理解しだしたのが最近です。

JPQLってなに?

JPAでDB(正確にはエンティティ)からデータを取得するSQLをJPQLと言います。

RepositoryでJPAが出てきてたな。これでデータベースから取得してクラスに格納できるようになるんだなと、なんとなく理解。

Repositoryでも@Queryアノテーションを付けてfindBy~みたいなメソッドを定義したことあったけど、なんのこっちゃわかっていませんでした。

でも今日わかったことが。

// 普通のSQL文を自分で書く
@Query(value="select * from なんちゃら~", nativeQuery = true)
// これがJPQLで、テーブルと紐付けたエンティティでSQL文を書ける的な
@Query("select エイリアス from オブジェクト エイリアス ~")

確かにわかりやすい記述ができるから便利だな~

エンティティの中に別のエンティティがあったらどうするの?

spring.jpa.show-sql=trueとしていると、
jpaで発行されたSQLがコンソールに出力されるので、それを見ると、結合しているテーブルがある場合はleft outer joinって書いてました。

ちょっと複雑な条件の場合は生のSQL文を書いて、結合の部分はleft outer joinって書けばいいんだなとあいまいに考えていましたが、ちゃいましたね。

@OneToOne@OneToManyなどのアノテーションで関連を記述していたら、そのエンティティを含むオブジェクトをデータベースから取得する際にjpaが勝手に中のエンティティのデータも持ってきてくれる。

イメージ

Book.java
public class Book {
private Integer id;
private String name;

@OneToOne( fetch = FetchType.EAGER ,cascade = CascadeType.ALL)
@JoinColumn(name="buyer_id")
private Buyer buyer;

}

Buyer.java
public class Buyer {
private name;
private address;
}

image.png

こんなイメージの関係性で、この場合は、

@Query("select b from Book b")

とするだけで、Buyerもちゃんと取ってきてくれる。

Joinとか書いてたら永遠に構文エラーが発生する

@Query("select b from Book b JOIN Buyer")

って書いてたら永遠に構文エラーになります。

しかもどこがおかしいっていうメッセージも、なんか場所がちがうんですよね。

near '' at line 1
``

ってどこ??`''`の中におかしいところがあるよってメッセージだけど、なんかちゃんと教えてくれませんでした。


###結論!

JPQLとSQLは違うよっていう明確な、強い意志と理念が必要!!



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

RPCとは何かを友達に説明する方法

RPCとは友達も理解できる説明方法

1.基本的なPRCモジュール

RPCとは何か?RPCとRESTの区別

2.概念

  • RPC(Remote Procedure Call)主にクライアントノードとサービスノード間に呼び出し方式です。

  • ローカルでよびだしの場合はsetName(String name)のようなローカル関数を呼び出して、データをメモリに渡し、関数ポインタでsetName();を呼び出して、データを処理します。

  • RPCの場合は以下のstepを実現されます

1.まず、クライアント端はさーばサーバーに保存したの関数のプロセスIDを見つけ、そして関数を実行する

2.クライアントはローカルデータがサーバーに伝送する必要があります。そのため、クライアントはデータをバイトストリームに変換してサーバー側に送り、サーバー側はバイトスクリームをデシナリオして、クライアントからのデータと関数IDを取得ます。

3.サーバー側に関数を実行して、結果がシナリオとデシナリオを通ってクライアント側に返送します。

clientserver.png
  • 分散システムでは、サーバーaがサーバーbにアクセスする場合。http restを用いてデータを伝送し、データもシナリオ化を処理する。

serverserver.png

HTTPはアプリケーション層のプロトコールです、Headerの内容が重いので、通信のコストが高いです。

RPCは主にSocketを依頼して通信効率は向上することができますが下層の実装はRESTよりもっと複雑である。

timeline.png

この文章は個人の学習記録です,指摘を歓迎する。

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

Gradle - How to Import Local .jar Files when Building?

If you need manual import .jar from local when building gradle project, you can add this in the build.gradle file.

Gradle Version : 6.8.1

dependencies {
...
...
...
    implementation fileTree('your/local/libs/folder') { include '*.jar' }
...
...
...
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む