- 投稿日:2020-06-23T23:40:21+09:00
今更ながらSpring Data JPAに入門してみた
始めに
Doma以外のOR Mapperを触ったことがなかったので、ふと思い立ち興味半分くらいで触ってみました。
Spring Bootアプリケーションへの導入方法 ~ 簡単なAPI作成で感じたDomaとの比較について書きたいと思います。
環境
IDE VSCode
Java 11.0.6
Spring Boot 2.3.1
PostgreSQL 11.6
前提
簡単なAPIを作成して、色々と触ってみたいと思います。作成するAPIは以下の様。
エンドポイント Http Method 概要 備考 /api/employee/{employeeId}
GET 従業員IDに一致する従業員の情報を取得する。 /api/employee
GET 従業員情報を取得する。 検索条件による絞り込みも実施する。 /api/employee
POST 従業員情報を登録する。 /api/employee/{employeeId}
PUT 従業員情報を更新する。 /api/employee/{employeeId}
DELETE 従業員情報を削除する。 API作成
(一応)アプリケーションのひな型作成
Spring Initializer Java SupportというVSCodeプラグインを使用してアプリケーションのひな型を作ります。
このプラグイン自体は、Spring Boot Extension Packに含まれているのでSpring Boot Extension Packをインストールしてもらえば十分です。
対話形式で、ひな型を作成します。コマンドパレットから、「Spring Initializr: Generate a Gradle Project」を選択する。
Javaを選択する。
パッケージ名を入力する。今回は、デフォルトで
com.example
のままとする。プロジェクト名を入力する。お好きな名前をどうぞ。(私は、employee-apiとしました。)
Spring Boot のバージョンを選択する。(2.3.1を選択)
依存ライブラリを選択する。簡単なAPIを作成したかっただけなので以下のライブラリを選択しています。
Spring Boot DevTools / Lombok / Spring Web / Spring Data JPA / PostgreSQL Driver
application.properties
に接続情報を定義するspring.jpa.database=postgresql spring.datasource.platform=postgres spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:5432/sample spring.datasource.username=postgres spring.datasource.password=postgresEntityクラスを定義する
テーブルの共通項目として、
insert_date
,update_date
を定義しています。CommonEntity.java/** * テーブルの共通項目を定義したクラスです。</br> * 全てのEntityクラスはこのクラスを継承して作成します。 */ @MappedSuperclass @Getter @Setter public class CommonEntity { /** データ登録日時 */ @Column(name = "insert_date") @Temporal(TemporalType.DATE) private Date insertdate; /** データ更新日時 */ @Column(name = "update_date") @Temporal(TemporalType.DATE) private Date updateDate; /** * データ登録前に共通的に実行されるメソッド */ @PrePersist public void preInsert() { Date date = new Date(); setInsertdate(date); setUpdateDate(date); } /** * データ更新前に共通的に実行されるメソッド */ @PreUpdate public void preUpdate() { setUpdateDate(new Date()); } }テーブル共通項目を定義した
CommonEntity
を継承し、業務用のEntityクラスを作成します。EmployeeEntity.java@Entity @Table(name = "employee") @Getter @Setter public class EmployeeEntity extends CommonEntity { /** 従業員ID */ @Id @Column(name = "id") @GeneratedValue(strategy = GenerationType.AUTO) private Integer employeeId; /** 従業員名 */ @Column(name = "name") private String employeeName; /** 年齢 */ @Column(name = "age") private Integer age; /** 役職ID */ @Column(name = "position_id") private String positionId; /** 所属部署ID */ @Column(name = "department_id") private String departmentId; }Repository インタフェースを定義する
org.springframework.data.jpa.repository.JpaRepository
を継承したインタフェースを定義します。EmployeeRepository.javapackage com.example.employeeapi.employee; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface EmployeeRepository extends JpaRepository<EmployeeEntity, Integer> { }(参考)
JpaRepository
基本的なCRUD操作を扱えるようなメソッドが用意されています。このインタフェースを継承したインタフェース(今回の例だと、
EmployeeRepository
)では、業務仕様等で予め用意されたメソッドで不十分な場合に独自のメソッドを定義することができます。(Joinしてレコードを取得するなど)JpaRepository.java/* * Copyright 2008-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.data.jpa.repository; import java.util.List; import javax.persistence.EntityManager; import org.springframework.data.domain.Example; import org.springframework.data.domain.Sort; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.QueryByExampleExecutor; /** * JPA specific extension of {@link org.springframework.data.repository.Repository}. * * @author Oliver Gierke * @author Christoph Strobl * @author Mark Paluch */ @NoRepositoryBean public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> { /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#findAll() */ @Override List<T> findAll(); /* * (non-Javadoc) * @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort) */ @Override List<T> findAll(Sort sort); /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable) */ @Override List<T> findAllById(Iterable<ID> ids); /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable) */ @Override <S extends T> List<S> saveAll(Iterable<S> entities); /** * Flushes all pending changes to the database. */ void flush(); /** * Saves an entity and flushes changes instantly. * * @param entity * @return the saved entity */ <S extends T> S saveAndFlush(S entity); /** * Deletes the given entities in a batch which means it will create a single {@link Query}. Assume that we will clear * the {@link javax.persistence.EntityManager} after the call. * * @param entities */ void deleteInBatch(Iterable<T> entities); /** * Deletes all entities in a batch call. */ void deleteAllInBatch(); /** * Returns a reference to the entity with the given identifier. Depending on how the JPA persistence provider is * implemented this is very likely to always return an instance and throw an * {@link javax.persistence.EntityNotFoundException} on first access. Some of them will reject invalid identifiers * immediately. * * @param id must not be {@literal null}. * @return a reference to the entity with the given identifier. * @see EntityManager#getReference(Class, Object) for details on when an exception is thrown. */ T getOne(ID id); /* * (non-Javadoc) * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example) */ @Override <S extends T> List<S> findAll(Example<S> example); /* * (non-Javadoc) * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Sort) */ @Override <S extends T> List<S> findAll(Example<S> example, Sort sort); }Service, Controller クラスを定義する
先ほど定義した
EmployeeRepository
を使用するServiceクラスとそれを呼び出すControllerクラスを定義します。EmployeeService.javapackage com.example.employeeapi.employee; import java.util.ArrayList; import java.util.List; import javax.transaction.Transactional; import com.example.employeeapi.employee.dto.Employee; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service @Transactional public class EmployeeService { @Autowired private EmployeeRepository employeeRepository; public Employee getEmployeeById(String employeeId) { EmployeeEntity entity = employeeRepository.findById(Integer.parseInt(employeeId)).get(); Employee employee = new Employee(); copyEntityToBean(entity, employee); return employee; } public List<Employee> getEmployeeList() { List<Employee> employees = new ArrayList<>(); List<EmployeeEntity> employeeEntityList = employeeRepository.findAll(); employeeEntityList.forEach(entity -> { Employee employee = new Employee(); copyEntityToBean(entity, employee); employees.add(employee); }); return employees; } public Employee createEmployee(Employee employee) { EmployeeEntity entity = new EmployeeEntity(); copyBeanToEntityForInsert(employee, entity); EmployeeEntity createdEntity = employeeRepository.save(entity); Employee newEmployee = new Employee(); copyEntityToBean(createdEntity, newEmployee); return newEmployee; } public Employee updateEmployee(Employee employee) { EmployeeEntity entity = new EmployeeEntity(); copyBeanToEntityForUpdate(employee, entity); EmployeeEntity updatedEntity = employeeRepository.save(entity); Employee updatedEmployee = new Employee(); copyEntityToBean(updatedEntity, updatedEmployee); return updatedEmployee; } public boolean deleteEmployeeById(String employeeId) { employeeRepository.deleteById(Integer.parseInt(employeeId)); return true; } private void copyEntityToBean(EmployeeEntity entity, Employee employee) { // サンプルのため、簡略的にコピーをする。 // 綺麗にやるのであれば、BeanUtils#copyPropertiesなどを使用してください。 employee.setId(String.valueOf(entity.getEmployeeId())); employee.setName(entity.getEmployeeName()); employee.setAge(String.valueOf(entity.getAge())); employee.setPositionId(entity.getPositionId()); employee.setDepartmentId(entity.getDepartmentId()); employee.setInsertDate(String.valueOf(entity.getInsertdate())); employee.setUpdateDate(String.valueOf(entity.getUpdateDate())); } private void copyBeanToEntityForInsert(Employee employee, EmployeeEntity entity) { // サンプルのため、簡略的にコピーをする。 // 綺麗にやるのであれば、BeanUtils#copyPropertiesなどを使用してください。 if (!"".equals(employee.getName())) { entity.setEmployeeName(employee.getName()); } if (!"".equals(employee.getAge())) { entity.setAge(Integer.parseInt(employee.getAge())); } if (!"".equals(employee.getPositionId())) { entity.setPositionId(employee.getPositionId()); } if (!"".equals(employee.getDepartmentId())) { entity.setDepartmentId(employee.getDepartmentId()); } } private void copyBeanToEntityForUpdate(Employee employee, EmployeeEntity entity) { // サンプルのため、簡略的にコピーをする。 // 綺麗にやるのであれば、BeanUtils#copyPropertiesなどを使用してください。 entity.setEmployeeId(Integer.parseInt(employee.getId())); copyBeanToEntityForInsert(employee, entity); } }EmployeeController.javapackage com.example.employeeapi.employee; import java.util.List; import com.example.employeeapi.common.dto.HttpResponseDto; import com.example.employeeapi.employee.dto.Employee; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class EmployeeController { @Autowired private EmployeeService employeeService; @GetMapping(path = "/api/employee/{employeeId}") public HttpResponseDto getEmployeeById(@PathVariable("employeeId") String employeeId) { HttpResponseDto httpResponseDto = new HttpResponseDto(); Employee employee = employeeService.getEmployeeById(employeeId); httpResponseDto.setHttpStatus(HttpStatus.OK); httpResponseDto.setResponseData(employee); return httpResponseDto; } @GetMapping(path = "/api/employee") public HttpResponseDto getEmployeeList() { HttpResponseDto httpResponseDto = new HttpResponseDto(); List<Employee> employees = employeeService.getEmployeeList(); httpResponseDto.setHttpStatus(HttpStatus.OK); httpResponseDto.setResponseData(employees); return httpResponseDto; } @PostMapping(path = "/api/employee") public HttpResponseDto createEmployee(@RequestBody Employee employee) { HttpResponseDto httpResponseDto = new HttpResponseDto(); Employee newEmployee = employeeService.createEmployee(employee); httpResponseDto.setHttpStatus(HttpStatus.CREATED); httpResponseDto.setResponseData(newEmployee); return httpResponseDto; } @PutMapping(path = "/api/employee/{employeeId}") public HttpResponseDto updateEmployee(@PathVariable("employeeId") String emplyeeId, @RequestBody Employee employee) { HttpResponseDto httpResponseDto = new HttpResponseDto(); employee.setId(emplyeeId); Employee updatedEmployee = employeeService.updateEmployee(employee); httpResponseDto.setHttpStatus(HttpStatus.CREATED); httpResponseDto.setResponseData(updatedEmployee); return httpResponseDto; } @DeleteMapping(path = "/api/employee/{employeeId}") public HttpResponseDto deleteEmployee(@PathVariable("employeeId") String employeeId) { HttpResponseDto httpResponseDto = new HttpResponseDto(); if (employeeService.deleteEmployeeById(employeeId)) { httpResponseDto.setHttpStatus(HttpStatus.OK); httpResponseDto.setMessage("delete success."); } else { // do something } return httpResponseDto; } }他のOR Mapperとの比較(主観)
他のOR Mapperとの比較と書きましたが、Domaとの比較です。
- 良いと感じた点
- いまいちだと感じた点
- 簡単なCRUD操作程度しか作っていないので、現状特に不満はありません。
- ただし、元はJPAなのでしっかりと学習してから採用しないと痛い目を見そうな予感
(補足)Database構築
検証用のDBはDockerベースで構築しています。コピペで動くのでDB作るのめんどい!って方は使ってください。
$ tree . ├── docker-compose.yml └── init-script ├── 01_create_table.sql └── 02_insert_data.sqldocker-compose.ymlversion: '3' volumes: db_data: services: database: image: postgres:11.6 container_name: postgres ports: - 5432:5432 volumes: - db_data:/var/lib/postgresql/data - ./init-script:/docker-entrypoint-initdb.d environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: sample01_create_table.sqlcreate table department ( --部署コード id varchar(3) primary key, -- 部署名 name varchar(50), -- データ投入日 insert_date date, -- データ更新日 update_date date ); create table "position" ( -- 役職ID id varchar(2) primary key, -- 役職名 name varchar(20), -- データ投入日 insert_date date, -- データ更新日 update_date date ); -- table生成 create table "employee" ( -- 従業員番号 id serial primary key, -- 従業員名 name varchar(50), -- 年齢 age integer, -- 役職 position_id varchar(2) references position(id), -- 所属部署id department_id varchar(3) references department(id), -- データ投入日 insert_date date, -- データ更新日 update_date date );02_insert_data.sqlinsert into department (id, name, insert_date, update_date) values ('001', '人事部', '2020-06-17', '2020-06-17'); insert into department (id, name, insert_date, update_date) values ('002', '総務部', '2020-06-17', '2020-06-17'); insert into department (id, name, insert_date, update_date) values ('003', '開発部', '2020-06-17', '2020-06-17'); insert into department (id, name, insert_date, update_date) values ('004', '広報部', '2020-06-17', '2020-06-17'); insert into position (id, name, insert_date, update_date) values ('01', '部長', '2020-06-17', '2020-06-17'); insert into position (id, name, insert_date, update_date) values ('02', '課長', '2020-06-17', '2020-06-17'); insert into position (id, name, insert_date, update_date) values ('03', '一般', '2020-06-17', '2020-06-17'); insert into employee ( name, age, position_id, department_id, insert_date, update_date ) values ( 'しゃっちょさん', 50, '01', '001', '2020-06-17', '2020-06-17' ); insert into employee ( name, age, position_id, department_id, insert_date, update_date ) values ( 'ぶっちょさん', 46, '02', '001', '2020-06-17', '2020-06-17' ); insert into employee ( name, age, position_id, department_id, insert_date, update_date ) values ( 'かっちょさん', 30, '03', '001', '2020-06-17', '2020-06-17' ); insert into employee ( name, age, position_id, department_id, insert_date, update_date ) values ( 'ぱんぴーさん', 30, '03', '002', '2020-06-17', '2020-06-17' );終わりに
実際のユースケースを想像して、もう少し実務チックなAPIを作ってみたいと思います。
参考
- 投稿日:2020-06-23T22:33:51+09:00
[Java]配列
配列の定義
データ型[] 配列名 = {};
String[] names = {"tanaka" , "sato" , "suzuki"}; System.out.println(names[0]);配列のfor文
配列の中身を全部出力する。
通常のfor文
String[] names = {"tanaka" , "sato" , "suzuki"}; for(int i = 0; i < names.length; i++) { System.out.println(names[i]); }拡張for文
for(データ型 変数:配列名){}
String[] names = {"tanaka" , "sato" , "suzuki"}; for(String name:names) { System.out.println(name); }
- 投稿日:2020-06-23T22:08:54+09:00
[Java]出力、変数
標準出力
System.out.println()
System.out.println("Hello World");変数
変数定義
データ型 変数名;
String hello; hello = "Hello World"; System.out.println(hello);こちらでも意味は一緒
String hello = "Hello World"; System.out.println(hello);型変換
強制型変換(キャスト)
出力の時に型を強制的に変換する例:int型同士の計算結果を小数点で出したい(本来は小数点はでない)
int num1 = 10; int num2 = 4; System.out.println((double)num1 / num2);
- 投稿日:2020-06-23T21:04:48+09:00
Java初学者が4日でポーカーゲームを作る(3日目)
モチベーションと目的
Javaについて少し知っておこうと思い6/21から勉強を始めました。
6/21にprogateでJavaの基礎的なことは学んだのでアウトプットしようと思います。
作るのは何でもよかったのですが、私が愛してやまない頭脳スポーツ「テキサスホールデム」を作ってみようと思います。就職に向けてアウトプットソースを増やす目的と、日記的な意味合いで学習の軌跡をここにメモっておくことにしました。
予定
1日目:progateでJavaについて知る(割愛)
2,3日目:とりあえず実装(できた!)
4日目:オブジェクト指向に倣いコードを整える(無理かも)実装機能(簡単のため相当簡略化)
昨日は
カードを配り、プリフロップのアクションを決定
するところまでできていた残すところ
フロップ、ターン、リバーの動き
ハンドの強弱、役の決定
all-in、foldなどの処理
ですハンドの強弱
一番頭を悩ませたハンドの役の決定から。
なお、簡単のためフラッシュ、ストレートは今回実装しなかった。また、最弱のカードは2ではなくAになってるが勘弁してほしい。以下のように考えた。
昨日に引き続き配列にフラグを詰め込むことでなんとか実現した。
public static int[] handRank(int array[]) { int count[] = {0,0,0,0,0,0,0,0,0,0,0,0,0}; for(int i : array) { count[i-1] = count[i-1] + 1; } int hand[] = new int[6]; int newCount[] = count.clone(); int pairNumber1; int pairNumber2; Arrays.sort(newCount); if(newCount[12] == 1) { //System.out.println("hicard"); Arrays.sort(array); System.arraycopy(array, 2, hand, 0, 5); return hand; }else if(newCount[12] == 2 && newCount[11] == 1) { //System.out.println("pair"); pairNumber1 = handNumber(count, 2).get(0); pairHand(array, pairNumber1, hand); return hand; }else if(newCount[12] == 2 && newCount[11] == 2) { //System.out.println("two-pair"); pairNumber1 = handNumber(count, 2).get(0); pairNumber2 = handNumber(count, 2).get(1); twoPairHand(array, pairNumber1,pairNumber2, hand); return hand; }else if(newCount[12] == 3 && newCount[11] == 1) { //System.out.println("three-card"); pairNumber1 = handNumber(count, 3).get(0); threePairHand(array, pairNumber1, hand); return hand; }else if(newCount[12] == 3 && newCount[11] == 2 || newCount[11] == 3) { //System.out.println("full-house"); pairNumber1 = handNumber(count, 3).get(0); pairNumber2 = handNumber(count, 2).get(0); fullHouseHand(pairNumber1, pairNumber2, hand); return hand; }else if(newCount[12] == 4) { //System.out.println("four-card"); pairNumber1 = handNumber(count, 4).get(0); fourCard(array, pairNumber1, hand); return hand; } return hand; }各分岐内のメソッドでは、7枚の中から一番強い手札になるように5枚選んでhandに格納する処理が行われる。
ペアなどの役ありの数字をまず入れて、残りを数字の大きい順に詰めていくようにした。public static void twoPairHand(int array[], int pairNumber1, int pairNumber2, int hand[]) { Arrays.sort(array); int handNum = 0; int i = 6; while(handNum < 1) { if(array[i] != pairNumber1 && array[i] != pairNumber2) { hand[handNum] = array[i]; i --; handNum ++; }else { i --; } }フロップ、ターン、リバーの動き
int list[] にフォールドフラグと、オールインフラグを追加、これらが立っている場合は各ストリートをスキップし、いきなりショウダウンになるようにした。
int list[] = {1000,1000,0,0,0,0}; //[0] herochip(自分)/ //[1] enemychip(相手)/ //[2] pot/ //[3] needChiptoCall //[4]foldFlag //===foldFlag=== // 0 foldしてない // 1 heroがfold // 2 enemy がfold //[5]alInFlag //===allInFlag=== // 0 allinしてない // 1 heroがallin // 2 enemy がallinstreetメソッドにはストリートにおける自分のアクション、相手のアクションを行う処理が入っている。
//プリフロップの処理 street(hero, list, scan); //ターン、リバーの処理 //フロップ3枚追加 if(list[4] == 0 && list[5] == 0) { System.out.println("フロップは" + toDescription(holeCard.get(0)) + toDescription(holeCard.get(1)) +toDescription(holeCard.get(2)) + "です"); street(hero, list, scan); } //ターン if(list[4] == 0 && list[5] == 0) { System.out.println("ターンカードは" + toDescription(holeCard.get(3)) + "です。ホールカードは"+ toDescription(holeCard.get(0)) + toDescription(holeCard.get(1)) +toDescription(holeCard.get(2)) +toDescription(holeCard.get(3)) + "です"); street(hero, list, scan); } //リバー if(list[4] == 0 && list[5] == 0) { System.out.println("リバーカードは" + toDescription(holeCard.get(4)) + "です。ホールカードは"+ toDescription(holeCard.get(0)) + toDescription(holeCard.get(1)) +toDescription(holeCard.get(2)) +toDescription(holeCard.get(3)) + toDescription(holeCard.get(4)) + "です"); street(hero, list, scan); } //ショーダウン。勝敗の決定 if(list[4] == 1) {//heroが降りた時 System.out.println("enemyは"+list[2]+"$のチップを獲得しました" ); list[1] = list[1] + list[2]; list[2] = 0; list[3] = 0; list[4] = 0; }else if(list[4] == 2) { System.out.println("heroは"+list[2]+"$のチップを獲得しました" ); list[0] = list[0] + list[2]; list[2] = 0; list[3] = 0; list[4] = 0; }else { int herorole[] = showdown(hero, holeCard); int enemyrole[] = showdown(enemy, holeCard); System.out.println("ショウダウンです。ホールカードは"+ toDescription(holeCard.get(0)) + toDescription(holeCard.get(1)) +toDescription(holeCard.get(2)) +toDescription(holeCard.get(3)) + toDescription(holeCard.get(4)) + "です"); System.out.println("hero : " + toDescription(hero.get(0)) + toDescription(hero.get(1)) + role(herorole[5])); System.out.println("enemy : " + toDescription(enemy.get(0)) + toDescription(enemy.get(1)) + role(enemyrole[5])); checkWinner(herorole, enemyrole, list); }実行結果
1ハンドで終わってますが、どちらかのチップが0になるまでゲームは続くようになっています。
セッションを開始します!グッドラック!
==========1ハンド目===========
あなたのハンドは♣8♣4です
あなたの所持チップは1000です。相手の所持チップは1000です
ポットは0です
アクションを選択してください check : c or bet : b
b
いくらベットしますか?最大1000です
10
10$ベットしました
ememyは20$にレイズしました。
あなたのハンドは♣8♣4です
あなたの所持チップは990です。相手の所持チップは980です
ポットは30です
アクションを選択してください call : c or raise : r or fold : f
c
callしました。
次のストリートに進みます
フロップは♥2♦8♥3です
あなたのハンドは♣8♣4です
あなたの所持チップは980です。相手の所持チップは980です
ポットは40です
アクションを選択してください check : c or bet : b
b
いくらベットしますか?最大980です
980
Allinです
980ベットしました
enemyはcallしました。All-inです
次のストリートに進みます
ショウダウンです。ホールカードは♥2♦8♥3♣10♥Jです
hero : ♣8♣4ワンペア
enemy : ♦7♠7ワンペア
勝者はhero!役はワンペア キッカーは8
heroは2000のチップを獲得しました
ゲーム終了です。お疲れ様でした。さいごに
配列は要素からインデックスを取り出すこともできないし、色々使い勝手が悪いのかなと思った。リストを優先して使ったほうがいいのかな?
600行を超えるコードを一気に書いたのは初めてだったので非常に疲れた。解説が超適当になってしまっている。これ一か月後に見返しても全然理解できないんだろうな・・・。
でも、かなり簡略化しているとはいえ、一昨日hello worldしてた自分が動くものを作れたことに感動している。明日大学の課題が順調に消化できたらオブジェクト指向の書き方を学んで書き直そうかと思います。
寝違えた首がまだ痛いいいいいいいいいいいいそれにしてもquitaって結構読まれるんですね・・・view数に驚きました。
ソースコードは以下
https://github.com/hmck8625/poker/blob/master/poker.java
- 投稿日:2020-06-23T17:40:18+09:00
JBcryptを使用したパスワードのハッシュ化とその認証
やったこと
Java(Spring) でJBcryptを使用してパスワードのハッシュ化とその認証を行いました。
準備
MVN REPOSITORYからjarファイルを取得
パスワードのハッシュ化
@PostMapping("/sample") public String sample(@Validated SampleForm sampleForm, BindingResult bindingResult, Model model) { // 入力値のpasswordをハッシュ値に変換 String hashedCode = BCrypt.hashpw(sampleForm.getPassword(), BCrypt.gensalt()); // 以下ハッシュ化されたパスワードをDBに格納する処理 ... }ソルトはハッシュ関数に入れる前のパスワードの前後にくっつける文字列です。
「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典より引用パスワードの認証
@Override public boolean isPasswordCorrect(String inputPassword) { final String SAMPLE_SQL = "SQL文をここに記載" // DBからパスワードを取得 Map<String, Object> PasswordFromDB = jdbcTemplate.queryForMap(SAMPLE_SQL); String passwordFromDB = (String)PasswordFromDB.get("password"); // 入力値である平文のinputPasswordとDBにあるハッシュ化されたpasswordFromDBを比較 if(BCrypt.checkpw(inputPassword, passwordFromDB)) { // 処理 } }まとめ
簡単にハッシュ化できる!
ハッシュ化する時は、
BCrypt.hashpw(ハッシュ化したい値, BCrypt.gensalt());ハッシュ化した値を認証する時は、
BCrypt.checkpw(比較したい平文の値, DBから取得したハッシュ化された値);以上です。最後まで読んでくださり、ありがとうございました。
- 投稿日:2020-06-23T16:37:42+09:00
Docker-compose ~volumeの罠~
はじめに
初投稿です。白菜と申します。プログラミングが趣味のしがない大学生です。先日コードを書いている最中に深めなツボにハマり、色々調べても関連する情報が出てこなかったので投稿させていただきます。
何が起こったのか
windowsを使っておりまして、wsl2が正式に導入されたと聞き、wsl2上でDocker-desktopを動かしてみようと思い立ちました。環境を整えてかれこれ一か月ほど遊んでいて、己のPCの環境を汚さずに様々な言語に触れられて、激アツやんと思っておりました。dockerで立ち上げたコンテナにvscodeのRemote Developmentを利用して接続し、コンテナ内でコードの編集・デバッグを行っていました。
しかし、問題発生
それはある日の暮れ、羅生門の下で雨やみを待ちながらコードを書いていた時のことでございます。springbootとかいう何某かがなんかとてもすごいつよいと聞いたので、試しておりました。日本語の公式チュートリアルのいくつかを軽く実装し、springMVCの基礎くらいはカンニングせずに書けるようになりました。調子に乗ってきたので、MySQLと連携させてデータベース処理やってみようと思い、dockerで環境を作りました。具体的には、
- javaコンテナ
- MySQLコンテナ
- phpmyadminコンテナ(データベース管理で楽するため)
の三つをdocker-composeで一つのネットワークとして作りました。ついでに書いたコード保存しときたかったので、volumeでテキトーなディレクトリをjavaコンテナ上にマウントしました。
しかしいざコードを書いてみると、データベースとの連携がうまくいきません。デバッグの度にデータベース接続エラーを吐き出します。コード自体はチュートリアルのほぼコピペなので、動かないはずがないのです・・・。これはコードじゃなくてdockerに原因があるんじゃないか?症状
上記以外にも、他のコンテナでも似たような症状が発生しました。以下はその一覧です。
- MySQLとの連携がうまくいかない
- というかおそらく連携用の設定ファイルの編集が反映されていない
- 手動で反映させたら上手くいった
- htmlを作成してコントローラと紐づけたのに、Not Foundと言われる
- React環境を作ったが、yarn startに一分以上かかる
- yarn start後、ファイルを編集してもブラウザに反映されない
共通しているのはファイルの編集・作成が反映されないということでした。
解決
色々実験してみたところ、dockerのvloumeに問題があるという結論に達しました。windows上のファイルをvolumeに指定した時に、volume内のファイルの変更がリアルタイムで反映されなくなるようです。なのでvolume指定しなけりゃいいというのも一つの解決ではあるのですが、コードを永続的に保存しておきたい場合、いちいちwindows上にコピーして持ってくるのは手間です。
volumeを使いつつ解決する方法があります。そもそもdocker for windows公式には以下のような記述があります。Store source code and other data that is bind-mounted into Linux containers (i.e., with docker run -v :) in the Linux filesystem, rather than the Windows filesystem.
「volumeマウントするファイルは、Windows上じゃなくてLinux上に置いといた方がいいよ!」
・・・・・・すみませんでした。
ちらっと見たことはあったんですけど、マウント自体はちゃんと機能してたし、別にWindows上でもよくね?と思っていたんです。まさかこんな変な不具合が出るとは思ってもいなかったんです。というわけで、wsl2のLinux上に作ったディレクトリをマウントしてReact環境を作り直したら解決しました。yarn startが20秒になったし、コードを変更したら逐一反映されるようになりました。Reactおもしれえ。
終わりに
結論、volumeでマウントするディレクトリはLinux上に作っておいた方がいいと思います。開発してる人たちが言ってるんだから多分間違いないです。我流でオラオラしようとしていた私が悪かったです。すみませんでした・・・。
公式ドキュメントはしっかり読まなきゃダメてことですね。
- 投稿日:2020-06-23T16:06:32+09:00
僕のAndroid学習参考資料
※随時アップデート
Google公式
Android Developer
API Reference
Material Designおすすめサイト/本
- 投稿日:2020-06-23T14:24:16+09:00
【質問】CSVファイルのデータをコンボボックスの選択肢として表示する方法
【質問】
CSVファイルから文字列型でデータの取得はできたのですが、そのデータをコンボボックスが選択肢として格納していないようで困っています。
改善策をお教えください。【やりたいこと】
トレーニング種目名をCSVファイルから取得してコンボボックスの選択肢として表示したい。【前提条件】
SceneBuiderのコンボボックスはfx:idの設定済【ソースコード】
public class SampleController implements Initializable{/*コンボボックス設定/
@FXML
private ComboBox trainingCombo;@Override
public void initialize(URL location, ResourceBundle resources) {
try{
File csv = new File(
"C:\pleiades\eclipse\Java1\TrainingApp\src\application\trainingList.csv"
);
BufferedReader br = new BufferedReader(new FileReader(csv));
String line = "";
while((line = br.readLine()) != null){
trainingCombo.getItems().addAll(line);
}
br.close();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
trainingCombo.getSelectionModel().select(0);
}
}
- 投稿日:2020-06-23T13:51:33+09:00
Java初心者 「Staticメソッド」「new演算子」
今回はStaticメソッド」と「new演算子」について。
「Staticメソッド」について
staticとは「静的」を意味し、Javaのメソッドは必ず何かのクラスに属します。
インスタンスメソッド:インスタンスが持つメソッド、インスタンスが実行する
staticメソッド(クラスメソッド):クラスが持つメソッド、クラスが実行する
staticメソッドの宣言の仕方
staticメソッドの宣言の仕方は、メソッド宣言での戻り値の前に “static” を付けるだけです。
- staticメソッド
アクセス修飾子(publicなど) static 型名 メソッド名(引数){}
- static変数
アクセス修飾子(publicなど) static 型名 変数名;staticメソッドはその特徴としてnewを使わずに呼び出すことができます。
class StaticMethodSample1 { // staticを付ければstaticメソッドになる static void staticMethod() { } // staticを付けなければインスタンスメソッドになる void instanceMethod() { } }「new演算子」について
new演算子は、Javaのクラスをインスタンス化するために利用されます。
JavaScriptに限らずオブジェクト指向のプログラムは、
あらかじめ用意されている「オブジェクト」をそのまま扱うことはありません。調べていたらわかりやすい例があったので参照
例えば、目的が違う「A」「B」という2つのプログラムがあったとします。
両方のプログラムがまったく同じ「オブジェクト」に対してデータを読み書きしてしまうと
お互いに干渉してしまって正しく動作しません!
「コピーをする」という行為がインスタンス化であり、コピーされた「オブジェクト」をインスタンスと呼ぶわけです。JavaScriptでは、このインスタンスを作成する役割を担っているのが「new演算子」ということです。クラスをnew(インスタンスを作成)した時点でプログラム上でメモリが確保され、初めて使用できるようになります。
標準で提供されているJavaScriptの組み込みオブジェクトについて
オブジェクト 内容 Array 配列を扱うための機能を提供する String 文字列を扱うための機能を提供する Boolean 真偽値を扱うための機能を提供する Number 数値を扱うための機能を提供する Function 関数を扱うための機能を提供する Date 日付を扱うための機能を提供する RegExp 正規表現を扱うための機能を提供する Object オブジェクトを扱うための機能を提供するnew演算子の使い方は以下のとおりです。
new コンストラクタ; new クラス名();
- 投稿日:2020-06-23T11:32:09+09:00
【Java】特定の曜日の日を取得する
忘備録として
LocalDateを使うとかんたん。
// 今日 LocalDate now = LocalDate.now(); // 2020-06-23:火曜日 // 今週の金曜日 LocalDate thisFriday = now.with(DayOfWeek.FRIDAY); // 2020-06-26 // 先週の水曜日 LocalDate lastWednesDay = now.minusWeeks(1).with(DayOfWeek.WEDNESDAY); // 2020-06-17 // 来週の木曜日 LocalDate nextThursDay = now.plusWeeks(1).with(DayOfWeek.THURSDAY); // 2020-07-02 // おまけ LocalDateTimeへ変換 LocalDateTime from = lastWednesDay.atTime(00, 00, 00); // 2020-06-17 00:00:00参考
日本人のための Date and Time API Tips (ja)
【Java】過去週の月曜日と日曜日の日付を順番に取得する @kazokclily
- 投稿日:2020-06-23T09:12:41+09:00
java comparator を使ったソート
- comparator を使うと便利にソートできます.
コード (昇順ソート)
import java.util.*; class Solution { public static void main(String[] args) { sort1(); sort2(); } public static void sort1 () { List<Integer> list1 = Arrays.asList(3,2,8,4); System.out.println("sort前: "+list1); Comparator<Integer> comparator = new Comparator<Integer>() { public int compare(Integer int1, Integer int2) { return (int1.compareTo(int2)); } }; Collections.sort(list1, comparator); System.out.println("comparator(インスタンス化する), sort 後: "+list1); } public static void sort2(){ List<Integer> list1 = Arrays.asList(3,2,8,4); System.out.println("sort前: "+list1); Collections.sort(list1, (a, b) -> Integer.compare(a, b)); System.out.println("ラムダ式comparator, sort 後: "+list1); } }出力結果
sort前: [3, 2, 8, 4] comparator(インスタンス化する), sort 後: [2, 3, 4, 8] sort前: [3, 2, 8, 4] ラムダ式comparator, sort 後: [2, 3, 4, 8]
- 投稿日:2020-06-23T07:48:15+09:00
RestTemplate でレスポンスの JSON とクラスのマッピングに失敗した場合に発生する例外
概要
- RestTemplate で HTTP レスポンスの JSON とマッピングするクラスが合わない場合に発生する例外を調査する
環境
- Spring Boot 2.3.1 (Spring Framework 5.2.7 + Jackson Databind 2.11.0)
- Java 11 (AdoptOpenJDK 11.0.7+10)
- Gradle 6.5
- macOS 10.15.5 Catalina
検証用コード
ファイル一覧
├── build.gradle └── src └── main └── java └── com └── example ├── DemoApplication.java └── MyResponse.javabuild.gradle
plugins { id 'org.springframework.boot' version '2.3.1.RELEASE' id 'io.spring.dependency-management' version '1.0.9.RELEASE' id 'java' } group = 'com.example' version = '0.0.1' sourceCompatibility = '11' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' }src/main/java/com/example/MyResponse.java
JSON をマッピングするクラス。
package com.example; import java.util.List; public class MyResponse { public String name; public List<MyData> list; // ここに想定外の値をマッピング試行した際の例外を調べる public static class MyData { public String foo; public String bar; } }src/main/java/com/example/DemoApplication.java
package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.net.URI; import java.util.List; import java.util.Map; @SpringBootApplication @RestController public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } // OK: JSON を返却 @GetMapping("/ok") public Map ok() { return Map.of( "name", "myname", "list", List.of( Map.of("foo", "myfoo", "bar", "mybar") ) ); } // NG1: MyResponse に合わない JSON を返却 @GetMapping("/ng1") public Map ng1() { return Map.of( "name", "myname", "list", "mylist" // List があるべき場所に文字列 "mylist" ); } // NG2: MyResponse に合わない JSON を返却 @GetMapping("/ng2") public Map ng2() { return Map.of( "name", "myname", "list", "" // List があるべき場所に空文字列 ); } // NG3: MyResponse に合わない JSON を返却 @GetMapping("/ng3") public Map ng3() { return Map.of( "name", "myname", "list", List.of("mylist") // MyData があるべき場所に文字列 "mylist" ); } // NG4: MyResponse に合わない JSON を返却 @GetMapping("/ng4") public Map ng4() { return Map.of( "name", "myname", "list", List.of("") // MyData があるべき場所に空文字列 ); } @GetMapping("/mydata/{path}") public MyResponse mydata(@PathVariable("path") String path) throws Exception { try { // 自らのサーバから JSON を取得 String uri = "http://localhost:8080/" + path; RequestEntity re = RequestEntity.get(new URI(uri)).build(); RestTemplate restTemplate = new RestTemplate(); // JSON を MyResponse に変換 // JSON が MyResponse に合わなければここで例外が発生する ResponseEntity<MyResponse> res = restTemplate.exchange(re, MyResponse.class); return res.getBody(); } catch (Exception e) { // どういう例外が発生しているか出力する System.out.println("例外クラス: " + e.getClass().getName()); // 原因となった例外のチェーンを出力する Throwable cause = e; while ((cause = cause.getCause()) != null) { System.out.println("原因例外クラス: " + cause.getClass().getName()); } throw e; } } }Spring Boot アプリケーションを起動
$ gradle bootRun Starting a Gradle Daemon (subsequent builds will be faster) > Task :bootRun . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.1.RELEASE)検証
他のコンソールから curl で HTTP リクエストを投げて、どのような例外が発生するかを検証する。
OK: MyResponse にマッピングできる JSON
$ curl http://localhost:8080/ok {"list":[{"foo":"myfoo","bar":"mybar"}],"name":"myname"}$ curl http://localhost:8080/mydata/ok {"name":"myname","list":[{"foo":"myfoo","bar":"mybar"}]}NG1: List があるべき場所に文字列 "mylist"
クラスにマッピング試行する JSON データ。
$ curl http://localhost:8080/ng1 {"list":"mylist","name":"myname"}JSON データをクラスにマッピングする処理をする URL にアクセス。
$ curl http://localhost:8080/mydata/ng1 {"timestamp":"2020-06-22T21:05:59.317+00:00","status":500,"error":"Internal Server Error","message":"","path":"/mydata/ng1"}Spring Boot サーバ側のログ (読みやすくするため一部を抜粋して整形したもの)。
例外クラス: org.springframework.web.client.RestClientException 原因例外クラス: org.springframework.http.converter.HttpMessageNotReadableException 原因例外クラス: com.fasterxml.jackson.databind.exc.MismatchedInputException 2020-06-23 06:05:59.311 ERROR 4196 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.RestClientException: Error while extracting response for type [class com.example.MyResponse] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.util.ArrayList<com.example.MyResponse$MyData>` out of VALUE_STRING token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList<com.example.MyResponse$MyData>` out of VALUE_STRING token at [Source: (PushbackInputStream); line: 1, column: 25] (through reference chain: com.example.MyResponse["list"])] with root causeSpring Boot サーバ側のログ (出力そのまま)。
例外クラス: org.springframework.web.client.RestClientException 原因例外クラス: org.springframework.http.converter.HttpMessageNotReadableException 原因例外クラス: com.fasterxml.jackson.databind.exc.MismatchedInputException 2020-06-23 06:05:59.311 ERROR 4196 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.RestClientException: Error while extracting response for type [class com.example.MyResponse] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.util.ArrayList<com.example.MyResponse$MyData>` out of VALUE_STRING token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList<com.example.MyResponse$MyData>` out of VALUE_STRING token at [Source: (PushbackInputStream); line: 1, column: 25] (through reference chain: com.example.MyResponse["list"])] with root cause com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList<com.example.MyResponse$MyData>` out of VALUE_STRING token at [Source: (PushbackInputStream); line: 1, column: 25] (through reference chain: com.example.MyResponse["list"]) at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59) ~[jackson-databind-2.11.0.jar:2.11.0] at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1464) ~[jackson-databind-2.11.0.jar:2.11.0] at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1238) ~[jackson-databind-2.11.0.jar:2.11.0] at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1190) ~[jackson-databind-2.11.0.jar:2.11.0] at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.handleNonArray(CollectionDeserializer.java:331) ~[jackson-databind-2.11.0.jar:2.11.0] at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:264) ~[jackson-databind-2.11.0.jar:2.11.0] at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244) ~[jackson-databind-2.11.0.jar:2.11.0] at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27) ~[jackson-databind-2.11.0.jar:2.11.0] at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138) ~[jackson-databind-2.11.0.jar:2.11.0] at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:371) ~[jackson-databind-2.11.0.jar:2.11.0] at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164) ~[jackson-databind-2.11.0.jar:2.11.0] at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4482) ~[jackson-databind-2.11.0.jar:2.11.0] at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3487) ~[jackson-databind-2.11.0.jar:2.11.0] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:269) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:257) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:105) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:998) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:981) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:741) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:641) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE] at com.example.DemoApplication.mydata(DemoApplication.java:86) ~[main/:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.36.jar:9.0.36] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na] at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.36.jar:9.0.36] at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]NG2: List があるべき場所に空文字列
クラスにマッピング試行する JSON データ。
$ curl http://localhost:8080/ng2 {"list":"","name":"myname"}JSON データをクラスにマッピングする処理をする URL にアクセス。
$ curl http://localhost:8080/mydata/ng2 {"timestamp":"2020-06-22T21:07:21.986+00:00","status":500,"error":"Internal Server Error","message":"","path":"/mydata/ng2"}Spring Boot サーバ側のログ (読みやすくするため一部を抜粋して整形したもの)。
例外クラス: org.springframework.web.client.RestClientException 原因例外クラス: org.springframework.http.converter.HttpMessageNotReadableException 原因例外クラス: com.fasterxml.jackson.databind.exc.MismatchedInputException 2020-06-23 06:07:21.984 ERROR 4196 --- [nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.RestClientException: Error while extracting response for type [class com.example.MyResponse] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `java.util.ArrayList` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value (''); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `java.util.ArrayList` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('') at [Source: (PushbackInputStream); line: 1, column: 25] (through reference chain: com.example.MyResponse["list"])] with root causeNG3: MyData があるべき場所に文字列 "mylist"
クラスにマッピング試行する JSON データ。
$ curl http://localhost:8080/ng3 {"list":["mylist"],"name":"myname"}JSON データをクラスにマッピングする処理をする URL にアクセス。
$ curl http://localhost:8080/mydata/ng3 {"timestamp":"2020-06-22T21:08:08.316+00:00","status":500,"error":"Internal Server Error","message":"","path":"/mydata/ng3"}Spring Boot サーバ側のログ (読みやすくするため一部を抜粋して整形したもの)。
例外クラス: org.springframework.web.client.RestClientException 原因例外クラス: org.springframework.http.converter.HttpMessageNotReadableException 原因例外クラス: com.fasterxml.jackson.databind.exc.MismatchedInputException 2020-06-23 06:08:08.315 ERROR 4196 --- [nio-8080-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.RestClientException: Error while extracting response for type [class com.example.MyResponse] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.example.MyResponse$MyData` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('mylist'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.example.MyResponse$MyData` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('mylist') at [Source: (PushbackInputStream); line: 1, column: 26] (through reference chain: com.example.MyResponse["list"]->java.util.ArrayList[0])] with root causeNG4: // MyData があるべき場所に空文字列
クラスにマッピング試行する JSON データ。
$ curl http://localhost:8080/ng4 {"list":[""],"name":"myname"}JSON データをクラスにマッピングする処理をする URL にアクセス。
$ curl http://localhost:8080/mydata/ng4 {"timestamp":"2020-06-22T21:08:36.354+00:00","status":500,"error":"Internal Server Error","message":"","path":"/mydata/ng4"}Spring Boot サーバ側のログ (読みやすくするため一部を抜粋して整形したもの)。
例外クラス: org.springframework.web.client.RestClientException 原因例外クラス: org.springframework.http.converter.HttpMessageNotReadableException 原因例外クラス: com.fasterxml.jackson.databind.exc.MismatchedInputException 2020-06-23 06:08:36.352 ERROR 4196 --- [nio-8080-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.RestClientException: Error while extracting response for type [class com.example.MyResponse] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.example.MyResponse$MyData` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value (''); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.example.MyResponse$MyData` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('') at [Source: (PushbackInputStream); line: 1, column: 26] (through reference chain: com.example.MyResponse["list"]->java.util.ArrayList[0])] with root cause発生している例外の公式資料
org.springframework.web.client.RestClientException
RestClientException はサーバのエラーレスポンス、入出力エラー、レスポンスをデコードする際のエラーなどで発生する例外。
RestClientException (Spring Framework 5.2.7.RELEASE API)
Base class for exceptions thrown by RestTemplate in case a request fails because of a server error response, as determined via ResponseErrorHandler.hasError(ClientHttpResponse), failure to decode the response, or a low level I/O error.
org.springframework.http.converter.HttpMessageNotReadableException
親クラスの HttpMessageConversionException の説明によると変換に失敗した際に投げられる例外とのこと。
HttpMessageNotReadableException (Spring Framework 5.2.7.RELEASE API)
Thrown by HttpMessageConverter implementations when the HttpMessageConverter.read(java.lang.Class<? extends T>, org.springframework.http.HttpInputMessage) method fails.
HttpMessageConversionException (Spring Framework 5.2.7.RELEASE API)
Thrown by HttpMessageConverter implementations when a conversion attempt fails.
com.fasterxml.jackson.databind.exc.MismatchedInputException
マッピングするクラス定義に合わない JSON が来たときに発生する例外。
MismatchedInputException (jackson-databind 2.11.0 API)
参考資料
- 投稿日:2020-06-23T02:34:31+09:00
ProgateでJavaについてお勉強メモ1
JavaScriptとはメロンパンとメロン、ハムとハムスターほど違うと有名なあのJavaについてProgateで勉強を始めたいと思います。行くぞージャバジャバ
基本編
System.out.println();がJSでいうconsole.log的なやつ。コンソールに結果を出力できる。
長い、長いよ。。console.logですら長いと文句言ったのに。「.」が2つもある・・!
(どうせ補完?されて長々と打つことはProgateやってる間だけだろうけども。)しかも、printlnこれ、lは小文字の”える”という衝撃の事実。
読み方はプリントラインというそうな。。Javaの構造はクラス部分・メソッド部分・メソッド内の処理の3つに別れている。まだ、ふーんそうなんだ程度でいいらしいが、きっと後ですごい伏線回収があると思っている。
変数
変数の定義
//変数の定義 データの型 変数名; //変数へ値の代入 変数名 = 値; //合体できる。変数の初期化というらしい データの型 変数名 = 値;変数にはintとかStringなどのデータの型を宣言する。
そして、変数の定義と同時に値を代入することを「変数の初期化」と呼ぶらしい。変数の更新
更新したい変数名 = 新しい値;更新するときはデータの型をつけてはいけない。
データの型がついてると、新しく変数を定義してしまうようで、
Java「この名前の変数もうあるのに。なんでまたそんなこと言うの?ふえぇ」
って困ってエラーを吐いてしまうらしいので、Javaちゃんを困らせないようにしましょう。自己代入
int X = 2; X = X+3;よくみる形。自己代入と呼ぶらしい初知り。
- 投稿日:2020-06-23T00:25:28+09:00
Java初学者が4日でポーカーゲームを作る(2日目)
モチベーションと目的
Javaについて少し知っておこうと思い昨日(6/21)から勉強を始めました。
6/21にprogateでJavaの基礎的なことは学んだのでアウトプットしようと思います。
作るのは何でもよかったのですが、私が愛してやまない頭脳スポーツ「テキサスホールデム」を作ってみようと思います。就職に向けてアウトプットソースを増やす目的と、日記的な意味合いで学習の軌跡をここにメモっておくことにしました。
予定
1日目:progateでJavaについて知る(割愛)
2,3日目:とりあえず実装(できたらいいな)
4日目:オブジェクト指向に倣いコードを整える(予定)実装機能(簡単のため相当簡略化)
プレイヤーは自分自身と相手(CPU)のみのヘッズアップ
CPUはハンドの強さではなく乱数でアクションを決定する
BTNは常に相手側でブラインドは無し
どちらかのチップがなくなったら終了実装にあたって、
デッキの作成
デッキのシャッフル
プレイヤーに手札を配る
の処理は非常にわかりやすいサイトがありましたのでこちらをパクっ参考にさせていただきました。
Javaでなにか作りたい人必見!入門書を読み終えた人向けゲーム作成2日目で実装できた機能は
プリフロップの一連の流れ
だけです、、値の参照渡しの理解に結構時間がかかったコード解説
public static void main(String[] args) { System.out.println("セッションを開始します!グッドラック!"); //デッキを作成 List <Integer> deck = new ArrayList<>(52); //デッキをシャッフル shuffleDeck(deck); //プレイヤーの手札リストを生成 List <Integer> hero = new ArrayList<>(); List <Integer> enemy = new ArrayList<>(); //プレイヤーにカードを二枚ずつ配る hero.add(deck.get(0)); enemy.add(deck.get(1)); hero.add(deck.get(2)); enemy.add(deck.get(3)); //山札の進行状況を記録する変数deckCountを定義 int deckCount = 4; //プレイヤーの持っているチップを定義(初期値100)potも定義(初期値0) int list[] = {100,100,0,0,0}; //[0] herochip(自分)/ //[1] enemychip(相手)/ //[2] pot/ //[3] needChiptoCall //[4]foldFlag //===foldFlag=== // 0 foldしてない // 1 heroがfold // 2 enemy がfold Scanner scan = new Scanner(System.in); //プリフロップの処理 preFlop(hero, list, scan); //各プレイヤーのアクションの後にneedtocallが0の場合にそのストリートが終了したとできる while(true) { enemyAction(list); if(list[4] == 2) { System.out.println("enemyは降りましたheroの勝ちです"); break; }else if(list[3] == 0) { System.out.println("フロップに進みます"); break; } preFlop(hero, list, scan); if(list[4] == 1) { System.out.println("heroは降りましたenemyの勝ちです"); break; }else if(list[3] == 0) { System.out.println("フロップに進みます"); break; } } //ターン、リバーの処理 //ショーダウン。勝敗の決定 //チップの移動 //チップがなくなったら終了まだ全然テクニカルなことがわからないため、配列listに必要なデータをぶちこんで丸ごと渡すっていう汚いやり方です。
自分(hero)のアクション
ポーカーでの基本的なアクションは、「チェック、コール、ベット、レイズ、フォールド」の五種類
相手のベット(レイズ)が入っていない場合は「チェック、ベット」の2種類
相手のベット(レイズ)が入ってる場合は「コール、レイズ、フォールド」の3種類のアクションがある。
それぞれ「checkBet」「callRaiseFold」のメソッドとして定義した。(命名のセンスが皆無である)checkBet
private static boolean checkBet(int list[], String str, Scanner scan){ if("c".equals(str)) {//チェックしたときの処理 //相手に回す System.out.println("checkしました。"); return true; }else if("b".equals(str)) {//ベットしたとき。ベット額を聞いてチップを移動、 //ベット額を聞く while(true) { System.out.println("いくらベットしますか?最大" + list[0] +"$です"); int bet = scan.nextInt(); if(bet <= list[0] && bet >= list[2]) {//ベット額が持ってる分を越えてる場合とポットより少ないときは bet(list, bet); break; }else { System.out.println("ベット額が不正です。正しい値を入力してください"); } } return true; }else { return false; } }callRaiseFold
private static boolean callRaiseFold(int list[], String str, Scanner scan){ if("c".equals(str)) {//コールしたときの処理 //相手に回す System.out.println("callしました。"); return true; }else if("r".equals(str)) {//ベットしたとき。ベット額を聞いてチップを移動、 //ベット額を聞く while(true) { System.out.println("いくらレイズしますか?最大" + list[0] +"$です"); int raise = scan.nextInt(); if(raise <= list[0] && raise >= list[3]) {//レイス額が持ってる分を越えてる場合と前のベット(レイズ)より小さいばあい raise(list, raise); break; }else { System.out.println("レイズ額が不正です。正しい値を入力してください"); } } return true; }else if("f".equals(str)){ System.out.println("フォールドします"); list[4] = 1; return true; }else { return false; } }boolean型で定義。コマンドラインからの入力が不正なものであった場合はfalseを返してループをするようにした。
また、foldした際はlist[4]をfoldFlagとして、heroがfoldした場合は1,enemyがfoldした場合は2とする。(デフォルトは0)相手(enemy)のアクション
randomライブラリを用いて乱数生成、数字で割ったあまりで分類し、アクションを決定する。
switch文は見やすくてすごい好き。private static void enemyAction(int list[]) {//敵のアクション(乱数で変動) Random rand = new Random(); int randomValue = rand.nextInt(100);//乱数を生成 if(list[3] == 0) {//check or bet switch(randomValue%2) { case 0://チェックする System.out.println("enemyはcheckしました。"); break; case 1://ベット int bet = list[2];//ベット額はポットサイズで固定 System.out.println("ememyは"+bet+"$ベットしました。"); //手持ちチップを減らし、ポットをふやす、needtoCallも増やす list[1] = list[1] - bet; list[2] = list[2] + bet; list[3] = bet; break; } }else {//call or raise or fold switch(randomValue % 3) { case 0://コールする System.out.println("enemyはcallしました。"); //手持ちチップを減らしポットをふやす。needtocallをリセット list[1] = list[1] - list[3]; list[2] = list[2] + list[3]; list[3] = 0; break; case 1: int raise = list[3] * 2; if(raise >= list[1]) { raise = list[1]; System.out.println("ememyはAll-inです。"); }else { System.out.println("ememyは"+raise+"$にレイズしました。"); } //needtocall-raise = 次のneedtocall list[1] = list[1] - raise; list[2] = list[2] + raise; list[3] = list[3] - raise; break; case 2: System.out.println("ememyはfoldしました"); list[4] = 2; break; } } }プリフロップの処理
list[3]が0かどうかでレイズが入っていない(フロップに進める)かどうかを判断した。ここの処理を決定するのに時間がかかった。銭湯の中で思いついた。
入力がエラーの場合は入力を再度要求する。foldFlagが立っていたら勝敗決定の処理に移動する。private static void preFlop(List<Integer> hero, int list[], Scanner scan) { printData(hero, list); if(list[3] == 0) {//needchiptocallが0の時つまり、ベットレイズが入っていないとき while(true) { System.out.println("アクションを選択してください check : c or bet : b"); String str = scan.next(); boolean flag = checkBet(list, str, scan); if(flag == true) { break; }else { System.out.println("入力が不正です!"); } } }else { while(true) { System.out.println("アクションを選択してください call : c or raise : r or fold : f"); String str = scan.next(); boolean flag = callRaiseFold(list, str, scan); if(flag == true) { break; }else { System.out.println("入力が不正です!"); } } } }実行結果
セッションを開始します!グッドラック!
あなたのハンドは♥A♣Kです
あなたの所持チップは100です
ポットは0です
アクションを選択してください check : c or bet : b
b
いくらベットしますか?最大100$です
3
3$ベットしました
ememyは6$にレイズしました。
あなたのハンドは♥A♣Kです
あなたの所持チップは97です
ポットは9です
アクションを選択してください call : c or raise : r or fold : f
r
いくらレイズしますか?最大97$です
30
30$にレイズしました
enemyはcallしました。
フロップに進みます明日以降
アクションのメソッドを使いまわせばターン、リバーの処理は簡単に実装できそう
ハンド(役)の勝敗の決定の仕方をどうしたらいいのかまだわからないそこに時間がかかりそう
寝違えた首が痛い(かなり)文章を普段書かない+人に見せる用ではないので、読みにくいのは勘弁してください・・・