yucatio@システムエンジニア

趣味で作ったものいろいろ

『Spring Framework超入門 ~やさしくわかるWebアプリ開発~』を読んだ

Spring Framework超入門 ~やさしくわかるWebアプリ開発~』(著: 樹下雅章)を読みました。


感じたことを書いておきます。

読みやすい

著者がプログラミングの講師ということもあってか、日本語が読みやすいです。内容がスッと入ってきます。

動くものを作ることで理解が深まる

本書はサンプルコードが豊富です。サンプルを書き写すことによって、今までなんとなく理解していたSpringの挙動が、実感をもって理解ができました。サンプルコードの量も、ちょうどよいと思いました。

内容は易しめ

内容は、Spring DI、AOP、Data、Validation、Thymeleafの基礎的な話題です。それぞれ丁寧なサンプルがあります。最後に簡単なアプリ作成を通して内容を総ざらいします。

Web開発の基礎知識は必要

Javaの基礎知識はもちろん、Webの基礎知識は読む前に必要と感じました。クライアントサーバ、DB、MVClombokなど簡単には説明されていますが少ないです。

Webの知識があってSpringが初めてならかなりおすすめ

というわけでWebの知識があってSpringが初めてならばかなりおすすめできます。SpringはDIなど初めてだとちょっと戸惑う部分があるので、書いてみてどう動くか体感するにちょうどよいです。

Springをなんとなくで使っている場合にもおすすめ

チームでSpringを使っていて途中から入ったため、なんとなく周囲のコードに合わせてコードを書いている、という場合にもおすすめです。Springの基礎について丁寧に説明してあるので頭の中が整理されます。また、知らない機能を知れるかもしれません。

個人的にはとても役に立ちました

私自身は「Springをなんとなくで使っている」、に該当していたので、この本で基礎を再確認してよかったです。どの機能をどのように使うか知ることで、現在担当しているアプリの問題点と改善点を発見できたのはよい経験でした。 欲を言えばテストまで書いてほしかったです。

基礎が固まったので今後もっと実践的な書籍も読んでみたいと思いました。

【Java】ネストした型のワイルドカードでエラーが出る場合の対処法

例えば、ListのListで、中身はなんでもよい、みたいな場合があるとする。コードであらわすとこんな感じ。

List<List<?>> wildcardList;

これに、例えばIntegerのListのListを割り当てようとするとエラーになる。

List<List<?>> wildcardList = new ArrayList<List<Integer>>();  // コンパイルエラー
// 型の不一致: ArrayList<List<Integer>> から List<List<?>> には変換できません

これを解消するには、境界型を使用して、

List<? extends List<?>> wildcardList = new ArrayList<List<Integer>>();

のように書くとよい。

参考: Java generics, nested collection of wildcard - Stack Overflow

『Spring Framework 超入門』のレイアウト化の作成がうまく出てこないときの対処法

Spring Framework超入門 ~やさしくわかるWebアプリ開発~』を読んでいます。


問題発生

6章の「レイアウト化の作成」のコードがうまく動きませんでした。

pageA.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorator="~{commons/layout}">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
  <div layout:fragment="content">
    <h1>Page A</h1>
  </div>
</body>
</html>

期待したページ

f:id:yucatio:20220209225619p:plain

実際に表示されたページ

f:id:yucatio:20220209225632p:plain

共通レイアウトであるcommons/layoutが読み込まれていません。

Spring-bootのバージョンは2.6.3です。

対応方法

4行目のlayout:decoratorlayout:decorateにします。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{commons/layout}">

原因

  • Spring boot 2.6からthymeleaf-layout-dialectのバージョン3を使うことになりました。

github.com

  • thymeleaf-layout-dialectのバージョン3でlayout:decoratorが廃止されました。

ultraq.github.io

という2つの要因が重なって動かなかったわけです。Spring boot 2.5まではサンプルコードのままで動くはずです。

Draft.jsで何も出てこない→出てるはず

Facebook社のReact用リッチエディタDraft.js。 Overview | Draft.jsにあるサンプルを試したけれど、何も出てこなかったときのメモ。

ソースコード

import React from 'react';
import {Editor, EditorState} from 'draft-js';
import 'draft-js/dist/Draft.css';

function MyEditor() {
  const [editorState, setEditorState] = React.useState(
    () => EditorState.createEmpty(),
  );

  return <Editor editorState={editorState} onChange={setEditorState} />;
}

export default MyEditor

このMyEditorコンポーネントを親コンポーネントから呼び出しています。

実行結果です。

f:id:yucatio:20211120092127p:plain

何も出てきてません。いえ、実は出ています。Editorがあるあたりをクリックします。

f:id:yucatio:20211120093511p:plain

そうするとカーソルが出てきます。何か文字を入力すると文字が出てきます。これがEditorの本体です。

f:id:yucatio:20211120092517p:plain

見やすくするためにborderを入れましょう。

function MyEditor() {
  const [editorState, setEditorState] = React.useState(
    () => EditorState.createEmpty(),
  );

  return (
    <div style={{border: '1px solid #666666'}}>
      <Editor editorState={editorState} onChange={setEditorState} />
    </div>
  );
}

f:id:yucatio:20211120091912p:plain

これで開発が始められそうです。

参考: reactjs - Can't figure out why draft-js is not shown - Stack Overflow

Javaで1日の始まりと終わりを取得する

Javaで1日の始まりと終わりを取得します。

LoalDateから取得する場合

LocalTime.MINLocalTime.MAXを使います。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse("2021-11-01", formatter);

LocalDateTime startOfDay = date.atTime(LocalTime.MIN);  // 2021-11-01T00:00
LocalDateTime endOfDay = date.atTime(LocalTime.MAX);  // 2021-11-01T23:59:59.999999999

LocalDateTimeから取得する場合

LocalDateTimetoLocalDate()で一旦LocalDateに変換してから、上記と同様にLocalTime.MINLocalTime.MAXを使います。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime datetime = LocalDateTime.parse("2021-11-01 12:15:30", formatter);

LocalDateTime startOfDay = datetime.toLocalDate().atTime(LocalTime.MIN);  // 2021-11-01T00:00
LocalDateTime endOfDay = datetime.toLocalDate().atTime(LocalTime.MAX);  // 2021-11-01T23:59:59.999999999

参考にしたページ

www.baeldung.com

JavaのLocalDateTime.parse()に日付だけ渡すとエラー

JavaのLocalDateTime.parse()で日付のみを指定したらエラーになりました。回避策も書いておきます。

コード

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDateTime datetime = LocalDateTime.parse("2021-11-01", formatter);

エラー

Exception in thread "main" java.time.format.DateTimeParseException: Text '2021-11-01' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 2021-11-01 of type java.time.format.Parsed
    at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:2023)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1958)
    at java.base/java.time.LocalDateTime.parse(LocalDateTime.java:494)

一旦LocalDateでパースし、LocalDateTimeに変換する必要があります。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse("2021-11-01", formatter);
LocalDateTime datetime = date.atStartOfDay();

Firestoreのtransaction中にドキュメントをaddする

Firestoreではtransaction機能が使え、複数の書き込みをまとめることができます。

transactionを使用するときには、以下のコードのようにrunTransactionを利用します。2つ目の引数にトランザクションの内容を書いた関数を渡します。

await runTransaction(db, async (transaction) => {
  // TODO ここにトランザクションの内容を書く
})

コールバックに渡ってくるtransactionにはgetsetupdatedeleteが用意されています。addがありませんね。

f:id:yucatio:20211026160219p:plain

addはdoc()とset()を組み合わせる

add()doc()set()を組み合わせることで実現します。

await runTransaction(db, async (transaction) => {
  const docRef = doc(collection(db, "my_collection"))
  transaction.set(docRef, {
    text: "addded from transaction"
  })
})

こちらを実行すると、以下のようにランダムなIDで登録されているのが分かります。

f:id:yucatio:20211026192835p:plain

環境

firebase: 9.1.3