iOS 15 では、「テキスト認識表示」を使って、写真の中のテキストをコピーして共有したり、翻訳したり、そのまま電話をかけるなど、さまざまなことができます。また、「画像を調べる」を使えば、写真の被写体について簡単に調べることができます。
⇧ Appleの技術力が半端ない...
標準の機能で「光学文字認識(OCR:Optical Character Recognition/Reader)」とかできてしまうって、どんだけ~!
そして、Twitterで見かけた「ロボット」の映像が衝撃的...
Humanoid Robots
— Figen (@TheFigen) 2021年12月2日
pic.twitter.com/8OXZmqEs46
⇧ なんか、こういう映像を見せられてしまうと、
⇧ 著名人とかが、「危機感」を抱くべきと提唱していた点も納得できてしまう気がしますね。
毎度のこと、冒頭から脱線しましたが、今回は、Javaについての話です。
「Write once, run anywhere」の謳い文句で「OS(Operation Systems)」に依存することなく動くことが、Javaの魅力という事ですが、開発環境には依存してしまうことを痛感する今日この頃です。
レッツトライ~。
Builder pattern(one of the Gang of Four design patterns)とは?
まずは、Javaに限らない、一般的な「Builder pattern 」とは?
The builder pattern is a design pattern designed to provide a flexible solution to various object creation problems in object-oriented programming. The intent of the Builder design pattern is to separate the construction of a complex object from its representation. It is one of the Gang of Four design patterns.
⇧「one of the Gang of Four design patterns」とあるように、「Gang of Four design patterns」の中の1つということで、そも「Gang of Four design patterns」って?
Design Patterns: Elements of Reusable Object-Oriented Software (1994) is a software engineering book describing software design patterns. The book was written by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, with a foreword by Grady Booch. The book is divided into two parts, with the first two chapters exploring the capabilities and pitfalls of object-oriented programming, and the remaining chapters describing 23 classic software design patterns. The book includes examples in C++ and Smalltalk.
It has been influential to the field of software engineering and is regarded as an important source for object-oriented design theory and practice. More than 500,000 copies have been sold in English and in 13 other languages. The authors are often referred to as the Gang of Four (GoF).
⇧ 「先人の知恵」という感じで、「ソフトウェア開発」におけるノウハウっていうことらしいですが、「オブジェクト指向プログラミング(OOP:Object Oriented Programing)」における23個の有益な「デザインパターン」ということですと。
で、そのうちの1つが「Builder pattern」ということで、
- Builder
- Abstract interface for creating objects (product).
- ConcreteBuilder
- Provides implementation for Builder. It is an object able to construct other objects. Constructs and assembles parts to build the objects.
⇧ という感じで、「インスタンス」の生成の処理を他のクラスに委譲してるってことだとは思うんだけど、「Factory Method pattern」との違いって?って思ったんだけど、同様の疑問を抱いた方がおられて、「stackoverflow」で質問が上がってました。
What is the difference between the Builder design pattern and the Factory design pattern?
⇧ ってな「問いかけ」に対して、いろいろ回答があり、「Abstract factory pattern」とかも比較対象に上がってきたりで、なかなかに使い分けが難しい感がありますな...
-
Constructing a complex object step by step : builder pattern
-
A simple object is created by using a single method : factory method pattern
-
Creating Object by using multiple factory method : Abstract factory pattern
さらに、
これの明確な答えはよく分からないが、「継承か委譲か問題」と関係が深いように思う。
Template Methodだとインスタンス生成はスーパークラスが担い(継承)、BuilderパターンだとDirectorが担う(委譲)。
BuilderパターンとFactory Method・Template Methodパターンの違いは? - yynsmk's tech blog
どちらでも良さそうだが、委譲の方がクラス同士が疎結合で抽象度が高く良いらしいし、GoFのデザインパターンでも継承よりも委譲が推奨されている。
なので、とりあえずBuilderパターンは委譲を使うものなんだと覚えておこう。
BuilderパターンとFactory Method・Template Methodパターンの違いは? - yynsmk's tech blog
⇧ 上記サイト様によりますと「Template Method pattern」とも似てるという話も。
ちなみに、Wikipediaさんの「Design Patterns」の説明によると、「Gang of Four (GoF)」の23個の「design pattern」のことについてだとは思うけど、大まかに、
- Creational
Creational patterns are ones that create objects, rather than having to instantiate objects directly. This gives the program more flexibility in deciding which objects need to be created for a given case. - Structural
These concern class and object composition. They use inheritance to compose interfaces and define ways to compose objects to obtain new functionality. - Behavioral
Most of these design patterns are specifically concerned with communication between objects.
の3つのTypeに分類できるらしく、
No. | Type | pattern name | description |
---|---|---|---|
1 | 1 | Abstract factory | groups object factories that have a common theme. |
2 | 1 | Builder | constructs complex objects by separating construction and representation. |
3 | 1 | Factory method | creates objects without specifying the exact class to create. |
4 | 1 | Prototype | creates objects by cloning an existing object. |
5 | 1 | Singleton | restricts object creation for a class to only one instance. |
6 | 2 | Adapter | allows classes with incompatible interfaces to work together by wrapping its own interface around that of an already existing class. |
7 | 2 | Bridge | decouples an abstraction from its implementation so that the two can vary independently. |
8 | 2 | Composite | composes zero-or-more similar objects so that they can be manipulated as one object. |
9 | 2 | Decorator | dynamically adds/overrides behaviour in an existing method of an object. |
10 | 2 | Facade | provides a simplified interface to a large body of code. |
11 | 2 | Flyweight | reduces the cost of creating and manipulating a large number of similar objects. |
12 | 2 | Proxy | provides a placeholder for another object to control access, reduce cost, and reduce complexity. |
13 | 3 | Chain of responsibility | delegates commands to a chain of processing objects. |
14 | 3 | Command | creates objects that encapsulate actions and parameters. |
15 | 3 | Interpreter | implements a specialized language. |
16 | 3 | Iterator | accesses the elements of an object sequentially without exposing its underlying representation. |
17 | 3 | Mediator | allows loose coupling between classes by being the only class that has detailed knowledge of their methods. |
18 | 3 | Memento | provides the ability to restore an object to its previous state (undo). |
19 | 3 | Observer | is a publish/subscribe pattern, which allows a number of observer objects to see an event. |
20 | 3 | State | allows an object to alter its behavior when its internal state changes. |
21 | 3 | Strategy | allows one of a family of algorithms to be selected on-the-fly at runtime. |
22 | 3 | Template method | defines the skeleton of an algorithm as an abstract class, allowing its subclasses to provide concrete behavior. |
23 | 3 | Visitor | separates an algorithm from an object structure by moving the hierarchy of methods into one object. |
⇧ という感じらしいけど、「pattern name」については、結構、ネットの情報を見た感じでも、統一感が無いので、認識合わせしたほうが良いのかもしれないのですが、参照元はハッキリしといたほうが良さ気ですかね、例えば、『「Wikipedia」の「Design pattern」を参照した限りでは』みたいな感じで。
Builder pattern(Effective Java’s)
で、Javaだと「Effective Java」という書籍の「Builder pattern」が有名らしく、
In the last post, I wrote about the Gang of Four (GoF) Builder Design Pattern. In this post, we will look at the builder pattern from the Effective Java by Joshua Bloch. The builder pattern is also called Joshua Bloch’s (Effective Java’s) builder pattern. It is commonly confused with the Gang of Four Builder Design pattern.
The Effective Java’s Builder pattern provides a nice and a safe way to build or construct an object that has a lot of optional parameters or instance variables.
⇧ という感じで、「Gang of Four(GoF)」の「design pattern」の「Builder pattern」とは異なるらしいですと、ややこしい話にしてくれますな...。
LombokのbuilderはどっちのBuilder patternを実現してる?
で、さらにややこしいことに、Javaには「Lombok」という「ライブラリ」が存在(Javaの標準APIではない)するのですが、その前に「Lombok」とは?
"Boilerplate" is a term used to describe code that is repeated in many parts of an application with little alteration. One of the most frequently voiced criticisms of the Java language is the volume of this type of code that is found in most projects. This problem is frequently a result of design decisions in various libraries, but it's exacerbated by limitations in the language itself. Project Lombok aims to reduce the prevalence of some of the worst offenders by replacing them with a simple set of annotations.
⇧ とあるように「Boilerplate」をコーディングする手間を省きますよ、そのために「anotation」を付与すればOKということらしい。
「Boilerplate」とは?
Boilerplate code
In computer programming, boilerplate is the sections of code that have to be included in many places with little or no alteration. Such boilerplate code is particularly salient when the programmer must include a lot of code for minimal functionality.
⇧「ほとんど変わらないけれど、無くてはならないコード」ということで、Javaだと、「getter」「setter」とかが「Boilerplate」の良い例かと。
で、そういった「Boilerplate」をプログラマー自身がコーディングすることなく、コーディングした時と同じように機能だけ利用できるようにしてくれるのが「Lombok」ということらしい。
「Object Computing, Inc.」って企業で「Lombok」について紹介されてたので、
⇧ Githubに「Lombok」の「プロジェクト」の情報があるのかと思ったのだけれど、何か別に管理してる模様。
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java. Never write another getter or equals method again, with one annotation your class has a fully featured builder, automate your logging variables, and much more.
⇧ Java用の「ライブラリ」ということらしい。
まぁ、脱線しまくりで申し訳ないのですが、この「Lombok」が用意してくれてる「annotation」を利用すると、「Boilerplate」をコーディングしなくても良くなるということのようなのですが、「annotation」の一覧としては、
The Lombok javadoc is available, but we advise these pages.
⇧ とあって、上記のページによると、2021年12月4日(土)時点では、
No. | anotation | description |
---|---|---|
1 | @NonNull | or: How I learned to stop worrying and love the NullPointerException. |
2 | @Cleanup | Automatic resource management: Call your close() methods safely with no hassle. |
3 | @Getter | public int getFoo() {return foo;} again. |
4 | @Setter | Never write |
5 | @ToString | No need to start a debugger to see your fields: Just let lombok generate a toString for you! |
6 | @EqualsAndHashCode | Equality made easy: Generates hashCode and equals implementations from the fields of your object.. |
7 | @NoArgsConstructor | Constructors made to order: Generates constructors that take no arguments |
8 | @RequiredArgsConstructor | Constructors made to order: Generates constructors that take one argument per final / non-nullfield |
9 | @AllArgsConstructor | Constructors made to order: Generates constructors that take one argument for every field. |
10 | @Data | All together now: A shortcut for @ToString, @EqualsAndHashCode, @Getter on all fields, and @Setter on all non-final fields, and @RequiredArgsConstructor! |
11 | @Value | Immutable classes made very easy. |
12 | @Builder | ... and Bob's your uncle: No-hassle fancy-pants APIs for object creation! |
13 | @SneakyThrows | To boldly throw checked exceptions where no one has thrown them before! |
14 | @Synchronized | synchronized done right: Don't expose your locks. |
15 | @With | Immutable 'setters' - methods that create a clone but with one changed field. |
16 | @Getter(lazy=true) | Laziness is a virtue! |
17 | @Log | Captain's Log, stardate 24435.7: "What was that line again?" |
となってるんだけど、このページで紹介してる「annotation」は全量ってわけではないらしく、APIドキュメントのほうを見ると、他にも「annotation」が存在する模様...う~ん、何だろうね、よく使うものを並べてみました、なのであれば、一言言及しておいて欲しい気がしますな...
そもそも「パッケージ」が、
No. | Package | Description |
---|---|---|
1 | lombok | This package contains all the annotations and support classes you need as a user of lombok. |
2 | lombok.experimental | This package contains the annotations and support classes you need as a user of lombok, for all features which aren't (yet) supported as a first class feature. |
3 | lombok.extern.apachecommons | |
4 | lombok.extern.flogger | |
5 | lombok.extern.java | |
6 | lombok.extern.jbosslog | |
7 | lombok.extern.log4j | |
8 | lombok.extern.slf4j |
8個あるというね...。
まぁ、それはさておき、「Lombok」には、
- @Builder
- @SuperBuilder
っていう、名前から察するに「Builder pattern」に関係してるんじゃないかと推測される「annotation」が存在するんだけども、実際のところ、「Builder pattern」に関係してるのか?
残念ながら、公式のドキュメントとかでは「Builder pattern」との関係についての言及は見られなかったのですが、
The Lombok library provides a great way to implement the Builder Pattern without writing any boilerplate code: the @Builder annotation.
⇧ 上記サイト様によりますと、「Builder pattern」を実現できる方法の1つとして「Lombok」の「annotation」の「@Builder」があるよ、と言っておりますと。
コーディング例を見た感じでは、「Effective Java」で紹介されてる「Builder pattern」を利用してるっぽい。
nested objectsの場合のBuilder pattern(Effective Java's)を試してみる
インストールしていた、PostgreSQL 14にログインして、「データベース」「スキーマ」「テーブル」を作成しておきます。
⇧「from」は「予約語」だったので、「カラム名」には使えないのでした...
データも入れておきます。
INSERT INTO enum_test.person (id, gender, blood_type, birthday, birthplace, first_name, last_name, height, weight, created_time) VALUES (1, '0', '0', '1921-01-01', '日本', '正造', '佐藤', 158.3, 52.1, current_timestamp), (2, '0', '1', '1931-01-01', 'クルンテープ・プラマハーナコーン・アモーンラッタナコーシン・マヒンタラーユッタヤー・ マハーディロックホップ・ノッパラット・ラーチャタニーブリローム・ラドムウーチャンウェートマハーサターン・ アモーンピマーン・アワターンサティット・サッカタッティヤウィサヌカムプラシット', 'トンチャイ', 'メーキンタイ', 164.5, 56.3, current_timestamp), (3, '1', '2', '1941-01-01', 'アメリカ合衆国', 'ヨナハン', 'スティーブ', 167.2, 48.7, current_timestamp), (4, '1', '3', '1951-01-01', 'スペイン', 'カルメン', 'アギレラ', 161.1, 45.1, current_timestamp);
「A5:SQL Mk-2」というツールで、「テーブル」を確認してみると、
データが入ってるのが確認できました。
もう少しデータを追加しときます。
INSERT INTO enum_test.person (id, gender, blood_type, birthday, birthplace, first_name, last_name, height, weight, created_time) VALUES (5, '0', '0', '1921-01-01', '日本', '一郎', '鈴木', 178.3, 72.1, current_timestamp), (6, '0', '1', '1931-01-01', 'クルンテープ・プラマハーナコーン・アモーンラッタナコーシン・マヒンタラーユッタヤー・ マハーディロックホップ・ノッパラット・ラーチャタニーブリローム・ラドムウーチャンウェートマハーサターン・ アモーンピマーン・アワターンサティット・サッカタッティヤウィサヌカムプラシット', 'ヤム', 'メーキンタイ', 174.5, 64.3, current_timestamp), (7, '1', '2', '1941-01-01', 'アメリカ合衆国', 'マリア', 'ダニエル', 159.2, 45.7, current_timestamp), (8, '1', '3', '1951-01-01', 'スペイン', 'ミシェル', 'ホセ', 151.1, 45.1, current_timestamp);
で、Eclipseで「Spring Boot」のプロジェクト(「ビルドツール」には「Gradle」を選択してます)を作成します。
今回、作成・修正したファイルは以下のような感じになりました。
■/nest-builder/src/main/resources/application.properties
# PostgreSQLを利用するための設定 spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:5434/mydb spring.datasource.username=postgres spring.datasource.password=postgres
■/nest-builder/build.gradle
plugins { id 'org.springframework.boot' version '2.6.1' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' // https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '2.6.1' // https://mvnrepository.com/artifact/org.postgresql/postgresql implementation group: 'org.postgresql', name: 'postgresql', version: '42.3.1' // https://mvnrepository.com/artifact/com.mchange/c3p0 implementation group: 'com.mchange', name: 'c3p0', version: '0.9.5.5' } test { useJUnitPlatform() }
■/nest-builder/src/main/java/com/example/demo/code/GenderEnum.java
package com.example.demo.code; import lombok.AllArgsConstructor; import lombok.Getter; @Getter @AllArgsConstructor public enum GenderEnum { MALE("0", "男性"), FEMALE("1", "女性"); private String code; private String value; }
■/nest-builder/src/main/java/com/example/demo/code/converter/GenderEnumConverter.java
package com.example.demo.code.converter; import java.util.Objects; import java.util.stream.Stream; import javax.persistence.AttributeConverter; import javax.persistence.Converter; import com.example.demo.code.GenderEnum; @Converter(autoApply = true) public class GenderEnumConverter implements AttributeConverter<GenderEnum, String> { @Override public String convertToDatabaseColumn(GenderEnum genderEnum) { // if (Objects.isNull(genderEnum)) { return null; } return genderEnum.getCode(); } @Override public GenderEnum convertToEntityAttribute(String dbData) { // if (Objects.isNull(dbData)) { return null; } return Stream.of(GenderEnum.values()) .filter(genderEnum -> genderEnum.getCode().equals(dbData)) .findFirst() .orElseThrow(IllegalArgumentException::new); } }
■/nest-builder/src/main/java/com/example/demo/code/BloodTypeEnum.java
package com.example.demo.code; import lombok.AllArgsConstructor; import lombok.Getter; @Getter @AllArgsConstructor public enum BloodTypeEnum { A("0", "A型"), B("1", "B型"), AB("2", "AB型"), O("3", "O型"); private String code; private String value; }
■/nest-builder/src/main/java/com/example/demo/code/converter/BloodTypeEnumConverter.java
package com.example.demo.code.converter; import java.util.Objects; import java.util.stream.Stream; import javax.persistence.AttributeConverter; import com.example.demo.code.BloodTypeEnum; public class BloodTypeEnumConverter implements AttributeConverter<BloodTypeEnum, String> { @Override public String convertToDatabaseColumn(BloodTypeEnum bloodTypeEnum) { // if (Objects.isNull(bloodTypeEnum)) { return null; } return bloodTypeEnum.getCode(); } @Override public BloodTypeEnum convertToEntityAttribute(String dbData) { // if (Objects.isNull(dbData)) { return null; } return Stream.of(BloodTypeEnum.values()) .filter(bloodTypeEnum -> bloodTypeEnum.getCode().equals(dbData)) .findFirst() .orElseThrow(IllegalArgumentException::new); } }
■/nest-builder/src/main/java/com/example/demo/dto/BaseDto.java
package com.example.demo.dto; import java.time.LocalDate; import com.example.demo.code.BloodTypeEnum; import com.example.demo.code.GenderEnum; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @Data @SuperBuilder @NoArgsConstructor @AllArgsConstructor public class BaseDto { private Long id; private GenderEnum genderEnum; private BloodTypeEnum bloodTypeEnum; private int age; private LocalDate birthDay; }
■/nest-builder/src/main/java/com/example/demo/dto/SeptuagenarianDto.java
package com.example.demo.dto; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.SuperBuilder; /** * 70歳代 */ @Data @SuperBuilder @EqualsAndHashCode(callSuper=false) public class SeptuagenarianDto extends BaseDto { private String septuaWord; }
■/nest-builder/src/main/java/com/example/demo/dto/OctogenarianDto.java
package com.example.demo.dto; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.SuperBuilder; /** * 80歳代 */ @Data @SuperBuilder @EqualsAndHashCode(callSuper=false) public class OctogenarianDto extends BaseDto { private String octoWord; }
■/nest-builder/src/main/java/com/example/demo/dto/NonagenarianDto.java
package com.example.demo.dto; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.SuperBuilder; /** * 90歳代 */ @Data @SuperBuilder @EqualsAndHashCode(callSuper=false) public class NonagenarianDto extends BaseDto { private String nonaWord; }
■/nest-builder/src/main/java/com/example/demo/dto/CentenarianDto.java
package com.example.demo.dto; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.SuperBuilder; /** * 100歳以上 */ @Data @SuperBuilder @EqualsAndHashCode(callSuper=false) public class CentenarianDto extends BaseDto { private String centenarianWord; }
■/nest-builder/src/main/java/com/example/demo/dto/SeniorCitizenDto.java
package com.example.demo.dto; import lombok.Builder; import lombok.Data; /** * 高齢者 */ @Data @Builder public class SeniorCitizenDto { private SeptuagenarianDto septuagenarianDto; private OctogenarianDto octogenarianDto; private NonagenarianDto nonagenarianDto; private CentenarianDto centenarianDto; }
■/nest-builder/src/main/java/com/example/demo/entity/BaseEntity.java
package com.example.demo.entity; import java.time.OffsetDateTime; import lombok.Data; @Data public class BaseEntity { private OffsetDateTime createdTime = OffsetDateTime.now(); private OffsetDateTime updatedTime; }
■/nest-builder/src/main/java/com/example/demo/entity/PersonEntity.java
package com.example.demo.entity; import java.time.LocalDate; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import com.example.demo.code.BloodTypeEnum; import com.example.demo.code.GenderEnum; import lombok.Data; import lombok.EqualsAndHashCode; @Data @Entity @Table(name="person", schema="enum_test") @EqualsAndHashCode(callSuper=false) public class PersonEntity extends BaseEntity { @Id @Column(name="id") private Long id; @Column(name="gender") private GenderEnum genderEnum; @Column(name="blood_type") private BloodTypeEnum bloodTypeEnum; @Column(name="birthday") private LocalDate birthDay; @Column(name="birthplace") private String birthPlace; @Column(name="first_name") private String firstName; @Column(name="last_name") private String lastName; @Column(name="height") private float height; @Column(name="weight") private float weight; @Column(name="img_url") private String imgUrl; }
■/nest-builder/src/main/java/com/example/demo/repository/PersonRepository.java
package com.example.demo.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.example.demo.entity.PersonEntity; @Repository public interface PersonRepository extends JpaRepository<PersonEntity, Long> { }
■/nest-builder/src/main/java/com/example/demo/service/PersonService.java
package com.example.demo.service; import java.util.List; import org.springframework.stereotype.Service; import com.example.demo.dto.SeniorCitizenDto; @Service public interface PersonService { public List<SeniorCitizenDto> createSeniorCitizenDtoList(); }
■/nest-builder/src/main/java/com/example/demo/service/impl/PersonServiceImpl.java
package com.example.demo.service.impl; import java.time.LocalDate; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.example.demo.dto.CentenarianDto; import com.example.demo.dto.NonagenarianDto; import com.example.demo.dto.OctogenarianDto; import com.example.demo.dto.SeniorCitizenDto; import com.example.demo.dto.SeptuagenarianDto; import com.example.demo.entity.PersonEntity; import com.example.demo.repository.PersonRepository; import com.example.demo.service.PersonService; @Service public class PersonServiceImpl implements PersonService { @Autowired private PersonRepository personRepository; public List<PersonEntity> getAllPesronEntity() { return personRepository.findAll(); } public List<SeniorCitizenDto> createSeniorCitizenDtoList() { List<PersonEntity> personEntityList = this.getAllPesronEntity(); Map<Long, List<PersonEntity>> groupGenerationsMap = personEntityList .stream() .collect(Collectors.groupingBy(personEntity -> (Long .valueOf(ChronoUnit.YEARS.between(personEntity.getBirthDay(), LocalDate.now()) / 10) * 10))); // Set<Long> keySet = groupGenerationsMap.keySet(); int maxValueInMap = groupGenerationsMap.values().stream().mapToInt(List::size).max().getAsInt(); CentenarianDto centenarianDto = null; // 100歳オーバー NonagenarianDto nonagenarianDto = null; // 90歳代 OctogenarianDto octogenarianDto = null; // 80歳代 SeptuagenarianDto septuagenarianDto = null; // 70歳代 List<SeniorCitizenDto> seniorCitizenDtoList = new ArrayList<>(); for (int index = 0; index < maxValueInMap; index++) { if (!groupGenerationsMap.get(100L).isEmpty() || Objects.nonNull(groupGenerationsMap.get(100L).get(index)) || Objects.nonNull(groupGenerationsMap.get(100L).get(index).getBirthDay()) || ChronoUnit.YEARS .between(groupGenerationsMap.get(100L).get(index).getBirthDay(), LocalDate.now()) >= 100) { centenarianDto = CentenarianDto.builder() .id(groupGenerationsMap.get(100L).get(index).getId()) .birthDay(groupGenerationsMap.get(100L).get(index).getBirthDay()) .age(Math.toIntExact(Long.valueOf( ChronoUnit.YEARS.between(groupGenerationsMap.get(100L).get(index).getBirthDay(), LocalDate.now())))) .genderEnum(groupGenerationsMap.get(100L).get(index).getGenderEnum()) .bloodTypeEnum(groupGenerationsMap.get(100L).get(index).getBloodTypeEnum()) .centenarianWord("百(ひゃく)にして") .build(); } if (!groupGenerationsMap.get(90L).isEmpty() || Objects.nonNull(groupGenerationsMap.get(90L).get(index)) || Objects.nonNull(groupGenerationsMap.get(90L).get(index).getBirthDay()) || ChronoUnit.YEARS .between(groupGenerationsMap.get(90L).get(index).getBirthDay(), LocalDate.now()) >= 90) { nonagenarianDto = NonagenarianDto.builder() .id(groupGenerationsMap.get(90L).get(index).getId()) .birthDay(groupGenerationsMap.get(90L).get(index).getBirthDay()) .age(Math.toIntExact(Long.valueOf( ChronoUnit.YEARS.between(groupGenerationsMap.get(90L).get(index).getBirthDay(), LocalDate.now())))) .genderEnum(groupGenerationsMap.get(90L).get(index).getGenderEnum()) .bloodTypeEnum(groupGenerationsMap.get(90L).get(index).getBloodTypeEnum()) .nonaWord("九十(きゅうじゅう)にして") .build(); } if (!groupGenerationsMap.get(80L).isEmpty() || Objects.nonNull(groupGenerationsMap.get(80L).get(index)) || Objects.nonNull(groupGenerationsMap.get(80L).get(index).getBirthDay()) || ChronoUnit.YEARS .between(groupGenerationsMap.get(80L).get(index).getBirthDay(), LocalDate.now()) >= 80) { octogenarianDto = OctogenarianDto.builder() .id(groupGenerationsMap.get(80L).get(index).getId()) .birthDay(groupGenerationsMap.get(80L).get(index).getBirthDay()) .age(Math.toIntExact(Long.valueOf( ChronoUnit.YEARS.between(groupGenerationsMap.get(80L).get(index).getBirthDay(), LocalDate.now())))) .genderEnum(groupGenerationsMap.get(80L).get(index).getGenderEnum()) .bloodTypeEnum(groupGenerationsMap.get(80L).get(index).getBloodTypeEnum()) .octoWord("八十(はちじゅう)にして") .build(); } if (!groupGenerationsMap.get(70L).isEmpty() || Objects.nonNull(groupGenerationsMap.get(70L).get(index)) || Objects.nonNull(groupGenerationsMap.get(70L).get(index).getBirthDay()) || ChronoUnit.YEARS .between(groupGenerationsMap.get(70L).get(index).getBirthDay(), LocalDate.now()) >= 70) { septuagenarianDto = SeptuagenarianDto.builder() .id(groupGenerationsMap.get(70L).get(index).getId()) .birthDay(groupGenerationsMap.get(70L).get(index).getBirthDay()) .age(Math.toIntExact(Long.valueOf( ChronoUnit.YEARS.between(groupGenerationsMap.get(70L).get(index).getBirthDay(), LocalDate.now())))) .genderEnum(groupGenerationsMap.get(70L).get(index).getGenderEnum()) .bloodTypeEnum(groupGenerationsMap.get(70L).get(index).getBloodTypeEnum()) .septuaWord("七十(しちじゅう)にして心(こころ)の欲っする所(ところ)に従がえども、矩(のり)を踰えず") .build(); } seniorCitizenDtoList.add(SeniorCitizenDto.builder() .centenarianDto(centenarianDto) .nonagenarianDto(nonagenarianDto) .octogenarianDto(octogenarianDto) .septuagenarianDto(septuagenarianDto) .build()); } return seniorCitizenDtoList; } }
■/nest-builder/src/main/java/com/example/demo/controller/PersonController.java
package com.example.demo.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.example.demo.dto.SeniorCitizenDto; import com.example.demo.service.PersonService; @RestController @RequestMapping("/") public class PersonController { @Autowired private PersonService personService; @GetMapping public String hello() { return "Hello"; } @RequestMapping("/personData") public List<SeniorCitizenDto> getPerson() { return personService.createSeniorCitizenDtoList(); } }
■/nest-builder/src/main/java/com/example/demo/NestBuilderApplication.java
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class NestBuilderApplication { public static void main(String[] args) { SpringApplication.run(NestBuilderApplication.class, args); } }
⇧ ってな感じで、編集・保存して、「Spring Boot App」で実行。
ブラウザから「http://localhost:8080/」にアクセス。
ブラウザから「http://localhost:8080/personData」にアクセス。
という感じで、一応、動くことは動いたけども、JavaのコーディングがStream APIとかでもっとスマートに書けたら良かったんですが、やり方が分からんかったです...
2021年12月8日(水)追記:↓ ここから
なんか、「AttributeCoverter」って「JPA(Java Persistence API)」の「Native Query」で使うとエラーになってしまうらしい...
そう考えると、普通に
- @Enumerated(EnumType.ORDINAL)
- @Enumerated(EnumType.String)
を使うのが良いってことですかね。いやはや、「AttributeCoverter」の存在意義が微妙ですな...
2021年12月8日(水)追記:↑ ここまで
ちなみに、クラスのフィールドに他のクラスが含まれてて、どちらのクラスもbuilderできるのであれば、以下のような感じで、builderの入れ子でコーディングすることもできるかと、値を設定してないのでエラーになるけど。
SeniorCitizenDto.builder() .centenarianDto(CentenarianDto.builder() .id() .birthDay() .age() .genderEnum() .bloodTypeEnum() .septuaWord() .build() ) .nonagenarianDto(NonagenarianDto.builder() .id() .birthDay() .age() .genderEnum() .bloodTypeEnum() .septuaWord() .build() ) .octogenarianDto(OctogenarianDto.builder() .id() .birthDay() .age() .genderEnum() .bloodTypeEnum() .septuaWord() .build() ) .septuagenarianDto(SeptuagenarianDto.builder() .id() .birthDay() .age() .genderEnum() .bloodTypeEnum() .septuaWord() .build() ) .build();
毎回モヤモヤ感が半端ない...
ちなみに、「タイ」の国名が世界一長いってネット情報があったんですが、
バンコク(タイ語名:クルンテープ・マハーナコーン※/英語名:Bangkok)
※正式名称:クルンテープ・マハーナコーン・アモーンラッタナコーシン・マヒンタラーユッタヤー・マハーディロック・ポップ・ノッパラット・ラーチャタニーブリーロム・ウドムラーチャニウェートマハーサターン・アモーンピマーン・アワターンサティット・サッカタッティヤウィサヌカムプラシット
⇧ 正しくは、「タイ」の「首都」名が世界一長いってことのようでした。
じゃあ、国名が世界一長いのは?
ユナイテッドキングダムオブグレートブリテンアンドノーザンアイルランド。「ユナイテッドキングダム」や「グレートブリテン」と聞くとピンとくる方も多いと思いますが、イギリスの正式名称です。社会の授業で「グレートブリテンおよび北部アイルランド連合王国」と覚えた人も多いのではないでしょうか。
⇧ 国名が世界一長いのは「イギリス」の正式名称らしいです。
今回はこのへんで。