20210726のJavaに関する記事は8件です。

Java基礎(データ型)

データ型について データ型とは データ型とは、プログラムで扱うことができるデータの種類のこと。データ型またはただ単に型ともいう。Javaでは多くの型があります。 代表的なデータの型 分類 型名 格納できるデータ 整数 byte とても小さい整数 short 小さな整数 int 普通の整数 long 大きな整数 小数 float 少しあいまいでもよい小数 double 普通の小数 真偽値 boolean trueかfalse 文字 char 1文字 文字列 String 文字列 使うときの注意点 整数 基本的には、整数の場合「int」小数の場合は「double」が多く使われてる気がします。プロジェクトによると思いますが。。 代入できる範囲は「byte」の場合は、±約128。「shot」の場合は、±約3.2万。「int」の場合は、±約21億。「long」の場合は、±約900京になります。小さい型に大きい値を格納する場合は、エラーになりますが大きい型に、小さい値を格納する場合はエラーにならないのでそこは気を付けてください。後は、メモリ容量も気を付けたほうがいいです。小さい数字を扱うだけにlongを使用していたらメモリ容量を使うのでコードレビュー時に変更してとお叱りをいただくと思いますw 小数 doubleの方がfloatより多くのメモリを消費しますが、厳密な計算を行うことができるのがdoubleです。なので小数を扱いたい場合には、特別な事情がない限りはdoubleを使用した方がいいと思います。 小数は真に厳密な計算ができないという弱点があります。計算を行った際にわずかな誤差が生じることがあります。誤差は、小さいですが積み重なると大きな問題が発生したりしますので、金額の計算や誤差が許されない計算には「float」、「double」を使用してはいけないです。 真偽値 真偽値は、その名の通り真か偽かを判定する型です。二者択一の情報を代入するための型とも言えます。代入できる値は「true」または「false」のどちらかしか入りません。「true」は肯定的な情報、「false」は否定的な情報を意味するものです。 とりあえず以上となります。インプットしたら都度都度更新していきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JPAという便利なものがあるらしいVer2

前回まで JPA(Java Persistence API)を利用して用意されている複数のSelect文を実行した 全件検索→findAll()メソッド 主キー検索→getOne()メソッド findBy[検索条件]→findByに続くフィールド名、キーワードに応じた条件検索を実行 本日行うこと JPAを用いて登録、更新、削除処理を行う レコードを登録、または更新する→save()メソッド 指定された主キーに該当するレコードを削除する→deleteById()メソッド 登録処理 ①新規登録画面へ遷移、値の入力 ②登録ボタン押下後 ③確認用(全検索処理実行) ※従業員番号:10、従業員名:test、部署番号:1のデータが新規登録されている。 更新処理 ①更新内容入力画面へ遷移時 ②入力内容を変更(従業員名をtest2に変更) ③更新ボタンを押下後 ④確認用(全検索処理実行) ※従業員番号:10のデータにおいて従業員名がtest2に更新されている。 更新処理 ①削除画面に遷移、値の入力(従業員番号10のデータを削除する) ②削除ボタン押下後 ※従業員番号:10のデータが削除されている。 Controller package jp.co.ttt.employee.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import jp.co.ttt.employee.entity.Employee; import jp.co.ttt.employee.form.EmployeeForm; import jp.co.ttt.employee.repository.EmployeeRepository; @Controller public class EmployeeController { @Autowired EmployeeRepository repository; // 全件検索 @RequestMapping(path = "/findAll") public String showemployeeList(Model model) { model.addAttribute("employees", repository.findAll()); return "employee/employee_list"; } // 主キー(従業員番号)検索 @RequestMapping(path = "/getOne/{id}") public String showEmployee(@PathVariable int id, Model model) { model.addAttribute("employees", repository.getOne(id)); return "employee/employee_list"; } // 部署番号検索を行う @RequestMapping(path = "/findByDeptNo/{deptNo}") public String showEmpoloyeeListByDeptNo(@PathVariable int deptNo, Model model) { System.out.println(repository.findByDeptNo(deptNo)); model.addAttribute("employees", repository.findByDeptNo(deptNo)); return "employee/employee_list"; } // 従業員名&部署番号検索を行う @RequestMapping(path = "/findByNameAndDeptNo/{name}/{deptNo}") public String showEmpoloyeeListByNameAndDeptNo(@PathVariable String name, @PathVariable int deptNo, Model model) { model.addAttribute("employees", repository.findByNameAndDeptNo(name, deptNo)); return "employee/employee_list"; } // 従業員名部分検索を行う @RequestMapping(path = "/findByNameLike/{name}") public String showEmployeeListNameLike(@PathVariable String name, Model model) { model.addAttribute("employees", repository.findByNameLike("%" + name + "%")); return "employee/employee_list"; } // 新規登録画面に遷移する @RequestMapping(path = "/create/input") public String createInput() { return "employee/create_input"; } // 従業員を登録する @RequestMapping(path = "/create/complete", method = RequestMethod.POST) public String createComplete(EmployeeForm form) { Employee employee = new Employee(); employee.setId(form.getId()); employee.setName(form.getName()); employee.setDeptNo(form.getDeptNo()); repository.save(employee); return "redirect:/getOne/" + employee.getId(); } // 更新内容入力画面に遷移する @RequestMapping(path = "/update/input/{id}") public String updateInput(@PathVariable int id, Model model) { model.addAttribute("employee", repository.getOne(id)); return "employee/update_input"; } // 従業員情報を更新する @RequestMapping(path = "/update/complete/{id}", method = RequestMethod.POST) public String updateComplete(@PathVariable int id, EmployeeForm form) { Employee employee = repository.getOne(id); employee.setId(form.getId()); employee.setName(form.getName()); employee.setDeptNo(form.getDeptNo()); repository.save(employee); return "redirect:/getOne/" + employee.getId(); } // 削除画面に遷移する @RequestMapping(path = "/delete/input") public String deleteInput() { return "employee/delete_input"; } // 従業員を削除する @RequestMapping(path = "/delete/complete", method = RequestMethod.POST) public String deleteComplete(EmployeeForm form) { repository.deleteById(form.getId()); return "redirect:/findAll"; } } 検索処理は前回と同様 登録、更新、削除処理において、入力画面遷移(新規登録入力画面など)と実行後の遷移の2パターンずつそれぞれメソッドを実装 Form package jp.co.ttt.employee.form; import lombok.Data; @Data public class EmployeeForm { private Integer id; private String name; private Integer deptNo; } 画面の入力情報をコントローラーに渡すためのクラス 従業員番号、従業員名、部署番号のフィールドとgetter, setter(Lombokで対応) HTML(登録入力画面) <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <div>商品の新規登録</div> <form method="post" th:action="@{/create/complete}"> 従業員ID:<input type="number" name="id"> 従業員名:<input type="text" name="name"> 部署番号:<input type="number" name="deptNo"> <input type="submit" value="登録する"> </form> </body> </html> 新規登録情報を入力する画面 登録ボタン押下後は、登録した1件のデータを再度表示 HTML(更新入力画面) <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <div>商品の編集</div> <form method="post" th:action="@{/update/complete/} + ${employee.id}"> 従業員ID:<input type="number" name="id" th:value="${employee.id}"> 従業員名:<input type="text" name="name" th:value="${employee.name}"> 部署番号:<input type="number" name="deptNo" th:value="${employee.deptNo}"> <input type="submit" value="更新する"> </form> </body> </html> 更新情報を入力する画面 画面遷移時に既に登録されている元のデータ入力されている状態で遷移 データを更新、更新ボタンを押下すると、更新された状態の1件のデータを表示 HTML(削除入力画面) <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <div>商品の削除</div> <form method="post" th:action="@{/delete/complete}"> 従業員番号:<input type="text" name="id"> <input type="submit" value="削除する"> </form> </body> </html> 削除したいデータを削除する画面 削除したい従業員番号を入力し、削除ボタンを押下するとデータが削除され全件検索画面を表示 ソースコード ふりかえり 登録処理と更新処理のメソッドが同じなのが驚きだった。(てっきり別のメソッドだと感じていたので…) JPAを用いると最低限のCRUD処理を実装する程度なら問題ない印象を受けた
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java 11 LTSから Java 17 LTSへ

これはなんですか 社内では現状みんなの共通言語として Java11 LTS を利用しているが、2021/9 にリリースされる Java17 LTS に乗り移るかどうか判断するため、各種 feature のまとめと、社内での議論のネタを積んでおくものです。 誰向け? 同様のことを検討しているお歴々には参考になるかもしれません。 後日コメントで弊社の結論も記載します。 Java 11 ~ 17 12~16までは @nowokay 様がまとめてくださっているため、そちらを参照します。 feature/java12 feature/java13 feature/java14 feature/java15 feature/java16 feature/java17 JEP 415: Context-Specific Deserialization Filters シリアライズ関連の変更(簡単になるよ的な) JEP 414: Vector API (Second Incubator) ベクトル計算のAPIが導入されるけど我々あんまり使わないと思う・・ JEP 412: Foreign Function & Memory API (Incubator) 内部制御に関するもの JEP 411: Deprecate the Security Manager for Removal 消えるry JEP 410: Remove the Experimental AOT and JIT Compiler 消える JEP 409: Sealed Classes https://openjdk.java.net/jeps/409 これは文法とクラス、インターフェースの制御に関する重要な変更 JEP 407: Remove RMI Activation 消える JEP 406: Pattern Matching for switch (Preview) https://openjdk.java.net/jeps/406 型による分岐が可能に static String formatterPatternSwitch(Object o) { return switch (o) { case Integer i -> String.format("int %d", i); case Long l -> String.format("long %d", l); case Double d -> String.format("double %f", d); case String s -> String.format("String %s", s); default -> o.toString(); }; } JEP 403: Strongly Encapsulate JDK Internals セキュリティ関連 JEP 398: Deprecate the Applet API for Removal ええ・・まだいたんすか・・・ JEP 391: macOS/AArch64 Port M1 Macのネイティヴサポート! JEP 382: New macOS Rendering Pipeline あんまり関係ない JEP 356: Enhanced Pseudo-Random Number Generators 乱数生成に関するもの。もしかしたらエントロピー枯渇を気にしなくてよくなる? JEP 306: Restore Always-Strict Floating-Point Semantics 浮動小数点に関する変更 これも普通に使っていればあまり意識しないでよい Javaのデメリット メモリ食いすぎる 某正規空母みたいな奴 fat jar 化しやすくビルドが長引く jarやjdkのスリム化を行うのが面倒でカスタマイズに知識が必要 他のフレームワーク python いろいろあるみたいだけどよくわからない go シンプルで速いともっぱらのうわさ 個人的にはC言語が最初の言語である私にはとっつきやすそう node 書いてくと複雑化しやすいから大規模開発ではやりたくない
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring boot + JPA + Flyway on herokuでLINE Botを作る

概要 Spring BootとJPAとFlywayとherokuを使って、DBから返信メッセージを取得するLINE Botを作る。 あくまでちょっと動かしてみた程度のソースなので、細かいところのツッコミはご容赦ください。 また以下については説明しないので、リンク先を参照してください。 LINE Channelの作成 https://developers.line.biz/ja/docs/messaging-api/getting-started/ herokuでアプリを作成 https://signup.heroku.com/ JPA (Java Persistence API) => Java標準のORM https://www.vogella.com/tutorials/JavaPersistenceAPI/article.html Flyway => Migrationツール https://flywaydb.org/ 関連 line-bot-sdk-javaを使ってSpring bootでLINE Botを作る 前提 lineが配布しているサンプルコードを使う。 https://github.com/line/line-bot-sdk-java.git 要件 LINEでbot宛にメッセージを送信すると、その送信文字列に応じた返信メッセージをDBのTableから抽出し、返信するシステムを作る。返信の条件は以下の通り。 送信Message 返信Message ハロー ハローワールド hello Hello world!! それ以外 返信しない アーキテクチャ heroku PostgreSQL 13.3 (on heroku) ディレクトリ構成 サンプルコードの中で、今回編集、新規追加するファイルだけを以下に記した。 . ├ ─ ─ Procfile └ ─ ─ sample-spring-boot-kitchensink    ├ ─ ─ build.gradle    └ ─ ─ src    └ ─ ─ main    ├ ─ ─ java    │    └ ─ ─ com    │    └ ─ ─ example    │    └ ─ ─ bot    │    └ ─ ─ spring    │    ├ ─ ─ KitchenSinkController.java    │    ├ ─ ─ models    │    │    └ ─ ─ TextMessageModel.java    │    ├ ─ ─ repositories    │    │    └ ─ ─ TextMessageRepository.java    │    └ ─ ─ services    │    └ ─ ─ TextMessageService.java    └ ─ ─ resources    ├ ─ ─ application.yml    └ ─ ─ db       └ ─ ─ migration       └ ─ ─ V1_0__create_table_text_messages.sql サンプルコードの中のsample-spring-boot-kitchensinkを利用するので、以下3つのディレクトリは消してしまってもOK。 sample-manage-audience sample-spring-boot-echo sample-spring-boot-echo-kotlin DB Table text_messagesという返信用メッセージを入れるテーブルを作成する。Flywayでmigrateするので、手動での作成は不要。 line-bot-sample::DATABASE=> \d text_messages; Table "public.text_messages" Column | Type | Collation | Nullable | Default ---------+------------------------+-----------+----------+------------------------------------------- id | integer | | not null | nextval('text_messages_id_seq'::regclass) message | character varying(255) | | not null | Indexes: "text_messages_pkey" PRIMARY KEY, btree (id) GitHUB 完成品はこちら https://github.com/Esfahan/line-bot-java-sample 作成手順 サンプルコードの取得 lineが配布しているサンプルコードを使う。 $ git clone https://github.com/line/line-bot-sdk-java.git JPAとflywayを利用できるようにする sample-spring-boot-kitchensink/build.gradleに以下を追記する。 sample-spring-boot-kitchensink/build.gradle dependencies { implementation project(':line-bot-spring-boot') implementation "org.springframework.boot:spring-boot-starter-web" implementation 'com.google.guava:guava' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.flywaydb:flyway-core' + runtimeOnly 'org.postgresql:postgresql' } application.ymlを作成 sample-spring-boot-kitchensink/src/main/resources/application.ymlを新規作成する。 環境変数の値については後述する。 line.bot: channel-token: ${CHANNEL_TOKEN} channel-secret: ${CHANNEL_SECRET} handler.path: /callback spring: jpa: database: POSTGRESQL open-in-view: True datasource: url: ${DATABASE_JDBC_URL} username: ${DATABASE_USER} password: ${DATABASE_PASSWORD} driver-class-name: org.postgresql.Driver connectionProperties: useUnicode=true;characterEncoding=utf-8; flyway: locations: classpath:/db/migration baseline-on-migrate: true その他のプロパティは以下を参照。 https://spring.pleiades.io/spring-boot/docs/current/reference/html/application-properties.html Migrationファイルを作成 マイグレーションファイルの命名規則は以下を参照。 https://garafu.blogspot.com/2020/06/how-to-use-flyway.html 今回は以下の名前で作成する。 V1_0__create_table_text_messages.sql application.ymlのflyway.locationsで定義したように、resources/db/migration/にsqlファイルを設置するとmigrationが実行されるので、ディレクトリを作ってsqlファイルを作成する。 $ mkdir -p sample-spring-boot-kitchensink/src/main/resources/db/migration/ $ vi sample-spring-boot-kitchensink/src/main/resources/db/migration/V1_0__create_table_text_messages.sql V1_0__create_table_text_messages.sql CREATE TABLE text_messages( id serial PRIMARY KEY, message varchar(255) NOT NULL ); ディレクトリ作成 各ソースコードを設置するmodels/,repositories/,services/ディレクトリを作成する。 $ mkdir sample-spring-boot-kitchensink/src/main/java/com/example/bot/spring/{models,repositories,services} Modelの作成 以下のファイルを新規作成する。 sample-spring-boot-kitchensink/src/main/java/com/example/bot/spring/models/TextMessageModel.java TextMessageModel.java package com.example.bot.spring.models; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="text_messages") public class TextMessageModel { @Id private Integer id; public String message; } Repositoryの作成 以下のファイルを新規作成する。 sample-spring-boot-kitchensink/src/main/java/com/example/bot/spring/repositories/TextMessageRepository.java TextMessageRepository.java package com.example.bot.spring.repositories; import com.example.bot.spring.models.TextMessageModel; import org.springframework.data.jpa.repository.JpaRepository; public interface TextMessageRepository extends JpaRepository<TextMessageModel, Integer> { } Serviceの作成 以下のファイルを新規作成する。 sample-spring-boot-kitchensink/src/main/java/com/example/bot/spring/services/TextMessageService.java TextMessageService.java package com.example.bot.spring.services; import com.example.bot.spring.repositories.TextMessageRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Slf4j @Service public class TextMessageService { private String replyTextMessage; private boolean replyFlg = false; @Autowired TextMessageRepository textMessageRepository; public void fetchReplyTextMessage (String message) { log.info("fetchReplyTextMessage is called: {}", message); Integer id = replyId(message); if (id > 0) { Optional<TextMessageModel> textMessage = textMessageRepository.findById(id); if (textMessage.isPresent()) { this.replyTextMessage = textMessage.get().message; this.replyFlg = true; } } } public String getReplyMessage() { return this.replyTextMessage; } public boolean getReplyFlg() { return this.replyFlg; } private Integer replyId(String message) { switch (message) { case "ハロー": { return 1; } case "hello": { return 2; } default: { return 0; } } } } controller 以下のファイルを編集する。 sample-spring-boot-kitchensink/src/main/java/com/example/bot/spring/KitchenSinkController.java ここでは、処理内容をシンプルに分かりやすくするため、handleTextMessageEventメソッド1つで全ての処理するように書き換える。 KitchenSinkController.java +import com.example.bot.spring.services.TextMessageService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; public class KitchenSinkController { @Autowired private LineBlobClient lineBlobClient; + @Autowired + private TextMessageService textMessageService; + @EventMapping public void handleTextMessageEvent(MessageEvent<TextMessageContent> event) throws Exception { TextMessageContent message = event.getMessage(); - handleTextContent(event.getReplyToken(), event, message); + + log.info("TextMessageContent is called"); + + textMessageService.fetchReplyTextMessage(message.getText()); + + if (textMessageService.getReplyFlg()) { + log.info("replyFlg is true"); + + String replyText = textMessageService.getReplyMessage(); + List<Message> replyMessage = singletonList(new TextMessage(replyText)); + String replyToken = event.getReplyToken(); + + log.info("Returns echo message {}: {}", replyToken, replyText); + + boolean notificationDisabled = false; + try { + BotApiResponse apiResponse = lineMessagingClient + .replyMessage(new ReplyMessage(replyToken, replyMessage, notificationDisabled)) + .get(); + log.info("Sent messages: {}", apiResponse); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } } Procfileを書き換え sample-spring-boot-kitchensinkを利用するため、Procfileを以下の様に書き換える。 -web: java $JAVA_OPTS -jar sample-spring-boot-echo/build/libs/sample-spring-boot-echo-*.jar --server.port=$PORT +web: java $JAVA_OPTS -jar sample-spring-boot-kitchensink/build/libs/sample-spring-boot-kitchensink-*.jar --server.port=$PORT .gitignore *.jarをignoreすると./gradle/wrapper/gradle-wrapper.jarがDeployされず、以下のエラーが出るので編集する。 Error: Could not find or load main class org.gradle.wrapper.GradleWrapperMain .gitignore # Package Files # -*.jar +#*.jar *.war *.ear herokuの環境変数設定 herokuに以下の環境変数を設定する。 Key Value CHANNEL_SECRET LINEのChannel access token CHANNEL_TOKEN LINEのChannel secret DATABSE_JDBC_URI jdbc:postgresql://herokuのpostgresのhostname:5432/dbname DATABSE_USER herokuのpostgresのuser name DATABSE_PASSWORD herokuのpostgresのpassword Deploy herokuにdeployする。 $ git remote add heroku https://git.heroku.com/${HEROKU_APP_NAME}.git $ git add --all $ git commit -m 'Added a sample codeset for Qiita' $ git push heroku master Migrationの結果を確認 herokuにdeployすると、postgresに以下のテーブルが作成される。 Intellijなどローカルの場合は、IDEでプログラムを実行すれば接続先のDBにテーブルが作成される。 $ heroku pg:psql postgresql-abcdefg-12345 --app line-bot-sample line-bot-sample::DATABASE=> \d List of relations Schema | Name | Type | Owner --------+-----------------------+----------+---------------- public | flyway_schema_history | table | sample-user public | text_messages | table | sample-user public | text_messages_id_seq | sequence | sample-user (3 rows) Insert DBに以下をinsertする。これが、返信用メッセージとなる。 line-bot-sample::DATABASE=> insert into text_messages (message) values ('ハローワールド'); INSERT 0 1 line-bot-sample::DATABASE=> insert into text_messages (message) values ('Hello world!!'); INSERT 0 1 line-bot-sample::DATABASE=> select * from text_messages; id | message ----+---------------- 1 | ハローワールド 2 | Hello world!! (2 rows) LINEでWebhook URLを設定 LINE DeveloperのMessage APIのWebhook Settingsに、herokuのURLを登録する。 Webhook URL: https://{HEROKU_APP_NAME}.herokuapp.com/callback Use webhook: onにする これでbotにメッセージを送信すると、以下のような結果となる。 テストコードを書く場合 以下にtest用ディレクトリを作成し、その下にテストコードを作成する。 $ mkdir -p sample-spring-boot-kitchensink/src/test/java/com/example/bot/spring/ 参考 https://tosi-tech.net/2019/04/flyway-with-spring-boot/ https://tomokazu-kozuma.com/how-to-migrate-mysql-using-flyway/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DB名にスキーマが含まれていると DBUnit の @DatabaseSetup とかでテーブルがみつからない

いや、普通データベースってスキーマ切るでしょ。 これが「DBUnit スキーマ」とかで探してもほとんど出てこなかったんですよ。なんか Oracle データベースの話しかみんなしてない。なんでやねん。 だめなやつ TestConfig.java /** * テスト用JDBCドライバのbean */ @Configuration public class TestConfig { @Bean DataSource dataSource(){ return dataSourceH2(); } DataSource dataSourceH2() { EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); builder.setType(EmbeddedDatabaseType.H2) .addScript("table/schema.sql").addScript("table/data.sql"); return builder.build(); } @Bean JdbcTemplate jdbcTemplate(DataSource dataSource){ return new JdbcTemplate(dataSource); } } schema.sql create schema sh; create table sh.test ( id int ); data.sql insert into sh.test (id) values (1); table/init/teble-ordering.txt sh.test table/init/sh.test.csv id 1 2 3 Test.java @SpringBootTest(classes = {TestConfig.class}) @RunWith(SpringRunner.class) @DbUnitConfiguration(dataSetLoader = CsvDataSetLoader.class) @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DbUnitTestExecutionListener.class }) @TestPropertySource("/application.properties") @Transactional public class Test { @Autowired JdbcTemplate jdbcTemplate; @Test @DatabaseSetup("/table/init") // ← これをコメントアウトするとちゃんとsqlで初期化した内容がプリントされる public void test(){ System.out.println(jdbcTemplate.queryForList("select * from sh.test")); } } CsvDataSetLoader の中身は割愛。CSV読めるようにするやつ。 とにかく、 jdbcTemplate から <スキーマ名>.<テーブル名> でアクセスできるもんだからDBUnitからでも同じ感じでアクセスできるもんだと勘違いしていたけどどうにもエラーが出て初期化をしてくれない。 org.dbunit.dataset.NoSuchTableException: sh.test 探した探した。 この stackoverflow は最初の方にみつけてはいたものの、ベストアンサーが「OracleConnection bean を使え」という物だったから「違うんだよなぁH2なんだよなぁ」と思いながらスルーしていた。けど近しいものがこの質問ぐらいしか無かったから繁々と眺めていたら下の方に少し詳しい回答がちゃんとあった。 飛び道具をベストアンサーにするなよfxxk If you are using the spring-test-dbunit then you need to create an IDatabaseConnection with a specific DBUnit configuration. Following the doc's example I've setup this one for me: (spring-test-dbunitを使用している場合は、特定のDBUnit構成でIDatabaseConnectionを作成する必要があります。ドキュメントの例に従って、これをセットアップしました。) 書いてあるのはポスグレ。けれどなんとなくちゃんと設定するものがどこかにあって適切に @DbUnitConfiguration に入れてあげればいいのかなぁと思いながら DBUnitのJavadoc にそれっぽいの無いかなぁ。 H2 って名前のついた IDataConnection 持ってるクラス無いかなぁ。って思ってたら ちゃんとありました。 public class H2Connection Constructor and Description H2Connection(java.sql.Connection connection, java.lang.String schema) スキーマ指定できるやーん…… うごくやつ TestConfig.java @Configuration public class TestConfig { @Bean DataSource dataSource(){ return dataSourceH2(); } DataSource dataSourceH2() { EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); builder.setType(EmbeddedDatabaseType.H2).addScript("table/scheme.sql").addScript("table/data.sql"); return builder.build(); } @Bean JdbcTemplate jdbcTemplate(DataSource dataSource){ return new JdbcTemplate(dataSource); } // DBUnit専用の接続設定を追加 @Bean H2Connection h2Connection() throws DatabaseUnitException, SQLException{ return new H2Connection(dataSource().getConnection(), "sh"); } } table/init/teble-ordering.txt test table/init/test.csv id 1 2 3 (↑ファイル名変えただけ) Test.java @SpringBootTest(classes = {TestConfig.class}) @RunWith(SpringRunner.class) // databaseConnection に接続設定を追加 @DbUnitConfiguration(databaseConnection = "h2Connection", dataSetLoader = CsvDataSetLoader.class) @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DbUnitTestExecutionListener.class }) @TestPropertySource("/application.properties") @Transactional public class JsonDatabaseTest { @Autowired JdbcTemplate jdbcTemplate; @Test @DatabaseSetup("/table/init") public void test(){ System.out.println(jdbcTemplate.queryForList("select * from sh.test")); // ちゃんとCSVの内容が表示される!!! } } めでたしめでたし。 なんでJDBCと同じ方法でアクセスさせてくれないんですかねぇ!!!!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IntelliJでGradleのプロジェクト作成

基本 プロジェクトの作成 IntelliJ IDEAの開始画面で新規プロジェクトをクリックします。 次の画面にて,Gradleを選択しProject SDKを選択し(自分の場合Macに11.0.11を事前にインストールしているのでそれを選択)その後、Javaにチェックが入ってることを確認し下のNextへ ウィザードの次のページで、プロジェクトの名前と場所を指定します。 Artifact Coordinatersに関しては今回デフォルトの設定を使いますがそれぞれ設定する際は以下のように設定します。 GroupId:プロジェクトを一意に識別するID。一般的にはドメイン名を逆に記載したパッケージ名。com.example.myproject001など。 ArtifactId:生成するプログラムの名前。Jarファイル名称など。 Version:プロジェクトのバージョン番号。リリースなどで更新していく値。最初は1.0など。 Gradleの初期化が無事に完了すると、Buildツールウインドウに初期化処理が成功した旨が表示されます。 またsrcフォルダーが作成されています。 プログラムファイルの追加 Projectエクスプローラで、src > main > javaを右クリックして適当なクラス名を入力します。 今回はMainと入力しました。 作成したクラスを実行できるように、main関数を追加する。 Main.java public class Main { public static void main(String[] args) { System.out.print( "Hello world." ); } } Gradleビルド設定ファイルでJavaソースファイルのエンコーディングを明示的に指定するため、build.gradleに追記をする build.gradle compileJava.options.encoding = 'UTF-8' compileTestJava.options.encoding = 'UTF-8' Gradleからプログラムを実行する Gradleにapplicationプラグインを導入して、Graldeからプログラムを実行できるようにする。 build.gradleに追記をする build.gradle plugins { id 'java' id 'application' //プラグインに追記 } mainClassName = 'Main' mainClassNameに、パッケージ名称を含むフルパス形式で実行したいクラス名称を指定。 次に、IntelliJ IDEAのメニューからRun > Edit Configurations...を実行し、Run/Debug Configurationsダイアログを表示する。もしくは画面上のAdd Configuration...をクリックする。 ダイアログ左上の+ボタンを押下してAdd New Configurationポップアップを表示し、一覧からGradleを選択し新しいRun設定が追加。 ・Name:Run設定の名前を入力(例:Main実行) ・Gradle projects:プロジェクト名を設定。右端のフォルダボタンを押下して、プロジェクトを選択する。 ・Tasks:runを入力 OKボタンを押下してRun/Debug Configurationsダイアログを閉じると、プロジェクトにRun設定が追加される。 正しくプログラムが実行できると、以下のようにRunツールウインドウに実行結果が表示される。 参考にさせていただいたサイト Gradle 入門 | IntelliJ IDEA IntelliJ IDEAでJavaプロジェクトを新規作成 | mikamiz
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring Boot(MVC) + Spring Security + Azure ADでのログインの実装がとても簡単になった件

以前も似たような記事を書いていましたが、AzureADのSpringスターターが整理され、実装が非常に簡単になっていたので改めて記事にしました。 前提条件 Spring Bootで"Hello World!"ができる(出来れば数ページのバリエーションが実装済みだと望ましい) AzureADの管理者アカウントを持っている Spring Boot+Spring WebのアプリにMicrosoft365アカンウントで認証させたい 上記のような方に向けた記事となります。 AzureAD側での下準備 ドメインにアプリを登録 AzureADスターターの開発者ガイドの「Azure Active Directory インスタンスの作成」を参考にテナントの作成を行います。また、認証には当然ユーザー情報が必要となりますので、ユーザーの作成が必要です。グループ別のアクセス認可を行う場合には、グループの作成も必要です。 既にテナントやユーザー、グループの設定が終わっている場合は、「Spring Boot アプリのアプリケーション登録を追加する」だけ済ませておけば大丈夫です。 アプリケーション登録の注意点 クライアント シークレットの値は登録時にしか表示されないことと、 API のアクセス許可で取得するアクセスの内容に注意しましょう。 以前の記事ではAPIにAzureAD Graphが使用されていましたが、バージョン3.6以降ではデフォルトでMicrosoft Graphを使用してアクセスするため、APIの許可もMicrosoft Graphから行うことになります。 アクセス許可の内容(必須) Directory.AccessAsUser.All:サインインしているユーザーとしてディレクトリにアクセスする User.Read:サインインの実行とそのユーザーのプロファイルを参照する 上記があれば最低限のログイン処理が可能です。 リダイレクトURIについて AzureADスターターを使用してログインを実装する場合、リダイレクトURIはhttp|https//{ドメイン}/login/oauth2/code/で固定されます。Spring SecurityのOAuth2(OpenID Connect)のテンプレートで生成されるURIとは異なりますので注意してください。 利用するStarterと依存関係の記述 利用するStarter(必須) 必須のスターターは下記となります。Spring Initializrで表示されるスターター名で記述してあります。 Spring Web OAuth2 Client Azure Active Directory Gradleで依存性を記述する場合 Spring Initializrを利用するのが確実ですが、執筆時点でAzureADスターターのバージョンが上がっているため、バージョンの指定を下記のように修正することをお勧めします。 build.gradle // 前略 ext { set('azureVersion', "3.6+") } dependencies { implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'com.azure.spring:azure-spring-boot-starter-active-directory' } dependencyManagement { imports { mavenBom "com.azure.spring:azure-spring-boot-bom:${azureVersion}" } } // 後略 application.ymlの編集 application.yml azure: activedirectory: authorization-clients: graph: scopes: - https://graph.microsoft.com/User.Read - https://graph.microsoft.com/Directory.AccessAsUser.All tenant-id: AzureADのテナントのID client-id: AzureADに登録したアプリのID client-secret: AzureADに登録したクライアントシークレット user-group: allowed-group-names: - グループ名1 - グループ名2 上記のように整理されて非常にシンプルになりました。 AzureADスターターの開発者ガイドの「構成プロパティ」のプロパティにはallowed-groupsという表記がありますが、アクセスを許可するグループをグループ名で指定するときはallowed-group-namesが推奨されます。 また、グループ名は変更される場合があるため、より安全なのはallowed-group-idsを使用して一意に指定することです。 Configurerクラスを作成する デフォルトでは、DefaultAADWebSecurityConfigurerAdapterにより、/login以外のパスが自動的に保護されるようになっています。/loginを除外しているのはログイン画面がないとどこにもアクセスできなくなってしまうからですね。 パスの一部に認証をし、また、グループによってアクセスできるパスを変更するなどの構成が必要な場合、AADWebSecurityConfigurerAdapterを継承してクラスを作成します。 AzureADスターターのGitHubによると、下記のような構成クラスではすべてのパスにAzureADの認証が行われます。 GitHubより @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class AADOAuth2LoginSecurityConfig extends AADWebSecurityConfigurerAdapter { /** * Add configuration logic as needed. */ @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http.authorizeRequests() .anyRequest().authenticated(); // Do some custom configuration } } 既にあるアプリケーションにAzureADによる認証を組み込んでみるだけのテストであれば、上記のクラスをそのまま使用しても良いでしょう。 実用的には明示的にsuper.configure(http);を呼ぶことでAzureADによる認証に関する諸々の設定がおこなわれ、http.authorizeRequests()以下で、認証の必要なパスやグループによるアクセス許可を指定することができます。 Spring Security導入時のCSRF対策について Spring Securityが導入されることでCSRF対策が導入されます。これにより、POST、DELETEなどのメソッドのリクエストに対してトークンのチェックが行われます。 テンプレートエンジンにThymeleafを使用している場合は、依存関係にthymeleaf-extras-springsecurity5を追加することでth:method="post"などのCSRF対策が必要なフォームタグに対し、自動的にトークンの隠しフィールドを追加してくれます。 また、Ajaxに対しても同様にトークンのチェックが行われるため、動作しなくなる場合があります。 Spring SecurityドキュメントのAjax および JSON リクエストに公式な対応方法がありますので、これもテンプレートで実装しておくと良いでしょう。 また、このあたりの情報は@opengl-8080さんの「Spring Security 使い方メモ CSRF」が大変参考になります。この記事のシリーズはSpring Securityを使用するうえで大変参考になるので他の記事も目を通すことをお勧めします。 参考資料 Azure AD Spring Boot Starter client library for Java:MicrosoftのAzureADスターターのGitHub Spring Security 使い方メモ CSRF Spring Security リファレンス
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Groovy 入門& 環境構築

Groovyとは Groovy(グルービー)は、Javaプラットフォーム上で動作する動的プログラミング言語. groovyはJavaと同じ型が使用できます。 Groovy導入 groovyをインストール homebrewを使ってインストール $ brew install groovy $ groovy --version Groovy Version: 3.0.8 JVM: 16.0.1 Vendor: Homebrew OS: Mac OS X GroovyConsoleを起動する 以下のコマンドをターミナルで実行します。 $ groovyConsole & コマンドを実行すると以下のようなウィンドウが表示されます。 基本的な構文&メソッド def 変数宣言の際に使用。defは型指定が行われない(暗黙型変換される) hello_print.groovy String a = "hello" println a 実行結果 $ groovy hello_print.groovy hello if文 if.groovy String a = "hello" if (a == "hello") { println a } else if (a == "test"){ println "test" } else { println "other" } 実行結果 $ groovy if.groovy hello 現在実行しているスクリプトのパス、またはファイル名を取得する path_print.groovy println("path => " + this.class.protectionDomain.codeSource.location.path) println("fileName => " + new File(this.class.protectionDomain.codeSource.location.file).name) 実行結果 $ pwd /Users/{ユーザ}/Desktop $ groovy path_print.groovy path => /Users/{ユーザ}/Desktop/path_print.groovy fileName => path_print.groovy 参考にさせていただいたサイト Macにgroovyをインストールする | 株式会社CONFRAGE ITソリューション事業部 Groovyで現在実行しているスクリプトのパス、またはファイル名を取得する - CLOVER?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む