- 投稿日:2021-03-08T23:52:51+09:00
Java Scannerを使う
Scannerについてまとめてみた
経緯
progate JavaⅢ の道場コースを進めている際にScannerというライブラリの存在を知った。
入力した値が意図せず改行されてしまったため、疑問に思いScannerについて調べてみたので、ついでに学んだことも含めてまとめます。結論
改行に関してはこの理解で解決
⬇︎
System.out.print と System.out.println の違い
- System.out.println は最後に改行を付与
- System.out.println は先頭には改行を付与しない初期化
Scanner scanner = Scanner(System.in);
Scannerではこの初期化したものを代入した変数を使ってメソッドを呼び出す。
scanner.next()とするとコンソールに入力された文字列を受け取ることができる。Scanner クラスの主なメソッド
next() 文字列を取り出す
nextInt() 整数値を取り出す
nextDouble() 実数値を取り出す
nextLine() 次の改行文字の前までを文字列として取り出し、改行文字をスキップする
hasNext() 次の入力があるときは値 true を、ないときは false を返す
close() スキャナを閉じる(ファイルから入力するとき使う)
- 投稿日:2021-03-08T22:13:36+09:00
javaでファイル更新監視アプリを作成する
目的
javaでアプリを作りたいと思った。
とりあえず人からこんなものを作ったらと言われて作ってみた作品になります。
ということで今回作ったのは監視対象ファイルをCSVから読み込んでファイルサイズが変わったら
デスクトップのポップアップで通知してくれるデスクトップアプリです。環境構築
windows8
Pleiades All in One photonソース
gitthubにて
https://github.com/mimicman/FileSurveillance/tree/main/src/jp/mainソースの説明
MainUpdateCheker.java
MenuItemクラスでデスクトップのメニューに追加します。
メニューには開始、停止、再開などアプリを操作するメニューを追加しています。
Timerクラスで定期的に処理を実行しています。CsvLoad.java
CSVを読み込んでCSVに記載されている監視対象ファイルを読み込みます。
Run.java
監視対象ファイルのサイズが変更されていないかを確認する処理を実施しています。
UpdateChekerFrame.java
監視対象ファイルを可視化するため、フレームにファイル名等を表示しています。
- 投稿日:2021-03-08T20:32:21+09:00
Spring+Thymeleafでのページング実装例(ページ番号を省略させるもの)
1 はじめに
Spring+Thymeleafでのページング処理方法を調べてみると、下記のようなページングの実装例は簡単に見つかる。
このページングの実装は、下記の記事を参考にさせていただいた。
Spring Boot + Thymeleafでページング機能を実装するしかし、この実装の内容だとページ数が増えた場合に横にページ番号を増やし続けてしまう。
(自分でカスタマイズしろ!というお話なのだが・・・)そこで、次のようなページ番号が増えた場合には「...」で省略するページングを実装してみたので共有する。
(JavaScriptを使用してのページングを実装する方法もあるそうなのだが、今回はThymeleafのみで実装した。)
2 実装例
Thymeleafのコーディング例はこちら
pagination.html<ul class="center padding-l-0"> <li th:class="inline"> <span th:if="${page.first}"><</span> <a th:if="${not page.first}" th:href="@{${url}(page=${page.number-1})}"><</a> </li> <!--/* 全ページ数が9以下の時は、全ページ番号を表示 */--> <li th:if="${page.totalPages <= 9}" th:each='i : ${#numbers.sequence(0, page.totalPages-1)}' th:class="inline"> <span th:if='${i}==${page.number}' th:text='${i+1}'></span> <a th:if='${i}!=${page.number}' th:href="@{${url}(page=${i})}"> <span th:text='${i+1}'></span> </a> </li> <!--/* 全ページが9以上の時 */--> <!--/* 1~5ページの時は後ろに「...」をつける */--> <li th:if="${page.totalPages > 9 && page.number < 5}" th:each='i : ${#numbers.sequence(0, 8)}' th:class="inline"> <span th:if='${i}==${page.number}' th:text='${i+1}'></span> <a th:if='${i}!=${page.number}' th:href="@{${url}(page=${i})}"> <span th:text='${i+1}'></span> </a> </li> <li th:if="${page.totalPages > 9 && page.number < 5}" class="inline"> <span th:text="..."></span> </li> <!--/* (6~TotalPage数-5)ページの時は前後に「...」をつける */--> <li th:if="${page.totalPages > 9 && 4 < page.number && page.number < page.totalPages - 5}" class="inline"> <span th:text="..."></span> </li> <li th:if="${page.totalPages > 9 && 4 < page.number && page.number < page.totalPages - 5}" th:each='i : ${#numbers.sequence(page.number-4, page.number+4)}' th:class="inline"> <span th:if='${i}==${page.number}' th:text='${i+1}'></span> <a th:if='${i}!=${page.number}' th:href="@{${url}(page=${i})}"> <span th:text='${i+1}'></span> </a> </li> <li th:if="${page.totalPages > 9 && 4 < page.number && page.number < page.totalPages - 5}" class="inline"> <span th:text="..."></span> </li> <!--/* (TotalPage数-5)以上ページの時は前方に「...」をつける */--> <li th:if="${page.totalPages > 9 && page.number >= page.totalPages-5}" class="inline"> <span th:text="..."></span> </li> <li th:if="${page.totalPages > 9 && page.number >= page.totalPages-5}" th:each='i : ${#numbers.sequence(page.totalPages-9, page.totalPages-1)}' th:class="inline"> <span th:if='${i}==${page.number}' th:text='${i+1}'></span> <a th:if='${i}!=${page.number}' th:href="@{${url}(page=${i})}"> <span th:text='${i+1}'></span> </a> </li> <li th:class="inline"> <span th:if="${page.last}">></span> <a th:if="${not page.last}" th:href="@{${url}(page=${page.number+1})}">></a> </li> </ul>コード中のpageオブジェクトに検索結果が格納されている。
(pageオブジェクトに検索結果を渡す処理(ControllerやServiceなど)のコーディング例は参考情報として文末に記載しておく)条件分岐について
全ページ数(page.totalPages)や現在閲覧しているページ番号(page.number)などを使用して、下記4パターンにif条件で分岐させている。
ここでは説明のために、全ページ数をN、現在閲覧しているページ番号をxと表記することにする。パターン1(どのページ番号も省略しない)
パターン2(後方に...がつく)
パターン3(前方、後方に...がつく)
このパターンはN>9 かつ 6<=x<=N-5
の時に発生する。パターン4(前方に...がつく)
上記のような分岐になっている。
ここでは上記のような条件分岐をさせているために、ページ番号として表示されるのは最大9件までとなっており、現在閲覧しているページから最大前後4ページまでが表示されるようになっている。
(つまり、8ページ目を閲覧している時には4~12のページ番号が表示される)
なので、表示させるページ数を増やしたい場合は上記の条件分岐に手を加えていただければ実装可能となっている。また、このような分岐以外にも条件分岐をさせている。
例えば、現在見ているページ(page.number)のページ番号を表示する時にはaタグではなくspanタグで表記するような条件を記載している。
また、同様に次ページ前ページボタン(<,>)についても現在見ているページ(page.number)が最初のページ(page.firstがtrue)もしくは最後のページ(page.lastがtrue)ならaタグではなくてspanタグで表記するような条件としている。参考情報(Controller、Service、Repositoryの実装例)
Controller
ItemSearchController.java@Controller public class ItemSearchController { // アイテム検索サービス @Autowired private ItemSearchService itemSearchService; @RequestMapping(value = "/") public ModelAndView searchAllItem(ModelAndView modelAndView, Pageable pageable) { // 全アイテムの検索 Page<Item> itemListPage = itemSearchService.searchAllItem(pageable); // htmlに値を渡す modelAndView.addObject("page", itemListPage); modelAndView.addObject("itemList", itemListPage.getContent()); modelAndView.addObject("url", "/"); // 遷移先:アイテム検索ページ modelAndView.setViewName("item_search"); return modelAndView; } }Service
ItemSearchService.java@Service public class ItemSearchService { // アイテムリポジトリ @Autowired private ItemRepository itemRepository; /** * 全アイテム検索メソッド * @return 全アイテムリスト */ public Page<Team> searchAllItem(Pageable pageable) { // 全チームを検索する return itemRepository.findAllItem(pageable); } }Repository
ItemRepository.java@Repository public interface ItemRepository extends JpaRepository<Item, Integer> { // アイテムを検索する @Query(value = "SELECT * FROM item", nativeQuery = true) public Page<Item> findAllItem(Pageable pageable); }
- 投稿日:2021-03-08T18:21:31+09:00
プログラミング初心者が基礎基本を地道に身に着ける日記(Java_ABC189編)
※本稿はAtCoderProblemsのBeginnerContest,A~C問題をJavaを用いて地道に解いていく人間の観察日記です
目次
- まえがき
- AtCoder Beginner Contest 189に挑戦
- A問題
- B問題
- C問題
- 感想
- お世話になったサイト(参考に使用)
まえがき
前回の記事(ABC190)
~前回のあらすじ~
前回は、bit全探索の条件分けの書き方を全く知らずに時間を浪費したe2vee。今回は1時間で解けるのか!?頑張れ~~!e2vee~~!!AtCoder Begginer Contest 189に挑戦
A問題
189A.javaimport java.util.*; public class Main{ public static void main(String args[]){ Scanner sc = new Scanner(System.in); //区切りが「""」(一文字ずつ!)なので //ちなみに読み取るものが(String型)文字列じゃないと使えないヨ! //多分そうだったハズ..(;・∀・) sc.useDelimiter(""); //読み取り String c1 = sc.next(); String c2 = sc.next(); String c3 = sc.next(); //答え String ans = "Lost"; //判定 if(c1.equals(c2) && c2.equals(c3)){ ans = "Won"; } //出力 System.out.println(ans); } }
.useDelimiter()
を知った自分に死角など無い。次の料理を持ってきたまへ。B問題
189B.javaimport java.util.*; public class Main{ public static void main(String args[]){ Scanner sc = new Scanner(System.in); //まずは飲んだ杯数を数える int n = sc.nextInt(); //浮動小数点だと環境によっては違う解を出力する //なので、int型に揃えるために、xとpに100をかけてどうにかする int x = sc.nextInt(); int alcr = 0; int ans = -1; for(int i=0; i<n; i++){ int v = sc.nextInt(); int p = sc.nextInt(); alcr = alcr + v*p; if(100*x<alcr){ ans = i+1; break; } } /*!浮動小数点数の演算は一般に誤差を含むため、ときたま間違う! //酔っぱらうライン //ちなみに自分はほろよい1缶で酔います(隙自語) double x = sc.nextDouble(); //摂取したアルコール double alcr = 0; //解 int ans = -1; for(int i=0; i<n; i++){ //次はv:量とp:アルコール度数を調べる double v = sc.nextDouble(); double p = sc.nextDouble(); //摂取したアルコール alcr = alcr + v*(p/100); //確認用 //System.out.println("x = "+x); //System.out.println("alcr = "+alcr); //System.out.println("ans = "+ans); //判定 if(x<alcr){ ans = i+1; break; } } */ //出力 System.out.println(ans); } }いや絶対に合ってるのに~~!!!なんでぇ!?と最初は思っていました。
浮動小数点数の演算は一般に誤差を含む
あ~~^なんか聞いたことある~~~~^
出来る限り少数の計算を避けるべきという、基礎を失念していたがために、30分以上も唸っていました。ノォオオオオ!
C問題
189C.javaimport java.util.*; public class Main{ public static void main(String args[]){ Scanner sc = new Scanner(System.in); //皿の枚数 int n = sc.nextInt(); //各皿に置かれてるみかんの個数 //みかんの配列 int[] orange = new int[n]; for(int i=0; i<n; i++){ orange[i] = sc.nextInt(); } //また全探索..(;・∀・) //l~r間を順に大きくしながらxを更新する //答え int ans = 0; //探索開始! for(int l=0; l<n; l++){ //まず" l "皿目の?を食べる(予定・基準 int x = orange[l]; //その後、" l+1~ "皿の?をどんどん食べる。 for(int r=l; r<n; r++){ //l皿とr皿に乗っている?の数を比較 //小さい方に基準を合わせる //※小さい方の?の数はxに、その後1つずつorange[r]で比較されていく x = Math.min(orange[r], x); //その後、現在のansとx個を(r-l" +1 ")皿分の?を食べた数を比較 ans = Math.max(ans, x*(r-l+1)); // } } //出力 System.out.println(ans); } }おきまりのC問題。ぶっちゃけ今までのC問題の中で一番早く解けたけました。
最初は「l~r間の最小の数をその都度数えて、?の数を数えて、最大を求めて~」という殆ど同じだけども、計算回数が多くて時間をより多くかけてしまうプログラムを書いちゃってました。
求める値を上書きしていくやり方に慣れていない感満載で、ちょっと不安が残りましたね..。
感想
今回は今までのABCと比べて簡単(?)でした! ただ、失念していたことも見つかり、まだまだだなぁと..。
次も一時間で終われるように頑張ります!(今回は1時間くらい)
お世話になったサイト
実例を用いて分かりやすく解説してくれています!今度から特に"割り算"するときは気を付けます..。
- 投稿日:2021-03-08T18:20:41+09:00
JAVA Convert short[] to byte[]
Method 1:
byte [] ShortToByte_ByteBuffer_Method(short [] input) { int index; int iterations = input.length; ByteBuffer bb = ByteBuffer.allocate(input.length * 2); for(index = 0; index != iterations; ++index) { bb.putShort(input[index]); } return bb.array(); }Method 2:
byte [] ShortToByte_Twiddle_Method(short [] input) { int short_index, byte_index; int iterations = input.length; byte [] buffer = new byte[input.length * 2]; short_index = byte_index = 0; for(/*NOP*/; short_index != iterations; /*NOP*/) { buffer[byte_index] = (byte) (input[short_index] & 0x00FF); buffer[byte_index + 1] = (byte) ((input[short_index] & 0xFF00) >> 8); ++short_index; byte_index += 2; } return buffer; }
- 投稿日:2021-03-08T17:11:50+09:00
tomcat8は始動に失敗しました
- 投稿日:2021-03-08T16:18:00+09:00
Java の SimpleDateFormat は「Jun」でも「June」でも Parse してくれる
SimpleDateFormatで「MMM」を指定すると、月の書き方が短縮系でもそうでなくてもParseしてくれる。
コード
public static void main(String[] args) throws Exception { { SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.ENGLISH); System.err.println(sdf.parse("01-June-2020"));// OK } { SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.ENGLISH); System.err.println(sdf.parse("01-Jun-2020"));// OK } { SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.ENGLISH); System.err.println(sdf.parse("01-September-2020"));// OK } { SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.ENGLISH); System.err.println(sdf.parse("01-Sep-2020")); // OK } { SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.ENGLISH); System.err.println(sdf.parse("01-Sept-2020")); // NG } }結果
Mon Jun 01 00:00:00 JST 2020 Mon Jun 01 00:00:00 JST 2020 Tue Sep 01 00:00:00 JST 2020 Tue Sep 01 00:00:00 JST 2020 Exception in thread "main" java.text.ParseException: Unparseable date: "01-Sept-2020" at java.text.DateFormat.parse(DateFormat.java:366) at hello.date.HelloSimpleDateFormat2.main(HelloSimpleDateFormat2.java:28)
- 投稿日:2021-03-08T15:40:52+09:00
SpringBootのExecutable JARでMyBatisのtypeAliasが解決できない件。
何についての話か
SpringBoot+MyBatisを利用したプロジェクトをEclipseやIntelliJで開発時に動かすと動くのに、実行可能JAR作ってコマンドラインで実行しようとすると
Exceptionログ(抜粋)Caused by: org.apache.ibatis.type.TypeException: Could not resolve type alias 'hogehoge'. Cause: java.lang.ClassNotFoundException: Cannot find class: hogehoge at org.apache.ibatis.type.TypeAliasRegistry.resolveAlias(TypeAliasRegistry.java:120) at org.apache.ibatis.builder.BaseBuilder.resolveAlias(BaseBuilder.java:149) at org.apache.ibatis.builder.BaseBuilder.resolveClass(BaseBuilder.java:116)とかなることありませんか??
本記事はその解決策になります。
環境
- SpringBoot 2.4.3
- MyBatis 3.5.6
- mybatis-spring-boot-starter 2.1.4
- 開発環境IntelliJ IDEA Community(ごめんなさい) 2020.3.2
- Kotlin 1.x(現時点で最新)
- Java 11
では始めます。
原因
JARファイルにまとめてしまうとJARファイル内にあるファイルたちにアクセスするためには素で書くと
resourcegetClass().getResource("classpath:hogehoge")などとしてアクセスする必要があります。
MyBatisでは仮想ファイルシステム(VFS)を経由することでJARファイルの中にアクセスするようになっているようです。このVFSはJARごとに用意されているのですが、デフォルトではSpringBoot pluginで生成するJAR用のVFSがセットされていません。
※用意はされている。このため、IDE上で実行する=実FS上の実ファイルにアクセスできる、が、JARを実行する=SpringBoot仮想FS上のクラスファイルにアクセスできない(見つからない)、となってClassNotFoundExceptionが起きます。
解決策
SpringBoot+MyBatisでは、MyBatisの設定をbeanで行えます。
最近Kotlin覚えたのでKotlinで書きます。MyBatisConfiguratorSample.ktimport org.apache.ibatis.session.AutoMappingBehavior import org.apache.ibatis.session.SqlSessionFactory import org.mybatis.spring.SqlSessionFactoryBean import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.core.io.support.PathMatchingResourcePatternResolver import org.springframework.jdbc.datasource.DataSourceTransactionManager import org.springframework.transaction.TransactionManager import javax.sql.DataSource @Configuration class MyBatisConfig { @Bean fun transactionManager(dataSource: DataSource): TransactionManager = DataSourceTransactionManager().apply { this.dataSource = dataSource } @Bean fun sqlSessionFactory(dataSource: DataSource): SqlSessionFactory { val config = org.apache.ibatis.session.Configuration().apply { isMapUnderscoreToCamelCase = true autoMappingBehavior = AutoMappingBehavior.FULL typeAliasRegistry.registerAliases("package.to.entities") } val sessionFactory = SqlSessionFactoryBean().apply { setDataSource(dataSource) setConfiguration(config) val resolver = PathMatchingResourcePatternResolver() setMapperLocations(* resolver.getResources("classpath:/sql/**/*.xml")) } return sessionFactory.getObject()!! } }テンプレです。
WARにパッケージングしたり、IDE上で動かしたり、build/binディレクトリごと持っていくなら上で問題ないですが、JARで実行するよ!となると、
MyBatisConfigSample修正版.ktimport org.apache.ibatis.session.AutoMappingBehavior import org.apache.ibatis.session.SqlSessionFactory import org.mybatis.spring.SqlSessionFactoryBean import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.core.io.support.PathMatchingResourcePatternResolver import org.springframework.jdbc.datasource.DataSourceTransactionManager import org.springframework.transaction.TransactionManager import javax.sql.DataSource import org.mybatis.spring.boot.autoconfigure.SpringBootVFS @Configuration class MyBatisConfig { @Bean fun transactionManager(dataSource: DataSource): TransactionManager = DataSourceTransactionManager().apply { this.dataSource = dataSource } @Bean fun sqlSessionFactory(dataSource: DataSource): SqlSessionFactory { val config = org.apache.ibatis.session.Configuration().apply { isMapUnderscoreToCamelCase = true autoMappingBehavior = AutoMappingBehavior.FULL typeAliasRegistry.registerAliases("package.to.entities") } val sessionFactory = SqlSessionFactoryBean().apply { vfs = SpringBootVFS::class.java // <- これが要る!! setDataSource(dataSource) setConfiguration(config) val resolver = PathMatchingResourcePatternResolver() setMapperLocations(* resolver.getResources("classpath:/sql/**/*.xml")) } return sessionFactory.getObject()!! } }とすると動くよ!っていう記事がたくさんあるのですが 私は動かなかったです。
以前はこれだけで動いた気がするのだけどなぁ。ちなみに、上のコードでは、SqlSessionFactoryBeanでclasspath以下のSQLのXMLファイルを参照しようとしているときにVFSを経由しているので、
SqlSessonFactoryにVFSはSpringBootVFSだよ!って教えてあげている1行です。解決策(最終版)
ソース追ってたら、どうやらTypeAliasRegistry#registerAliasesでパッケージの展開ができてない模様。
ん?これはもしや・・・と思い、もうちょっとソース追ってみたところ、org.apache.ibatis.io.ResolverUtil.javapublic ResolverUtil<T> find(Test test, String packageName) { String path = getPackagePath(packageName); try { List<String> children = VFS.getInstance().list(path); // <- ここ!! for (String child : children) { if (child.endsWith(".class")) { addIfMatching(test, child); } } } catch (IOException ioe) { log.error("Could not read package: " + packageName, ioe); }うおい!!
5行目でVFSクラス直接参照しているのですね、、、こっちは。。ということで、ソースコードをこんな感じに修正。
MyBatisConfigSample最終版.ktimport org.apache.ibatis.session.AutoMappingBehavior import org.apache.ibatis.session.SqlSessionFactory import org.mybatis.spring.SqlSessionFactoryBean import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.core.io.support.PathMatchingResourcePatternResolver import org.springframework.jdbc.datasource.DataSourceTransactionManager import org.springframework.transaction.TransactionManager import javax.sql.DataSource import org.apache.ibatis.io.VFS import org.mybatis.spring.boot.autoconfigure.SpringBootVFS @Configuration class MyBatisConfig { @Bean fun transactionManager(dataSource: DataSource): TransactionManager = DataSourceTransactionManager().apply { this.dataSource = dataSource } @Bean fun sqlSessionFactory(dataSource: DataSource): SqlSessionFactory { val config = org.apache.ibatis.session.Configuration().apply { isMapUnderscoreToCamelCase = true autoMappingBehavior = AutoMappingBehavior.FULL VFS.addImplClass(SpringBoot::class.java) // <= これも要る!! typeAliasRegistry.registerAliases("package.to.entities") } val sessionFactory = SqlSessionFactoryBean().apply { vfs = SpringBootVFS::class.java // <- これが要る!! setDataSource(dataSource) setConfiguration(config) val resolver = PathMatchingResourcePatternResolver() setMapperLocations(* resolver.getResources("classpath:/sql/**/*.xml")) } return sessionFactory.getObject()!! } }てな感じで、SqlSessionFactoryだけじゃなく、TypeAliasRegistryから呼ばれるResolverUtilにもVFSを教えてあげる必要がありました。
これで、IDE上でも実行可能JARででも両方とも動きました!
お悩みの方々、ご参考になれば幸いです。
おまけにJavaのコード
上のコードはKotlinのapplyとか使ってますが、Javaで書くとこんな感じです。
MyBatisConfigSampleJava.javapackage com.axiohelix.minpaku.diplas.config; import org.apache.ibatis.io.VFS; import org.apache.ibatis.session.AutoMappingBehavior; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; @Configuration public class MyBatisConfig { @Bean public DataSourceTransactionManager transactionManager(DataSource dataSource) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource); return transactionManager; } @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setVfs(SpringBootVFS.class); org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration(); config.setMapUnderscoreToCamelCase(true); config.setAutoMappingBehavior(AutoMappingBehavior.FULL); VFS.addImplClass(SpringBootVFS.class); config.getTypeAliasRegistry().registerAliases("package.to.entities"); sessionFactory.setConfiguration(config); PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); sessionFactory.setMapperLocations(resolver.getResources("classpath:/sql/**/*.xml")); return sessionFactory.getObject(); } }つぶやき
ClassNotFoundExceptionの対応策一生懸命探してたんですがなかなかなくてソースを見る羽目になりました。
ただ、ちょっと趣深いなーと思ったのが、同様のエラーが発生している人に対して、registerAliasesでパッケージ指定せずに、1つずつクラス指定すればいいんだよ!
つまり...typeAliasRegistry.registerAlias(packages.to.entity.Entity1::class.java) typeAliasRegistry.registerAlias(packages.to.entity.Entity2::class.java) typeAliasRegistry.registerAlias(packages.to.entity.Entity3::class.java) typeAliasRegistry.registerAlias(packages.to.entity.Entity4::class.java)とすればエラー出ないよ!
という回答でした。
趣深い。
- 投稿日:2021-03-08T14:56:07+09:00
【Java】SpringBoot勉強してみた
はじめに
こんにちは。
少し前にはなりますが、とある企業のセミナーに参加した際に、SpringBootというフレームワークを使っていると知り、気になったので勉強してみました。
復習がてらに、備忘録として記事に残しておこうと思います今回は概念的な内容になります。
Spring Frameworkってなんぞや
SpringBootを紹介する前に、SpringFrameworkについて説明しておきます
◆ SpringFrameworkについて
- Javaの世界では老舗とも言えるフレームワーク
- DI(依存性注入)を実現するためのもの
- DIをベースに様々な機能を実装して「 統合フレームワーク 」に成長
◆ Spring Frameworkの「DI」とは?
- Dependency Injection(依存性注入)
- オブジェクト間の依存関係を、オブジェクト内のコードに記述せず、
実行時に外部から呼び出す手法◆ 多数のフレームワークから構成
SpringFrameworkは多数のフレームワークの集合体です。中でも有名なものとして、SpringMVC、SpringRoo、SpringBootなどがあります。
- SpringMVC:Model-View-Controllerアーキテクチャーによる開発
- SpringRoo:Spring MVCを補完するライブラリとして登場
- コードの自動生成機能ライブラリ
- 簡単なコマンドを実行するだけで、アプリケーションの枠組みが自動的にできあがるらしい(Railsみたいなイメージ)
- SpringBoot
◆ SpringBoot誕生
先ほど説明したSpringMVCやSpringRooはどちらも便利そうで良さげに思えますが、問題点もいくつかあります。SpringMVCは、多くのSpring系のライブラリを正確に組み合わせる必要があり、環境構築も大変。SpringRooもある程度は自動化できましたが、やっぱりまだまだ大変、、
そこで登場したのがSpringBootです!
SpringBootは、最良のWebアプリケーション開発を素早く構築するスターターキット的なライブラリです。Spring Boot = 「Spring MVCの完成形」と言えます【SpringBootの特徴】
- 専用のスターターライブラリを設定ファイルに記述するだけで、 一通りのライブラリの読み込み、環境構築が可能
- データベースアクセスなどの機能をほとんど自動化
Spring Bootのメリット
Javaの世界でも「Railsライク」とも言われる、効率的で高速開発ができるフレームワークがたくさん出来てきました。 それでも、「 Spring Boot 」を選ぶ理由は何なのでしょうか?
ここではSpringBootのメリットを5つ紹介します
① 面倒なXML設定ファイルの記述がない
従来のJava EE開発はXMLによる設定ファイルだらけで、入力が面倒であったり、エラーの特定が困難である等、問題も多々ありました。
SpringBootでは 以下のような事でXML設定ファイルの記述を少なくしています。
- Auto-configuration: 各種設定を必要最低限の記述のみで自動設定
- 不足する部分はJavaConfigを追加
- アノテーションによる開発
② アノテーション(注釈)記述による コーディング量の削減
アノテーションとは、"@ Xxx" の形式でコンパイラや実行環境にJava の言語機能としては表現できない事や補足などを伝えるものです。
例:@ before(メソッド実行前に呼び出す)アノテーションは大きく3つの種類に分けることが出来ます。
マーカーアノテーション:何らかの用途でマークする
単一アノテーション:データーを持つ事ができる
フルアノテーション:複数のデータを持つ事ができる③ サーバー内蔵方式
一般的なものは、アプリケーションとは別にApache ,Tomcat等のサーバーを準備し、 開発したアプリを追加して動かします。
これに対して、SPringBootにはJavaサーバーが内蔵されており、 直接サーバーを起動して実行します。④ DIをベースとする一貫した実装
DIをベースとしているため、スッキリしていて分かりやすいのもSpringBootの特徴です
- 数多くのライブラリがあっても、基本的な設計思想は一貫している
- 新しいライブラリが追加されても覚え直す必要なし
- テスト時のインスタンスの差し替え可能
⑤ AOP( アスペクト指向プログラミング )
AOPとは、プログラミングを別の視点、側面から考えていこうよ!っていうプログラミング手法です。
オブジェクト指向の時、オブジェクト単位に処理を上手く分解できない...どうしよう...
複数のオブジェクトで同じ操作が散在してしまう.....えらいこっちゃ...
こんな悩みを抱える事があります ( この状態を「横断的関心事」って言うらしいです)そんな時はAOP!!
アスペクトというモジュールに分離します。AOPによって、オブジェクト指向ではカバーできない部分をカバーする事が出来ます⑥ STS ( Spring Tool Suite)
一般的なフレームワーク「本体は提供するから、あとはそれぞれで自由に使ってくれ」 っていうスタンスですが、Spring Bootはフレームワーク本体に加えて専用開発ツール(STS)も提供しています。
【STSの特徴】
- ベースは「 Eclipse 」で、Spring Framework利用のためのプラグインが追加されている
- 必要な処理が自動化され、コードの作成のみに注力できる
- ここまで環境整備を行っているフレームワークはSpring Framework以外にはほぼ無い(らしい)
Spring Bootは「 十分条件 」ではない
フレームワークが提供するのは「プレゼンテーション層」の部分です。MVCにおける、View(画面)、Controller(全体の制御)の部分のみを提供します。つまり、ただ「仕組みがある」というだけで、別の技術も必要になってくるのです...
例えば、データベース関連ではSpring Data JPAやSpring Data MongoDBが必要ですし、画面実装にはテンプレートエンジン( Thymeleaf )というモノも使われます。
おわりに
ここまで、SpringBootについて紹介してきました。まとめます
- SpringBootでは、主にSpring Frameworkのフレームワークを組み合わせて開発
- 複雑な設定処理の記述が必要ない
- SpringBootを使えば、従来よりも簡単に素早く Webアプリケーションを開発できる
最後まで読んでいただきありがとうございます
間違っている点などございましたら、ご指摘いただけると嬉しいです!参考文献
今回は主に「Spring Boot2 プログラミング入門」を参考にまとめさせていただきました。初心者にも分かりやすく説明してくれているので、興味のある方は是非
- 投稿日:2021-03-08T13:19:42+09:00
JavaからMySQLのデータベースにアクセルする時に発生する例外の対処法
JavaでMySQLのデータベースにアクセスしようとしたら拒否されて例外が発生してアクセスできなかったので、色々と調べていました。
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)どうやらrootユーザが設定されていなくてこのようなエラーが発生することがあるようです。
このエラーが出た時はまずMySQLを停止してから、オプション付きで起動することによって解決しました。
1、まずは一旦停止します。
# service mysqld stop2、次にMySQLを–skip-grant-tablesオプション付きで起動します。
これはMySQLの権限システムを使用しないで起動するためのものだそう。# mysqld_safe --skip-grant-tables &僕の場合はこれで解決しました。
- 投稿日:2021-03-08T12:02:27+09:00
React+springbootお試し実装参考サイトリンク
react 環境構築
https://qiita.com/rspmharada7645/items/25c496aee87973bcc7a5springboot お試しAPI
https://qiita.com/Haru_3/items/319b626afcfe8131ad43reactで画面から呼び出し
https://qiita.com/Haru_3/items/d1644226682dfbc3a9a3
- 投稿日:2021-03-08T01:00:23+09:00
【Java】メソッド情報から引数名を取得する方法
概要
Javaのクラス情報(
java.lang.Class
)に含まれるメソッド情報(java.lang.reflect.Method
)からは、メソッドで定義した引数の名前が取得できます。
今回はその引数名の取得方法を紹介します。条件など
- こちらのStackOverflowの記事にある通り、Java8以降が前提となります。Java8以降ではMethodからParameterクラスが取得可能なので、Parameterクラスの
getName()
を使用することで引数名が取得できます。- Javaのコンパイルオプションで、classファイルにパラメータ情報を含める必要があります。この設定を行わないと引数名を取得してもarg0、arg1のような形になります。コンパイルオプションにどういう指定をすれば良いか、リフレクションで引数名を取得するときはjavacに-parametersオプションをつけるにて紹介されています。なお、Intellijでの設定方法はこちらのStackOverflowの記事を参照ください。
実装サンプル
大した内容ではないのですが、MethodクラスからParameterを取得し、それを出力する実装サンプルものせておきます。
public static void outputMethodParamName(Method m) { Arrays.stream(m.getParameters()).forEach(p -> { System.out.println(p.getName()); }); }
- 投稿日:2021-03-08T00:45:07+09:00
Gradle(Spring boot)で作ったWeb APIをHerokuにデプロイしてMySQL接続してダンプをリストアするまで
理由
制作中のAndroidアプリでWeb APIをたたくときにいちいち起動するのが面倒くさい
せっかくなので作ったWeb APIをデプロイしてみたい
初歩的なところでつまずいてしまったので同じ過ちを繰り返さないための備忘録ですゴール
- Gradle(Spring boot)で作ったWeb APIをherokuにデプロイする
- MySQL接続をする
- 自作したAndoroidアプリからたたける
herokuとは
インフラストラクチャ管理が不要で、アプリケーションの開発から実行、運用までのすべてをクラウドで完結できるクラウドベースの PaaS(サービスとしてのプラットフォーム)
(公式より)
https://jp.heroku.com/homeやったこと
環境
- macOS Big Sur 11.2.1
- Spring boot 2.4.3
- MySQL 5.7
0. 事前準備
- MySQL接続するWeb APIをGradleで作る
- 作ったアプリケーションをgit管理している
- herokuアカウントを持っている
- インポートしたいダンプを用意している
1. herokuとアプリの準備
1.1 heroku CLI(Command Line Interface)をインストールする
↑からインストーラをダウンロードしてもいいですし、Homebrewが入っている人はターミナルで↓のコマンドを叩いてもいいです。
terminal$ brew install heroku/brew/herokuちょっぴり時間がかかるのでコーヒーでも飲んで待ちましょう。
1.2 ターミナルでherokuにログインする
terminal$ heroku login heroku: Press any key to open up the browser to login or q to exit (略) Logged in as {メールアドレス}最後に
Logged in as {メールアドレス}
で自分のherokuアカウントに登録しているメールアドレスが表示されたら成功です。2. アプリをデプロイする
2.1 ターミナルでアプリの場所に移動する
初心者のつまずき1: gitで1つのリポジトリにGradleプロジェクトとDockerとAndroidアプリをまとめちゃってるんだけどこの場合はどうすれば?
新たにリポジトリを作ってGradleプロジェクト単体だけ移動しました。
適解かはわからないけど、スターターガイドのサンプルプロジェクトはbuild.gradle
がroot階層にある状態になっていたのでそれに倣いました。そして新しく作ったリポジトリに移動します。
terminal$ cd path/to/repository2.2 herokuでアプリを作成する
terminal$ heroku create {アプリ名} Creating *** ...done https://***.herokuapp.com/ | https://git.heroku.com/***(
{アプリ名}
は省略可能)2.3 コードをデプロイする
terminal$ git push heroku masterできませんでした。
初心者のつまずき2: heroku側とのJDKバージョンのミスマッチ
terminalremote: > Task :compileJava FAILED remote: remote: FAILURE: Build failed with an exception. remote: remote: * What went wrong: remote: Execution failed for task ':compileJava'. remote: > Could not target platform: 'Java SE 11' using tool chain: 'JDK 8 (1.8)'.Java 8でコンパイルしようとしている。。。
公式のサンプルプロジェクトに参考になりそうなファイルがあったので真似してみました。system.propertiesjava.runtime.version=11この
system.properties
というファイルを新たに作って親階層に置きます。コミットを忘れずに。
そして再度プッシュ。成功!!
terminal... remote: remote: BUILD SUCCESSFUL in 54s remote: 4 actionable tasks: 4 executed remote: -----> Discovering process types remote: Procfile declares types -> (none) remote: Default types for buildpack -> web remote: remote: -----> Compressing... remote: Done: 92.3M remote: -----> Launching... remote: Released v3 remote: https://***.herokuapp.com/ deployed to Heroku remote: remote: Verifying deploy... done.2.4 起動しているか確認してみる
terminal$ heroku ps:scale web=1 Scaling dynos... done, now running web at 1:Freeもしくは
terminal$ heroku openでアプリ名で生成されたURLにあるアプリが開きます。が、まだデータベース接続の設定をしていないのでエラー画面が表示されます。
3. MySQLと接続する
DB接続をします。herokuはデフォルトではpostgresQLらしいですが今回はMySQLを使います。
ClearDB MySQL
とJawsDB MySQL
というアドオンがあり、どちらも5MBまでは無料で使えます。
- ClearDB MySQL: https://elements.heroku.com/addons/cleardb
- JawsDB MySQL: https://elements.heroku.com/addons/jawsdb
ただ、注意が必要なのが
ClearDB MySQL
はバージョンがデフォルトで5.6
で、かつ無料プランでは変えられないそうなので、それ以外を使っている人はJawsDB
を使うしかなさそうです。ちなみにJawsDB MySQLのデフォルトは5.7
とのこと。(2021年3月時点)私は
5.7
を使っていたのでJawsDB MySQL
を使用します。3.1 DB作成
terminal$ heroku addons:create jawsdb:kitefin --name=[your DB name] --version=5.7
--name=[your DB name]
オプションでデータベースの名前を任意に変えられるらしい。重要
それよりも大事なのが--version=5.7
オプション。後述しますが、最初これをつけなかったせいでダンプのインポートに失敗しました。というか知らずにClearDB MySQLを入れていたので全然だめだった。
もしダンプをインポートしようと考えている場合は、エクスポートしたMySQLのバージョンと同じものを設定すべし。3.2 接続に必要な情報を取得
一旦、configを見てみます。
terminal$ heroku config JAWSDB_URL: mysql://******いましがた取得した
CLEARDB_DATABASE_URL
をよく見ると以下のようなフォーマットになっています。mysql://[username]:[password]@[host]:[port]/[database name]ただ今回はGradle用に
JDBC_DATABASE_URL
を使用します。が、あとでダンプをインポートするときに使うのでメモはしておきましょう。
公式の説明にはJava、Scala、Clojure、Gradle 用の公式 Heroku buildpack では、dyno の起動時に JDBC_DATABASE_URL 環境変数の作成を試みます。
とあるので、特に何もしなくても変数を作ってくれるみたいです。
試しに以下のコマンドで表示してみます。terminal$ heroku run echo \$JDBC_DATABASE_URL jdbc:mysql://*******/*******?password=*******&reconnect=true&user=*******ばっちり!
今回のGradleアプリケーションはapplication.yml
ファイルでデータベースを設定していたので、それぞれ値を以下のように修正します。application.ymlspring: datasource: url: ${JDBC_DATABASE_URL} username: ${JDBC_DATABASE_USERNAME} password: ${JDBC_DATABASE_PASSWORD}3.3 確認
再度
$ heroku open
してみます。
ルート直下に表示用のビューを用意している場合はそれが表示されるはずです。
私は用意していないので404 エラーですが。とはいえ問題なくデータベース接続はできていそうです。
3.4 ダンプをインポートする
いつものコマンドです。各パラメータには3.2 でメモした値を使います。
terminal$ mysql -u[username] -p[password] [database name] --host=[host] < [path/to/your/dump]初心者のつまずき3: MySQLのバージョンが違うのでダンプインポートに失敗する
(結局、根本原因は2択から絞れませんでした。)
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version最初にClearDBでDBを作るときにバージョンを指定しなかったらこんなエラーがでました。
???と思ってバージョンを確認してみることに。
terminal$ mysql -u[username] -p[password] [database name] --host=[host] Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 205064 Server version: 5.6.50-log MySQL Community Server (GPL) Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>バージョンが
5.6
だった。というわけで一旦このDBは削除してバージョン指定して再度作り直しましたが、バージョン指定したはずなのにやっぱり5.6
。どうやら無料プランでは変えられないらしい?なのでJawsDB
に変えました。
初心者のつまずき4: 短時間に接続を繰り返しすぎて上限超える
うまくいかないなあと何度もダンプインポートのコマンドをたたきすぎたせいか、やりすぎって怒られてしまいました。1時間くらい放置してから再度試すことにします。
java.sql.SQLSyntaxErrorException: User '********' has exceeded the 'max_user_connections' resource (current value: 10)
初心者のつまずき5: MySQLのバージョン合わせてもダンプインポートに失敗する
1時間後、無事接続できるようにはなりましたが、なぜかまだ同じエラーが出ます。バージョン合わせたはずなのに。
ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'mysqldump: [Warning] Using a password on the command line interface can be insec' at line 1ダンプファイルを開いて1行目を確認すると、エラー文言にも出ている警告文
mysqldump: ~ be insec
が書いてあるだけでした。
つまりインポートする処理には影響なし。
そして、ちょっとしたバージョンの差でもちょっとした書き方の違いでエラーになることがあるということを見かけたので、思い切ってエラーだと言われている1行目を削除することにしました。するとうまくいきました!!!
結局バージョンの違いのせいだったのか1行目の警告文のせいだったのか、はたまたその両方だったのかは今となってはわかりませんが、似たような事象に陥っている人がいたら試してみてください。
4 APIをたたいてDBのデータを取得してみる
ようやくWebAPIとDBの接続が完了したのでAPIをたたいてみて動作確認をします。
成功!!!
このあとAndroidアプリでもちゃんとデータが取ってこれていることを確認できました。
めでたしめでたし。おまけ
ちなみにお手軽にGET、POST、PUT、DELETEなどなどのAPIを叩くことのできるこのアプリケーション。めっちゃ便利です。
Advanced REST client - Chrome ウェブストア参考