- 投稿日:2020-08-12T23:42:17+09:00
JSFがさっぱりうまくいかないときに役立ちたいリンク集
スコープアノテーション バッキングBeanの寿命 @RequestScoped 一回の呼び出し(リクエスト)とそれへの応答(レスポンス)の間存続 @SessionScoped HTTPセッションが続いている間(ログインしている間)存続 @ApplicationScoped ウェブアプリケーションが実行されている間存続 @ConversationScoped 一回以上のリクエストの間で、開始と終了をプログラムで制御する @Dependent インジェクト先のスコープを引き継ぐ(予めスコープを決められない場合に使う) @ViewScoped リクエストにより表示されたJSFページが他のページへ切り替わる直前まで存続。画面をリロードしても存続。 るーるJavaEE7からのCDIビーンクラスの基本条件 a. 具象クラスであること b. 引数なしのデフォルトコンストラクタを持つこと c. static付きのインナークラスではないこと JSFのaction属性に指定するメソッドのルール a. publicであること b. 引数がないこと c. Stringを返値とすること。この返値がoutcomeになります。
- JSF 2.2 View Declaration Language: Facelets Variant
- javax.faces 2.4.0 javadoc (org.glassfish)
- JavaEE7をはじめよう(12) - CDI Beanのインジェクション - エンタープライズギークス (Enterprise Geeks)
- Faceletsで画面が正常にレンダリングされない現象 - wadahiroの日記
- JSFによるWebアプリケーション開発
- JSFのEL式でOR,AND条件を使う - Qiita
- JSFのカスタムバリデータでメッセージを表示する時は、メッセージにSEVERITY_ERRORを設定しないとh:messageのerrorClassは適用されません。 - Qiita
- JSFのValidatorにおいて2回目のgetSubmittedValue()で値がnullになった時の対応方法 - Qiita
- JSFのバージョンを確認する方法 - Qiita
- JSF2.2のパラメータの受け渡しの方法その2。f:paramタグを使った方法。 | JavaServer Faces入門
- MavenでJSFのプロジェクトをEclipseで作ってみた。 - ponsuke_tarou’s blog
- h:selectOneRadioのラジをボタンでいろいろやってみる - Qiita
- JSFでid属性を指定する方法 - Qiita
- h:commandButtonで確認ダイアログでOKの時だけaction属性の処理を実行する方法 - Qiita
- h:buttonタグのonclick属性にEL式を書いて失敗した - Qiita
その他Java
困った
- JSFでCSSが適用されなくてserver.logにOne or more resources have the target of 'head', but no 'head' component has been defined within the view.と出力されていた - Qiita
- JSFの画面を表示したらjavax.el.ELException: Not a Valid Method Expression: - Qiita
- Cannot apply expression operators to method binding - Qiita
- Cannot format given Object as a Date - Qiita
- <f:ajax> Unable to attach <f:ajax> to non-ClientBehaviorHolder parent - Qiita
- f:ajaxでMethod not found - Qiita
- JSFのタグがHTMLにならないときの対応方法 - Qiita
- FacesMessageを設定したのに表示されないときの対応方法 - Qiita
- 投稿日:2020-08-12T23:34:18+09:00
FacesMessageを設定したのに表示されないときの対応方法
事象 : FacesMessageをコンテンツに設定したのにメッセージが表示されない
ajaxでメソッドを実行して<h:form id="formId"> <div id="uploadArea"> <!--...省略...--> <f:ajax event="change" execute="uploadArea" render="uploadArea" listener="#{uploadBean.uploadFile}" /> <!--...省略...--> <div> <h:message for="uploadArea" errorClass="error" warnClass="warn" infoClass="info" /> </div> </div> </h:form>メソッドでメッセージをコンテンツに設定したのに表示されないpublic void uploadFile(AjaxBehaviorEvent event) throws IOException { if (!isUpload()) { FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "", "ファイルが選択されなかったよ。"); event.getFacesContext().addMessage("uploadArea", message); //...省略...出力されたHTML<form id="formId" name="formId" method="post" action="/tryJsf/base.jsf" enctype="application/x-www-form-urlencoded"> <input type="hidden" name="formId" value="formId"> <div id="uploadArea"> <!--...省略...--> <input id="formId:file" type="text" name="formId:file" style="display:none;" onchange="mojarra.ab(this,event,'change','uploadArea','uploadArea')"> <div> </div> </div> <input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="-7689768516583343150:2206148480801750608" autocomplete="off"> </form>原因 : 不明(誰か教えてください。)
どうも
f:ajax
のメソッドが実行された後に再表示されていないもよう。
render="uploadArea"
が・・・無視られた?HTMLのタグはダメ?JSFのタグじゃなきゃダメ?
JSFのタグで書かないものはコンポーネントではない?The clientIds of components that will participate in the "render" portion of the Request Processing Lifecycle.
ajax(JSF 2.2 View Declaration Language: Facelets Variant)対応方法 : divタグをh:panelGroupに変える
ajaxでメソッドを実行して<h:form id="formId"> <h:panelGroup id="uploadArea"> <!--...省略...JSFのタグにしたので指定するid属性にする..--> <f:ajax event="change" execute="formId:uploadArea" render="formId:uploadArea" listener="#{uploadBean.uploadFile}" /> <!--...省略...--> <div> <h:message for="formId:uploadArea" errorClass="error" warnClass="warn" infoClass="info" /> </div> </div> </h:form>メソッドでメッセージをコンテンツに設定したら表示されたpublic void uploadFile(AjaxBehaviorEvent event) throws IOException { //...省略...対応前と同じ...指定するid属性だけ変える... event.getFacesContext().addMessage("formId:uploadArea", message); //...省略...対応前と同じ...
- 投稿日:2020-08-12T22:58:52+09:00
Eclipseのリファクタリングまとめ
Eclipse には様々なリファクタリング機能が備わっていますが,正直,実行するとどうなるのかよくわからないものが多かったのでまとめてみました.
リファクタリングとは
リファクタリングとは、ソフトウェアの外部の振る舞いを保ったままで、内部の構造を改善していく作業を指します。
リファクタリングを行う理由
ソフトウェアの設計を改善する
完璧な設計は存在しません.厳密には設計当時は完璧に思えても時間経過とともに完璧ではなくなります.
さらに,今後どんな要望が出てきて,誰が,どこに機能追加することになるかは誰にも分かりません.設計が完璧ではなくなる例
- 決済方法として現金,クレジットカードのみを考えていたが,他のキャッシュレス決済に対応する必要が出てきた
- 納期が短かったので,影響範囲を限定的にするために場当たり的な対応をした
- 新たに参画した人が,既存コードの理解が足らないままコードを追加し,スパゲッティコードになる
時間経過で設計が劣化していくので,定期的にメンテナンスしてやる必要があります.
ソフトウェアを理解しやすくする
きれいに整理されたコードは可読性が高く,開発者がコードを読む時間を短縮できます.
さらに,バグを見つけやすくなります.長期的に開発スピードが向上する
設計がよくないと技術的負債が蓄積され,同じ変更を何箇所にも適用する,影響範囲がわからず調査に時間がかかるようになります.
継続的にリファクタリングすることで負債を解消し,機能が増えても開発にかかる工数が少なくて済みます.リファクタリングを避けるとき
残念ながらリファクタリングは銀の弾丸ではありません.
今後もコードを塩漬けにする
既存のコードに変更を加えることがなければ,リファクタリングしても効果は薄く,リスクだけが残ります.
そこに,経済的合理性はなく,ただの自己満足に過ぎません.外部公開されたインタフェース
API の開発をしていて,その API を誰が使っているかわからない場合は変更することができません.
上司を説得できない
リファクタリングはその性質上,システムの機能は増えません.
上司がリファクタリングの重要性を理解できていない場合は全力で説得してみましょう.
どうしても説得できない場合は,責任を負わないために潔く諦めましょう.(そして裏で,ソフトウェアのプロフェッショナルとして黙ってリファクタリングしましょう.)
納期が厳しいときや変更箇所を全て手動テストしなければならない時は避けるべきかもしれません.Eclipse でのリファクタリング
リファクタリングと言ってもローカル変数の名前を変更するような小さな変更から,switch 文をクラスのポリモーフィズムに置き換えるような大掛かりな変更まであります.
リファクタリング対象のコードがテストで保護されているのであれば,自信を持ってリファクタリングできますが,レガシーコードにはテストがありませんので,安心してリファクタリングできません.
しかし,Eclipse などの統合開発環境に備わっているリファクタリングツールに限って言えば,全世界で何千何万回と実行(テスト)されているので,自動テストがない環境でも比較的安全にリファクタリングできます.
⚠️ 100%安全ではありません.確認環境
Version: 2019-06 (4.12.0)
Build id: 20190614-1200リファクタリングの方法
リファクタリングしたい箇所を選択して右クリック → リファクタリングのメニューから実行可能です.
ショートカットの場合はAlt
+Ctrl
+T
です.(Mac の場合はAlt
+⌘
+T
)リファクタリング実行時の注意点
バックアップは頻繁にとる
リファクタリングは複数のクラスに影響する可能性があり,何度か実行すると
ctrl + z
で戻せない可能性があります.
変更を戻せるようにこまめにバックアップを取得しましょう.自動テストが存在するのであれば,頻繁に実行する
IDE のリファクタリングは基本的に安全ですが,リファクタリングにより振る舞いが変わっていないことを確認するために,できる限り頻繁に自動テストを実行しましょう.
リフレクションが使われている場合は影響範囲について十分調査する
Java にはリフレクションという強力な仕組みが導入されていますが,コンパイラの型チェックなどの恩恵を受けることができません.
IDE のリファクタリングツールでも対象外となってしまいますので,コードの中で使っている場合は実行するまでコードを壊したことを検知できません.
プログラム中で利用している場合は,影響範囲について十分調査しましょう.
フレームワークやプラグインを開発しているのであれば,リフレクションを利用している可能性があります.
テストコード中でアクセス修飾子を破壊するために使っている?誰ですかそんなことしたのは?各リファクタリング
やっと本題です.
独断と偏見で,よく使うもの,使えそうなものの順に並べてみました.名前変更
![]()
適用対象
パッケージ,クラス,メソッド,変数
効能
名前を変更する.
どうしてその略称を使った?やa
とかinstanse2
とか命名を放棄した怠慢のような変数名,メソッド名,クラス名を,意味のわかる名前に変えることができる.
オプション(メソッドに適用した場合)
名前変更されたメソッドへの委譲として元のメソッドを保持 + 非推奨としてマーク
外部公開しているメソッドなどを互換性維持のため元メソッドを残したまま変更することも可能です.
非推奨としてマークを選択した場合は@deprecated
が付与されます.beforeString getCpyNm() { return cpyNm; }afterString getCompanyName() { return companyName; }after+オプション/** * @deprecated {@link #getCompanyName()} の代用 */ public String getCpyNm() { return getCompanyName(); } public String getCompanyName() { return companyName; }オプション(クラスに適用した場合)
以下のオプションが利用可能です.
- 類似の名前の変数とメソッドの更新
- コメントおよびストリング内のテキスト出現箇所の更新
- 非 Java テキストファイル内の完全修飾名の更新
参照している箇所ではなく,コメントや似たメソッド名を変更し安全とは言い切れないため,プレビューでの確認が必須です.
メソッドの抽出
![]()
適用対象
メソッド内の選択した行
効能
選択箇所を別メソッドとして分離する.
コード内に重複が存在すれば,複数の重複箇所もまとめて置き換えることが可能.beforepublic void printAverage(int a, int b, int c) { int sum = a + b+c; int average = sum/3; System.out.println(average); }afterpublic void printAverage(int a, int b, int c) { int average = getAverage(a, b, c); System.out.println(average); } private int getAverage(int a, int b, int c) { int sum = a + b+c; int average = sum/3; return average; }ローカル変数の抽出
![]()
適用対象
メソッド内の選択した計算式,条件式
効能
長い式などを分割,または,説明変数の導入
インライン化の逆beforepublic int getTotal(int price) { int result = (int) (price * 0.8 * 1.10); return result; }afterpublic int getTotal(int price) { double discountPrice = price * 0.8; int result = (int) (discountPrice * 1.10); return result; }インライン化
![]()
適用対象
変数
効能
冗長な変数宣言をその値で置き換える
ローカル変数の抽出の逆beforepublic int getTotal(int price) { double discountPrice = price * 0.8; int result = (int) (discountPrice * 1.10); return result; }afterpublic int getTotal(int price) { int result = (int) ((price * 0.8) * 1.10); return result; }定数の抽出
![]()
適用対象
定数
効能
定数をメンバ変数に変更する.
static な値のみ利用可能.beforepublic int getTotal(int price) { int result = (int) (price * 0.8 * 1.10); return result; }afterpublic static final double TAX_RATE = 1.10; public int getTotal(int price) { int result = (int) (price * 0.8 * TAX_RATE); return result; }フィールドのカプセル化
![]()
適用対象
メンバ変数
効能
メンバ変数の getter/setter を生成し,参照方法を getter/setter を利用したものに置き換える.
オプション
宣言型でのフィールドアクセス
をフィールド参照の保持
にすると,直接フィールドの値を参照したままにできる.beforepublic class Refactoring { private String companyName = ""; private int companyId = 0; public Refactoring(String companyName, int companyId) { this.companyName = companyName; this.companyId = companyId; } }afterpublic class Refactoring { private String companyName = ""; private int companyId = 0; public Refactoring(String companyName, int companyId) { this.companyName = companyName; this.setCompanyId(companyId); } /** * @return companyId */ private int getCompanyId() { return companyId; } /** * @param companyId セットする companyId */ private void setCompanyId(int companyId) { this.companyId = companyId; } }ローカル変数をフィールドに変換
![]()
適用対象
ローカル変数
効能
ローカル変数をメンバ変数に変更する.
定数の抽出と似てる
オプション
フィールドのアクセス修飾子や初期化位置を変更可能beforepublic class Refactoring { public int getTotal(int price) { double discountRate = 0.8; int result = (int) (price * discountRate * 1.10); return result; } }afterpublic class Refactoring { private double discountRate = 0.8; public int getTotal(int price) { int result = (int) (price * discountRate * 1.10); return result; } }移動
![]()
適用対象
パッケージ,クラス
効能
クラスを別パッケージに移動する,パッケージを移動する/名称変更する.
パッケージを移動した場合はサブパッケージもまとめて変更できる.package com.example.refactoring;package com.example.refactoring.util;適用対象
static 変数,static メソッド
効能
別のクラスに static 変数や static メソッドを移動することができる.
メンバ変数にも使うことができるが,参照を保持できないので高確率でコンパイルエラーになる...(リファクタリングと言えるのか?)beforepublic class Refactoring { public static final String staticString = "s"; public static String getStaticString() { return staticString; } } public class OtherClass { }afterpublic class Refactoring { public static String getStaticString() { return OtherClass.staticString; } } public class OtherClass { public static final String staticString = "s"; }メソッド・シグネチャーの変更
![]()
適用対象
メソッド
効能
メソッドの引数を追加,削除,変更する
コピペでは面倒な並び替えも可能
オプション
変更されたメソッドへの委譲として元のメソッドを保持
にチェックすると元のシグネチャーのメソッドを保持できる.beforepublic Refactoring(String companyName) { this.companyName = companyName; }afterpublic Refactoring(String companyName, String newParam) { this.companyName = companyName; }after(変更されたメソッドへの委譲として元のメソッドを保持)/** * @deprecated {@link #Refactoring(String,String)} の代用 */ public Refactoring(String companyName) { this(companyName, null); } public Refactoring(String companyName, String newParam) { this.companyName = companyName; }インタフェースの抽出
![]()
適用対象
クラス
効能
既存クラスからインタフェースを作成する.
既存のクラスの任意の public メソッドを選択して,インターフェースを作成でき,作成したインターフェースが自動的にimplements
に指定され,メソッドには@Override
アノテーションが付与される.beforepublic class Refactoring { private String companyName = ""; public String getCompanyName() { return companyName; } }afterpublic interface RefactoringInterface { String getCompanyName(); } public class Refactoring implements RefactoringInterface { private String companyName = ""; @Override public String getCompanyName() { return companyName; } }クラスの抽出
![]()
適用対象
クラス(実質メンバ変数)
効能
メンバ変数を別クラスにまとめる.
クラスが肥大化したとき,または,IP アドレスとポート番号など関連するものを一つにまとめる時に使う.
メンバ変数だけで,メソッドは抽出できない.
オプション
抽出先をトップレベルクラス,匿名クラスから選択可能.
トップレベルクラスの場合は,別のクラスとして抽出され,
匿名クラスの場合は,同一クラス内にクラスが抽出される.beforepublic class Refactoring { private String companyName = ""; private String postNo = ""; public void main() {} }after(トップレベルクラスの場合)public class Refactoring { private RefactoringData data = new RefactoringData("", ""); public void main() {} } public class RefactoringData { public String companyName; public String postNo; public RefactoringData(String companyName, String postNo) { this.companyName = companyName; this.postNo = postNo; } }after(匿名クラスの場合)public class Refactoring { public static class RefactoringData { public String companyName; public String postNo; public RefactoringData(String companyName, String postNo) { this.companyName = companyName; this.postNo = postNo; } } private RefactoringData data = new RefactoringData("", ""); public void main() {} }パラメーターオブジェクトの導入
![]()
適用対象
メソッド
効能
任意の引数を1つのオブジェクトとしてまとめる
オプション
変更されたメソッドへの委譲として元のメソッドを保持
にチェックすると元のシグネチャーのメソッドを保持できる.
生成するパラメータオブジェクトをトップレベルクラス
or匿名クラス
のどちらかから選択できる.beforepublic class Refactoring { public Refactoring(String companyName, int companyId) { this.companyName = companyName; this.companyId = companyId; } }after(トップレベルクラスを指定)public class Refactoring { public Refactoring(RefactoringParameter parameterObject) { this.companyName = parameterObject.companyName; this.companyId = parameterObject.companyId; } } public class RefactoringParameter { public String companyName; public int companyId; public RefactoringParameter(String companyName, int companyId) { this.companyName = companyName; this.companyId = companyId; } }after(匿名クラスを指定)public class Refactoring { /** * @deprecated {@link #Refactoring(RefactoringParameter)} の代用 */ public Refactoring(String companyName, int companyId) { this(new RefactoringParameter(companyName, companyId)); } public Refactoring(RefactoringParameter parameterObject) { this.companyName = parameterObject.companyName; this.companyId = parameterObject.companyId; } } public class RefactoringParameter { // パラメータオブジェクトは同じ }パラメーターの導入
![]()
適用対象
メソッド内の変数,定数
効能
メソッド内の変数,定数をメソッドの引数に変更するbeforepublic String getCompanyName() { return "prefix_" + companyName; }afterpublic String getCompanyName(String prefix) { return prefix + companyName; }スーパークラスの抽出
![]()
適用対象
クラス
効能
既存のクラスからスーパークラスを作成する.
既存のクラスから任意のメンバ変数,メソッドを指定してスーパークラスを作成でき,自動的にextends
指定される.beforepublic class Refactoring { private String companyName = ""; public String getCompanyName() { return companyName; } }afterpublic class RefactoringSuper { private String companyName = ""; public String getCompanyName() { return companyName; } } public class Refactoring extends RefactoringSuper { }プルアップ/プッシュダウン
![]()
適用対象
スーパークラス/サブクラスのメンバ変数,メソッド
効能
スーパークラス/サブクラス間でメンバ変数,メソッドを移動する
プルアップがサブクラス → スーパークラスへの移動,
プッシュダウンがスーパークラス → サブクラスへの移動.
オプション(プッシュダウン時)
abstract宣言を残す
を選択するとスーパークラスに移動したメソッドを abstruct として残すことができる.プルアップ後public class SuperClass { private String companyName = ""; public String getCompanyName() { return companyName; } } public class Refactoring extends SuperClass { }プッシュダウン後public class SuperClass { } public class Refactoring extends SuperClass { private String companyName = ""; public String getCompanyName() { return companyName; } }プッシュダウン後+abstruct宣言を残すpublic abstract class SuperClass { public abstract String getCompanyName(); } public class Refactoring extends SuperClass { private String companyName = ""; public String getCompanyName() { return companyName; } }ファクトリーの導入
![]()
適用対象
コンストラクタ
効能
コンストラクタを static なファクトリメソッドに置き換える.
対象のクラスをシングルトンクラスにしたい場合や,生成方法を柔軟に変更したい場合に利用する.beforepublic class Refactoring { private String companyName = ""; public Refactoring(String companyName) { this.companyName = companyName; } }afterpublic class Refactoring { public static Refactoring createRefactoring(String companyName) { return new Refactoring(companyName); } private String companyName = ""; private Refactoring(String companyName) { this.companyName = companyName; } }使用可能な場合にスーパータイプを使用
![]()
適用対象
クラス(実質他のメソッド内での参照)
効能
スーパークラスで代用可能な場合に,スーパークラスに置き換える.
以下の例では Use クラスの宣言が置き換わっているが,リファクタリングを実行するのは SubClass です.
※ リファクタリングを実行したクラスに変化はない.
サブクラスを削除する前の処理として使うんだと思います.(滅多に使わない)クラスの定義public class SuperClass { protected String companyName = ""; public String getCompanyName() { return companyName; } } public class Refactoring extends SuperClass { }beforepublic class Use { public void main() { Refactoring instance = new Refactoring("", 0); System.out.println(instance.getCompanyName()); } }afterpublic class Use { public void main() { SuperClass instance = new Refactoring("", 0); System.out.println(instance.getCompanyName()); } }型を新規ファイルに移動
![]()
適用対象
匿名クラス(インナークラス)
効能
匿名クラスを別ファイルに移動するbeforepublic class Refactoring { private String companyName = ""; private int companyId = 0; public static class RefactoringParameter { public String companyName; public int companyId; public RefactoringParameter(String companyName, int companyId) { this.companyName = companyName; this.companyId = companyId; } } public Refactoring(RefactoringParameter parameterObject) { this.companyName = parameterObject.companyName; this.companyId = parameterObject.companyId; } }afterpublic class Refactoring { private String companyName = ""; private int companyId = 0; public Refactoring(RefactoringParameter parameterObject) { this.companyName = parameterObject.companyName; this.companyId = parameterObject.companyId; } } public class RefactoringParameter { public String companyName; public int companyId; public RefactoringParameter(String companyName, int companyId) { this.companyName = companyName; this.companyId = companyId; } }宣言された型の一般化
![]()
適用対象
メンバ変数の型,メソッドの戻り値の型,変数の型
効能
型をスーパークラスの型に置き換える.
String 型 →Object 型のようにスーパークラスに変更できる.
ただし,他のクラスで String 型として参照していて互換性がない場合は変更できない.beforepublic String getCompanyName() { return companyName; }afterpublic Object getCompanyName() { return companyName; }最後に
Eclipseのリファクタリングについてまとめて,初めて知る機能がたくさんありました.
個人的に使いどころが分からなかったものはの数が少なくなっています.便利な使いどころがあれば教えてください.
100%安全とは言えませんが,変更してみてエラーの出た箇所を手で直す方法よりは圧倒的に速く,かつ,安全にリファクタリングできるのでどんどんリファクタリングしていきましょう.
- 投稿日:2020-08-12T21:58:41+09:00
f:ajaxでMethod not foundとなった時の対応方法
事象 : f:ajaxを動かそうとしたらコンソールにエラーが出て動かなかった
- 環境
- CentOS Linux release 7.8.2003 (Core)
- Eclipse IDE for Enterprise Java Developers.Version: 2020-03 (4.15.0)
- openjdk version "11.0.7" 2020-04-14 LTS
- JSF 2.3.9
Eclipseのコンソールのログjavax.el.MethodNotFoundException: /base.xhtml @17,113 listener="#{uploadBean.uploadFile}": Method not found: class brans.UploadBean.uploadFile(javax.faces.event.AjaxBehaviorEvent) at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:69) at com.sun.faces.facelets.tag.jsf.core.AjaxBehaviorListenerImpl.processAjaxBehavior(AjaxHandler.java:403) at javax.faces.event.AjaxBehaviorEvent.processListener(AjaxBehaviorEvent.java:100) at javax.faces.component.behavior.BehaviorBase.broadcast(BehaviorBase.java:82) at javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:481) at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:847) at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1395) at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:58) at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:76) at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:177) at javax.faces.webapp.FacesServlet.executeLifecyle(FacesServlet.java:707) at javax.faces.webapp.FacesServlet.service(FacesServlet.java:451) ...省略...UploadBean.java...省略... public void uploadFile(FacesContext fc, UIComponent uic, Object value) throws IOException { if (!isUpload()) { ...省略...base.xhtml...省略... <f:ajax event="change" execute="uploadArea" render="uploadArea" listener="#{uploadBean.uploadFile}" /> ...省略...原因 : メソッドのパラメータが違うから(オーバロード)
「メソッドのパラメータが違う」 = 「違うメソッド」 = 「listener属性に指定したメソッドはない」
いろいろ試していたら・・・うっかり・・・よく考えるとあたりまえだけど「えーーー?」っとなった対応方法1 : メソッド名だけじゃなくて引数までlistener属性と合わせる
UploadBean.javapublic void uploadFile() throws IOException { // または public void uploadFile(AjaxBehaviorEvent event) throws IOException {javax.faces.event.AjaxBehaviorEventを受け取っておけば
getFacesContext
でFacesコンテキストを取得できたりする。AjaxBehaviorEvent は、Ajax に固有のコンポーネントの動作を表します)。
AjaxBehaviorEvent (Jakarta EE 8 仕様 API) - Javadoc 日本語訳対応方法2 : listener属性に指定したメソッドに引数を追加する
base.xhtml<f:ajax event="change" execute="uploadArea" render="uploadArea" listener="#{uploadBean.uploadFile(hoge, fuga, value)}" />
- 投稿日:2020-08-12T21:04:02+09:00
Proxy環境で VS Code Remote Development + Java開発をさせてくれ
こん**は。
突然ですが、VS Code で しかも実行環境はコンテナで Java、開発したいですよね。
でも邪魔してくるやつが居ますよね。そう Proxy です。社内Proxyの場合企業ごとに状況が異なると思いますが、うまくいった例としてすこしでもあなた様の参考になれば……。
モチベーション
- Proxy 環境下で Docker を利用した VS Code の Remote Development をしたい!
- いろんなレイヤからプロキシの設定を求められる……
![]()
- プロキシに悩める人に、少しでもヒントとなれば!
動作確認環境(ホスト)
- OS
- Windows 10
- Docker
- Docker for Windows
- VS Code
- 1.46.0
前提
- VS Code と Docker for Windows がインストールされていること
- Docker for Windows のプロキシ設定は既に完了していること
用意するファイルたち
Dockerfile
Remote Development 環境の
Dockerfile
はこちら。${workspaceFolder}/.devcontainer/DockerfileFROM adoptopenjdk/openjdk11:jdk-11.0.7_10-centos # This Dockerfile adds a non-root user with sudo access. Use the "remoteUser" # property in devcontainer.json to use it. On Linux, the container user's GID/UIDs # will be updated to match your local UID/GID (when using the dockerFile property). # See https://aka.ms/vscode-remote/containers/non-root-user for details. ARG USERNAME=vscode ARG USER_UID=1000 ARG USER_GID=${USER_UID} ARG MAVEN_VERSION=3.6.1 ARG MAVEN_SHA=b4880fb7a3d81edd190a029440cdf17f308621af68475a4fe976296e71ff4a4b546dd6d8a58aaafba334d309cc11e638c52808a4b0e818fc0fd544226d952544 # プロキシ設定 # devcontainer.json で指定する # see https://code.visualstudio.com/docs/remote/containers-advanced#_option-2-use-an-env-file ARG PROXY=${PROXY} ENV http_proxy=${PROXY} ENV https_proxy=${PROXY} ENV HTTP_PROXY=${PROXY} ENV HTTPS_PROXY=${PROXY} RUN echo "building..." \ # # Create a non-root user to use if preferred # see https://aka.ms/vscode-remote/containers/non-root-user. && groupadd --gid ${USER_GID} ${USERNAME} \ && useradd -s /bin/bash --uid ${USER_UID} --gid ${USER_GID} -m ${USERNAME} \ # # Install Maven && mkdir -p /usr/share/maven /usr/share/maven/ref \ && curl -fsSL -o /tmp/apache-maven.tar.gz https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz \ && echo "${MAVEN_SHA} /tmp/apache-maven.tar.gz" | sha512sum -c - \ && tar -xzf /tmp/apache-maven.tar.gz -C /usr/share/maven --strip-components=1 \ && rm -f /tmp/apache-maven.tar.gz \ && ln -s /usr/share/maven/bin/mvn /usr/bin/mvn \ # # Maven User Settings # /home/vscode/.m2/ を作成し、vscode ユーザに所有者変更 && mkdir /home/${USERNAME}/.m2 \ && chown -R ${USERNAME}:${USERNAME} /home/${USERNAME}/.m2 # Maven パス設定 ENV MAVEN_HOME /usr/share/maven開発に
Maven
を利用しているので、Maven
をインストールしています。
また、本番実行環境がCentOS 7
系のため、Remote Development 環境もCentOS 7
系 + Java をベースイメージとして使っています。
${PROXY}
とありますが、こちらは後述するdevcontainer.json
からイメージビルド時に渡してしまいます。
ビルド時にイメージに埋め込むのはちょっと…とは思いましたが、まあ各人の開発環境でイメージを直接共有する予定もないので、この状態でも問題ないと判断しました。devcontainer.json
続いて、Remote Development 環境の設定ファイルである
devcontainer.json
です。${workspaceFolder}/.devcontainer/devcontainer.json{ "name": "Sample Project", "dockerFile": "Dockerfile", // docker build(イメージ作成)時の設定 // see - https://github.com/microsoft/vscode-remote-release/issues/46 "build": { "args": { // TODO: 環境に合わせて記載 "PROXY": "http://USER:PASS@HOST:PORT" } }, // コンテナ作成時の VS Code のデフォルト設定値(ワークスペースの設定が勝つため注意) "settings": { "terminal.integrated.shell.linux": "/bin/bash", "java.home": "/opt/java/openjdk", // Language Server // see - https://github.com/redhat-developer/vscode-java/wiki/Using-a-Proxy // TODO: 環境に合わせて記載 "java.jdt.ls.vmargs": "-Dhttp.proxyHost=HOST -Dhttp.proxyPort=PORT -Dhttp.proxyUser=USER -Dhttp.proxyPassword=PASS -Dhttps.proxyHost=HOST -Dhttps.proxyPort=PORT -Dhttps.proxyUser=USER -Dhttps.proxyPassword=PASS", // see - https://github.com/redhat-developer/vscode-java/issues/399#issuecomment-355113311 "java.import.gradle.enabled": false }, // コンテナ作成時にインストールされる VS Code 拡張(IDで指定) "extensions": [ // Java "vscjava.vscode-java-pack", // Java Extension Pack ], // コンテナ作成後に実行されるコマンド "postCreateCommand": "./.devcontainer/postCreateCommand.sh", // Use 'forwardPorts' to make a list of ports inside the container available locally. "forwardPorts": [ 8090 // app ], // Dockerコンテナログインユーザ "remoteUser": "vscode", }ポイントは
settings
のjava.jdt.ls.vmargs
です。
↓ のページにあるとおり、Java Language Server
に対して PROXY を設定してあげる必要があるようです。Using a Proxy - redhat-developer/vscode-java - GitHub
Maven 設定ファイル
Maven は Maven で Proxy の設定を行います。
自分はこれらのファイルを
.devcontainer
フォルダ内に置きました。
後述するシェルでvscode
ユーザホーム直下の.m2 フォルダ
(/home/vscode/.m2
)にコピーします。settings.xml<proxies> <proxy> <!-- TODO: 環境に合わせて記載 --> <id>sample</id> <active>true</active> <protocol>http</protocol> <host>proxy.sample.com</host> <port>1234</port> <username>user</username> <password>{encrypted-password=}</password> <nonProxyHosts>localhost</nonProxyHosts> </proxy> </proxies>settings-security.xml<?xml version="1.0" encoding="UTF-8"?> <settingsSecurity> <!-- TODO: 環境に合わせて記載 --> <master>{encrypted-master-password=}</master> </settingsSecurity>参考:Password Encryption - Maven
※ ここはもう少しなんとかしたかった…ができませんでした。環境依存でうまくパスワード類を埋めてやりたかったのですが……。うまい方法をご存じの方はぜひコメントでご指摘いただけると……。
シェル
${workspaceFolder}/.devcontainer/postCreateCommand.sh#!/usr/bin/bash # 初期処理 echo '' echo '--------------------------------------------' echo ' Init' echo '--------------------------------------------' # 設定・基本情報 MAVEN_SETTING_DEST=`realpath ~/.m2/` echo '基本情報' echo "作業ディレクトリ: `pwd`" echo "maven: `mvn --version`" # Mavenの設定ファイルをコピー echo '' echo '--------------------------------------------' echo ' Copy Maven settings files' echo '--------------------------------------------' echo 'コピーを開始します。' cp ./.devcontainer/settings.xml ./.devcontainer/settings-security.xml ${MAVEN_SETTING_DEST} echo 'コピーが完了しました。' ls -la ${MAVEN_SETTING_DEST} # 終了処理 echo '' echo '--------------------------------------------' echo ' Exit' echo '--------------------------------------------' echo '処理を終了します。' exit 0
.devcontainer/devcontainer.json
のpostCreateCommand
で指定しているシェルです。コンテナ作成後に叩かれます。内容としては、Maven 設定ファイルたちを
vscode
ユーザホーム直下の.m2 フォルダ
(/home/vscode/.m2
)にコピーして差し上げております。
これだけだったらシェルに切り出さずにdevcontainer.json
のpostCreateCommand
に直接書けばいいと思いますが……![]()
起動する
あとは普通に起動してあげるだけ!
本筋とはズレるので詳細は割愛しますが、ざっくりと…
- VS Code の Remote - Containers 拡張をインストールし、
- Docker を起動して
- VS Codeが「ここコンテナで開けそうだよ?」って聞いてくるので
Reopen in Container
ですね!
このとき
.devcontainer
フォルダが VS Code ワークスペースのルートフォルダ直下にある必要がありそうです。
終わりに
ちなみに…自分はマシンのスペックが足らず、最終的にこの方式での開発は諦めました
が、環境構築が一気にしやすくなりめっちゃ便利だと思うので、スペックに問題なければぜひ前向きに検討すべきと思います!Proxy 環境下 + VS Code Remote Development + Java な人に、少しでも力なっていれば幸いです。
それでは!
リンク
- 投稿日:2020-08-12T20:52:45+09:00
AWSサーバレスでSPARQLエンドポイントを構築(Apache Jena編)
AWSサーバレスでSPARQLエンドポイントの構築を試してみた第2弾です。
第1弾はこちらです。AWSサーバレス環境でSPARQLエンドポイントを作ろうとしたが上手くいかなかった話
https://qiita.com/uedayou/items/bdf7a802e27fe330044e前回は利用したライブラリの関係で検索速度に難があり限定した用途であれば使える、という感じでした。
今回はRDFストアとしては実績があるApache Jenaを使ってみました。環境
前回と同じくAWS Lambda+API Gatewayという構成です。
Apache Jena はを使用しています。
TDBで使う方法以外にRDFファイルを直接使用することもできますが、以下の結果も踏まえてTDBに変換して使用することをお勧めします。ソースコードは以下で公開しています。
https://github.com/uedayou/jena-sparql-server-aws-serverlessクエリ検索時間計測
検索にかかる時間は
- TDBを使用する方法
- RDFファイルを直接利用する方法
の2つについて計測しています。TDBは一旦ZIP圧縮したものをデプロイしています。
SPARQLクエリ
クエリは前回と同じです。
データセットも前回と同じく「図書館及び関連組織のための国際標準識別子(ISIL)」試行版LODを使いました。
分割されたTurtleファイルを1つずつ追加して作成したデータセット毎(RDFファイルは全ファイルを統合したもののみ)に計測しています。(1) トリプルを100件取得
select * where {?s ?p ?o} limit 100(2) 全トリプル数を取得
select (count(*) as ?count) where {?s ?p ?o}(3)
filter
を使って文字列の絞り込みprefix schema: <http://schema.org/> prefix org: <http://www.w3.org/ns/org#> prefix dbpedia: <http://dbpedia.org/ontology/> select * where { ?uri dbpedia:originalName ?name; org:hasSite/org:siteAddress/schema:addressRegion ?pref. filter( regex(?pref, "東京") ) } limit 10TDBの結果
TDBを使えばかなり高速に検索ができました。
ただ、AWS Lambdaはコンテナが生成される場合にその初期化とZIP圧縮されたTDBを解凍する時間が追加かかるため(一度生成されてしまえばしばらくはそのコンテナが再利用される)、その時(例えば初回起動時やしばらく実行がなくコンテナが破棄された状態の場合)にはそれらの処理の時間がかかり、今回使用したファイルでは約4秒ほどかかりました。
以下はコンテナがすでに生成されているときの時間です。コンテナ未生成時は以下の時間に+4秒かかることになります。
前回は10秒以上かかるクエリもあり単純なクエリでも場合によってはタイムアウトで検索結果が得られないことが起こり得ました。
TDBコンテナ生成が必要な時でも5秒以内には結果が取得でき、よっぽど複雑なクエリでなければタイムアウトはしないと思います。
トリプル数 (1) (2) (3) 21,788 242ms 494ms 159ms 42,585 254ms 531ms 102ms 63,448 148ms 502ms 67ms 84,587 166ms 504ms 100ms 104,826 154ms 572ms 85ms 124,718 176ms 367ms 112ms 144,669 153ms 583ms 80ms 160,491 141ms 579ms 104ms RDFファイルの結果
RDFファイルを直接利用する場合は、TDBよりも時間がかかりました。
以下はTDBと同じくコンテナ生成時の時間ですが、TDBよりも初期化に時間がかかりました(約7秒)。
TDBはZIPの解凍処理も入るのに、RDFファイルを使うほうが初期化の時間がかかるのは調べてみないとよくわかりませんが、それを差し引いてもTDBを使うほうが良いと思いました。
トリプル数 (1) (2) (3) 160,491 1587ms 1664ms 1215ms まとめ
- (個人的には)実用に耐えうる検索速度が出せるSPARQLエンドポイントがサーバレス環境で構築できた
- AWS Lambdaのコールドスタート問題?によりコンテナ生成時に数秒時間がかかる
- RDFファイルを直接利用するのではなくTDBに変換したほうがよい
Apache Jenaを使うAWSサーバレス版SPARQLエンドポイントは個人的には満足できるパフォーマンスだと思いますので、今後いろいろ使いたいと思います。
早速、鉄道オープンデータ提供サイト鉄道駅LODのSPARQLエンドポイントをApache Jena版に変更しました。
実際に試してみたい人は、以下の記事を参照してください。
鉄道駅LODのSPARQLエンドポイントを実験的に公開しました
https://qiita.com/uedayou/items/3ba823c5d3bede12af9c
- 投稿日:2020-08-12T20:47:55+09:00
Spring Boot セッション属性(@SessionAttributes)の使い方
Spring Bootのセッションのやり方です。
Formクラス
LoginForm.javapublic class LoginForm implements Serializable { @NotEmpty(message = "Enter Id") private String id; @NotEmpty(message = "Enter Password") private String password; private String check; private String radio; private String select; //getter,setter省略Controllerクラス
IndexController.java@Controller @RequestMapping("/index") //@SessionAttributesは1つのController内で扱う複数のリクエスト間で //データを共有する場合に有効。 //types属性にHTTPセッションに格納するオブジェクトクラスを指定する。 @SessionAttributes(types=LoginForm.class) public class IndexController { /* * オブジェクトをHTTPセッションに追加する */ @ModelAttribute("loginForm") public LoginForm setUpLoginForm(){ return new LoginForm(); } //Modelから取得するオブジェクトの属性名は、@ModelAttributeのvalue属性に指定する。 //今回だと、LoginFormクラスのselectを指定している。 @PostMapping("check") public String loginCheck(@ModelAttribute("loginForm") @Validated LoginForm loginForm, BindingResult res, @ModelAttribute("select") String select, Model model) { //入力チェック if (res.hasErrors()) { return "login"; } } //今回だと、LoginFormクラスのidを指定している。 @GetMapping("form") public String create(Model model, @ModelAttribute("id") String id) { return "create"; } }Viewからアクセスする実装例
<h3 th:text=${loginForm.id}></h3> <h3 th:text=${loginForm.select}></h3>
- 投稿日:2020-08-12T17:23:37+09:00
JPA Auditingとは
JPA Auditingとは
JPAを使用してDomainをRDBSのテーブルにマッピングする際、共通的にDomainを持っているフィールドやカラムが存在します。
代表的には以下となります。
- CreateDate
- UpdateDate
- 識別子
のようなフィールドおよびカラムがありますよね。
Domainごとに存在するということはコードが重複されていることでしょう。
データベースを誰が、いつ作成したのかなど記録を残したほうがメンテナンスにも役に立つためです。
そのため、生成日、修正日のようなカラムは本当に重要なデータです。それでJPAではAuditという機能を提供しています。Auditは監視するという意味でSpring Data JPAで時間に対して自動的に値を入れてくれる機能です。ドメインを永続性コンテキストに保存したり照会などを行ってからupdateを行う場合、毎回時間データを入力しなければならないのですが、auditを利用することで自動的に時間をマッピングしデータベースのテーブルに入れてくれます。
練習
1.domain package内にBaseTimeEntityクラスを作ります。
2.BaseTimeEntityを以下のように作成します。
package jojoidu.boot.springboot.domain; import lombok.Getter; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.EntityListeners; import javax.persistence.MappedSuperclass; import java.time.LocalDateTime; @Getter @MappedSuperclass @EntityListeners(AuditingEntityListener.class) public abstract class BaseTimeEntity { @CreatedDate private LocalDateTime createdDate; @LastModifiedDate private LocalDateTime modifiedDate; }BaseTimeEntityクラスはすべてのEntityクラスの親クラスになり、EntityクラスのcreatedDate、modifiedDateを自動で管理する役割をします。
@MappedSuperclass
- JPA EntityクラスがBaseTimeEntityを継承する場合フィールド(createdDate、modifiedDate)もカラムとして認識されるようにします。
@EntityListeners(AuditingEntityListener.class)
- BaseTimeEntityクラスにAuditing機能を与えます。
@CreatedDate
- Entityが生成され、保存されるとき時間が自動に保存されます。
@LastModifiedDate
- 照会したEntityの値を変更する際、時間が自動に保存されます。
3.EntityクラスにBaseTimeEntityクラスを継承します。
package jojoidu.boot.springboot.domain.posts; import jojoidu.boot.springboot.domain.BaseTimeEntity; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import javax.persistence.*; @Getter @NoArgsConstructor @Entity // テーブルとリンクされるクラスを示す public class Posts extends BaseTimeEntity { @Id // PKフィールドを示す @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Column(length = 500, nullable = false) private String title; @Column(columnDefinition = "TEXT", nullable = false) private String content; private String author; @Builder public Posts(String title, String content, String author) { this.title = title; this.content = content; this.author = author; } public void update(String title, String content) { this.title = title; this.content = content; } }4.最後にJPA Auditingアノテーションが活性化されるようにmainクラスに活性化アノテーションを追加します。
package jojoidu.boot.springboot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @EnableJpaAuditing @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }テスト
テストは欠かせないので、テストメソッドも追加してみました。
@Test public void saveBaseTimeEntity() { // given LocalDateTime now = LocalDateTime.of(2020, 8,12,0,0,0); postsRepository.save(Posts.builder() .title("title") .content("content") .author("author") .build()); // when List<Posts> postsList = postsRepository.findAll(); //then Posts posts = postsList.get(0); System.out.println(">>>>>>>createdDate=" + posts.getCreatedDate() + ", modifiedDate=" + posts.getModifiedDate()); assertThat(posts.getCreatedDate()).isAfter(now); assertThat(posts.getModifiedDate()).isAfter(now); }結果
- 投稿日:2020-08-12T16:58:29+09:00
Java 変数に評価結果を代入
変数に評価結果を代入するコードがわかりづらかったので、頭を整理するためにまとめました。
変数に評価結果を代入
int number = 1; boolean flag = number == 1; System.out.println(flag);この時表示される処理結果は
になる。
numberに「0」を入れた表示結果は「false」になるが、わかりづらかったので自分のために補足。boolean flag = number == 1;を分解
/* numberが「1」なら、flagは「ture」*/ if(number == 1){ boolean flag = true; } /* numberが「0」なら、flagは「false」*/ else { boolean flag = false; }
- 投稿日:2020-08-12T16:58:29+09:00
Java学習メモ2
変数に評価結果を代入
int number = 1; boolean flag = number == 1; System.out.println(flag);この時表示される処理結果は
になる。
numberに「0」を入れた表示結果は「false」になるが、わかりづらかったので自分のために補足。boolean flag = number == 1;を分解
/* numberが「1」なら、flagは「ture」*/ if(number == 1){ boolean flag = true; } /* numberが「0」なら、flagは「false」*/ else { boolean flag = false; }
- 投稿日:2020-08-12T16:10:44+09:00
[spring]Spring Data JPAを使ってみよう
javaには触れた経験がありましたが、
フレームワークが盛り込まれたソースコードを見た時に
\(^o^)/<「なにこれー」となり、立ち止まる事が多かったのでメモ書き程度に。Spring Data JPA とはなんぞや
Javaで言うところの、DAO(Data Access Object)にあたるものが
Spring では、Repositoryクラスと呼ばれています。
※なぜそう呼ばれているのかは本稿では触れませんそのRepositoryクラスを便利に(お手軽に)使えるようにするものが
Spring Data JPAというライブラリなのです。Spring Data JPA 使用例
早速ですが、以下のコードは「社員名を取得する」ためのコードです。
EmployeeRepository.java@Service("employeeService") public class EmployeeServiceImpl implements EmployeeService{ @Autowired EmployeeRepository employeeRepository; public Employee getName(String name) { return Employee employee = employeeRepository.findByName(name); } }そして、以下の簡素なコードでデータベースから社員名を取得してくる事が可能です。
PureJavaしか知らなかった私からすると、「えっ?これだけ?」と思いました。EmployeeRepository.java@Repository("EmployeeRepository") public interface EmployeeRepository extends JpaRepository<Employee, String>{ public Employee findByName(String Name); }上記のRepositoryクラスでは、以下の手順が行われています。
「Employeeテーブルから、nameというフィールドで指定した文字列に完全一致するものを取得する。」使い方のまとめ
簡単にまとめますと、
・検索したいフィールド名の最初を大文字にする
・findBy~などの特定のフォーマットに合わせてメソッドを定義する
漏らさずにやるのはこの2点だけで、後の処理はライブラリであるJPAが自動で実装してくれるのです!
ひとまず、データベースアクセスを試してみたい!という方は要検証です。あとがき
本来、書き方はドキュメントを見てもらえればわかるはずなのですが
専門的な見た事も無い単語が並び、学び始めたばかりの方にはハードルの高いドキュメントです。
フレームワークに精通した者のみが読めるものだと感じました。
正確に納得しながら読むには、単語の意味を調べながらになるので数カ月は必要でしょう。
- 投稿日:2020-08-12T15:44:20+09:00
[Java]SpringBoot + Doma2 + H2
公式チュートリアルやってみた。
https://github.com/domaframework/doma-spring-bootapplication.propertiesdoma.dialect=h2pom.xml<!-- h2追加--> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <!-- Doma関連追加--> <dependency> <groupId>org.seasar.doma.boot</groupId> <artifactId>doma-spring-boot-starter</artifactId> <version>1.4.0</version> <exclusions> <exclusion> <groupId>org.seasar.doma</groupId> <artifactId>doma-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.seasar.doma</groupId> <artifactId>doma</artifactId> <version>2.29.0</version> </dependency>Entityimport org.seasar.doma.Entity; import org.seasar.doma.GeneratedValue; import org.seasar.doma.GenerationType; import org.seasar.doma.Id; @Entity public class Reservation { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) public Integer id; public String name; }Daoimport java.util.List; import org.seasar.doma.Dao; import org.seasar.doma.Insert; import org.seasar.doma.Select; import org.seasar.doma.boot.ConfigAutowireable; import org.springframework.transaction.annotation.Transactional; @ConfigAutowireable @Dao public interface ReservationDao { @Select List<Reservation> selectAll(); @Insert @Transactional int insert(Reservation reservation); }エラーシューティング
ビルドエラーが発生した場合・・
参考:https://doma.readthedocs.io/en/2.5.0/build/
[Eclipse を使ったビルド]
・注釈処理の有効化
・Factory Path の設定
- 投稿日:2020-08-12T15:20:00+09:00
[Java]MinecraftのModを作成しよう 1.14.4【99. Modの出力】
(この記事は一連の解説記事の一つになります)
先頭記事:入門編
Modの出力
丹精込めて作ってきたModがいい感じになってきたならば、他の人が遊べるようにjarファイル(Java Archive)に出力してみましょう!
D:\projects\mc_example_mod ├ src ├ build.gradle ├ gradlew ├ gradlew.bat └ gradle └ wrapper └ gradle-wrapper.jar └ gradle-wrapper.properties入門編で同じようにセットアップしている場合、プロジェクトファイルはこのようになっていると思います。
build.gradle
を編集します。build.gradlebuildscript { repositories { maven { url = 'https://files.minecraftforge.net/maven' } jcenter() mavenCentral() } dependencies { classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true } } apply plugin: 'net.minecraftforge.gradle' // Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. apply plugin: 'eclipse' apply plugin: 'maven-publish' version = '1.0' group = 'jp.koteko.example_mod' // http://maven.apache.org/guides/mini/guide-naming-conventions.html archivesBaseName = 'example_mod' sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly. //...16~18行目の
version
group
archivesBaseName
の項目を自分のModに合うよう書き換えます。そのほかの部分は必要ない記述も多い気がしますが、問題にはならないのでそのままでよいです。書き換えたら、プロジェクトフォルダ(この例では
D:\projects\mc_example_mod
)でPowerSellを起動します(エクスプローラー上でShift+右クリック、あるいは普通にcd
で移動)。
.\gradlew.bat build
のコマンドを実行して10秒ほど待ちます。BUILD SUCCESSFUL
の表示が出たら成功です。
\build\libs
フォルダ内に[archivesBaseName]_[version].jar
のファイルが生成されているはずです。これがいわゆるModとして配布されるファイルになります。Modの導入に関しては
めんd技術解説の範疇を超える(というか世に解説が溢れている)ので省略しますが、Forgeを導入してmodsフォルダに先ほどのjarファイルを配置し、ゲームを起動します。
タイトルからmods画面を開き、自分のModが認識されていれば成功です。不安であればゲーム内で実際に確認しましょう。長旅おつかれさまでした。
余談
今後の記事について
まだまだ解説が足りていない部分や、触れられていない要素も数多くありますが、一旦Minecraft 1.14.4における解説記事を区切りにして、1.16.1の環境を立ててそちらで改めて開発をしていこうかなと考えております。Mod開発は、仕様の大幅変更や公式アプデ間隔などの理由から、いくつか主流なバージョンがあり、一つ前だと1.12.2がそれにあたります。1.14.4もある程度の数はあるようですが、1.12.2残留組、開発意欲の高い追従組のどちらと比べてもやや数が少なく、このバージョンでの開発を進めるデメリットが感じられたのでこのように判断しました。1.16.1がどれだけ多くのModが作られるバージョンになるかは不明ですが、2020年8月現在で最新版なので、将来性と情報の少なさという点からこちらの開拓を優先しようと思います。
参考
Minecraft 1.12 modding with forge – 10 – Export Mod – suppergerrie2.com
- 投稿日:2020-08-12T13:49:31+09:00
Java学習のメモ
学習サイトpaizaを元に詰まった箇所をメモしています。
Math.random()
0.0~1.0未満の範囲でdouble型の乱数を取得。戻り値はdouble。
double number = Math.random();1〜100の値で取得したいとき。
double number = Math.random() * 100 + 1;この時、
「* 100」は何個の数から数を出すか、範囲の数
「+ 1」は数値がいくつから始まるか、下限の値
を範囲指定して入れることができる。4〜10までの範囲(7つの数)で値を取得したい時は以下と表示する。
double number = Math.random() * 7 + 4;戻り値に整数にする場合、キャストする。
int num = (int)number; または int num = (int)(Math.random());
- 投稿日:2020-08-12T13:49:31+09:00
Java Math.randomとimport (Calendar)について まとめ
学習サイトpaizaを元に詰まった箇所をメモしています。
Math.random()
0.0~1.0未満の範囲でdouble型の乱数を取得。戻り値はdouble。
double number = Math.random();1〜100の値で取得したいとき。
double number = Math.random() * 100 + 1;この時、
「* 100」は何個の数から数を出すか、範囲の数
「+ 1」は数値がいくつから始まるか、下限の値
を範囲指定して入れることができる。4〜10までの範囲(7つの数)で値を取得したい時は以下と表示する。
double number = Math.random() * 7 + 4;戻り値に整数にする場合、キャストする。
int num = (int)number; または int num = (int)(Math.random());import (Calendar)
日付を取得するクラスライブラリ。
記載例:
import java.util.Calendar; public class Main { public static void main(String[] args) { Calendar calendar = Calendar.getInstance(); int seireki = calendar.get(Calendar.YEAR); System.out.println(seireki + "年"); } }月を取得
int month = calendar.get(Calendar.MONTH);日を取得
int data = calendar.get(Calendar.DATE);importについて参照したサイト
【Java】初心者のための簡単解説!importを別の角度から捉える。ついでに時間を取得するクラスも知りたかったので、記載されているサイト
【サンプルプログラム付き】Java時間の取得方法まとめ!
- 投稿日:2020-08-12T13:49:31+09:00
Java学習のメモ1
学習サイトpaizaを元に詰まった箇所をメモしています。
Math.random()
0.0~1.0未満の範囲でdouble型の乱数を取得。戻り値はdouble。
double number = Math.random();1〜100の値で取得したいとき。
double number = Math.random() * 100 + 1;この時、
「* 100」は何個の数から数を出すか、範囲の数
「+ 1」は数値がいくつから始まるか、下限の値
を範囲指定して入れることができる。4〜10までの範囲(7つの数)で値を取得したい時は以下と表示する。
double number = Math.random() * 7 + 4;戻り値に整数にする場合、キャストする。
int num = (int)number; または int num = (int)(Math.random());import (Calendar)
日付を取得するクラスライブラリ。
記載例:
import java.util.Calendar; public class Main { public static void main(String[] args) { Calendar calendar = Calendar.getInstance(); int seireki = calendar.get(Calendar.YEAR); System.out.println(seireki + "年"); } }月を取得
int month = calendar.get(Calendar.MONTH);日を取得
int data = calendar.get(Calendar.DATE);importについて参照したサイト
【Java】初心者のための簡単解説!importを別の角度から捉える。ついでに時間を取得するクラスも知りたかったので、記載されているサイト
【サンプルプログラム付き】Java時間の取得方法まとめ!
- 投稿日:2020-08-12T10:20:18+09:00
CentOS8でApacheとTomcatをアッという間に連携させる
概要
定番ネタ
環境
- CentOS 8.2
- http 2.4
- tomcat 8.0
前提
apache, tomcatの設定は終わっているものとします。
- apacheの設定ファイル:/etc/httpd/conf
- tomcatの設定ファイル:/usr/java/tomcat8/conf
手順
以下設定ファイルを作成。とりあえず全部転送で。
/etc/httpd/conf.d/proxy.confProxyPass / ajp://localhost:8009/以下を実行(おまじない)
/usr/sbin/setsebool -P httpd_can_network_connect 18080をつぶすため以下コメントオフ
/usr/java/tomcat8/conf/server.xml<!-- <Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> -->
Tomcat再起動
systemctl restart tomcatURL:http://[IPアドレス]/ でtomcatのホーム画面が見えるはずだ。
参考
- 投稿日:2020-08-12T08:58:03+09:00
Iteratorパターン
Iterator(反復子)の役
要素を順番にスキャンしていく役
package iterator; public interface Iterator { public abstract boolean hasNext(); public abstract Object next(); }ConcreteIterator(具体的な反復子)の役
Iterator役が定めたインターフェースを実装する役
package iterator; public class BookShelfIterator implements Iterator { private BookShelf bookShelf; private int index; public BookShelfIterator(BookShelf bookShelf) { this.bookShelf = bookShelf; this.index = 0; } public boolean hasNext() { return index < bookShelf.getLength(); } public Object next() { Book book = bookShelf.getBookAt(index); index++; return book; } }Aggregate(集合体)の役
Iterator役を作り出すインターフェース。
package iterator; public interface Aggregate { public abstract Iterator iterator(); }ConcreteAggregate(具体的な集合体)の役
Aggregate役が定めたインターフェースを実装する役
package iterator; import java.util.ArrayList; import java.util.List; public class BookShelf implements Aggregate { private List<Book> books; public BookShelf() { this.books = new ArrayList<>(); } public Book getBookAt(int index) { return books.get(index); } public void appendBook(Book book) { books.add(book); } public int getLength() { return books.size(); } public Iterator iterator() { return new BookShelfIterator(this); } }本棚(集合体)を表すクラス
package iterator; import java.util.ArrayList; import java.util.List; public class BookShelf implements Aggregate { private List<Book> books; public BookShelf() { this.books = new ArrayList<>(); } public Book getBookAt(int index) { return books.get(index); } public void appendBook(Book book) { books.add(book); } public int getLength() { return books.size(); } public Iterator iterator() { return new BookShelfIterator(this); } }本を表すクラス
package iterator; public class Book { private String name; public Book(String name) { this.name = name; } public String getName() { return this.name; } }呼び出し元
package iterator; public class Main { public static void main(String[] args) { BookShelf bookShelf = new BookShelf(); bookShelf.appendBook(new Book("Effective Java")); bookShelf.appendBook(new Book("CODE COMPLETE")); bookShelf.appendBook(new Book("リーダブルコード")); bookShelf.appendBook(new Book("レガシーコード改善")); Iterator it = bookShelf.iterator(); while (it.hasNext()) { Book book = (Book)it.next(); System.out.println(book.getName()); } } }なぜわざわざ集合体の外にIterator役を作る必要があるのか?
大きな理由としてはIteratorを使うことで実装とは切り離して数え上げを行うことができるため。while (it.hasNext()) { Book book = (Book)it.next(); System.out.println(book.getName()); }上記コードで使われているのはhasNextメソッドとnextメソッドというIteratorのメソッドのみ。
BookShelfの実装で使われているメソッドは呼び出されていない。
つまりこのループはBookShelfの実装には依存していない。BookShelfでListで本を管理することを辞め、配列を使うように変更した場合を考える。
BookShelfをどのように変更したとしても、BookShelfがiteratorを持っており、
正しいiteratorを返せば(hasNextとnextメソッドが正しく実装されているクラスのインスタンスを返してくれれば)、上記ループは全く変更する必要がない。抽象クラスやインターフェースを使ってプログラミングする
抽象クラスやインターフェースの使い方がよく分からない人はAggregateインターフェースやIteratorインターフェースではなく、いきなりConcreteAggregate役やConcreteIterator役の上でプログラミングしてしまいがち(今の自分…)
具体的なクラスだけを使うと、クラス間の結合が強くなってしまい、部品として再利用することが難しくなる。
結合を弱め、クラス部品として再利用しやすくするために抽象クラスやインターフェースが導入される。
- 投稿日:2020-08-12T06:54:05+09:00
PreferenceScreenをネストすると外のレイアウトが崩れる
PreferenceFragmentを使った設定画面で、例えば、複数画面のPreferenceを簡単に作ろうとすると
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceScreen android:title="test1"> <Preference android:title="test1-1"> <Preference android:title="test1-2"> </PreferenceScreen> </PreferenceScreen>とネストをかければ複数画面を作れますが、これだとネストされたPreferenceScreen画面には、親のPreferenceScreenでデザインしたであろうレイアウト(ActionBarやbuttombarなど)が適用されません。
そこで、親のPreference要素をクリックすると別のフラグメントに置き換えるという、従来のFragmentの切り替え方でなんとかしました。SettingActivity.javapublic class SettingActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // activity_setting.xmlはfragmentを入れるレイアウトxml setContentView(R.layout.activity_setting); getFragmentManager().beginTransaction().replace(R.id.fragment_container, new UserPreferenceFragment()).commit(); } } class UserPreferenceFragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // preference_header.xmlは親の設定画面xml addPreferencesFromResource(R.xml.preference_header); Preference.OnPreferenceClickListener subpreference = new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { getPreferenceScreen().removeAll(); switch(preference.getKey()) { // preference_test1,2,3 は子の設定画面 case "test1": addPreferencesFromResource(R.xml.preference_test1); break; case "test2": addPreferencesFromResource(R.xml.preference_test2); break; case "test3": addPreferencesFromResource(R.xml.preference_test3); break; } return true; } }; final Preference test1 = (Preference) findPreference("test1"); final Preference test2 = (Preference) findPreference("tesst2"); final Preference test3 = (Preference) findPreference("test3"); test1.setOnPreferenceClickListener(subpreference); test2.setOnPreferenceClickListener(subpreference); test3.setOnPreferenceClickListener(subpreference); } }ホント地味にですが、setOnPreferenceClickListenerをできるだけきれいに、複数作るのに苦労しました。(ひとつずつ定義して当てはめるには量が多かったので)
参考:
- 投稿日:2020-08-12T06:51:34+09:00
BottomNavigationViewのFragmentの中に更にTabとFragmentを作る
BottomNavigationViewを一回作り、その中にTabhostとそのフラグメントを作った際、ハマったのでメモ。
主に、一番下の参考を参照しましたが、いくつかエラーが発生したため修正を入れています。
まず、BottomNavigationViewで分けた各フラグメントのうち一つです。親フラグメント.javapublic class 親フラグメント extends Fragment { public static Record newInstance() { return new Record(); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.record, container, false); //FragmentManagerの取得 FragmentManager mFragmentManager = getChildFragmentManager(); //xmlからFragmentTabHostを取得、idが android.R.id.tabhost である点に注意 FragmentTabHost tabHost = (FragmentTabHost)v.findViewById(android.R.id.tabhost); Log.d("tabHost", String.valueOf(tabHost)); //ContextとFragmentManagerと、FragmentがあたるViewのidを渡してセットアップ tabHost.setup(getActivity().getApplicationContext(), mFragmentManager, R.id.content); //String型の引数には任意のidを渡す //今回は2つのFragmentをFragmentTabHostから切り替えるため、2つのTabSpecを用意する TabHost.TabSpec mTabSpec1 = tabHost.newTabSpec("tab1"); TabHost.TabSpec mTabSpec2 = tabHost.newTabSpec("tab2"); //Tab上に表示する文字を渡す mTabSpec1.setIndicator("This is tab1"); mTabSpec2.setIndicator("This is tab2"); Bundle args = new Bundle(); args.putString("string", "message"); //それぞれのTabSpecにclassを対応付けるように引数を渡す //第3引数はBundleを持たせることで、Fragmentに値を渡せる。不要である場合はnullを渡す tabHost.addTab(mTabSpec1, 子フラグメント1.class, args); tabHost.addTab(mTabSpec2, 子フラグメント2.class, null); return v; } }子フラグメント1.javapublic class 子フラグメント1 extends Fragment { static 子フラグメント1 newInstance() {return new 子フラグメント1();} @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); //addTabの際にBundleを渡す場合は、Bundleから値を取得する //渡さない(nullを渡している)場合は、実装しなくてよい。そうでないとgetString("string")時にエラーが発生する Bundle args = getArguments(); String str = args.getString("string"); } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ return inflater.inflate(R.layout.子フラグメントレイアウト1, null); } }子フラグメント2.javapublic class 子フラグメント2 extends Fragment { static 子フラグメント2 newInstance() {return new 子フラグメント2();} @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ return inflater.inflate(R.layout.子フラグメントレイアウト2, null); } }親フラグメントレイアウト.xml<?xml version="1.0" encoding="utf-8"?> <!-- FragmentTabHostのidは必ず @android:id/tabhost にする--> <android.support.v4.app.FragmentTabHost xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/tabhost" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- FragmentTabHost同様、idの指定あり。idは必ず @android:id/tabs にする--> <TabWidget android:id="@android:id/tabs" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="0"/> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="0dp" android:layout_height="0dp" android:layout_weight="0"/> <!-- contentにFragmentが追加される--> <FrameLayout android:id="@+id/content" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> </LinearLayout> </android.support.v4.app.FragmentTabHost>子フラグメントレイアウト.xml(子フラグメントレイアウト1,子フラグメントレイアウト2)<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/textView3" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="子フラグメント" /> </LinearLayout>備考:
Fragment系は"android.app.Fragment~"と"android.support.v4.app.Fragment~"がありますが、どちらかに統一したほうが良いっぽいです(当たり前ですが。)
Android標準のFragmentよりsupport.v4.app.Fragmentを使うべき、という理由を徹底調査という記事を参考にして、わたしはv4を使っています。
- 投稿日:2020-08-12T06:46:37+09:00
BottomNavigationViewを少しいじってみた①
android.support.design.widget.BottomNavigationViewは公式ライブラリのひとつです。
Bottom navigation
■ アイコンとテキストの色を変更する(選択時、未選択時)
1. resのなかにcolorディレクトリを作る
2. そのcolorディレクトリの中にbuttom_navigation.xmlをつくり、次の内容を記載する
res/color/buttom_navigation.xml<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true" android:color="#FFF" /> <item android:state_pressed="true" android:color="#FFF" /> <item android:color="#969696" /> </selector>3. buttomnavigationviewの方に指定をする
res/layout/main.xml... <android.support.design.widget.BottomNavigationView android:id="@+id/navigation" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:background="?android:attr/windowBackground" app:itemBackground="@color/themecolor" app:itemIconTint="@color/buttom_navigation" app:itemTextColor="@color/buttom_navigation" app:menu="@menu/navigation" /> ...参考:
BottomNavigationViewのカスタマイズ■ 選択されていないタブの文字を表示させ続ける
main.java... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mTextMessage = (TextView) findViewById(R.id.message); BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation); navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener); // no-hide menu-text BottomNavigationMenuView menuView = (BottomNavigationMenuView) navigation.getChildAt(0); for (int i = 0; i < menuView.getChildCount(); i++) { BottomNavigationItemView itemView = (BottomNavigationItemView) menuView.getChildAt(i); itemView.setShiftingMode(false); itemView.setChecked(false); // Viewの状態を反映させるために呼んでいる } }■ 全てのタブの横幅、マージンを固定する
デフォルトの状態だと、タブ(item)が4つあると、選択されたタブのwidth(横幅)が大きく広がり、ほかが狭まる。この4つの横幅を全て固定したい。
BottomNavigationViewHelper.javapublic class BottomNavigationViewHelper { public static void disableShiftMode(BottomNavigationView view) { BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0); try { Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode"); shiftingMode.setAccessible(true); shiftingMode.setBoolean(menuView, false); shiftingMode.setAccessible(false); for (int i = 0; i < menuView.getChildCount(); i++) { BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i); //noinspection RestrictedApi item.setShiftingMode(false); // set once again checked value, so view will be updated //noinspection RestrictedApi item.setChecked(item.getItemData().isChecked()); } } catch (NoSuchFieldException e) { Log.e("BNVHelper", "Unable to get shift mode field", e); } catch (IllegalAccessException e) { Log.e("BNVHelper", "Unable to change value of shift mode", e); } } }main.java... mTextMessage = (TextView) findViewById(R.id.message); BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation); BottomNavigationViewHelper.disableShiftMode(navigation); navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener); ...
- 投稿日:2020-08-12T05:53:03+09:00
【JDBC】JavaからSQLite3のデータベースアクセスをSQL文ごとにメソッド化してみた。
前回の記事でJDBCを使ってJavaからのSQLiteデータベースにアクセスすることに成功しました。
予めデータベースを用意しておいてJavaプログラムでSELECT文を記述してデータベースからデータを取得するだけだったので、もう一歩踏み込んでSELECT文に加えてINSERT文・UPDATE文・DELETE文等を組み込んだものに挑戦してみました。環境
今回の開発環境については以下のとおりです。
- Ubuntu 18.04.5LTS
- OpenJDK 11.0.8
- SQLite3 3.22.0
参考にしたサンプルコード
今回は複数のSQL文を使いたかったので、JDBCリポジトリのREADMEにあったサンプルコードをもとにメソッド化していこうと思います。
参考元のサンプルコードimport java.sql.*; public class Sample { public static void main(String[] args) throws ClassNotFoundException { // load the sqlite-JDBC driver using the current class loader Class.forName("org.sqlite.JDBC"); Connection connection = null; try { // create a database connection connection = DriverManager.getConnection("jdbc:sqlite:sample.db"); Statement statement = connection.createStatement(); statement.setQueryTimeout(30); // set timeout to 30 sec. statement.executeUpdate("drop table if exists person"); statement.executeUpdate("create table person (id integer, name string)"); statement.executeUpdate("insert into person values(1, 'leo')"); statement.executeUpdate("insert into person values(2, 'yui')"); ResultSet rs = statement.executeQuery("select * from person"); while(rs.next()) { // read the result set System.out.println("name = " + rs.getString("name")); System.out.println("id = " + rs.getInt("id")); } } catch(SQLException e) { // if the error message is "out of memory", // it probably means no database file is found System.err.println(e.getMessage()); } finally { try { if(connection != null) connection.close(); } catch(SQLException e) { // connection close failed. System.err.println(e); } } } }SQL文ごとにメソッド化する
上記のサンプルコードは、データベスにアクセス → テーブルを消去 → テーブル作成 → データの入力 → データの読み込みの全てをmainメソッドの中で処理しています。
実際のプログラムでは各動作を別々に行うので、それらをメソッド化して必要時にmainメソッドに呼び出して使うのがより現実的だと思います。自分なりにメソッド化したコードが以下になります。
尚、データベース内の名前は解りやすくするため、適宜変えてあります。メソッド化したコードimport java.sql.*; /** * TestDataBaseAccess */ public class TestDataBaseAccess { static Connection connection; static String URL = "jdbc:sqlite:sample.db"; public static void main(String[] args) throws ClassNotFoundException { // load the sqlite-JDBC driver using the current class loader Class.forName("org.sqlite.JDBC"); connection = null; dropTable(); createTable(); insertData(); loadData(); updateData(); loadData(); deleteData(); loadData(); } /** * SELECT文 */ public static void loadData() { try { // create a database connection connection = DriverManager.getConnection(URL); Statement statement = connection.createStatement(); statement.setQueryTimeout(30); // set timeout to 30 sec. ResultSet rs = statement.executeQuery("SELECT * FROM person"); while(rs.next()){ // read the result set System.out.println("id = " + rs.getInt("id") + " | name = " + rs.getString("name")); } } catch(SQLException e) { // if the error message is "out of memory", // it probably means no database file is found System.err.println(e.getMessage()); } finally { try { if(connection != null) connection.close(); } catch(SQLException e) { // connection close failed. System.err.println(e); } } } /** * INSERT文 */ public static void insertData() { try { // create a database connection connection = DriverManager.getConnection(URL); Statement statement = connection.createStatement(); statement.setQueryTimeout(30); // set timeout to 30 sec. statement.executeUpdate("INSERT INTO person VALUES(1, 'Satou')"); statement.executeUpdate("INSERT INTO person VALUES(2, 'Tanaka')"); statement.executeUpdate("INSERT INTO person VALUES(3, 'Suzuki')"); } catch(SQLException e) { // if the error message is "out of memory", // it probably means no database file is found System.err.println(e.getMessage()); } finally { try { if(connection != null) connection.close(); } catch(SQLException e) { // connection close failed. System.err.println(e); } } } /** * UPDATE文 */ public static void updateData() { try { // create a database connection connection = DriverManager.getConnection(URL); Statement statement = connection.createStatement(); statement.setQueryTimeout(30); // set timeout to 30 sec. statement.executeUpdate("UPDATE person SET name = 'Takahashi' WHERE id = 1"); } catch(SQLException e) { // if the error message is "out of memory", // it probably means no database file is found System.err.println(e.getMessage()); } finally { try { if(connection != null) connection.close(); } catch(SQLException e) { // connection close failed. System.err.println(e); } } } /** * DELETE文 */ public static void deleteData() { try { // create a database connection connection = DriverManager.getConnection(URL); Statement statement = connection.createStatement(); statement.setQueryTimeout(30); // set timeout to 30 sec. statement.executeUpdate("DELETE FROM person WHERE id = 3"); } catch(SQLException e) { // if the error message is "out of memory", // it probably means no database file is found System.err.println(e.getMessage()); } finally { try { if(connection != null) connection.close(); } catch(SQLException e) { // connection close failed. System.err.println(e); } } } /** * テーブル作成 */ public static void createTable() { try { // create a database connection connection = DriverManager.getConnection(URL); Statement statement = connection.createStatement(); statement.setQueryTimeout(30); // set timeout to 30 sec. statement.executeUpdate("CREATE TABLE person (id INTEGER, name STRING)"); } catch(SQLException e) { // if the error message is "out of memory", // it probably means no database file is found System.err.println(e.getMessage()); } finally { try { if(connection != null) connection.close(); } catch(SQLException e) { // connection close failed. System.err.println(e); } } } /** * テーブル削除 */ public static void dropTable() { try { // create a database connection connection = DriverManager.getConnection(URL); Statement statement = connection.createStatement(); statement.setQueryTimeout(30); // set timeout to 30 sec. statement.executeUpdate("DROP TABLE IF EXISTS person"); } catch(SQLException e) { // if the error message is "out of memory", // it probably means no database file is found System.err.println(e.getMessage()); } finally { try { if(connection != null) connection.close(); } catch(SQLException e) { // connection close failed. System.err.println(e); } } } }メソッド化した上で、処理の流れをmainメソッドに呼び出してみました。
実行結果id = 1 | name = Satou id = 2 | name = Tanaka id = 3 | name = Suzuki id = 1 | name = Takahashi id = 2 | name = Tanaka id = 3 | name = Suzuki id = 1 | name = Takahashi id = 2 | name = Tanaka
まとめ
各SQL文をメソッド化し、必要に応じてmainメソッドに呼び出すことになんとか成功しました。
SQLを扱う場合、各メソッドごとにtry-catchをしないとエラーになるようでした。
しかし現状では、データの入力内容・変更内容をメソッド内で定義しているため、再利用性が低く未だ未完成です。
今回のコードをベースに再利用性のあるコードにしていこうと思います。
参考サイト