- 投稿日:2020-07-20T22:09:21+09:00
Unable to find CDI BeanManager.となったときの対応方法
- 環境
- CentOS Linux release 7.8.2003 (Core)
- Eclipse IDE for Enterprise Java Developers. Version: 2020-03 (4.15.0)
- openjdk version "11.0.7"
- Payara Server 5.194
事象 : JSFのプロジェクトをPayaraを起動したらエラーログが表示された
エラーログのメッセージcannot Deploy tryJsf deploy is failing=Error occurred during deployment: Exception while loading the app : java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: java.lang.RuntimeException: javax.faces.FacesException: Unable to find CDI BeanManager. Please see server.log for more details.]server.log[2020-07-20T21:13:22.486+0900] [Payara 5.194] [重大] [] [javax.enterprise.resource.webcontainer.jsf.config] [tid: _ThreadID=49 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1595247202486] [levelValue: 1000] [[ Critical error during deployment: javax.faces.FacesException: Unable to find CDI BeanManager at com.sun.faces.el.ELUtils.tryAddCDIELResolver(ELUtils.java:288) at com.sun.faces.el.ELUtils.buildFacesResolver(ELUtils.java:218) at com.sun.faces.application.ApplicationAssociate.initializeELResolverChains(ApplicationAssociate.java:467) ...省略... ]] [2020-07-20T21:13:22.490+0900] [Payara 5.194] [重大] [AS-WEB-CORE-00174] [javax.enterprise.web.core] [tid: _ThreadID=49 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1595247202490] [levelValue: 1000] [[ Startup of context /tryJsf failed due to previous errors]] [2020-07-20T21:13:22.492+0900] [Payara 5.194] [重大] [AS-WEB-CORE-00175] [javax.enterprise.web.core] [tid: _ThreadID=49 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1595247202492] [levelValue: 1000] [[ Exception during cleanup after start failed org.apache.catalina.LifecycleException: Manager has not yet been started at org.apache.catalina.session.StandardManager.stop(StandardManager.java:868) at org.apache.catalina.core.StandardContext.stop(StandardContext.java:5940) at com.sun.enterprise.web.WebModule.stop(WebModule.java:648) ...省略... ]] [2020-07-20T21:13:22.493+0900] [Payara 5.194] [重大] [AS-WEB-CORE-00108] [javax.enterprise.web.core] [tid: _ThreadID=49 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1595247202493] [levelValue: 1000] [[ ContainerBase.addChild: start: org.apache.catalina.LifecycleException: java.lang.RuntimeException: javax.faces.FacesException: Unable to find CDI BeanManager at org.apache.catalina.core.StandardContext.start(StandardContext.java:5760) at com.sun.enterprise.web.WebModule.start(WebModule.java:619) at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:956) ...省略... Caused by: java.lang.RuntimeException: javax.faces.FacesException: Unable to find CDI BeanManager at com.sun.faces.config.ConfigureListener.contextInitialized(ConfigureListener.java:283) at org.apache.catalina.core.StandardContext.contextListenerStart(StandardContext.java:5165) ...省略... Caused by: javax.faces.FacesException: Unable to find CDI BeanManager at com.sun.faces.el.ELUtils.tryAddCDIELResolver(ELUtils.java:288) at com.sun.faces.el.ELUtils.buildFacesResolver(ELUtils.java:218) ...省略... ]] [2020-07-20T21:13:22.495+0900] [Payara 5.194] [警告] [] [javax.enterprise.web] [tid: _ThreadID=49 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1595247202495] [levelValue: 900] [[ java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: java.lang.RuntimeException: javax.faces.FacesException: Unable to find CDI BeanManager java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: java.lang.RuntimeException: javax.faces.FacesException: Unable to find CDI BeanManager at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:960) at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:939) at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:684) ...省略... ]] [2020-07-20T21:13:22.504+0900] [Payara 5.194] [重大] [] [javax.enterprise.system.tools.deployment.common] [tid: _ThreadID=49 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1595247202504] [levelValue: 1000] [[ Exception while invoking class com.sun.enterprise.web.WebApplication start method java.lang.Exception: java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: java.lang.RuntimeException: javax.faces.FacesException: Unable to find CDI BeanManager at com.sun.enterprise.web.WebApplication.start(WebApplication.java:136) at org.glassfish.internal.data.EngineRef.start(EngineRef.java:123) ...省略... ]] [2020-07-20T21:13:22.505+0900] [Payara 5.194] [重大] [NCLS-CORE-00026] [javax.enterprise.system.core] [tid: _ThreadID=49 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1595247202505] [levelValue: 1000] [[ Exception during lifecycle processing java.lang.Exception: java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: java.lang.RuntimeException: javax.faces.FacesException: Unable to find CDI BeanManager at com.sun.enterprise.web.WebApplication.start(WebApplication.java:136) at org.glassfish.internal.data.EngineRef.start(EngineRef.java:123) ...省略... ]]原因 : JSFのライブラリを指定していないから
プロジェクトに[JavaServer Faces]で「2.3」と指定したわりにライブラリをなんにも指定していなかった・・・。
参考 : 新卒SEのJavaEE挑戦記2(JSF編) | 株式会社イテレイティブ
pom.xmlは初期状態でなんにもしていなかった<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.ponsuke</groupId> <artifactId>tryJsf</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> </project>対応 : JSFのライブラリをpom.xmlに入れる
- Maven Repository: org.glassfish » javax.facesから任意のバージョンの定義をコピる
- pom.xmlに追記する
- Payaraをクリーン > プロジェクトをクリーン > プロジェクトで[Run As] > [Run Os Server]
pom.xml...省略... <dependencies> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.faces</artifactId> <version>2.3.9</version> </dependency> </dependencies> </project>
- 投稿日:2020-07-20T18:26:39+09:00
基礎Webアプリケーション作成 Servlet/JSP(投稿画面)
はじめに
今回は前回、基礎Webアプリケーション作成 Servlet/JSP(ログイン機能)の続きを行います。
この記事で目指すのは投稿画面でのログインしているかしていないかの確認やしていない場合の処理をすることを目指します。実行環境
- Eclipse4.16
- Tomcat9
- Java11
1.投稿のModel作成
投稿画面を表示するので投稿情報のモデルを作成します。
投稿にはユーザー名と文章が存在することとする。Article.javapublic class Article implements Serializable{ private String userName; private String text; Article(){} Article(String userName,String text){ this.userName = userName; this.text = text; } public String getUserName() { return this.userName; } public String getText() { return this.text; } }2.記事に関するリクエスト処理のコントローラ
このコントローラでログインしているかの確認の処理を行い
- ログインしている場合→
bulletin-board.jspをフォワード
- ログインしていない場合→
ログイン画面へリダイレクト
BulletinBoard.javaprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext application = this.getServletContext(); List<Article> articleList = (List<Article>) application.getAttribute("articleList"); // 1 if(articleList == null) { articleList = new ArrayList<Article>(); application.setAttribute("articleList",articleList); // 2 } HttpSession session = request.getSession(); User loginUser = (User) session.getAttribute("loginUser"); // 3 if(loginUser == null) { response.sendRedirect("./login"); // 4 }else { request.getRequestDispatcher("/WEB-INF/view/bulletin-board.jsp").forward(request, response); // 5 } }
- アプリケーションスコープから取得
- 1が
null
の場合、空の記事リストをアプリケーションスコープにセット- セッションスコープから取得
- ログインしていない場合ログイン画面にリダイレクト
- ログインしている場合掲示板画面を表示
3.掲示板画面のビュー作成
bulletin-board.jsp<h2>こちらは掲示板画面です。</h2> <p><c:out value="${loginUser.name}"/>さんがログイン中です。</p>2行目で誰がログインしているか出力します。
4.実行
動作確認してログインしていれば掲示板画面が表示されて
ログインしていなければログインページにリダイレクトされれば成功!
一度セッションスコープを削除したい場合は以下を記載するrequest.getSession().removeAttribute("loginUser");
最後に
次回はログアウト機能実装を行います。
- 投稿日:2020-07-20T18:02:27+09:00
文字列の分割(Java)
はじめに
文字の分割のいろいろなパターンをまとめてみた。
区切り文字1つ、第2引数なしのパターン
public class Sample { public static void main(String[] args) { String str = "1,2,3,4"; String[] strs = str.split(","); for (String num : strs) { System.out.println(num); } System.out.println("配列の数:"+strs.length); } }実行結果.1 2 3 4 配列の数:4区切り文字1つ、第2引数ありのパターン(第2引数が0より大きいとき)
public class Sample { public static void main(String[] args) { String str = "1,2,3,4"; String[] strs = str.split(",",3); for (String num : strs) { System.out.println(num); } System.out.println("配列の数:"+strs.length); } }実行結果.1 2 3,4 配列の数:3第2引数は要素数を指定する。
今回の場合は3を指定したため、要素数が3つになる。区切り文字1つ、第2引数ありのパターン(第2引数が負の値のとき)
public class Sample { public static void main(String[] args) { String str = "1,2,3,4,"; String[] strs = str.split(",",-1); for (String num : strs) { System.out.println(num); } System.out.println("配列の数:"+strs.length); } }実行結果.1 2 3 4 配列の数:5第2引数が負の値のときは配列の要素数は制限されず、配列の終わりの空の文字列は削除されない。
今回の場合は-1を指定したが負の値のため要素数は制限されていない。また、空の文字列も含んでいる。区切り文字が複数のとき
public class Sample { public static void main(String[] args) { String str = "1+2=3"; String[] strs = str.split("[=+]"); for (String num : strs) { System.out.println(num); } System.out.println("配列の数:"+strs.length); } }実行結果.1 2 3 配列の数:3区切り文字を複数指定する場合は[]内に区切る文字を入れる。
区切り文字を含めて区切るとき
public class Sample { public static void main(String[] args) { String str = "1,2,3"; String[] strs = str.split("(?<=,)"); for (String num : strs) { System.out.println(num); } System.out.println("配列の数:"+strs.length); } }実行結果.1, 2, 3 配列の数:3区切り文字を含める場合は(?<=)を使用する。
複数の区切り文字を含めて区切るとき
public class Sample { public static void main(String[] args) { String str = "1,2a3"; String[] strs = str.split("(?<=[,a])"); for (String num : strs) { System.out.println(num); } System.out.println("配列の数:"+strs.length); } }実行結果.1, 2a 3 配列の数:3複数の区切り文字を含める場合は(?<=[])の[]内に区切る文字を入れる。
- 投稿日:2020-07-20T16:29:46+09:00
【Java】Listに格納された値を繰り返し処理で取得する諸方法
前段
この記事では実装を問わず
List
のデータ構造を使用した繰り返し処理の操作方法をいくつか紹介します。記事の内容に関しては普段から Java を使用してプログラミングを行っている方からすれば常識レベルのことなので、読者の対象は Java を学び始めた初心者〜中級者を想定しています。しかし、JDK 1.8 で追加された
forEach
メソッドを使用した繰り返し処理に関しては未だに開発現場でも使用している方が少ない印象なので、この際にforEach
メソッドの使い方に関しても覚えてほしいと思います。なお、この記事では List コレクションを用いた単純な繰り返し処理を扱うため、
filter
といった中間操作を行うStream API
には触れません。Stream API
に関してはまた別の記事で紹介できればと考えています。この記事を読むことでできるようになること
- List コレクションを使用した繰り返し処理
主な繰り返し処理の操作方法
get(int)
メソッドを使用したランダムアクセス拡張for文
を使用したシーケンシャルアクセスforEach(Consumer<T>)
メソッドを使用したシーケンシャルアクセス (JDK 1.8 以降)
get
メソッドを使用したランダムアクセス
拡張for文
や JDK 1.8 でforEach
メソッドといった便利かつ効率的な機能が登場してからは List コレクションに対してget
メソッドを使用しランダムアクセスで繰り返し処理を行うという場面はほとんどなくなりました。しかし、このランダムアクセスは実装を問わず List コレクションを用いた最も基本的な繰り返し処理の方法ですので、Java を使ってプログラミングをする方には必ず覚えておいてほしいです。基本的な操作方法は以下のようになります。
import java.util.List; import java.util.ArrayList; public final class TestGet { public static void main(String[] args) { final List<String> testList = new ArrayList<>(3); testList.add("test1"); testList.add("test2"); testList.add("test3"); for (int i = 0, size = testList.size(); i < size; i++) { // 変数i番目の要素を取得(ランダムアクセス) System.out.println(testList.get(i)); } } }上記のサンプルコードではまず 3 つの要素を格納することができるテスト用の List を
ArrayList
として実装し、テスト用の値をadd
しています。その後は基本的な for 文を使用し、先に用意した
testList
のサイズ分だけ処理を繰り返します。上記のサンプルコードですと繰り返し処理の度に変数i
はインクリメントされていきますので、コメントにもあるようにtestList.get(i)
の処理はtestList
の要素に対して順次にランダムアクセスしていくものになります。そのため、上記のサンプルコードの実行結果は以下のように出力されます。
test1 test2 test3注意点
- ランダムアクセスを行う際にはアクセス可能な範囲を必ず明確にする
上記のサンプルコードのように
get
メソッドを使用したランダムアクセスを使用する場合は必ずアクセス対象の List のサイズを確認してください。もしget
メソッドに対象 List のサイズよりも大きいインデックスを指定した場合は実行時に必ずIndexOutOfBoundsException
(範囲外例外)が発生し処理が失敗します。例えば、以下のような場合です。
import java.util.List; import java.util.ArrayList; public final class TestIndexOutOfBoundsException { public static void main(String[] args) { final List<String> testList = new ArrayList<>(); testList.add("test1"); testList.add("test2"); testList.add("test3"); // 存在しない4個目のインデックスを指定 testList.get(3); } }上記のサンプルコードを実行すると以下の例外が発生します。
java.lang.IndexOutOfBoundsException: Index 3 out of bounds for length 3 at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64) at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70) at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248) at java.base/java.util.Objects.checkIndex(Objects.java:373) at java.base/java.util.ArrayList.get(ArrayList.java:425)ランダムアクセスを行う対象の List のサイズは先の正常終了するサンプルコードにもあるように
size()
メソッドで取得できます。なお、データベースから一意のキーをもとに一つのレコードのみが必ず取得できるといったような場合では、size()
メソッドで List のサイズを検査せずにtestList.get(0);
といった処理で対象の値を取得することは認められます。重要な点は、ランダムアクセスを行う際には
IndexOutOfBoundsException
が実行時に発生しないようにアクセス可能な範囲を明確にすることです。
LinkedList
に対するランダムアクセスは極力避けるJava の List コレクションの中には
LinkedList
というArrayList
同様によく使われるデータ構造が存在します。このLinkedList
は格納した要素同士の前後関係をリンクで保持する仕組みから、配列に新しく要素を挿入することや配列内の要素を削除する処理に適したデータ構造になります。しかし、
LinkedList
に対するランダムアクセスはパフォーマンスが最悪です。ランダムアクセスする要素数が 100 件程度であればArrayList
使用時とほとんど変わらない処理時間になりますが、LinkedList
で 10 万件以上の規模の大きいデータに対してランダムアクセス繰り返し処理を行うことはパフォーマンスの面から現実的ではありません。そのため、
LinkedList
で規模の大きいデータを繰り返し処理で扱う際には、get
メソッドを使用したランダムアクセスではなく、後に紹介する拡張for文
やforEach
メソッドといったシーケンシャルアクセスで操作を行ってください。余談
ランダムアクセスに関して以下のサンプルコードを先に紹介しました。
import java.util.List; import java.util.ArrayList; public final class TestGet { public static void main(String[] args) { final List<String> testList = new ArrayList<>(3); testList.add("test1"); testList.add("test2"); testList.add("test3"); for (int i = 0, size = testList.size(); i < size; i++) { // 変数i番目の要素を取得(ランダムアクセス) System.out.println(testList.get(i)); } } }もしかしたら、上記のサンプルコードの for 文が初心者の方には見慣れない書き方なのではないかと思いましたので説明を残しておきます。
上記のサンプルコードですが for 文は以下のように書くことができます。for (int i = 0; i < testList.size(); i++) { // do something }最初に紹介したサンプルコードでは初期化式部分にあった
size()
メソッドの呼び出しを条件式部分で実行しtestList
のサイズを取得するようにしてみました。しかし、上記の for 文の書き方は避けるべきです。なぜなら、for 文の条件式部分
i < testList.size()
は繰り返し処理の度に実行されるため、上記コードの場合ですとtestList
のサイズ分だけsize()
メソッドが呼び出されることになります。10 件や 100 件程度であればパフォーマンスに影響はほとんど無いですが、それでも小さな差も積もれば大きな差になります。例えば、以下のようなサンプルコードで条件式部分の処理が毎回実行されていることを確認することができます。
public final class Main { public static void main(String[] args) throws Exception { // 条件式部分が毎回実行されていることを確認 for (int i = 0; i < 10 && saySomething(); i++) { // do something } } private static boolean saySomething() { System.out.println("Hello World!"); return true; } }上記のサンプルコードの実行結果は以下のように出力されます。普段の開発で上記のようなコードを書くことはないですが、条件式部分の
saySomething()
メソッドが 10 回実行されていることを確認できました。Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World!以上から、for 文開始時に 1 回だけ実行される初期化式部分で条件式部分で使用する件数を求めておくことで、無駄のないより効率的なプログラムを書くことができます。これは List コレクションにおける
size()
メソッドに限らず多様な場面で応用できるテクニックですので、より良いプログラムを目指す方々にはぜひとも日々心がけるようにしていただきたいです。
拡張for文
を使用したシーケンシャルアクセス
拡張for文
が Java の機能として追加されたからしばらく経ち、既に List コレクションの順次繰り返し処理ではデファクトスタンダードな書き方になりました。for 文の書き方もランダムアクセス時のものより簡潔で効率的なものになっています。
拡張for文
が導入される以前はIterator
インターフェースを自前で実装することでシーケンシャルアクセスを実現していました。Iterator
インターフェースでのシーケンシャルアクセスは実装する処理が複雑でかつ多く、既にレガシーな書き方で使用する場面もほとんどないのでこの記事では詳しく扱いませんが、知識として拡張for文
の登場以前にはIterator
インターフェースを自前で実装することでシーケンシャルアクセスを実現していたと頭の片隅に置いておいてください。また、
get
メソッドを使用したランダムアクセス時に紹介したIndexOutOfBoundsException
(範囲外例外)やLinkedList
使用時のパフォーマンス劣化は、拡張for文
を使用したシーケンシャルアクセスでは発生しません。そのため、
拡張for文
を使用できる場面であればランダムアクセスでの繰り返し処理は行わず、拡張for文
を使用した繰り返し処理を書くように心がけてください。さて、先に紹介した
get
メソッドを使用したランダムアクセスのサンプルコードを拡張for文
を使用したシーケンシャルアクセスで書き直すと以下のようになります。import java.util.List; import java.util.ArrayList; public final class TestEnhancedForStatement { public static void main(String[] args) { final List<String> testList = new ArrayList<>(3); testList.add("test1"); testList.add("test2"); testList.add("test3"); for (String value : testList) { // testListに格納された値を先頭から順に取得 System.out.println(value); } } }上記サンプルコードの
拡張for文
の読み方としては、「testList
に格納された値を先頭から順番にString
型の値として取得して変数名value
に格納する」という感じになります。他の Python や Ruby といった高級言語でも同じような文法なので、他の高級言語を既に触れている方すれば特に難しい点はないと思います。そして、上記のサンプルコードを実行した出力結果は以下のようになります。
test1 test2 test3
forEach
メソッドを使用したシーケンシャルアクセスJDK1.8 にて関数型インターフェースの導入に伴い、List コレクションを繰り返し処理で操作する
forEach(Consumer<T>)
メソッドが追加されました。このメソッドは以前私が Map コレクションに関する繰り返し処理に関して書いた記事1 で紹介したforEach(BiConsumer<T,U>)
メソッドによく似た機能になります。拡張for文
よりもさらに簡潔で直感的にシーケンシャルアクセスの処理を書くことができるようになりました。基本的な書き方は以下のようになります。
import java.util.List; import java.util.ArrayList; public final class TestForEach { public static void main(String[] args) { final List<String> testList = new ArrayList<>(3); testList.add("test1"); testList.add("test2"); testList.add("test3"); testList.forEach((value) -> { // testListに格納された値を先頭から順に取得する System.out.println(value); }); } }難しそうに見える点は引数ですが、
forEach()
メソッドの引数には関数型インターフェースであるConsumer<T>
を実装するラムダ式を渡します。Consumer<T>
は 1 つだけ引数を貰ってなにも値を返却しない操作を抽象化した関数型インターフェースです。ラムダ式や関数型インターフェースに関してはこの記事の趣旨とズレるので割愛しますが、List で
forEach()
メソッドを使用する際には上記サンプルコードの書き方を覚えておけば大丈夫です。そして、上記のサンプルコードを実行した出力結果は以下のようになります。
test1 test2 test3注意点
forEach
メソッドは値を返すことができない先に関数型インターフェースの説明でも軽く触れましたが、
Consumer<T>
の特性からforEach
メソッドは値を返すことができません。そのため、List の繰り返し処理中で何らかのエラーを検知した場合は真偽値としてfalse
を返却したいといった場合には拡張for文
の使用を検討してください。
- 投稿日:2020-07-20T16:25:48+09:00
JavaでTODOアプリを制作しよう4 投稿機能の実装
こんにちは。今回はJavaでTODOアプリの投稿機能の部分をやってみたいと思います。
ちなみに今回はバリデーションについては触れませんでしたが、後の記事で登場するので正常処理だけのやり方を紹介します。
TODOアプリ作成リンク集
1: MVCの簡単な説明
2: Spring Initializrで雛形を作ってHello worldしたい
3: MySQLに仮のデータを保存 -> 全取得 -> topに表示する
4: 投稿機能の実装 (今ここ)TodoFormクラスを作成する
まずはユーザーがTODOを登録する際のデータの入れ物を用意したいと思います。今回はそれをTodoFormクラスに実装していきたいと思います。
クラスを作る階層はTodoContorollerと同じで良いかと思います。
java/com/example/todo/TodoForm.java@Data public class TodoForm { private long Id; @NotNull @Size(min = 1, max =30) private String title; @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate deadline; private boolean status; }↑こんな感じで良いでしょう。
アノテーションが使えない場合は・・・
Spring2.3から仕様が変わったみたいでvalidation系のアノテーションを使う為には手動で追加してやる必要があります。
build.gradledependencies { compile group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final' }dependenciesに上記を追加すれば@Sizeとかが使える様になります!
TodoServiceにTODOを保存するロジックを追加する
お次はServiceの編集になります。
java/com/example/todo/TodoService.java@Service @RequiredArgsConstructor public class TodoService { private final TodoRepository todoRepository; // 〜略〜 public void setTodo(TodoForm formData) { TodoEntity todo = new TodoEntity(); todo.setTitle(formData.getTitle()); todo.setDeadline(formData.getDeadline()); todoRepository.save(todo); } }今回は登録するだけなので戻り値はvoidにしています。
引数で渡ってきたTodoForm型のformDataを新しく作成したTodoEntityにセットして保存します。
set~~とかget~~はそれぞれのクラス内で@Dataとつけている為、LomBokが自動的にセッターとゲッターを作り、使用することが出来ています。ここら辺はいきなりLombokを使うと理解しづらいかもしれないので、より深く理解したい方はゲッター・セッターという概念について調べてみると良いでしょう。
最後にRepositoryクラスの関数saveで保存してDBへ保存します!
TodoContorollerからServiceクラスの関数を呼び出す
Serviceクラスの実装が終わったので今度はコントローラから呼び出してみましょう!
@Controller @RequiredArgsConstructor public class TodoController { // ~略~ @PostMapping("/register") public String register(@ModelAttribute TodoForm formData) { todoService.setTodo(formData); return "redirect:/top"; } }ここで注目したいのが@ModelAttributeアノテーションですね!
このアノテーションは引数に使う時とメソッドの直前で使う時とで用途が違います!
今回の使用方法はフロント側から送られてきたデータをTodoForm型にして関数内で使える様にする、というものです。
こちらのサイトに分かりやすい説明があるので参考にしてみてください。
formDataを先ほど作成したsetTodoに送ってやる事でTODOをDBに登録します。
また処理が終わったら/topへ遷移するようになっています。
TOPページの編集
最後にフロント側を編集しましょう!
~略 <body> <!-- 投稿フォーム --> <div class=" w-75 h-auto my-1 mx-auto pt-5"> <p class="pl-5">新しいToDoを作成する</p> <form th:action="@{/register}" th:object="${ToDoForm}" method="POST" class="container d-flex w-auto my-0 mx-auto"> <div class="w-100"> <label class="row"> <span class="col-2 text-center">ToDo名</span> <input type="text" name="title" placeholder="ToDoを30字以内で入力" class="col-9"> </label> <label class="row my-0"> <span class="col-2 text-center">期日</span> <input type="date" id="date" name="deadline" class="col-9 my-0"> </label> </div> <button class="btn btn-primary w-25 col-2 mr-3" type="submit">ToDoの追加</button> </form> </div> </body>th:actionで/registerとマッピングされたコントローラの関数へ登録内容を飛ばすようにしています。
またmethod="POST"となっているのでPOSTかつ/registerな関数という細かい指定をしています。
この考え方はHTTPメソッドの性質を理解する上で非常に重要なのでこちらのサイトを参照すると良いかもしれません。
th:objectは送信されたデータの入れ物を指定するのに使っています。
今回の記事で一番最初につくったTodoFormオブジェクトに送信データをぶち込んでいるわけです。
登録ボタンが押された際に
<input type="text" name="title" placeholder="ToDoを30字以内で入力" class="col-9">name="title"に入力された内容がFormDataクラスの同名の変数(当然 title)に格納されます。
簡単なまとめ
どうでしたでしょうか?簡単なまとめをすると・・・
- フロントからTODOに必要なデータが送信される
- 送信先はth:action~~とmesod=~~によって特定される。送信されたデータは特定のオブジェクトに格納される(今回はFormDataと命名した)
- コントローラからビジネスロジックを記述しているサービスクラスへそのオブジェクトが送られる。
- サービスクラスでTODO登録用のEnitty(データの入れ物)を作成してその中にコントローラから来たオブジェクトの中身を詰める
- Repositoryの関数でそれを保存する。
こんな感じです!
MVCの流れが徐々に理解できたのではないでしょうか?
次回は編集ページについて触れていきたいと思います。それではまた!
- 投稿日:2020-07-20T14:50:20+09:00
Elasticsearch APIをJavaで使ってみた
きっかけ
Elasticsearch APIをJavaで使って実装するにあたって、
基本的な使い方はQiitaにもあったり公式リファレンスにもありますが、
ちょっと複雑化したり、より実践的な記述やコードが記載されている記事が少ないので、本記事を執筆することにしました。この記事の対象となる方
- Elasticsearch APIをJavaで使って開発をこれから行いたい・行う予定の方
- Elasticsearchビギナーの方
環境(開発当時)
- Elasticsearch APIバージョン:7.3.2
- Javaバージョン:8
- Spring Bootバージョン:1.5.15
- mavenバージョン:2.17
準備
pom.xml<dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>7.3.2</version> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>7.3.2</version> </dependency>上記を記載するとElasticsearchのライブラリが使えるようになります。
基本的なライブラリの説明と使い方
※以降に出てくるカラム名やフィールド名やIDなどはすべて実在するものではありません。
SearchRequest
リファレンスの英語を翻訳にかけると
SearchRequestは、ドキュメント、集約、サジェストを検索する操作に使用され、結果として得られるドキュメントのハイライト表示を要求する方法も提供します。
とありますが、APIを使ってElasticsearchに対してリクエストを送るための大本となるものみたいな感じだと思います。
javaSearchRequest searchRequest = new SearchRequest();参考:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/master/java-rest-high-search.html
SearchSourceBuilder
SearchSourceBuilder
は検索パラメータを追加するためのものです。javaSearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();参考:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/master/java-rest-high-search.html
RestHighLevelClient
RestHighLevelClientはそれまで利用されていたTransportClientに代わって推奨されている、RESTクライアント。
使用することで、Javaアプリからhttpを介してElasticsearchへアクセスできます。javafinal CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("user", "password") ); RestHighLevelClient client = new RestHighLevelClient( RestClient.builder(new HttpHost("localhost", 9200, "https")) .setHttpClientConfigCallback(httpAsyncClientBuilder -> httpAsyncClientBuilder .setDefaultCredentialsProvider(credentialsProvider)) ); searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);参考:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/_basic_authentication.html
function score query
function score queryは、functionsセクションに複数の条件(function)を記載でき、それぞれの条件でのスコアを合算した値を使ってソートを行います。
あくまで一例FunctionScoreQueryBuilder functionScoreQueryBuilder = null; ArrayList<FunctionScoreQueryBuilder.FilterFunctionBuilder> functionScoreArrayList = new ArrayList<>(); // 複数addしてもOK filterFunctionList.add( new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("FashionItemId", "1"), ScoreFunctionBuilders.fieldValueFactorFunction("custom_score.").factor(Float.valueOf("0.0254389" )).missing(0.2))); // ArrayList型をFunctionScoreQueryBuilder.FilterFunctionBuilder[]にする FunctionScoreQueryBuilder.FilterFunctionBuilder[] functions = functionScoreArrayList.toArray(new FunctionScoreQueryBuilder.FilterFunctionBuilder[functionScoreArrayList.size()]); functionScoreQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder, functions).scoreMode(FunctionScoreQuery.ScoreMode.SUM).boostMode(CombineFunction.REPLACE); searchSourceBuilder.query(functionScoreQueryBuilder);※FunctionScoreQueryBuilderは本当に参考になる記事が少ないです・・・。
参考:https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html
count
RestHighLevelClientに対して
.count
で指定できます。javaCountResponse countResponse = null; countResponse = restHighLevelClient.count(searchRequest, RequestOptions.DEFAULT);とするとsearchRequestにヒットした件数をCountResponseの形式で取得することができます。
参考:https://www.elastic.co/guide/en/elasticsearch/reference/current/search-count.html
search
RestHighLevelClientに対して
.search
で指定できます。javaSearchResponse searchResponse = null; searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);とするとsearchRequestにヒットした検索結果をSearchResponseの形式で取得することができます。
参考:https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html
_source
Elasticsearchの
_source
はSQLでいうSELECT句みたいなものです。
APIの場合は取得するフィールドをfetchSourceを使って指定します。
取得するフィールドを絞り込むことでデータ量の削減にもつながるので、速度の改善が期待できます。
第一引数には取得するフィールド、第二引数に除外するフィールドを指定します。
前述したcountの場合では指定なしで大丈夫です。javasearchSourceBuilder.fetchSource(new String[]{"FashionItemId", "ItemPrice", "FashionItemSize", "FashionItemLargeCategory", "FashionItemSmallCategory"}, "ExclusionFashionItemId");Sort
SQLでいうOrder Byです。
FieldSortBuilderを使ってソートを指定します。複数カラムでソートの場合return searchSourceBuilder.sort(new FieldSortBuilder("ItemPrice").order(SortOrder.ASC)) .sort(new FieldSortBuilder("FashionItemId").order(SortOrder.DESC)) .sort(new FieldSortBuilder("StartDatetime").order(SortOrder.DESC));from & size
SQLでいうoffset & limitに該当するものです。
前述したSearchSourceBuilderに対して.from
.to
で指定できます。javaSearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.from(0); searchSourceBuilder.size(100);BoolQuery
ほかのクエリ同士を組み合わせるために使います。
AND, OR, NOTを組みあわせる事ができます。
BoolQueryには4種類あります。
クエリ 説明 must SQLでいうANDです。指定された条件によってスコアが計算されます。 filter SQLでいうANDです。mustと違いスコアが計算されません。 should SQLでいうORです。 must not SQLでいうNOTです。 代表的な検索クエリ
termQuery
合致するかどうか。SQLでいう=(イコール)
参考:https://www.elastic.co/guide/en/elasticsearch/reference/7.8/query-dsl-term-query.htmltemrsQuery
合致するものがあるかどうか。SQLでいうIN句
参考:https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/java-term-level-queries.html#java-query-dsl-terms-queryrangeQuery
指定した範囲のものがあるか。SQLでいう >=や<=、<、>のこと
参考:https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/java-term-level-queries.html#java-query-dsl-range-query例(SQL vs ElasticSearch vs Java)
SQLの場合AND StartDatetime >= '2019-12-06 17:33:18' AND ( ( FashionItemLargeCategory <> 1 AND FashionItemSmallCategory NOT IN (10,20,30) AND FashionItemSize IN (1,2) ) OR ( ( FashionItemLargeCategory = 2 OR FashionItemSmallCategory IN (40,50,60) ) AND FashionItemSize IN (9,10) ) )Elasticsearchの場合"bool": { "filter": [ { "range": { "StartDatetime": { "from": null, "to": "2019-12-06 17:33:18", "include_lower": true, "include_upper": false } } }, { "bool": { "filter": [ { "bool": { "should": [ { "bool": { "filter": [ { "terms": { "FashionItemSize ": [ 1, 2 ] } } ], "must_not": [ { "term": { "FashionItemLargeCategory ": { "value": 1, "boost": 1 } } }, { "terms": { "FashionItemSmallCategory ": [ 10, 20, 30 ] } } ] } }, { "bool": { "filter": [ { "terms": { "FashionItemSize": [ 9, 10 ] } } ], "should": [ { "term": { "FashionItemLargeCategory ": { "value": 1 } } }, { "terms": { "FashionItemSmallCategory ": [ 40, 50, 60 ] } } ] } } ] } } ] } } ] }(すごい複雑で見にくい・・・・。)
javaの場合integer[] smallCategories1 = {10, 20, 30}; integer[] itemSize1 = {1, 2}; integer[] smallCategories2 = {40, 50, 60}; integer[] itemSize2 = {9, 10}; BoolQueryBuilder qb1 = boolQuery() .mustNot(termQuery("FashionItemLargeCategory", 1)) .mustNot(termsQuery("FashionItemSmallCategory", smallCategories1)) .filter(termsQuery("FashionItemSize" , itemSize1)); BoolQueryBuilder qb2 = boolQuery() .should(termQuery("FashionItemLargeCategory", 2)) .should(termsQuery("FashionItemSmallCategory", smallCategories2)) .filter(termsQuery("FashionItemSize", itemSize2)); BoolQueryBuilder qb3 = boolQuery() .should(qb1) .should(qb2); BoolQueryBuilder qb4 = boolQuery() .filter(rangeQuery("StartDatetime").from(null).lt("2019-12-06 17:33:18")) .filter(qb3);検証方法
BoolQueryBuilderに対してtoString()してあげることでElasticsearchのクエリを取得することができます。
その結果を想定していたクエリと照らし合わせたり、Kibanaで叩いてみるなどしてクエリが正しいものか確認しましょう。例 (SQL vs ElasticSearch vs Java) のBoolQueryBuilderのクエリを検証する場合System.out.println(qb4.toString());最後に
この記事はあくまでも初めて自分がさわった当時の情報などになります。
当時は記事も少なかったものの現在では調べれば良質な記事が多くなっているかもしれません。
少しでも初めてElasticsearchAPIを触る方の参考になれば幸いです。
- 投稿日:2020-07-20T13:56:14+09:00
【Java入門】文字列の扱いについて(Stringクラス、StringBuilderクラス)
目的
Java言語を含めたプログラミングの学習を始めたばかりの方、既学習者の方は復習用に、
今回は文字列の操作について学ぶために書いています。【Java入門目次】
・変数と型
・型変換
・変数のスコープ
・文字列の操作 ←今ここ
・配列の操作
・演算子
・条件分岐
・繰り返し処理(準備中)
・クラスについて(準備中)
・抽象クラス(準備中)
・インターフェース(準備中)
・カプセル化(準備中)
・モジュールについて(準備中)
・例外処理について
・ラムダ式について
・Stream APIについてStringクラスとは
複数の文字を格納する事が出来るデータ型として提供されているクラスです。(文字列を扱うクラス)
""
で文字列を囲み変数に代入しているのをよく目にするかと思います。String msg = "Hello World!!!"; System.out.println(msg); // Hello World!!!
new String(文字列)
と記述して、明示的に変数に代入する事も出来ます。
また、char型の配列をString型の文字列にする事もできます。String banana = new String("バナナ"); System.out.println(banana); // バナナ // char型の配列を渡してもOK String tomato; char[] chars = {'ト', 'マ', 'ト'}; tomato = new String(chars); System.out.println(tomato); // トマトStringクラスは、初期化されたString型の変数に異なる文字列を再代入すると、元の文字列が書き換わるのではなく、
immutable(不変)なオブジェクトですので、参照先が変わっている(新たに別の領域を確保している)という特徴があります。同じ参照型の配列(ミュータブル)と比べてみてみましょう。
// イミュータブル(不変)の例 String a = "あ"; String b = a; a = "い"; System.out.println(a); // い System.out.println(b); // あ // ミュータブル(可変)の例 int[] x = {1, 2, 3}; int[] y = x; x[0] = 100; for (int xVal : x) { System.out.print(xVal + " "); // 100 2 3 } for (int yVal : y) { System.out.print(yVal + " "); // 100 2 3 }Stringクラスのメソッド紹介
Stringクラスが提供してくれているメソッドはたくさんあります。
構文はString型の変数名.メソッド名();
です。いくつか紹介していきます。
charAt
引数で指定されたインデックスにあるchar型を返してくれるメソッドです。
引数が負の値、文字列の長さと同じか大きい値の場合、例外が発生します。String name = "ケイスケホンダ"; System.out.println(name.charAt(3)); // ケ // 以下は例外が発生します System.out.println(name.charAt(7)); // 例外が発生 System.out.println(name.charAt(10)); // 例外が発生concat
指定された文字列を最後に連結するメソッドです。
String greet = "おはよう"; System.out.println(greet.concat("ございます。")); // おはようございます。equals
文字列と引数で指定されたオブジェクトを比較してbooleanを返してくれるメソッドです。
String name = "ケイスケホンダ"; System.out.println(name.equals("リトルホンダ")); // false System.out.println(name.equals("ケイスケホンダ")); // trueindexOf
引数の文字が最初に出現する位置のインデックスを返してくれるメソッドです。
該当する文字が文字列にない場合は-1を返します。String str = "banana"; System.out.println(str.indexOf('a')); // 1 System.out.println(str.indexOf('z')); // -1length
文字列の長さを返してくれるメソッドです。
String name = "ジェームズ・ハーデン"; System.out.println(name.length()); // 10replace
第一引数の文字を第二引数で指定した文字に置き換え、結果の文字列を返してくれるメソッドです。
String name = "ボボボーボ・ボーボボ"; System.out.println(name.replace("ボ", "バ")); // バババーバ・バーババisEmpty
length()が0の場合にのみtrueを返してくれるメソッドです。
String teruma = "ここにいるよ"; System.out.println(teruma.isEmpty()); // false String none = ""; System.out.println(none.isEmpty()); // trueメソッド数が多すぎて全て紹介しきれないので、その他のメソッドはOracleのStringクラスを参考にしてください。
StringBuilderクラスとは
Stringクラスと同様に文字列を扱うクラスです。
変数に格納した文字列を変更する事が可能です。(元の文字列を書き換える事が出来る)
何故ならStringBuilderクラスはmutable(可変)なオブジェクトだからです。StringBuilder型の文字列を作成するには、配列作成時と同じように、
newキーワード
を使用します。StringBuilder sb = new StringBuilder("Hello World!!!"); System.out.println(sb); // Hello World!!!Stringクラスの様に直接代入しようとするとエラーになります。
エラーStringBuilder name = "田中"; // Type mismatch: cannot convert from String to StringBuilderStringBuilderクラスのメソッド紹介
StringBuilderクラスが提供してくれているメソッドもたくさんあります。
構文はStringクラス同様にStringBuilder型の変数名.メソッド名();
です。いくつか紹介していきます。
append
引数で指定された文字列を現在の文字列に追加するメソッドです。
StringBuilder name = new StringBuilder("田中"); System.out.println(name); // 田中 name.append("マルクス"); System.out.println(name); // 田中マルクス name.append("闘莉王"); System.out.println(name); // 田中マルクス闘莉王insert
引数で指定された文字列を引数で指定された位置にある文字の前に挿入するメソッドです。
StringBuilder member = new StringBuilder("柳沢、巻"); member.insert(3, "玉田..."); // 3なので、"巻"の前が挿入位置となる System.out.println(member); // 柳沢、玉田...巻delete
第一引数から第二引数の位置の1つ前までの位置にある文字を削除するメソッドです。
StringBuilder member = new StringBuilder("クラウド、エアリス、ティファ、バレット"); member.delete(5, 10); // 開始位置5なので「エ」、終了位置は10なので「、」 System.out.println(member); // クラウド、ティファ、バレットreplace
第一引数から第二引数の位置の1つ前までの位置にある文字を、第三引数で指定された文字列に置換するメソッドです。
StringBuilder sb = new StringBuilder("デュフフフ!!"); sb.replace(2, 5, "ラララ"); // 開始位置2なので「フ」から、終了位置5なので「フ」までの文字列を第三引数の文字「ラララ」に置換する System.out.println(sb); // デュラララ!!substring
引数で指定した位置から最後までの部分文字列を返してくれるメソッドです。
StringBuilder oosako = new StringBuilder("大迫半端ないって"); // インデックス2番目から最後までの文字列を返している System.out.println(oosako.substring(2)); // 半端ないってStringBuilderクラスもメソッド数が多いため全て紹介しきれないので、その他のメソッドはOracleのStringBuilderクラスを参考にしてください。
文字列の結合方法まとめ
文字列を結合する方法は複数種類がある事が分かりました。
プラス演算子String str1 = "Hello"; String str2 = "World!!!"; String greet = str1 + str2; System.out.println(greet); // HelloWorld!!!StringクラスのconcatメソッドString str = "Hello"; String greet2 = str.concat("World!!!"); System.out.println(greet2); // HelloWorld!!!StringBuilderクラスのappendメソッドStringBuilder sb = new StringBuilder("Hello"); StringBuilder greet3 = sb.append("World!!!"); System.out.println(greet3); // HelloWorld!!!出力結果は同じですが、それまでの過程が違います。
文字列比較の注意点
では、
==演算子
を用いて各変数を比較してみます。
出力結果が同じですので、trueになると思いきや、// プラス演算子 String str1 = "Hello"; String str2 = "World!!!"; String greet = str1 + str2; System.out.println(greet); // HelloWorld!!! // Stringクラスのconcatメソッド String str = "Hello"; String greet2 = str.concat("World!!!"); System.out.println(greet2); // HelloWorld!!! System.out.println(greet == greet2); // falseHelloWorld!!!という文字列は同じですが、falseとなっています。
==演算子を用いた参照型の比較では、参照先が同じかどうかを比較しているためこのような結果になってしまいます。
また、文字列の宣言方法によっても比較結果が変わってきます。
new String()で文字列を作成した場合、必ず新しい領域にその文字列は生成されます。
それ以外の場合は、同じ文字列が既に存在していれば、既存の文字列を参照します。
よって、==演算子
で比較した場合以下のような結果になります。String str1 = "山田"; String str2 = new String("山田"); String str3 = "山田"; System.out.println(str1 == str2); // false System.out.println(str1 == str3); // trueStringクラスで文字列を比較したい場合は、
equalsメソッド
を用いて比較します。// プラス演算子 String str1 = "Hello"; String str2 = "World!!!"; String greet = str1 + str2; System.out.println(greet); // HelloWorld!!! // Stringクラスのconcatメソッド String str = "Hello"; String greet2 = str.concat("World!!!"); System.out.println(greet2); // HelloWorld!!! // 参照先の比較ではなく、変数の中の文字列を比較している System.out.println(greet.equals(greet2)); // trueちなみに、StringBuilderは既存の文字列が書き換わるため、
sbという変数とgreet3という変数を比較するとtrueが返ってきます。StringBuilder sb = new StringBuilder("Hello"); StringBuilder greet3 = sb.append("World!!!"); System.out.println(greet3); // HelloWorld!!! System.out.println(sb == greet3); // true終わりに
文字列を扱うためのStringクラス、StringBuilderクラスについて学びました。
文字列の比較する際は、意図せぬ処理にならないよう注意しなければいけませんね。
また本記事では紹介する事の出来なかった各クラスのメソッドも、理解して扱えるようにしておきたいですね。参考サイト
- 投稿日:2020-07-20T10:59:33+09:00
Javaのフレームワークを学習する#1(Mac版)
Javaのフレームワークを使って開発
まず初めにこのEclipseの画面を開きます。
黒く塗り潰しているところに、作っていきます。(黒く塗り潰したのは先に作ってしまったので、隠しています...)
次にプロジェクト・エクリプスローラのところで、右クリックをして新規、その他を選択します。
ウェザードを選択の画面が出てきます。ちなみに今回は、Eclipseの2020
年ヴァージョンを使っています。次にSpring スターター・プロジェクトを選択します。
パッケージ名と名前を書きます。(パッケージ名と名前は何でも大丈夫!)
次にSpringスタータ・依存関係に移ります。
Spring Boot DevTools
MyBatis Framework
MySQL Driver
Spring Security
Thymeleaf
Spring web
を選択して完了をクリックしてください。次の画面は、そのまま完了のクリックをしてください。
完了したら、最初の画面のプロジェクト・エクスプローラーに、プロジェクトが出来ましたね。
(下のプロジェクトは、関係無いので無視してくださいね。)Webアプリケーションが起動出来るところまで行きたいので、その前にいくつかの設定をしなければならないので、説明を踏まえて進めて行きます。
まずは、MySQLとの接続情報をアプリケーションサーバーのTomcatの方に教えてあげないといけないので、Springスタータ・プロジェクトのプロジェクトを作った時点で、すでにソースメインに、リソースイズの配下にアプリケーションプロパティというプロパティファイルがあります。
こちらの方にデータベースとの接続設定を記述します。(コードは覚えとく方が良い)
設定は、ご自身の環境設定に合わせて、コードを書いてください。Springセキュリティの設定もしないといけないので、設定を行わないといけないです。
Javaクラスで設定する事が出来るので、その方法を使います。
新たにクラスを作るにあたって、クラス名はお好みで決めてあげてください。
名前を記入したら、スーパークラスの参照をクリックしてください。
Springの組み込みのウェブセキュリティー、コンフィギアアダプタを継承します。
クリックで選択して、OKをクリックしてください。そのまま完了をクリックしてください。
コードに関しては、『こうだ!』という具体的な説明が難しい為に、書きながら覚えて頂く方が良い。
まずは、コンフィギレーションを行うクラスは、イネーブルウェブセキュリティというテーションを付けます。
スーパークラスのウェブセキュリティコンフィギアアダプタから、コンフィギアメソッドをオーバーロードをするんですけど、コンフィギアメソッドは、何個かあり用途があるのですが、今回は起動させたいだけなので、オーセンテケーションマネージャービルダーという引数を取る、コンフィギアメソッドとHTTPセキュリティというオブジェクトを引数に取るコンフュギアメソッドの2つをオーバーライドします。
これで出来ました。
オーセンテケーションマネージャビルダーというオブジェクトを引数に取る、コンフュギアメソッドはユーザーの認識方式を決定する為のメソッドと考えて頂いたら、問題無いです。
DB(データーベース)を使ったユーザー情報を管理とLDAP(Lightweight Directory Access Protocol)を使った認証方式があるのですが、今回はデータベースの方を使います。
簡単に動作させたいだけなので、インメモリーの方で設定していきます。これが出来たら、次にここから新規で、その他でHTMLファイルを作ってください。
(上手く載せる事が出来なかったので、省略して載せますHTMLファイル名はindex.htmlと付けてください。)このように出来ていたらOK
次にコントローラクラスを作ってください。
このようにクラス名を付けて作れていたらOk
(先に作っていたので、間違えないように)コードもこのように書いたら、とりあえずはWebアプリケーションを起動する準備は整いました。
次にSpring bootを使ってアプリケーションを起動させます。
Tomcatが起動しましたね
次にブラウザ上で見てみます。
ここに先ほど決めたパスワードを入力していただくと
このように開きます。
Spring boot と Springセキュリティを絡めて作る事が出来ましたね。
このように書いたのですが、(少し違うかも知れないのですが)参考にして頂いた動画があるので、こちらを見ながら作っていただくと分かりやすいと思います。
YouTube
TechShare チャンネル
- 投稿日:2020-07-20T10:48:51+09:00
convertDateTimeでCannot format given Object as a Dateとなった時の対応方法
- 環境
- Windows10 64bit バージョン1909
- penjdk 11 2018-09-25
- Eclipse IDE for Enterprise Java Developers Version: 2020-03 (4.15.0)
2020-07-20 09:49:54:387 X-TrackingId:6079d04e-6554-492e-b2be-e01eed06 ERROR MyExceptionHandler.handle:63 Cannot format given Object as a Date java.lang.IllegalArgumentException: Cannot format given Object as a Date at java.base/java.text.DateFormat.format(DateFormat.java:338) at java.base/java.text.Format.format(Format.java:158) at javax.faces.convert.DateTimeConverter$FormatWrapper.format(DateTimeConverter.java:495) at javax.faces.convert.DateTimeConverter$FormatWrapper.access$300(DateTimeConverter.java:470) at javax.faces.convert.DateTimeConverter.getAsString(DateTimeConverter.java:544) at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getFormattedValue(HtmlBasicRenderer.java:491) at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getFormattedValue(HtmlBasicRenderer.java:509) at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue(HtmlBasicRenderer.java:330) at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd(HtmlBasicRenderer.java:143) at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:595) at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1654) at com.sun.faces.facelets.component.RepeatRenderer.encodeChildren(RepeatRenderer.java:64) at com.sun.faces.facelets.component.UIRepeat.process(UIRepeat.java:559) at com.sun.faces.facelets.component.UIRepeat.encodeChildren(UIRepeat.java:1068) at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:278) at com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:90) at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:566) at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1647) at javax.faces.render.Renderer.encodeChildren(Renderer.java:152) at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:566)<h:outputText value="#{bean.myTimestamp}" > <f:convertDateTime pattern="yyyy/MM/dd HH:mm" timeZone="Japan" /> </h:outputText>原因 : 変換対象の日付がLocalDateTimeだから
bean.setMyTimestamp(LocalDateTime.now());対応案1 : 独自のConverterを作成する
java - f:convertDateTime support for Java8 LocalDate / LocalDateTime? - Stack Overflow
対応案2 : LocalDateTimeをDateに変換して使う
Java8 LocalDateTimeと旧Dateの相互変換 - 気軽に楽しくプログラムと遊ぶ
対応案3 : LocalDateTimeを使うのをやめる
古い世界に閉じこもる
- 投稿日:2020-07-20T10:48:51+09:00
JSFのconvertDateTimeでCannot format given Object as a Dateとなった時の対応方法
- 環境
- Windows10 64bit バージョン1909
- penjdk 11 2018-09-25
- Eclipse IDE for Enterprise Java Developers Version: 2020-03 (4.15.0)
- JSF 2.3.9
2020-07-20 09:49:54:387 X-TrackingId:6079d04e-6554-492e-b2be-e01eed06 ERROR MyExceptionHandler.handle:63 Cannot format given Object as a Date java.lang.IllegalArgumentException: Cannot format given Object as a Date at java.base/java.text.DateFormat.format(DateFormat.java:338) at java.base/java.text.Format.format(Format.java:158) at javax.faces.convert.DateTimeConverter$FormatWrapper.format(DateTimeConverter.java:495) at javax.faces.convert.DateTimeConverter$FormatWrapper.access$300(DateTimeConverter.java:470) at javax.faces.convert.DateTimeConverter.getAsString(DateTimeConverter.java:544) at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getFormattedValue(HtmlBasicRenderer.java:491) at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getFormattedValue(HtmlBasicRenderer.java:509) at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue(HtmlBasicRenderer.java:330) at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd(HtmlBasicRenderer.java:143) at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:595) at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1654) at com.sun.faces.facelets.component.RepeatRenderer.encodeChildren(RepeatRenderer.java:64) at com.sun.faces.facelets.component.UIRepeat.process(UIRepeat.java:559) at com.sun.faces.facelets.component.UIRepeat.encodeChildren(UIRepeat.java:1068) at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:278) at com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:90) at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:566) at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1647) at javax.faces.render.Renderer.encodeChildren(Renderer.java:152) at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:566)<h:outputText value="#{bean.myTimestamp}" > <f:convertDateTime pattern="yyyy/MM/dd HH:mm" timeZone="Japan" /> </h:outputText>原因 : 変換対象の日付がLocalDateTimeだから
bean.setMyTimestamp(LocalDateTime.now());対応 : typeに「localDateTime」を指定する
Specifies what contents the string value will be formatted to include, or parsed expecting. Valid values are "date", "time", "both", "localDate", "localDateTime", "localTime", "offsetTime", "offsetDateTime", and "zonedDateTime".
convertDateTime (JSF 2.3 View Declaration Language: Facelets Variant, generated with VDLDoc.)こうするとうまくいく<h:outputText value="#{bean.myTimestamp}" > <f:convertDateTime pattern="yyyy/MM/dd HH:mm" timeZone="Japan" type="localDateTime" /> </h:outputText>他の対応
やってないけどいつかのために
- 独自のConverterを作成する
- LocalDateTimeをDateに変換して使う
- LocalDateTimeを使うのをやめる(古い世界に閉じこもる)
- 投稿日:2020-07-20T06:12:46+09:00
基礎Webアプリケーション作成 Servlet/JSP(ログイン機能)
はじめに
ServletとJSPで掲示板のような投稿ができるようなWEBアプリケーションを作成します。
まずはJDBC,DB,Mysqlは使用せずに作成していきます。実行環境
- Eclipse4.16
- Tomcat9
- Java11
機能について
今回実装する機能について考えます。
掲示板のようなアプリを作成するので以下のような機能を作成します。
- 1.ログイン機能
- 2.ログアウト機能
- 3.投稿機能
- 4.投稿閲覧機能
今回はSevletとJSPを使用してWebアプリケーションの作成に慣れることを目標にするため簡単な構成にします。
ログイン機能作成
この記事ではログイン機能を作成します。
1. ユーザーのModel作成
ログイン機能にはユーザーが必要なのでユーザーモデルを作成します。
作成するユーザーモデルは名前とパスワードの二つの情報を持っていることとします。User.javapackage model; import java.io.Serializable; public class User implements Serializable { private String name; private String password; public User() {} public User(String name,String password) { this.name = name; this.password = password; } public String getName(){ // 1. ユーザー名取得 return this.name; } public String getPassword(){ // 2. パスワード取得 return this.password; } }メンバ変数name(ユーザー名)とpassword(パスワード)は他クラスから参照できないようにアクセス修飾子はprivateにします。
またコンストラクタでセットした値を取得するための1と2のメソッドを作成します。2.トップページのView作成
モデルは作成したのでユーザーがログインするためのビューをJSPで作成します。
まずは簡単なビューを作成します。top-page.jsp<form action="./login" method="post"> <div> <label>ユーザー名</label> <input type="text" name="userName"> </div> <div> <label>パスワード</label> <input type="text" name="userPassword"> </div> <div> <input type="submit" value="登録"> </div> </form>3.ログインに関する処理を行うModel作成
LoginProcess.javapackage model; public class LoginProcess { public boolean execute(User user) { if (user.getPassword().equals("hoge")) { return true; } return false; } }登録実行されたパスワードが
hoge
だった場合trueを返すログイン処理。4.ログインに関するリクエストを処理するコントローラー作成
login.javaprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); // 1 String name = request.getParameter("userName"); String password = request.getParameter("userPassword"); User user = new User(name,password); // 2 LoginProcess loginProcess = new LoginProcess(); boolean isLogin = loginProcess.isLogin(user); // 3 if(isLogin) { request.getSession().setAttribute("loginUser",user); // 4 } request.getRequestDispatcher("/WEB-INF/view/login-result.jsp").forward(request, response); // 5 }
- リクエストパラメータを取得
- 取得したパラメータをUserクラスのnameとpasswordをセットする
- パスワードが正しいかの処理をし真偽値を初期化
- セッションスコープにUserクラスの情報をセット
- ログイン結果を表すビュー
login-result.jsp
にフォワード5.ログイン結果を表すビューの作成
セッションスコープで指定した
loginUser
をもとにjspを作成します。
※今回はJSTLを使用します。login-result.jsp<c:choose> <c:when test="${loginUser != null}"> // 1 <p>ログインが完了しました。</p> <p>こんにちは!<c:out value="${loginUser.name}"/>さん</p> //2 </c:when> <c:otherwise> // 3 <p>ログインに失敗しました。</p> <p>もう一度ログインをやり直してください。</p> <a href="./login">ユーザーログイン</a> //4 </c:otherwise> </c:choose>
loginUser
がnullではない場合(パスワードの入力値が"hoge"である場合)loginUser.name
でUserクラスのnameを出力- 1以外の条件の場合
- ユーザー登録画面のリンク
6.実行
ユーザーログイン画面でパスワードに
hoge
かそれ以外かを入力してログイン結果がしっかり表示されれが成功です。最後に
次回は投稿画面でのログインしているかしていないかの確認やしていない場合の処理について行います。
- 投稿日:2020-07-20T00:40:04+09:00
TDD勉強#5(July 18th, 2020)
- 投稿日:2020-07-20T00:38:57+09:00
Java初心者が感覚だけに頼るのをやめていく
更新履歴
- 2020年 07月21日 データ型は細部まで記憶する必要はない!
追加追加- 2020年07月20日
タグ修正タグ修正
変更前 #Java #初心者 #プログラミング初心者
変更後 Java 初心者 プログラミング初心者
@shiracamusさん編集リクエストありがとうございましたm(_ _"m)|- 2020年07月19日 前書き/命名規則/型推論
追加追加記事を書くことになったきっかけ(前書き)
何かアウトプットしてみたい、うまく伝えられる力をつけたい!
文章力を鍛えたい!などなど様々な理由で書こうと考えました。(本当はLGTMを沢山もらいたいなんて言えない)
是非いいと思ったら押して帰ってください♪
※所々取り消し線が入っています。
私が記事を見やすいように後から修正したりしているだけなのでお気になさらずにm(_ _"m)こちらの記事では私が「へ~そうだったのか」的な感じで再度理解した知識のみを記載していきます。
基礎を網羅したい方を対象とした記事ではないため、ご注意ください。
また、間違いがあれば指摘いただけると幸いですm(_ _"m)今回参考させていただいたリンクも本記事の最後に載せています。
もっと詳しく知りたい!って方はそちらを参考にしてください。
結論
から言いますと、
私が基礎を学び直すきっかけは1つの赤っ恥をかいたことから生まれました♪以下暇な方は読んでください!(^^)!
私がプログラミングを始めたのは1年前の4月!
完全未経験、htmlのテーブルの仕組みさえも分からなかったドドド素人の頃でした(゚Д゚)
研修では周りよりもできず。。最初の現場は結合テストで、実際にコーディングをやらせてもらったのは今年の1月中旬くらいでしょうか。
基礎知識も中途半端なまま現場へと駆り出され右も左も分からないので先輩には両手じゃ収まらないくらい迷惑をかけたました(~~;)
そんな私ですが、教えられながら完全に感覚でコーディングを行い約半年、先輩から「ちびまろが良ければ新人の教育を手伝ってみない?」という話が舞い降りてきました\(^^)/
(自信はなかったがOUTPUTもかねて経験してみたかった(* ̄▽ ̄)フフフッ♪)というわけでいざ教えてみたところ、聞かれたことに対してうまく伝えられない + 理解していたつもりで分からない箇所が多々あったということに気がつきました。
私が理解していることを説明してもうまく伝わらない。挙句の果てに後輩の前で先輩に間違いを指摘されるという赤っ恥をかいてしまいました(_ _。)・・・シュン目次
ここから本題に入っていきます!
クラス名・変数の命名規則に使われる記法
記法名 記法例 ルール アッパーキャメルケース SampleWord 先頭は大文字で書き始める。複数単語の場合は単語の先頭のみ大文字。 ローワーキャメルケース sampleWord 先頭は小文字で書き始める。複数単語の場合は2つ目の単語の先頭を大文字。 スネークケース sample_word 全て小文字で書く。大文字を使わない代わりに単語の繋ぎ目をアンダースコアを使う。 パスカルケース SampleWord アッパーキャメルケースと同じ。一般的にキャメルケース=ローワーキャメルケースと考える人が多いため、アッパーキャメルケースはパスカルケースと呼ばれる。 Javaには「型推論」という物(仕様)があった!
Java 10以降ではvarキーワードを利用することで、変数を宣言する際にデータ型を省略可能になった。
ん?What do you mean?
詳しく調べてみると下記のようなことだと分かりました!//型推論を使用しない場合の変数の初期化 Stiring sampleWord = "Hello Wordl!"; /*文字列型*/ int sampleNum = 10; /* 数値型 */ //型推論を使用した場合の変数の初期化 var sampleWord = "Hello World!"; System.out.println(samoleWord); //結果 Hello World! var sampleNum = 10; System.out.println(samoleNum); //結果 10なんとびっくり!!Σ(゚Д゚)
Stringやintのデータ型を指定しなくてもJava側(コンパイラー)が初期値から自動で何の型なのかを推論して自動的に型を指定(型推論)してくれるらしいです!
でもここで注意していただきたいのが、「1度型を指定した物は別の型で置き換えることができない」ということです。
というのも下記を見ていただければ分かります。上記の例で見ると「Hello World!」が代入されている変数の型は推論された結果、Stringです。変数「sampleWord」の方は宣言時にStringにと決定されているためint型の値を代入することはできません。var sampleWord = "Hello World!"; sampleWord = 10; //結果 コンパイルエラーその他varを使用する際には以下注意点があります。
- 初期値は省略不可(初期値から型を推論するため)
- 複数の変数を列挙できない(列挙とは※1のようなこと)
- 使用できるのはローカル変数だけ
※1 var sampleNum = 1, sampleNum2 = 2;データ型は細部まで記憶する必要はない!
データ型には大きく分けて2つあります。
- プリミティブ型(基本型)
- 参照型
まずはプリミティブ型から軽い説明に入りますと
私は整数型と浮動小数点型の~バイトまでは覚えない!!
詳しく説明すると、整数型はそれぞれ~桁までという決まりがあります。
1バイト = 半角英数字1文字
2バイト = 日本語1文字・5 = 1バイト ・k = 1バイト ・あ = 2バイト ・縦 = 2バイト整数型
byteはあくまでバイト単位。
shortよりもintの方が大きい数を代入できる。
intよりもlongが大きい数を代入できる。くらいです(笑)細かい桁数はググればすぐヒットします。(私の場合整数型ならintしか使ったことありまへん)
浮動小数点も整数型と同様です。
floatよりもdoubleの方が大きい数が代入できます。特に完全に暗記しなくてもある程度の仕組みや存在さえ知っていればググればすぐでます。
ネットって便利ですね(* ̄▽ ̄)フフフッ♪参照型 ⇒ インターフェイスに関しては後日継承とインターフェイスの章で記載致します。
参考サイト