20220127のJavaに関する記事は9件です。

【Java文法】スレッド

こんにちは、コイキングです。 本記事では'スレッド'について、まとめてみます。 1. プロセスとスレッド プロセス : 実行中のプログラム。 スレッド : プロセス内で実行されるフローの単位。 マルチスレッド : プロセス内に複数のスレッドが存在すること。 2. Javaでスレッドを使う方法 1) マルチスレッドを使わない。 ThreadMain01.java package thread.ex01; public class ThreadMain01 { public static void main(String[] args) { Calculator calculator = new Calculator(0L, 1000000000L); execute(calculator); } public static void execute(Calculator calculator) { long startTime = System.currentTimeMillis(); System.out.println("StartTime : "+ startTime); calculator.calc(); long result = (long) calculator.getSum(); long endTime = System.currentTimeMillis(); System.out.println("EndTime : "+ endTime); System.out.printf("Operating Time : [%.3f] Sec, Result : [%d] \n", (endTime - startTime) / 1000.0, result); } } class Calculator { private long start; private long end; private long sum; public Calculator(long start, long end) { setInit(start, end); } public void calc() { long sum = 0; for (long i = start; i <= end; i++) { sum += i; } this.sum = sum; } public long getSum() { return this.sum; } public void setInit(long start, long end) { this.start = start; this.end = end; this.sum = 0; } } // 実行結果 //StartTime : 1643158486533 //EndTime : 1643158487070 //Operating Time : [0.537] Sec, Result : [500000000500000000] 指定した始まりの数から最後の数まで重ねて合算するコードです。 2) Threadクラスを使う。 ThreadMain02.java package thread.ex01; import java.util.ArrayList; public class ThreadMain02 { public static void main(String[] args) { // 非同期問題をjoinメソッドを使い飛ばす System.out.println("########## Solved concurrency issue. ##########"); execute(5, 1000000000L, true); System.out.printf("\n\n"); // 非同期問題のため、合算値が違う System.out.println("########## Not Solved concurrency issue. ##########"); execute(5, 1000000000L); } public static void execute(int worker, long input) { execute(worker, input, false); } public static void execute(int worker, long input, boolean isJoin) { ArrayList<Calculator2> list = new ArrayList<Calculator2>(); long startTime = System.currentTimeMillis(); System.out.println("StartTime : "+ startTime); long start = 0; long end = input / worker; for (int i=1; i <= worker; i++) { if (input < end) end = input; Calculator2 calc = new Calculator2(start , end, i); calc.start(); list.add(calc); if (end == input) break; start = end + 1; end = end * 2; } long result = 0; for (int i=0; i < list.size(); i++) { Calculator2 calc = list.get(i); try { if (isJoin) { calc.join(); } result += calc.getSum(); } catch (Exception e) {} } long endTime = System.currentTimeMillis(); System.out.println("EndTime : "+ endTime); System.out.printf("Operating Time : [%.3f] Sec, Result : [%d] \n", (endTime - startTime) / 1000.0, result); list.clear(); list = null; } } class Calculator2 extends Thread { private long start; private long end; private long sum; private int num; public Calculator2(long start, long end, int num) { setInit(start, end, num); } public void calc() { System.out.printf("Thread [%d] Before, Num [%d], End Num [%d], sum() : [%d] \n", num, start, end, start); long sum = 0; for (long i = start; i <= end; i++) { sum += i; } System.out.printf("Thread [%d] After, Num [%d], End Num [%d], sum() : [%d] \n", num, start, end, sum); this.sum = sum; } public long getSum() { return this.sum; } public void setInit(long start, long end, int num) { this.start = start; this.end = end; this.sum = 0; this.num = num; } @Override public void run() { calc(); } } // 実行結果 //########## Solved concurrency issue. ########## //StartTime : 1643159110620 //Thread [4] Before, Num [800000001], End Num [1000000000], sum() : [800000001] //Thread [1] Before, Num [0], End Num [200000000], sum() : [0] //Thread [3] Before, Num [400000001], End Num [800000000], sum() : [400000001] //Thread [2] Before, Num [200000001], End Num [400000000], sum() : [200000001] //Thread [1] After, Num [0], End Num [200000000], sum() : [20000000100000000] //Thread [4] After, Num [800000001], End Num [1000000000], sum() : [180000000100000000] //Thread [2] After, Num [200000001], End Num [400000000], sum() : [60000000100000000] //Thread [3] After, Num [400000001], End Num [800000000], sum() : [240000000200000000] //EndTime : 1643159110874 //Operating Time : [0.254] Sec, Result : [500000000500000000] // // //########## Not Solved concurrency issue. ########## //StartTime : 1643159110876 //EndTime : 1643159110876 //Operating Time : [0.000] Sec, Result : [0] //Thread [4] Before, Num [800000001], End Num [1000000000], sum() : [800000001] //Thread [3] Before, Num [400000001], End Num [800000000], sum() : [400000001] //Thread [2] Before, Num [200000001], End Num [400000000], sum() : [200000001] //Thread [1] Before, Num [0], End Num [200000000], sum() : [0] //Thread [1] After, Num [0], End Num [200000000], sum() : [20000000100000000] //Thread [4] After, Num [800000001], End Num [1000000000], sum() : [180000000100000000] //Thread [2] After, Num [200000001], End Num [400000000], sum() : [60000000100000000] //Thread [3] After, Num [400000001], End Num [800000000], sum() : [240000000200000000] 単一スレッドを使ったコードをマルチスレッドを使い、実装したものです。 実行結果をみると単一スレッドより、処理速度が速いのが分かります。 (1) マルチスレッドの長所 長所 : - 単一スレッドで処理することより、マルチスレッドを使い並列で処理すればより速く処理できるかもしれない。(状況によって違う。。) - どちらかのスレッドが止まってしまっても、他のスレッドが止まらないとプログラムとして機能できるかもしれない。 短所 : - プログラムの性能が低下する可能性があり。  (例1) 臨界領域の問題を解決するため、不要な所まで同期化をする場合。  (例2) 余りにも多くスレッドを生成する場合、コンテキストスイッチやスレッドのインスタンスを生成することでリソースを消耗しすぎってしまう。 - 非同期問題が発生する可能性がある。(例) 複数のスレッドで作業した結果を一つに合わせる場合。  -> Threadクラスのjoinメソッドを使って、解決する。 - 臨海領域問題が発生する可能性がある。(例) 複数のスレッドから一つの変数に同時にアクセスする場合。 -> synchronizedキーワードを使って同期化して解決する。 ※ 非同期問題の臨海領域問題解決例示コード ThreadMain04.java package thread.ex01; import java.util.ArrayList; public class ThreadMain04 { public static int sharedNum = 0; public static void main(String[] args) { SyncData sd = new SyncData(0); ArrayList<SyncTest> list = new ArrayList<SyncTest>(); for (int i=1; i<=3; i++) { SyncTest sync = new SyncTest(i, sd); sync.start(); list.add(sync); } for (SyncTest sync : list) { try { sync.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Test1-Result : "+sd.balance); System.out.printf("\n\n"); ArrayList<SyncTest2> list2 = new ArrayList<SyncTest2>(); for (int i=1; i<=3; i++) { SyncTest2 sync2 = new SyncTest2(i); sync2.start(); list2.add(sync2); } for (SyncTest2 sync2 : list2) { try { sync2.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Test2-Result : "+sharedNum); } } class SyncData { Integer balance; public SyncData(int balance) { this.balance = balance; } } class SyncTest extends Thread{ SyncData syncData; int number; public SyncTest(int number, SyncData syncData) { this.number = number; this.syncData = syncData; } @Override public void run() { sum(); } public void sum() { System.out.println("sum() Thread ["+number+"] Before : "+syncData.balance); for (int i=1; i<=3; i++) { synchronized (this) { syncData.balance += i; System.out.println("sum() Thread ["+number+"] After : "+syncData.balance); } } } } class SyncTest2 extends Thread { int number; public SyncTest2(int number) { this.number = number; } @Override public void run() { sum(); } public synchronized void sum() { System.out.println("sum() Thread ["+number+"] Before : "+ThreadMain04.sharedNum); for (int i=1; i<=3; i++) { ThreadMain04.sharedNum += i; System.out.println("sum() Thread ["+number+"] After : "+ThreadMain04.sharedNum); } } } 3) Runnableインターフェースを使う ThreadMain03.java import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; public class ThreadMain03 { public static void main(String[] args) throws InterruptedException { PrintTime pt = new PrintTime(); Thread t = new Thread(pt); t.start(); for (int i=0; i<20; i++) { System.out.println("test"+i); Thread.sleep(700); } } } class PrintTime implements Runnable { static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS"); @Override public void run() { for (int i=0; i<10; i++) { System.out.println(sdf.format(new Date())); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } } } Threadクラスでスレッドを使う場合、継承が使えない短所があります。 Threadクラスではなく、他のクラスを継承したい場合はRunnableインターフェースを使えばいいです。 ※ ThreadクラスはRunnableインターフェースを継承したクラスだった。。 4) Callableインターフェースを使う package thread.ex01; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; public class ThreadMain05 { public static void main(String[] args) throws InterruptedException, ExecutionException { GetTime gt = new GetTime(); System.out.println("########## Callable Test (1) ##########"); FutureTask<ArrayList<String>> futureTask = new FutureTask<ArrayList<String>>(gt); new Thread(futureTask).start(); for (int i=0; i<5; i++) { System.out.println("test1 wating Count : "+i); Thread.sleep(500); } System.out.println(futureTask.get()); System.out.println("########## Callable Test (2) ##########"); ExecutorService executor = Executors.newSingleThreadExecutor(); Future<ArrayList<String>> future = executor.submit(gt); for (int i=0; i<5; i++) { System.out.println("test2 wating Count : "+i); Thread.sleep(300); } System.out.println(future.get()); } } class GetTime implements Callable<ArrayList<String>> { static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS"); @Override public ArrayList<String> call() throws Exception { ArrayList<String> list = new ArrayList<String>(); for (int i=0; i<5; i++) { String time = sdf.format(new Date()); System.out.println(time); list.add(time); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } return list; } } Runnable インターフェースのrun()メソッドは戻り値を持つ事ができないし、例外処理を付けられません。 スレッドの各作業の戻り値と例外処理をする必要があるプログラムを実装する時はCallableインターフェースを使えばよいです。 5) Thread Poolを使う ThreadMain06.java package thread.ex01; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class ThreadMain06 { public static void main(String[] args) throws InterruptedException{ // 決めた数分スレッドを生成 int workers = 4; ExecutorService executorService = Executors.newFixedThreadPool(workers); for (int i=1; i<=10; i++) { ThreadPoolTest threadPoolTest = new ThreadPoolTest(i); executorService.submit(threadPoolTest); } if (executorService.awaitTermination(5, TimeUnit.SECONDS)) { System.out.println("All Tasks over"); } executorService.shutdown(); } } class ThreadPoolTest implements Runnable { int number; @Override public void run() { counting(); } public ThreadPoolTest(int number) { this.number = number; } public void counting() { for (int i=0; i<5; i++) { System.out.printf("Thread [%d], Count [%d] \n", number, i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } スレッドフルは予め決めた数のスレッドを生成して置いて、使えることにするものです。 マルチスレッドの短所のうち、余りにも多くスレッドを生成する場合にプログラムの性能が低下する短所を解決するために使えばよいです。 ※ 例示コード https://github.com/leeyoungseung/algorithmBasic/tree/master/algorithm/src/thread/ex01 ※ 韓国語のポストは以下のURLで確認できます。 https://koiking.tistory.com/93
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SpringSecurity5.xでShaPasswordEncoderが無くなっていた

4.xから5.xにバージョンアップしたらコンパイルエラーになりました。 親クラスのMessageDigestPasswordEncoderにメッセージダイジェスト名を渡してインスタンス化すれば同じことはできますが、こちらも非推奨クラスになっています。 幸いなことに アプリ上、このクラスで実現していることが単なるSHA-256へのハッシュ化 このクラスのencodePasswordでやっていることは、UTF-8のバイト配列にした文字列に対して一回SHA-256でハッ シュ化し、16進数の文字列で返す だけなので、commons-codecのDigestUtils.sha256Hexメソッドで事足りました。 本来はPasswordEncoderFactoriesから取得するっぽいのですが詳細わからず……。今度調べます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java6しか使ってこなかった人がJava8のStreamAPIに感動した話

記事はこちらに移管しましたmm
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

@initBinderでString型のリクエストパラメータを空文字からNullにしました

記事はこちらに移管しましたmm
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IntelliJ IDEAにLombokを設定する

記事はこちらに移管しました
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JAVA入門 〜PHPとの相違点だけを抜粋〜

変数と型 ・変数を使用する場合は型を宣言しないといけない ・型には基本データ型と参照型がある データ型の種類 1、基本データ型 boolean true or false char 16ビットUnicode文字 \u0000~\uFFFF byte 8ビット整数 -128~127 short 16ビット整数 -32,768~32,767 int 32ビット整数 -2,147,483,648~2,147,483,647 long 64ビット整数 -9,223,372,036,854,775,808~9,223,372,036,854,775,807 float 32ビット単精度浮動小数点数 double 64ビット倍精度浮動小数点数 2、参照型 ※参照型は基本データ型とは異なる特殊なデータ型 String 文字列 参照型について詳しくはこちら 使用例 java int age; //型宣言 age = 16; //初期化 宣言と初期化を省略した書き方 int age = 16; //型宣言と初期化を同時に実行 初期化しないで配列を使うと・・・ int age; //型宣言 System.out.println(age); //エラー: 変数ageは初期化されていない可能性があります 初期化しないで配列を使うとエラーになる phpで変数を使う場合 $age = 16; 配列の初期化について ・配列の場合は初期化しなくてもよい ・配列の場合はデフォルトの初期値が設定されている int[] number = new int[5]; System.out.println(number); //0 各データ型のデフォルトの初期値 boolean false char '' byte 0 short 0 int 0 long 0 float 0 double 0 String null キャスト(型の変換)について 詳しくはこちら 配列 java 配列の作り方 データ型[] 変数名; 変数名 = new データ型[要素数] String[] customer; // String型の配列を宣言 customer = new String[3]; //初期化 配列の作成例 String[] customer; // String型の配列を宣言 customer = new String[3]; //初期化 customer[0] = "山田"; customer[1] = "yamada@gmail.com"; customer[2] = "090-1234-5678"; 配列を宣言と初期化を省略した書き方 String[] customer = {"山田","yamada@gmail.com","090-1234-5678"}; php $age = ['山田','yamada@gmail.com','090-1234-5678']; 繰り返し構文 拡張for文 配列要素を最初から最後まで順番に使用すること(しかできない)ができる。 String[] customer = {"山田","yamada@gmail.com","090-1234-5678"}; for(String info:customer) { System.out.println(info); } クラス 基本構造 ・javaではmainメソッドは必ず必要 ・クラスが呼ばれるとmainメソッドが実行される public class クラス名 { public static void main(String[] args) { //処理 }    public static void メソッド名() { //処理 } } フィールド クラス直下で定義された変数 class Sample { String name = ""; int age = 0; public static void main(String[] args) { //処理 } } インスタンス クラスを使用可能な状態にする メソッドと同様にオーバーロードできる 基本構造 クラス名 インスタンス名 = new クラス名(引数); 使用例 class Sample { Student taro = new Student("太郎",30); } コンストラクタ インスタンスをすると必ず実行される 初期化に使用される ルール 1、コンストラクタの定義において戻り値の情報は何も記述しない 2、コンストラクタ名はクラス名と同じにする class Student { String name; int age; Student(String name, int age) { name = name; age = age; } } thisついて thisには2つの使い方がある 1、this() ・コンストラクタを呼ぶ ・実践的な使い方は初期化を行う際の初期値の設定に使用される 使用例 class Sample { Student taro = new Student("太郎",30); } class Student { String name; int age;   //引数がない場合はこのコンストラクタが実行される Student() { name = null; age = 0; }   //引数がある場合はこのコンストラクタが実行される   Student(String studentName, int studentAge) { this(); //引数がない場合のコンストラクタを実行させる name = studentName; age = studentAge; } } 1、this.変数名 ・フィールドの変数を指定する ・ローカル変数名とフィールド変数名が一緒の場合はローカル変数が優先される 使用例 class Sample { Student taro = new Student("太郎",30); } class Student { String name; int age;   Student(String name, int age) { name = name; //thisがない場合この変数nameが示しているのはこのコンストラクタの変数name age = age; //thisがない場合この変数ageが示しているのはこのコンストラクタの変数age }   Student(String name, int age) { this.name = name; //thisがある場合この変数this.nameが示しているのはこのフィールドの変数name this.age = age; //thisがある場合この変数this.ageが示しているのはこのフィールドの変数age } } メソッド名の前の型について メソッド名の前にメソッドの戻り値の型を設定する void -> 戻り値なし int -> 数値型 String -> 文字列 public class calculate { public static void main(String[] args) { int num1 = 5; int num2 = 10; sum = add(num1,num2); System.out.println(sum); }    public static int add(int num1, int num2) { int sum = 0; sum = num1 + num2; return sum; } } mainメソッドの引数について main(String[] args) mainメソッドの引数(String[] args)を使うにはコマンドラインで引数を渡すことで使用できる 詳しくはこちら メソッドのオーバーロード 同じメソッド名で複数のメソッドを定義できる 条件 1、引数の数が異なっている public static void test(int n1){ // ... } public static void test(int n1, int n2){ // ... } 2、引数の種類が異なっている public static void test(int n1){ // ... } public static void test(double d1){ // ... } 3、引数のデータ型と数は同じでも順番が異なっている public static void test(int n1, double d1){ // ... } public static void test(double d1, int n1){ // ... } 詳しくはこちら 可変長引数について 可変長引数とは、引数の数が決まっていない引数のこと 使い方 public static void メソッド名(型... 引数名) { // } 使用例 public static void main(String[] args) { int sum = add(10,20,30); System.out.println(sum); } public static int add(int... numbers) { int sum = 0; for(int i = 0; i < numbers.length; i++) { sum += numbers[i]; } return sum; } パッケージの使い方 ・パッケージはクラスを分類するための仕組み ・phpのnamespaceと似た機能 使い方 ・階層の上から順に「.」でパッケージ名を連結 ・クラスの先頭で「package」と宣言 ・小文字で記述 ・パッケージ名は通常、インターネットドメインを逆にした形式のパッケージ名とする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Webで使えるJavaのUIフレームワークのVaadinをさわってみた(その3)

これはつづきものです Accessing The Database 今回は「Accessing The Database」から始めます。 Connecting the View to the Backend 前回は一覧画面と入力フォームを用意しました。 This chapter covers: Spring Boot. How to create a Spring Service for interfacing the backend. How to access a service from the view. ということでVaadinのコンポーネントの話というよりかはSpringがメインになりそうな予感。 それでは見ていきましょう。 Backend Overview The starter you downloaded contains the entities and repositories you need, along with a sample data generator. Domain Model: Entities The Vaadin CRM application has three JPA entities that make up its domain model: Contact, Company, and Status. A contact belongs to a company and has a status. 一覧画面を作るときにでてきた Contact, Company, Status の説明が出てきました。データベースのテーブルと対応するEntityだろうなとは思っていましたので、慣れている人たちからすると案の定という感じですかね。 The application uses Spring Data JPA repositories for database access. Spring Data provides implementations of basic create, read, update, and delete (CRUD) database operations when you extend from the JpaRepository interface. DBにアクセスする手段にはSpringのJpaRepositoryを使います。JpaRepositoryを使用するロジックの実装にServiceクラスを用意して、という常套手段でやっていきます。 package com.example.application.data.service; import com.example.application.data.entity.Company; import com.example.application.data.entity.Contact; import com.example.application.data.entity.Status; import com.example.application.data.repository.CompanyRepository; import com.example.application.data.repository.ContactRepository; import com.example.application.data.repository.StatusRepository; import org.springframework.stereotype.Service; import java.util.List; @Service public class CrmService { private final ContactRepository contactRepository; private final CompanyRepository companyRepository; private final StatusRepository statusRepository; public CrmService(ContactRepository contactRepository, CompanyRepository companyRepository, StatusRepository statusRepository) { this.contactRepository = contactRepository; this.companyRepository = companyRepository; this.statusRepository = statusRepository; } public List<Contact> findAllContacts(String stringFilter) { if (stringFilter == null || stringFilter.isEmpty()) { return contactRepository.findAll(); } else { return contactRepository.search(stringFilter); } } public long countContacts() { return contactRepository.count(); } public void deleteContact(Contact contact) { contactRepository.delete(contact); } public void saveContact(Contact contact) { if (contact == null) { System.err.println("Contact is null. Are you sure you have connected your form to the application?"); return; } contactRepository.save(contact); } public List<Company> findAllCompanies() { return companyRepository.findAll(); } public List<Status> findAllStatuses(){ return statusRepository.findAll(); } } 最近思うのですが、Serviceクラスにインターフェースを用意しないケースが増えてきた印象が個人的にあります。理想は理解できるのですが、実際にはクラスが2倍になるんで手間の割に恩恵が少ないと思っていたので今後は私もインターフェースを用意しない派に属そうとか画策しています。 ここまではVaadinではなくSpringです。 Using the Backend Service From the View それではVaadinからSpringのServiceにつないでいきましょう。変更する画面は一覧画面です。ListViewクラスのフィールドにSpringのServiceを追加します。 ServiceはSpringのDIコンテナが管理しているので依存性の注入(インスタンスをセット)はListViewのコンストラクタで行います。 画面のテキストボックスの値と TextField filterText = new TextField(); がbindされていて値の変更反映をリスナーを通して容易にできます。 package com.example.application.views.list; import com.example.application.data.entity.Contact; import com.example.application.data.service.CrmService; import com.vaadin.flow.component.Component; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.data.value.ValueChangeMode; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; import java.util.Collections; @Route(value = "") @PageTitle("Contacts | Vaadin CRM") public class ListView extends VerticalLayout { Grid<Contact> grid = new Grid<>(Contact.class); TextField filterText = new TextField(); ContactForm form; CrmService service; public ListView(CrmService service) { this.service = service; addClassName("list-view"); setSizeFull(); configureGrid(); configureForm(); add(getToolbar(), getContent()); updateList(); } private Component getContent() { HorizontalLayout content = new HorizontalLayout(grid, form); content.setFlexGrow(2, grid); content.setFlexGrow(1, form); content.addClassNames("content"); content.setSizeFull(); return content; } private void configureForm() { form = new ContactForm(service.findAllCompanies(), service.findAllStatuses()); form.setWidth("25em"); } private void configureGrid() { grid.addClassNames("contact-grid"); grid.setSizeFull(); grid.setColumns("firstName", "lastName", "email"); grid.addColumn(contact -> contact.getStatus().getName()).setHeader("Status"); grid.addColumn(contact -> contact.getCompany().getName()).setHeader("Company"); grid.getColumns().forEach(col -> col.setAutoWidth(true)); } private HorizontalLayout getToolbar() { filterText.setPlaceholder("Filter by name..."); filterText.setClearButtonVisible(true); filterText.setValueChangeMode(ValueChangeMode.LAZY); filterText.addValueChangeListener(e -> updateList()); Button addContactButton = new Button("Add contact"); HorizontalLayout toolbar = new HorizontalLayout(filterText, addContactButton); toolbar.addClassName("toolbar"); return toolbar; } private void updateList() { grid.setItems(service.findAllContacts(filterText.getValue())); } } ここのチュートリアルではJpaRepositoryを使っていますが、実際はなんでもいいです。個人的にはJdbcTemplateです。そもそもVaadinというよりService以降のレイヤーはSpringの世界です。 Forms and Validation それではフォームとバリデーションです。 Vaadin Forms: Data Binding and Validation ここでは入力フィールドのbindとバリデーションですね。 This chapter covers: Creating a Vaadin Binder. Binding input fields. Field validation. Using Vaadin Binder to Create a Form and Validate Input フォームの画面上の入力フィールドとJavaのデータオブジェクトのフィールドの名前が一致するものをバインドさせることを基本にしているようです。 It binds UI fields to data object fields by name. For instance, it takes a UI field named firstName and maps it to the firstName field of the data object, and the lastName field to the lastName field, and so on. This is why the field names in Contact and ContactForm are the same. Vaadin uses the Binder class to build forms. Defining Bean Validation Rules in Java Bean Validation を使って定義できるそうです。 You can define data validation rules as Java Bean Validation annotations on the Java class. そのため、Javaのデータオブジェクトのフィールドに@NotEmptyとかをつけます。 @NotEmptyの場合は入力を必須にするという感じでしょうね。 その場合のユーザに表示するメッセージはどこに書く感じでしょうか。Bean Validationにそういう機能があるんですかね。筆者の知識がまだ足りてないですが。 @Email @NotEmpty private String email = ""; Creating the Binder 用意したデータオブジェクトを入力フィールドと括る(Bind)させる方法です。 // Other fields omitted Binder<Contact> binder = new BeanValidationBinder<>(Contact.class); public ContactForm(List<Company> companies, List<Status> statuses) { addClassName("contact-form"); binder.bindInstanceFields(this); // Rest of constructor omitted } ここの例のように binder.bindInstanceFields() メソッドで括れます。 Setting the Contact データオブジェクトの値を入力フィールドに設定する方法です。 Next, you need to create a setter for the contact. Unlike the companies and statuses, it can change over time as a user browses through the contacts. public class ContactForm extends FormLayout { private Contact contact; // other methods and fields omitted public void setContact(Contact contact) { this.contact = contact; binder.readBean(contact); } } Save a reference to the contact, so you can save the form values back into it later. Calls binder.readBean() to bind the values from the contact to the UI fields. readBean() copies the values from the contact to an internal model; that way you don’t overwrite values if you cancel editing. Setting up Component Events UIで行われた操作はイベントとして扱われます。Webアプリケーションを作っている人はなじみがあるでしょう。このイベントをVaadinでどのように定義するのかがここで登場します。 今回イベントとして扱うのはフォームを編集することを想定して保存(Save)、削除(Delete)、閉じる(Close)の3種です。3種に対応するクラスをContactForm内に作ります。 SaveEvent DeleteEvent CloseEvent // Events public static abstract class ContactFormEvent extends ComponentEvent<ContactForm> { private Contact contact; protected ContactFormEvent(ContactForm source, Contact contact) { super(source, false); this.contact = contact; } public Contact getContact() { return contact; } } public static class SaveEvent extends ContactFormEvent { SaveEvent(ContactForm source, Contact contact) { super(source, contact); } } public static class DeleteEvent extends ContactFormEvent { DeleteEvent(ContactForm source, Contact contact) { super(source, contact); } } public static class CloseEvent extends ContactFormEvent { CloseEvent(ContactForm source) { super(source, null); } } public <T extends ComponentEvent<?>> Registration addListener(Class<T> eventType, ComponentEventListener<T> listener) { return getEventBus().addListener(eventType, listener); } Saving, Deleting, and Closing the Form 定義したクラスをボタンのクリックのリスナーにひもづけて動作するようにします。 private Component createButtonsLayout() { save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); delete.addThemeVariants(ButtonVariant.LUMO_ERROR); close.addThemeVariants(ButtonVariant.LUMO_TERTIARY); save.addClickShortcut(Key.ENTER); close.addClickShortcut(Key.ESCAPE); save.addClickListener(event -> validateAndSave()); delete.addClickListener(event -> fireEvent(new DeleteEvent(this, contact))); close.addClickListener(event -> fireEvent(new CloseEvent(this))); binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid())); return new HorizontalLayout(save, delete, close); } private void validateAndSave() { try { binder.writeBean(contact); fireEvent(new SaveEvent(this, contact)); } catch (ValidationException e) { e.printStackTrace(); } } Handling View State Passing Data and Events Between Vaadin Components これまでにフォームのコンポーネントを作成してきました。ここからはその状態管理をしていきます。 扱う内容は以下の3点です。 表で選択されたContactをフォームで見せる Contactが選択されていなければフォームを隠す Contactの保存や削除をデータベースに反映する 意訳が入っています。原文は以下です。 The form: shows the selected contact in the grid; is hidden when no contact is selected; saves and deletes contacts in the database. Showing the Selected Contact in the Form The first step is to show the selected grid row in the form. ListViewでフォームの状態を操作します。操作するためにいくつかのメソッドを用意します。 package com.example.application.views.list; import com.example.application.data.entity.Contact; import com.example.application.data.service.CrmService; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.data.value.ValueChangeMode; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; @Route(value = "") @PageTitle("Contacts | Vaadin CRM") public class ListView extends VerticalLayout { Grid<Contact> grid = new Grid<>(Contact.class); TextField filterText = new TextField(); ContactForm form; CrmService service; public ListView(CrmService service) { this.service = service; addClassName("list-view"); setSizeFull(); configureGrid(); configureForm(); add(getToolbar(), getContent()); updateList(); closeEditor(); } private HorizontalLayout getContent() { HorizontalLayout content = new HorizontalLayout(grid, form); content.setFlexGrow(2, grid); content.setFlexGrow(1, form); content.addClassNames("content"); content.setSizeFull(); return content; } private void configureForm() { form = new ContactForm(service.findAllCompanies(), service.findAllStatuses()); form.setWidth("25em"); } private void configureGrid() { grid.addClassNames("contact-grid"); grid.setSizeFull(); grid.setColumns("firstName", "lastName", "email"); grid.addColumn(contact -> contact.getStatus().getName()).setHeader("Status"); grid.addColumn(contact -> contact.getCompany().getName()).setHeader("Company"); grid.getColumns().forEach(col -> col.setAutoWidth(true)); grid.asSingleSelect().addValueChangeListener(event -> editContact(event.getValue())); } private HorizontalLayout getToolbar() { filterText.setPlaceholder("Filter by name..."); filterText.setClearButtonVisible(true); filterText.setValueChangeMode(ValueChangeMode.LAZY); filterText.addValueChangeListener(e -> updateList()); Button addContactButton = new Button("Add contact"); addContactButton.addClickListener(click -> addContact()); HorizontalLayout toolbar = new HorizontalLayout(filterText, addContactButton); toolbar.addClassName("toolbar"); return toolbar; } public void editContact(Contact contact) { if (contact == null) { closeEditor(); } else { form.setContact(contact); form.setVisible(true); addClassName("editing"); } } private void closeEditor() { form.setContact(null); form.setVisible(false); removeClassName("editing"); } private void addContact() { grid.asSingleSelect().clear(); editContact(new Contact()); } private void updateList() { grid.setItems(service.findAllContacts(filterText.getValue())); } } 作るとこんな感じの画面がでてきました。 gridである表の行を選択するとフォームが表示されたり、非表示になったりします。 Making the Layout Responsive なぜこのタイミングでこの話がでてくるのかわかりませんが、、、レスポンシブなレイアウトをつくるときには以下のcssを追加する必要があります。 @media all and (max-width: 1100px) { .list-view.editing .toolbar, .list-view.editing .contact-grid { display: none; } } このcssをいれて横幅を縮めると行選択時にgridが消えてフォームだけになる画面になりました。 Handling Form Events SaveEvent、DeleteEvent、CloseEventのクラスを定義した意味がここで理解できます。正直感動しました。ListViewでは子コンポーネントであるフォームをどうやって扱うのかと思ったらListenerを使ってfireEventされたものをフックするんですね。この設計考えた人すごい。 private void configureForm() { form = new ContactForm(service.findAllCompanies(), service.findAllStatuses()); form.setWidth("25em"); form.addListener(ContactForm.SaveEvent.class, this::saveContact); form.addListener(ContactForm.DeleteEvent.class, this::deleteContact); form.addListener(ContactForm.CloseEvent.class, e -> closeEditor()); } private void saveContact(ContactForm.SaveEvent event) { service.saveContact(event.getContact()); updateList(); closeEditor(); } private void deleteContact(ContactForm.DeleteEvent event) { service.deleteContact(event.getContact()); updateList(); closeEditor(); } 今回はここまで 次回はサイドメニューなどのレイアウトにうつっていきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Intellij Unit テスト実行方法

1, 直接Junit の設定をして、テスト実行するパターン 1, intellij の右上、からedit configuration  2, 設定を以下のように。 3, 緑三角の実行ボタンをクリック。 他のパターン 2, gradle ツールから実行するパターン Tasks > verification > test をクリックする。  
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VsCodeの拡張機能「Remote-Containers」を利用してコンテナ上で「plantuml」を実行するための設定

VsCodeの拡張機能「Remote-Containers」を利用してコンテナ上で「plantuml」を実行するための設定 業務端末で拡張機能を入れる場合、利用申請が必要になるがコンテナ上であれば申請がいらないということなので、その設定ファイルをメモとして残しておく。 動かすことを目的としているため、設定項目が理想的かどうかは要確認。 devcontainer.json { "name": "myjava", "dockerComposeFile": "docker-compose.yml", "service": "workspace", "workspaceFolder": "/var/sample_sandbox", "settings": { "editor.tabSize": 4 }, "shutdownAction": "stopCompose", "extensions": [ "jebbs.plantuml", "vscjava.vscode-java-pack" ] } docker-compose.yml version: "3" services: workspace: build: workspace command: sleep infinity volumes: - ../:/var/sample_sandbox/ ports: - 8000:8000 FROM openjdk フォルダ構成 C:. ├─.devcontainer │ devcontainer.json │ docker-compose.yml └─workspace Dockerfile
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む