20190829のJavaに関する記事は13件です。

spring.datasource.schema で指定するパスには classpath: を付ける

Spring Boot 起動時にエラーが発生

「InvalidConfigurationPropertyValueException: Property spring.datasource.schema with value 'ServletContext resource [/schema.sql]' is invalid: The specified resource does not exist.」とのこと。

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sampleController': Unsatisfied dependency expressed through field 'service'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sampleService': Unsatisfied dependency expressed through field 'jdbcTemplate'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'jdbcTemplate' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/JdbcTemplateConfiguration.class]: Unsatisfied dependency expressed through method 'jdbcTemplate' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker': Invocation of init method failed; nested exception is org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException: Property spring.datasource.schema with value 'ServletContext resource [/schema.sql]' is invalid: The specified resource does not exist.
(中略)
Caused by: org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException: Property spring.datasource.schema with value 'ServletContext resource [/schema.sql]' is invalid: The specified resource does not exist.
    at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.getResources(DataSourceInitializer.java:163) ~[spring-boot-autoconfigure-2.2.0.M5.jar!/:2.2.0.M5]
    at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.getScripts(DataSourceInitializer.java:146) ~[spring-boot-autoconfigure-2.2.0.M5.jar!/:2.2.0.M5]
    at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.createSchema(DataSourceInitializer.java:93) ~[spring-boot-autoconfigure-2.2.0.M5.jar!/:2.2.0.M5]
    at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker.afterPropertiesSet(DataSourceInitializerInvoker.java:62) ~[spring-boot-autoconfigure-2.2.0.M5.jar!/:2.2.0.M5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863) ~[spring-beans-5.2.0.RC1.jar!/:5.2.0.RC1]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800) ~[spring-beans-5.2.0.RC1.jar!/:5.2.0.RC1]
    ... 77 common frames omitted

ファイル名の前に「classpath:」を付けて解決

application.properties の spring.datasource.schema や spring.datasource.data で指定するファイル名の前には「classpath:」を付ける必要がある。

application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=java
spring.datasource.password=cafebabe
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.initialization-mode=always
spring.datasource.schema=classpath:schema.sql
spring.datasource.data=classpath:data.sql
spring.datasource.sql-script-encoding=utf-8

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring Boot で Spring JDBC を使う

概要

  • Spring Boot + Spring JDBC で動作するサンプルコードを試す
  • データベースには H2 Database を使用する

H2 Database を Spring Framework で使う際のデフォルトの設定情報

デフォルトの設定を application.properties に書き出すと以下のようになる。

applicatin.properties
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.initialization-mode=embedded

この設定内容は自動で適用されるため application.properties に記述する必要は無い。この設定値ではない値を使いたいときには application.properties に記述する必要がある。

デフォルト設定情報は以下の Spring JDBC のソースコードなどに記述されている。

spring-framework/H2EmbeddedDatabaseConfigurer.java at v5.2.0.RC1 · spring-projects/spring-framework · GitHub

ClassUtils.forName("org.h2.Driver", H2EmbeddedDatabaseConfigurer.class.getClassLoader()));
@Override
public void configureConnectionProperties(ConnectionProperties properties, String databaseName) {
    properties.setDriverClass(this.driverClass);
    properties.setUrl(String.format("jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false", databaseName));
    properties.setUsername("sa");
    properties.setPassword("");
}

spring-framework/EmbeddedDatabaseFactory.java at v5.2.0.RC1 · spring-projects/spring-framework · GitHub

public static final String DEFAULT_DATABASE_NAME = "testdb";

application.properties の設定項目は Spring Boot Reference Documentation - Appendices - Appendix A: Common application properties にまとまっている。

schema.sql と data.sql

schema.sql と data.sql は Spring Boot 起動時に実行される SQL 文を記述したファイル。

  • schema.sql には DDL (Data Definition Language: データ定義言語) テーブル作成などの SQL 文を記述する
  • data.sql には DML (Data Manipulation Language: データ操作言語) レコード追加などの SQL 文を記述する

schema-h2.sql や data-h2.sql のようにデータベースの種類によって実行される SQL 文を変えることも可能。

“How-to” Guides - 10. Database Initialization

Spring Boot can automatically create the schema (DDL scripts) of your DataSource and initialize it (DML scripts). It loads SQL from the standard root classpath locations: schema.sql and data.sql, respectively. In addition, Spring Boot processes the schema-\${platform}.sql and data-\${platform}.sql files (if present), where platform is the value of spring.datasource.platform. This allows you to switch to database-specific scripts if necessary. For example, you might choose to set it to the vendor name of the database (hsqldb, h2, oracle, mysql, postgresql, and so on).

applicatin.properties に記述することで読み込む SQL ファイルを変更することも可能。

applicatin.properties
spring.datasource.schema=classpath:foo-schema.sql
spring.datasource.data=classpath:bar-data.sql

サンプルコード

ソースコード一覧

  • pom.xml : Maven のビルド設定用ファイル
  • .java : Java のソースコードファイル
  • .sql : 起動時に実行されるSQL文を記述したファイル
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           ├── Person.java
        │           ├── SampleController.java
        │           └── SampleService.java
        └── resources
            ├── data.sql
            └── schema.sql

pom.xml

Maven のビルド設定用ファイル。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.0.M5</version>
    <relativePath/>
  </parent>

  <groupId>com.example</groupId>
  <artifactId>spring-jdbc-sample</artifactId>
  <version>0.0.1</version>
  <name>spring-jdbc-sample</name>
  <description>Spring JDBC sample</description>

  <properties>
    <java.version>11</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring JDBC を使う -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <!-- H2 Database を使う -->
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <scope>runtime</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

  <repositories>
    <repository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
    </repository>
  </repositories>
  <pluginRepositories>
    <pluginRepository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
    </pluginRepository>
  </pluginRepositories>

</project>

SampleController.java

HTTP リクエストを受けるためのクラス。

package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@SpringBootApplication
@RestController
public class SampleController {

  public static void main(String[] args) {
    SpringApplication.run(SampleController.class, args);
  }

  @Autowired
  private SampleService service;

  // 指定した name のデータを追加する
  @RequestMapping("/add/{name}")
  public List<Person> index(@ModelAttribute Person person) {
    service.save(person);
    return service.findAll();
  }
}

SampleService.java

Spring JDBC の JdbcTemplate クラスを使ってデータベースを操作するクラス。

package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class SampleService {

  @Autowired
  private JdbcTemplate jdbcTemplate;

  // データの一覧を返す
  public List<Person> findAll() {
    // 実行する SQL を組み立てて実行
    String query = "SELECT * from person";
    List<Person> persons = jdbcTemplate.query(query, new BeanPropertyRowMapper<>(Person.class));
    return persons;
  }

  // データを追加する
  public Person save(Person person) {
    // 実行する SQL を組み立てる
    SqlParameterSource param = new BeanPropertySqlParameterSource(person);
    SimpleJdbcInsert insert =
      new SimpleJdbcInsert(jdbcTemplate)
        .withTableName("person")
        .usingGeneratedKeyColumns("id");
    // SQL を実行して、AUTO_INCREMENT の値を取得する
    Number key = insert.executeAndReturnKey(param);
    person.setId(key.longValue());
    System.out.println("Add: " + person);
    return person;
  }
}

Person.java

データベースの1レコードを表現するクラス。

package com.example;

public class Person {

  private Long id; // AUTO_INCREMENT で ID を付与
  private String name;

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  @Override
  public String toString() {
    return "Person(id=" + id + ", name=" + name + ")";
  }
}

schema.sql

起動時に実行される SQL 文を記述したファイル。

CREATE TABLE IF NOT EXISTS person (
  id   INTEGER      NOT NULL AUTO_INCREMENT,
  name VARCHAR(256) NOT NULL,
  PRIMARY KEY (id)
);

data.sql

起動時に実行される SQL 文を記述したファイル。

INSERT INTO person (name) VALUES('Alice');

ビルドと Spring Boot の起動

mvn コマンドでビルドして JAR ファイルを生成。

$ mvn package

java コマンドで Spring Boot を起動。

$ java -jar target/spring-jdbc-sample-0.0.1.jar

起動した Spring Boot にアクセスする

データベースにデータが追加される様子がわかる。

$ curl http://localhost:8080/add/Bob
[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]
$ curl http://localhost:8080/add/Cindy
[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"},{"id":3,"name":"Cindy"}]

今回の動作確認環境

  • OpenJDK 11.0.2
  • Spring Boot 2.2.0.M5
  • Spring JDBC 5.2.0.RC1
  • H2 Database 1.4.199

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Listから条件を(指定回数以上)連続して満足する区間を求める(会議室の空き時間を調べる)

目次 ⇒ Javaアルゴリズムライブラリ-Artery-サンプル

Q01_14.java
package jp.avaj.lib.algo;

import java.util.ArrayList;
import java.util.List;

import jp.avaj.lib.def.ArUseStatus;
import jp.avaj.lib.test.L;

/**
 Listから条件を(指定回数以上)連続して満足する区間を求める(会議室の空き時間を調べる)
  ・条件はArValidatorで指定する.
  ・Listの後ろからも求めることができる.

  ・このような処理の例には、以下のようなものがある.
    ・会議室が二時間以上空いている時間帯を求める.
    ・売上げが100個以上の日が三日以上連続した期間を求める.
    ・テスト結果が80点以上だったのが三回以上続いた期間を求める.
    ・ナンパが三回以上連続成功した期間を求める(笑).

  ・本サンプルでは、会議室の予約を例にする.
    ・会議室は一つ、9時~17時の間に一時間単位で予約が可能.注、18時まで使用可能.

 */
public class Q01_14 {
  public static void main(String[] args) {
    List<MeetingRoom> list = new ArrayList<MeetingRoom>();
    // 0時から23時の枠を作成
    for (int i=0; i<23; i++) {
      list.add(new MeetingRoom(i,((i>=9)&&(i<=17))?ArUseStatus.AVAILABLE:ArUseStatus.ILLEGAL));
    }
    // 前日までの予約で少し埋まっている.
    list.get(12).setStatus(ArUseStatus.IN_USE);
    list.get(13).setStatus(ArUseStatus.IN_USE);
    list.get(14).setStatus(ArUseStatus.IN_USE);
    // 状況を確認する
    L.p(list.toString());

    // 空きを調べるArValidatorを定義する
    ArValidator<MeetingRoom> validator = new ArValidator<MeetingRoom>() {
      @Override
      public boolean check(MeetingRoom value) {
        return value.getStatus() == ArUseStatus.AVAILABLE;
      }
    };

    List<Integer> availableList;
    // 10時に来客なので2時間予約したい⇒空いてる?
    availableList = ArList.sameValueSequence(list, validator,2,10);
    // 結果を確認する ⇒ 10時、15時、16時から二時間空いているはず..
    L.p(availableList.toString());
    // 10時から二時間予約
    list.get(10).setStatus(ArUseStatus.IN_USE);
    list.get(11).setStatus(ArUseStatus.IN_USE);
    L.p(list.toString());

    // 16時に早退の予定⇒その前に1時間打合せをしたい⇒16時終了で一時間予約⇒空いてる?
    availableList = ArList.sameValueSequenceReverse(list, validator,1,16);
    L.p(availableList.toString()); // ⇒ 15時と9時が空いているはず...
    // 15時から一時間予約
    list.get(15).setStatus(ArUseStatus.IN_USE);

    // 状況を確認する
    L.p(list.toString());
  }
  //
  /** 会議室(一時間)クラス */
  static class MeetingRoom {
    /** コンストラクタ */
    public MeetingRoom(int time,ArUseStatus status) {
      this.time = time;
      this.status = status;
    }
    /** (開始)時刻 */
    private int time;
    /** ステータス */
    private ArUseStatus status;
    public int getTime() {
      return time;
    }
    public void setTime(int time) {
      this.time = time;
    }
    public ArUseStatus getStatus() {
      return status;
    }
    public void setStatus(ArUseStatus status) {
      this.status = status;
    }
    @Override
    public String toString() {
      String str = String.format("%02d:",time);
      return (status == ArUseStatus.ILLEGAL) ? "" : str+status.toString();
    }
  }
}


結果は次のとおり。

result.txt
[, , , , , , , , , 09:使用可, 10:使用可, 11:使用可, 12:使用中, 13:使用中, 14:使用中, 15:使用可, 16:使用可, 17:使用可, , , , , ]
[10, 15, 16]
[, , , , , , , , , 09:使用可, 10:使用中, 11:使用中, 12:使用中, 13:使用中, 14:使用中, 15:使用可, 16:使用可, 17:使用可, , , , , ]
[15, 9]
[, , , , , , , , , 09:使用可, 10:使用中, 11:使用中, 12:使用中, 13:使用中, 14:使用中, 15:使用中, 16:使用可, 17:使用可, , , , , ]


  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「好きな季節」のアンケート結果を集計する、性別や年齢でも集計する

目次 ⇒ Javaアルゴリズムライブラリ-Artery-サンプル

Q01_00.java
package jp.avaj.lib.algo;

import java.util.ArrayList;
import java.util.List;

import jp.avaj.lib.def.ArCharDef;
import jp.avaj.lib.def.ArRepeat;
import jp.avaj.lib.test.L;

/**
 *  「好きな季節」のアンケート結果を集計する、性別や年齢でも集計する
 *
 *  ・好きな季節を答えてもらう.ついでに名前、性別、年齢を教えてもらう.
 *  ・結果を集計する.
 *    ・季節ごとに集計する.
 *    ・女性の場合を集計する.
 *    ・年齢層別に集計する
 *
 *  ・使用するのはArListの以下のメソッド.
 *    Integer[] histogram(Collection<T0> collection,ArCreator<T0,T1> creator,ArValidator<T1>[] validators)
 *  ・T0オブジェクトからcreatorでT1オブジェクトを作成し、それをvalidatorsでチェックする.
 *  ・validatorsの配列に対応したカウント数が戻される.
 *
 *  ・「年齢層別に集計する」のようにチェック対象のフィールドが複数となる場合⇒サンプルを見て再度読んでね.
 *    ・ArCreatorSelf(引数自身を戻すArCreator)を使用する
 *    ・T0を引数にとって、それぞれフィールドをチェックするArValidator(の配列)を作成する。
 *    ・上記の複数のArValidatorの配列の組み合わせを作成する.
 *    ・T0に対応した汎用のArValidatorは(普通は)ありえないので、ArValidatorは独自実装となる.
 *
 */
public class Q01_00 {
  public static void main(String[] args) throws Exception {
    L.p("アンケート実施");
    List<EnquetePaper> papers = execEnquete();
    //
    L.p("季節ごとに集計する");
    {
      // creatorの作成 ⇒ 集計対象フィールドは季節
      ArCreator<EnquetePaper,ArSeason> creator = new ArCreator<EnquetePaper,ArSeason>() {
        @Override
        public ArSeason convert(EnquetePaper obj) throws Exception {
          return obj.getSeason();
        }
      };
      // それぞれの季節のArValidatorを作成する
      // ArValidatorEqはチェック対象がコンストラクタの引数と等しいかを判定するArValidator
      ArValidatorEq<ArSeason>[] validators = ArValidatorUtil.createArValidatorEqArray(ArSeason.values());
      // 集計する
      Integer[] results = ArList.histogram(papers,creator,validators);
      // 結果を見てみる
      L.p(ArObj.toString2Arrays(validators,results));
    }

    L.p("季節ごとに集計する-別解");
    {
      /*
       * 別解としてArCreatorSelfを使用するもの.
       * creatorの戻りがEnquetePaperになるので、ArValidatorは独自実装となる.
       * チェック対象フィールドが一つの場合は、このようにする必要はないので、サンプルとして見てほしい.
       */
      //
      ArCreator<EnquetePaper,EnquetePaper> creator = new ArCreatorSelf<EnquetePaper>();
      // 季節をチェックするArValidator
      class SeasonValidator implements ArValidator<EnquetePaper> {
        public SeasonValidator(ArSeason season) {
          this.season = season;
        }
        @Override
        public boolean check(EnquetePaper value) {
          return value.getSeason() == season;
        }
        @Override
        public String toString() {
          return ArObj.toString(season);
        }
        private ArSeason season;
      }
      // それぞれの季節のArValidatorを作成する
      ArValidator<EnquetePaper>[] validators = new ArValidator[] {
        new SeasonValidator(ArSeason.SPRING),
        new SeasonValidator(ArSeason.SUMMER),
        new SeasonValidator(ArSeason.AUTUMN),
        new SeasonValidator(ArSeason.WINTER),
      };
      // 集計する
      Integer[] results = ArList.histogram(papers, creator, validators);
      // 結果を見てみる
      L.p(ArObj.toString2Arrays(validators,results));
    }

    L.p("女性の場合を集計する");
    {
      // 集計対象フィールドは季節
      ArCreator<EnquetePaper,ArSeason> creator = new ArCreator<EnquetePaper,ArSeason>() {
        @Override
        public ArSeason convert(EnquetePaper obj) throws Exception {
          // 男性の場合はnullを戻すようにする ⇒ nullの時は集計対象にならない(できない).
          return obj.getGender()==ArGender.Female ? obj.getSeason() : null;
        }
      };
      // それぞれの季節のArValidatorを作成する
      ArValidator<ArSeason>[] validators = ArValidatorUtil.createArValidatorEqArray(ArSeason.values());
      // 集計する
      Integer[] results = ArList.histogram(papers, creator, validators);
      // 結果を見てみる
      L.p(ArObj.toString2Arrays(validators,results));
    }

    L.p("女性の場合を集計する-別解");
    {
      // 集計対象フィールドは季節
      ArCreator<EnquetePaper,ArSeason> creator = new ArCreator<EnquetePaper,ArSeason>() {
        @Override
        public ArSeason convert(EnquetePaper obj) throws Exception {
          return obj.getSeason();
        }
      };
      // それぞれの季節のArValidatorを作成する
      ArValidator<ArSeason>[] validators = ArValidatorUtil.createArValidatorEqArray(ArSeason.values());
      //
      // 女性だけを抜き出したデータを作成する.
      List<EnquetePaper> femalePapers;
      {
        // 女性のデータの時にtrueとなるArValidatorを作成する
        ArValidator<EnquetePaper> femaleValidator = new ArValidator<EnquetePaper>() {
          @Override
          public boolean check(EnquetePaper value) {
            return value.getGender()==ArGender.Female;
          }
        };
        // 抜き出す
        femalePapers = ArList.select(papers,femaleValidator);
      }
      // femalePapersを対象にして集計する
      Integer[] results = ArList.histogram(femalePapers, creator, validators);
      // 結果を見てみる
      L.p(ArObj.toString2Arrays(validators,results));
    }

    L.p("年齢層別に集計する");
    {
      /*
       * 考え方は、EnquetePaperを元にして年齢をチェックするArValidatorと季節をチェックするArValidatorを作成する.
       * EnquetePaperが固有のクラスで、またチェックするのが、その中のフィールドなのでArValidatorは独自実装となる.
       * 二種類のArValidator(の配列)を元に、(ArValidatorUtilを使って)その組み合わせを作成する.
       */
      // 年齢と季節のフィールドを見るのでArCreatorSelfを使用する
      ArCreator<EnquetePaper,EnquetePaper> creator = new ArCreatorSelf<EnquetePaper>();
      // 年齢のArValidator
      class AgeValidator implements ArValidator<EnquetePaper> {
        public AgeValidator(int low,int high) {
          this.low = low;
          this.high = high;
        }
        @Override
        public boolean check(EnquetePaper value) {
          return ((low <= value.getAge()) && (value.getAge() <= high));
        }
        @Override
        public String toString() {
          return low+"-"+high;
        }
        private int low;
        private int high;
      }
      // 年齢を二十代前半と後半に分ける
      ArValidator<EnquetePaper>[] validators0 = new ArValidator[] {
        new AgeValidator(20,24),
        new AgeValidator(25,29),
      };
      // 季節のArValidator
      class SeasonValidator implements ArValidator<EnquetePaper> {
        public SeasonValidator(ArSeason season) {
          this.season = season;
        }
        @Override
        public boolean check(EnquetePaper value) {
          return season == value.getSeason();
        }
        @Override
        public String toString() {
          return ArObj.toString(season);
        }
        private ArSeason season;
      }
      //
      ArValidator<EnquetePaper>[] validators1 = new ArValidator[] {
        new SeasonValidator(ArSeason.SPRING),
        new SeasonValidator(ArSeason.SUMMER),
        new SeasonValidator(ArSeason.AUTUMN),
        new SeasonValidator(ArSeason.WINTER),
      };
      // 年齢と季節のArValidatorの配列の組合せを作成する
      // ArValidatorGroupは複数のArValidatorを組み合わせたArValidator
      ArValidatorGroup<EnquetePaper>[] validators = ArValidatorUtil.createCombination(validators0,validators1);
      // 集計する
      Integer[] results = ArList.histogram(papers, creator, validators);
      // 結果を見てみる
      L.p(ArObj.toString2Arrays(validators,results));
    }
  }

  /** アンケートを実施する */
  private static List<EnquetePaper> execEnquete() {
    /*
     * 注、下記のRandomFromはCollectionや配列からランダムに要素を取り出すインターフェース.
     * ArRepeatで同じものを再度取り出すか否かの指定ができる
     */
    // 名前 ⇒ 大文字一字からランダムに生成 ⇒ ArRepeat.NO(同じ名前はない)
    ArRandomFromArray<String> names = new ArRandomFromArray<String>(ArCharDef.upperStr,ArRepeat.NO);
    // 性別 ⇒ 男性/女性のどちらか(他は無視)
    ArRandomFromArray<ArGender> genders = new ArRandomFromArray<ArGender>(new ArGender[]{ArGender.Male,ArGender.Female},ArRepeat.YES);
    // 年齢 ⇒ 20歳から29歳まで..
    ArRandomFromCollection<Integer> ages;
    {
      // 20から10個の数値を間隔1で生成する
      List<Integer> values = ArList.constructSeqList(20,10,1);
      ages = new ArRandomFromCollection<Integer>(values,ArRepeat.YES); // 同じ年齢の人はいるはず..
    }
    // 季節
    ArRandomFromArray<ArSeason> seansons = new ArRandomFromArray<ArSeason>(ArSeason.values(),ArRepeat.YES);
    // アンケート実施
    List<EnquetePaper> list = new ArrayList<EnquetePaper>();
    for (int i=0; i<20; i++) {
      EnquetePaper paper = new EnquetePaper(names.get(),genders.get(),ages.get(),seansons.get());
      // 目視確認のため出力しておく ⇒ toStringを再定義してなくても動作する
      L.p(ArObj.toString(paper));
      list.add(paper);
    }
    return list;
  }

  /** アンケート用紙(Enqueteはフランス語) */
  static class EnquetePaper {
    /** コンストラクタ */
    public EnquetePaper(String name,ArGender gender,int age,ArSeason season) {
      this.name = name;
      this.gender = gender;
      this.age = age;
      this.season = season;
    }
    /** 氏名 */
    private String name;
    /** 性別. */
    private ArGender gender;
    /** 年齢 */
    private int age;
    /** 季節 */
    private ArSeason season;
    public String getName() {
      return name;
    }
    public void setName(String name) {
      this.name = name;
    }
    public ArGender getGender() {
      return gender;
    }
    public void setGender(ArGender gender) {
      this.gender = gender;
    }
    public int getAge() {
      return age;
    }
    public void setAge(int age) {
      this.age = age;
    }
    public ArSeason getSeason() {
      return season;
    }
    public void setSeason(ArSeason season) {
      this.season = season;
    }
  }
}

結果は以下の通り。

アンケート実施
[age=23, gender=男性, name=O, season=]
[age=26, gender=女性, name=S, season=]
[age=22, gender=女性, name=U, season=]
[age=29, gender=男性, name=B, season=]
[age=20, gender=女性, name=C, season=]
[age=27, gender=男性, name=D, season=]
[age=26, gender=女性, name=P, season=]
[age=23, gender=男性, name=T, season=]
[age=23, gender=女性, name=W, season=]
[age=20, gender=男性, name=N, season=]
[age=29, gender=男性, name=E, season=]
[age=20, gender=女性, name=M, season=]
[age=22, gender=男性, name=A, season=]
[age=26, gender=男性, name=H, season=]
[age=26, gender=男性, name=I, season=]
[age=20, gender=女性, name=L, season=]
[age=29, gender=女性, name=V, season=]
[age=27, gender=女性, name=Z, season=]
[age=20, gender=女性, name=X, season=]
[age=20, gender=女性, name=J, season=]
季節ごとに集計する
 -> 3
 -> 6
 -> 8
 -> 3
季節ごとに集計する-別解
 -> 3
 -> 6
 -> 8
 -> 3
女性の場合を集計する
 -> 3
 -> 5
 -> 2
 -> 1
女性の場合を集計する-別解
 -> 3
 -> 5
 -> 2
 -> 1
年齢層別に集計する
20-24: -> 1
20-24: -> 4
20-24: -> 4
20-24: -> 2
25-29: -> 2
25-29: -> 2
25-29: -> 4
25-29: -> 1


  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring Framework / Spring Bootに入門する人はまずこの資料を読もう! #jsug

昨日2019年8月29日、日本Springユーザ会(JSUG)でSpring初心者向けの勉強会を開催しました。

https://jsug.doorkeeper.jp/events/95750

Springのコンテナ・DIからMVC・Security・Bootまで2時間で一気に学べる勉強会で、手前味噌ながらとても良い勉強会になったと思います。

コンテナから紐解く本当のSpring入門(by @suke_masa

スライド→https://speakerdeck.com/masatoshitada/understanding-spring-container

最初は僕の発表で、Springの根幹の機能である「コンテナ」について解説しています。Spring MVCをはじめ、他のSpringプロダクトのほとんどはこのコンテナの機能を活用して作られています。

僕もそうだったんですが、どの資料でもコンテナは「DI」と一緒に説明されることがほとんどです。しかし、「Springの根幹はコンテナであり、DIはそのイチ機能として解説したほうが分かりやすいのでは」と思い、今回新しく作ったのがこの資料です。

当日の発表では時間の関係上「③Dependency Injection」までしか解説していませんが、上記の資料にはそれより後の内容もすべて掲載しています。

特に最後の「⑤プロキシ」は知らない方も多い内容と思いますので、必読です!

図解で学ぶSpring MVC(by @otty_375

スライド→https://speakerdeck.com/otty375/architecture-of-spring-mvc

次はいけさんによるSpring MVCのアーキテクチャ解説です。タイトル通り図が多く取り入れられていて、とても分かりやすい資料になっています。

アーキテクチャだけでなく、コントローラーやビューの作り方も解説されているので、本当に初心者におすすめです。

Spring MVCのアーキテクチャは、普通に機能を作る(=コントローラーとビューのみ作る)だけなら、理解する必要はありません。しかし、何か共通的な処理を作りたいという場合は、しっかり理解しておく必要があります。

ぜひアーキテクチャを理解して、ワンランク上のエンジニアを目指しましょう!

FORM認証で学ぶSpring Security(by @b1a9idps

スライド→https://www.slideshare.net/RyosukeUchitate/formspring-security

次は内立さんによるSpring Securityの解説。ログイン画面からログインするFORM認証を例に、Spring Securityを使ったサンプルや、アーキテクチャの解説です。正直、やや難易度高めの資料になっています。

というのも、そもそもSpring Security自体が難しいんですよね・・・。アーキテクチャの複雑さは、数あるSpringプロダクトの中でも随一だと思います。

まずはアーキテクチャの部分は置いておいて、表面的な作り方だけでも押さえておくと良いでしょう。ソースコードも公開されていますので、ぜひ参考になさってください。→https://github.com/b1a9id/spring-security-sample-jsug-201908

アーキテクチャの理解にチャレンジするのは、後回しでも問題ありません。というか、Spring Securityのアーキテクチャを完全に理解している人ってどんだけいるのかな・・・?

開発者のためのSpring Boot Actuator入門(by @shindo_ryo

スライド→https://speakerdeck.com/rshindo/jsug-2019-08

最後に、進藤さんによるSpring Boot Actuatorの解説です。Actuatorは基本的には運用・監視のための機能なのですが、開発者にとっても役立つよ!という内容でした。

Actuatorは本当に多機能なので、単なる死活監視だけに使うのはもったいないです!アプリの再起動なしでログレベルを変更したり、コントローラーメソッドとURLの対応関係を一覧で見ることが出来たりします。

また、Spring Boot AdminというActuatorの結果をGUIで表示できるアプリもあります(進藤さん曰く、本番の運用ではなく開発時のみ使っているそうです)。

Actuatorを使いこなすと、色々と良いことがあるかもしれません!

その他

今回の勉強会で紹介が無かった内容で、ぜひ初心者の方に読んでおいて資料をまとめておきます。

市販書籍

前出の僕の資料にも載せてありますが、下記の2冊をおすすめします。

他の本は、内容が古かったり品質的に問題があったりするので、おすすめしません。

ブログ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【メモ】MyBatisの謎の動き、リストオブジェクトの要素にnullを設定して返してくる。

参考までに、
SQLの「Select項目が全部null」で、かつ「returnをList型で受けてる」と、
「sizeが1」で「リスト要素にnullを設定した」すげーこまるリストオブジェクトが返却されるので気を付けてください。
僕は、使わないけど行番号を付けてとりあえず回避しました。

事前に下記の内容をチェックしていると、実行バグを回避可能かと思われる。
1.必須カラムを含んでいる
2.WHERE句で「IS NOT NULL」のチェックしているカラムを含んでいる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

List<Person>から10歳ごとの人数を集計する

目次 ⇒ Javaアルゴリズムライブラリ-Artery-サンプル

Q01_10.java
package jp.avaj.lib.algo;

import java.util.ArrayList;
import java.util.List;

import jp.avaj.lib.def.ArCharDef;
import jp.avaj.lib.def.ArMatch;
import jp.avaj.lib.def.ArRepeat;
import jp.avaj.lib.test.L;

/**
 *  List<Person>から10歳ごとの人数を集計する
 *
 *  ・ArListの以下のメソッドを使用する.
 *  Integer[] histogram(Collection<T0> collection,ArCreator<T0,T1> creator,ArValidator<T1>[] validators)
 *
 *  ・ListのオブジェクトT0から集計対象データを抽出するためにcreatorを使用する.
 *    ・当サンプルではPersonオブジェクトから年齢を抽出する.
 *
 *  ・抽出された年齢を層別するためにvalidatorsを使用する.
 *    ・配列になっているが、それぞれ10歳ごとにtrueを戻すものにする.
 *
 */
public class Q01_10 {
  public static void main(String[] args) throws Exception {
    List<Person> personList = createPersonList();
    //
    // ArCreator ⇒ Personから年齢を抽出する
    ArCreator<Person,Integer> creator = new ArCreator<Person,Integer>() {
      @Override
      public Integer convert(Person obj) throws Exception {
        return obj.getAge();
      }
    };
    // 年齢を判定するためのArValidator
    ArValidatorIntRange[] validators = new ArValidatorIntRange[] {
      new ArValidatorIntRange(10,20,ArMatch.YES), // 10(含む)~20(含まない),ArMatch.YES,NOは判定を逆転させる
      new ArValidatorIntRange(20,30,ArMatch.YES),
      new ArValidatorIntRange(30,40,ArMatch.YES),
      new ArValidatorIntRange(40,50,ArMatch.YES),
      new ArValidatorIntRange(50,60,ArMatch.YES),
    };

    Integer[] result;
    // ヒストグラムを作成する
    result = ArList.histogram(personList,creator, validators);
    L.p(ArObj.toString2Arrays(validators,result));

    L.p("別解");
    // ArCreatorの別解.汎用的なArCreatorとしてフィールド名から値を取得するArCreatorByNameを使用する.
    creator = new ArCreatorByName<Person,Integer>("age");
    // ArValidatorの別解
    // 最初の解のように一つ一つArValidatorを作成するのは面倒.
    // 以下のように簡単に生成することもできる.注、両端にnullを指定することができる⇒無限範囲
    validators = ArValidatorUtil.createArValidatorIntRangeArray(new Integer[]{10,20,30,40,50,60});
    result = ArList.histogram(personList,creator, validators);
    L.p(ArObj.toString2Arrays(validators,result));
  }

  private static List<Person> createPersonList() {
    // 名前を生成するためのクラス ⇒ 名前は大文字一文字
    // ArRandomFromArrayは配列からランダムに値を取り出す、ArRepeatで同じ値を取り出すかを指定する.
    ArRandomFromArray<String> names = new ArRandomFromArray<String>(ArCharDef.upperStr,ArRepeat.NO);
    // 年齢を生成するためのクラス ⇒ 10(含む)~60(含まない)のIntegerをランダムに生成する
    ArRandomFromInteger ages = new ArRandomFromInteger(10,60);

    List<Person> list = new ArrayList<Person>();
    for (int i=0; i<20; i++) {
      Person person = new Person(names.get(),ages.get());
      L.p(ArObj.toString(person)); // このようにすればtoStringを実装していなくても内容が表示される.
      list.add(person);
    }
    return list;
  }

  static class Person {
    public Person(String name,int age) {
      this.name = name;
      this.age = age;
    }
    private String name;
    private int age;
    public String getName() {
      return name;
    }
    public void setName(String name) {
      this.name = name;
    }
    public int getAge() {
      return age;
    }
    public void setAge(int age) {
      this.age = age;
    }
  }
}


結果は次の通り

result.txt
[age=31, name=S]
[age=26, name=F]
[age=42, name=K]
[age=43, name=Y]
[age=22, name=M]
[age=34, name=N]
[age=11, name=E]
[age=36, name=Q]
[age=13, name=U]
[age=41, name=X]
[age=39, name=Z]
[age=47, name=O]
[age=47, name=P]
[age=59, name=H]
[age=52, name=A]
[age=17, name=D]
[age=35, name=C]
[age=47, name=W]
[age=55, name=J]
[age=11, name=R]
[10-20) -> 4
[20-30) -> 2
[30-40) -> 5
[40-50) -> 6
[50-60) -> 3
別解
[10-20) -> 4
[20-30) -> 2
[30-40) -> 5
[40-50) -> 6
[50-60) -> 3


  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

List<売上>から移動平均、初期からの変動率・変動幅、N個前からの変動率・変動幅、パーセントを求めて、List<Double>を作成する

目次 ⇒ Javaアルゴリズムライブラリ-Artery-サンプル

Q01_01.java
package jp.avaj.lib.algo;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import jp.avaj.lib.test.L;

/**
 *  List<売上>から移動平均、初期からの変動率・変動幅、N個前からの変動率・変動幅、パーセントを求めて、List<Double>を作成する
 *
 */
public class Q01_01 {
  public static void main(String[] args) throws Exception {
    // 売上データを取得する
    List<Sales> list = createSalesData();

    // ArCreatorの定義 ⇒ Salesクラスの売上数(count)を抽出する
    ArCreator<Sales,Integer> creator = new ArCreator<Sales,Integer>() {
      @Override
      public Integer convert(Sales obj) throws Exception {
        return obj.getCount();
      }
    };

    List<? extends Number> result;

    L.p("四か月移動平均");
    result = ArList.movingAvg(list, creator,4);
    L.p(ArObj.toString(result));

    L.p("初期からの変動率");
    result = ArList.fluctuationRatio(list,creator);
    L.p(ArObj.toString(result));

    L.p("初期からの変動幅");
    result = ArList.fluctuationWidth(list,creator);
    L.p(ArObj.toString(result));

    L.p("4個前からの変動率");
    result = ArList.fluctuationRatio(list,creator,4);
    L.p(ArObj.toString(result));

    L.p("4個前からの変動幅");
    result = ArList.fluctuationWidth(list,creator,4);
    L.p(ArObj.toString(result));

    L.p("パーセント");
    result = ArList.percent(list,creator);
    L.p(ArObj.toString(result));

  }

  /** 売上データを作成する */
  private static List<Sales> createSalesData() {
    // 商品名
    final String NAME = "item";
    Random ran = new Random();
    List<Sales> list = new ArrayList<Sales>();
    // 二年分のデータを作成する
    for (int i=0; i<24; i++) {
      Sales sales = new Sales(NAME,1000+i*10+ran.nextInt(200));
      list.add(sales);
    }
    L.p("元データ");
    L.p(ArObj.toString(list));
    return list;
  }

  /** ある商品のある月の売上データ */
  static class Sales {
    /** コンストラクタ */
    public Sales(String name,int count) {
      this.name = name;
      this.count = count;
    }
    /** 商品名 */
    private String name;
    /** 売上数 */
    private int count;
    public String getName() {
      return name;
    }
    public void setName(String name) {
      this.name = name;
    }
    public int getCount() {
      return count;
    }
    public void setCount(int count) {
      this.count = count;
    }
    @Override
    public String toString() {
      return count+"";
    }
  }
}


結果は次の通り

result.txt
元データ
[1189, 1191, 1177, 1205, 1216, 1129, 1253, 1108, 1218, 1164, 1253, 1307, 1288, 1267, 1178, 1153, 1200, 1349, 1266, 1378, 1381, 1230, 1402, 1273]
四か月移動平均
[1189, 1190, 1185.6666666666667, 1190.5, 1197.25, 1181.75, 1200.75, 1176.5, 1177, 1185.75, 1185.75, 1235.5, 1253, 1278.75, 1260, 1221.5, 1199.5, 1220, 1242, 1298.25, 1343.5, 1313.75, 1347.75, 1321.5]
初期からの変動率
[1, 1.001682085786375, 0.9899074852817493, 1.0134566862910008, 1.022708158116064, 0.9495374264087468, 1.0538267451640033, 0.9318755256518082, 1.024390243902439, 0.9789739276703112, 1.0538267451640033, 1.0992430613961313, 1.0832632464255676, 1.065601345668629, 0.990748528174937, 0.9697224558452481, 1.0092514718250631, 1.1345668629100083, 1.0647603027754415, 1.1589571068124473, 1.1614802354920102, 1.0344827586206897, 1.1791421362489487, 1.0706476030277545]
初期からの変動幅
[0, 2, -12, 16, 27, -60, 64, -81, 29, -25, 64, 118, 99, 78, -11, -36, 11, 160, 77, 189, 192, 41, 213, 84]
4個前からの変動率
[1, 1.001682085786375, 0.9899074852817493, 1.0134566862910008, 1.022708158116064, 0.9479429051217464, 1.064570943075616, 0.9195020746887966, 1.0016447368421053, 1.0310008857395925, 1, 1.1796028880866427, 1.0574712643678161, 1.088487972508591, 0.9401436552274541, 0.8821729150726856, 0.9316770186335404, 1.0647198105761642, 1.0747028862478778, 1.1951431049436254, 1.1508333333333334, 0.9117865085248332, 1.1074249605055293, 0.9238026124818578]
4個前からの変動幅
[0, 2, -12, 16, 27, -62, 76, -97, 2, 35, 0, 199, 70, 103, -75, -154, -88, 82, 88, 225, 181, -119, 136, -105]
パーセント
[3.99328295549958, 4, 3.9529806884970613, 4.047019311502939, 4.083963056255247, 3.7917716204869856, 4.208228379513014, 3.7212426532325775, 4.090680100755668, 3.9093198992443323, 4.208228379513014, 4.389588581024349, 4.325776658270361, 4.255247691015953, 3.956339210747271, 3.8723761544920237, 4.030226700251889, 4.530646515533165, 4.251889168765743, 4.6280436607892526, 4.638119227539883, 4.130982367758186, 4.70864819479429, 4.275398824517213]


  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

List<Person>から年齢の平均値を求める.

目次 ⇒ Javaアルゴリズムライブラリ-Artery-サンプル

Q01_07.java
package jp.avaj.lib.algo;

import java.util.ArrayList;
import java.util.List;

import jp.avaj.lib.test.L;

/**
 *  List<Person>から年齢の平均値を求める.
 *
 *  ・Personから対象データ(年齢)を取り出すには、ArCreatorを定義する必要がある.
 *  ・最初の方法では、convertメソッドを実装してageを取り出している.
 *  ・しかしこの方法では、取り出すフィールドごとに実装を作成するので面倒.
 *  ・二番目の方法では、汎用的なArCreatorByNameを作成してフィールド名を指定している.
 *  ・むやみにArCreatorの実装が増えることを防ぐことができる.
 *
 */
public class Q01_07 {
  public static void main(String[] args) throws Exception {
    List<Person> list = createList();

    // ArCreatorの定義 - 最初の方法
    ArCreator<Person,Integer> creator = new ArCreator<Person,Integer>() {
      @Override
      public Integer convert(Person obj) throws Exception {
        return obj.getAge();
      }
    };
    // 平均値を求める
    L.p(ArList.avg(list,creator)+"");

    // ArCreatorの定義 - 二番目の方法
    creator = new ArCreatorByName<Person,Integer>("age");
    // 平均値を求める
    L.p(ArList.avg(list,creator)+"");
  }

  /** 人物情報を作成する  */
  private static List<Person> createList() {
    List<Person> list = new ArrayList<Person>();
    list.add(new Person("a",25));
    list.add(new Person("b",30));
    list.add(new Person("c",28));
    list.add(new Person("d",22));
    list.add(new Person("e",38));
    return list;
  }

  /** 人物情報のクラス(名前と年齢だけ..) */
  static class Person {
    public Person(String name,int age) {
      this.name = name;
      this.age = age;
    }
    private String name;
    private int age;
    public String getName() {
      return name;
    }
    public void setName(String name) {
      this.name = name;
    }
    public int getAge() {
      return age;
    }
    public void setAge(int age) {
      this.age = age;
    }
  }
}


結果は次の通り。

result.txt
28.6
28.6

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

List<Integer>から集約値(最大、最小、合計、平均、標準偏差)を計算する.

目次 ⇒ Javaアルゴリズムライブラリ-Artery-サンプル

Q01_06.java
package jp.avaj.lib.algo;

import java.util.List;

import jp.avaj.lib.test.L;

/**
 *  List<Integer>から集約値(最大、最小、合計、平均、標準偏差)を計算する.
 *
 *  ・Listの要素から集約対象データを取り出すためにはArCreatorを使用する.
 *  ・今回は要素自体が集計対象なので、要素自体を戻すArCreatorSelfを使用する.
 *
 */
public class Q01_06 {
  public static void main(String[] args) throws Exception {
    // テストデータ
    List<Integer> list = ArList.construct(new Integer[]{1,2,3,4,5,6,7,8,9,10});

    L.p("最大="+ArList.max(list,new ArCreatorSelf<Integer>()));

    L.p("最小="+ArList.min(list,new ArCreatorSelf<Integer>()));

    L.p("合計="+ArList.sum(list,new ArCreatorSelf<Integer>()));

    L.p("平均="+ArList.avg(list,new ArCreatorSelf<Integer>()));

    L.p("標準偏差="+ArList.standardDeviation(list,new ArCreatorSelf<Integer>()));
  }
}


結果は以下の通り

result.txt
最大=10
最小=1
合計=55
平均=5.5
標準偏差=2.8722813232690143

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaアルゴリズムライブラリ-Artery-サンプル

■■■■ Javaアルゴリズムライブラリ-Artery-サンプル ■■■■

・私が開発しているJavaアルゴリズムライブラリArteryの使用サンプルを紹介します。当記事が目次になります。

・サンプルは順次追加していきます。しかしすべての機能を解説するものではありません。

・なお、ArteryにはJFreeChart用ライブラリも含まれています。こちらを参照してください。
  JFreeChartサンプル
  注、こちらはJFreeChart自体の解説が主。

・Arteryライブラリは、以下よりダウンロードできます。
  ベクター⇒プログラミング⇒Java言語
  version201809リリース(2019/08/28)

データ構造構築用クラス(List,Set,Map)関連(01)

List,Set,Map関連の機能は以下のクラスにまとめられている。

・ArList,ArSet,ArMap
・Collectionでよいものは、ArListとArSetの両方に記述されている。

List,Setから数値を抽出して、集約値(最大、最小、合計、平均、標準偏差)を計算する。

・対象数値は以下のように取得できる。ArCreatorで数値に変換する。
 ・List<Integer>のように要素が数値。
 ・要素のオブジェクトから数値フィールドを抽出。
 ・要素のオブジェクトから計算される数値。例えばあるフィールドと別のフィールドの和。

ArList.java
  /** listからcreatorで取得される数値の最小値を取り出す. */
  public static <T> Number min(Collection<T> list,ArCreator<T,? extends Number> creator)
  /** listからcreatorで取得される数値の最大値を取り出す. */
  public static <T> Number max(Collection<T> list,ArCreator<T,? extends Number> creator)
  /** listからcreatorで取得される数値の合計値を計算する. */
  public static <T> Number sum(Collection<T> list,ArCreator<T,? extends Number> creator)
  /** listからcreatorで取得される数値の平均値を計算する. */
  public static <T> Double avg(Collection<T> list,ArCreator<T,? extends Number> creator)
  /** listからcreatorで取得される値の標準偏差を計算する. */
  public static <T> Double standardDeviation(Collection<T> list,ArCreator<T,? extends Number> creator)

List<Integer>から集約値(最大、最小、合計、平均、標準偏差)を計算する
List<Person>から年齢の平均値を求める

Listから数値を抽出して、移動平均、初期からの変動率・変動幅、N個前からの変動率・変動幅、パーセントを計算する。

・対象数値の取得は集約値の場合と同じ。
・結果は、新しいListを作成するか、元のオブジェクトの指定フィールドに設定する。下記メソッドは新しいListを作成するもの。

ArList.java
  /** listからcreatorで取得される値の移動平均を計算する.移動値に満たない場合は短い幅で計算する. */
  public static <T> List<Double> movingAvg(List<T> list,ArCreator<T,? extends Number> creator,int span)
  /** listからcreatorで取得される値の初期値から変動率を計算する. */
  public static <T> List<Double> fluctuationRatio(List<T> list,ArCreator<T,? extends Number> creator)
  /** listからcreatorで取得される値の初期値から変動幅を計算する. */
  public static <T> List<Number> fluctuationWidth(List<T> list,ArCreator<T,? extends Number> creator) 
  /** listからcreatorで取得される値のN個前との変動率を計算する.N個に満たない場合は短い幅で計算する. */
  public static <T> List<Double> fluctuationRatio(List<T> list,ArCreator<T,? extends Number> creator,int span) 
  /** listからcreatorで取得される値のN個前との変動幅を計算する.N個に満たない場合は短い幅で計算する. */
  public static <T> List<Number> fluctuationWidth(List<T> list,ArCreator<T,? extends Number> creator,int span) 
  /** listからcreatorで取得される値の全体とのパーセントを計算する. */
  public static <T> List<Double> percent(List<T> list,ArCreator<T,? extends Number> creator)

・[List<Integer>から移動平均、初期からの変動率・変動幅、N個前からの変動率・変動幅、パーセントを求めてList<Number>を作成する]
List<売上>から移動平均、初期からの変動率・変動幅、N個前からの変動率・変動幅、パーセントを求めて、List<Number>を作成する
・[List<売上>から三か月移動平均を求めてを求めて、売上データ中に設定する]

List,Setから値(数値でなくてもよい)を抽出してヒストグラムを作成する。

・対象値の取得は、集約値の場合と同じ。
・集計のための値域はArValidatorの配列で指定する。ArValidatorは対象値の真偽を判定する。
・結果は集計の値域に対応したInteger[]で戻される。

ArList.java
  /** collectionからcreatorで取得される値のヒストグラムを求める */
  public static <T0,T1> Integer[] histogram(Collection<T0> collection,ArCreator<T0,T1> creator,ArValidator<T1>[] validators)

・[List<Integer>に0~9の数字が入っているので出現頻度を調べる]
List<Person>から10歳ごとの人数を集計する
「好きな季節」のアンケート結果を集計する、性別や年齢でも集計する
★[値域を示すArValidatorの配列の作成例]

Listから条件を(指定回数以上)連続して満足する区間を求める

・条件はArValidatorで指定する。
・Listの後ろからも求めることができる。
・例は以下。
 ・会議室が二時間以上空いている時間帯を求める。
 ・売上げが100個以上の日が三日以上連続した期間を求める。
 ・テスト結果が80点以上だったのが三回以上続いた期間を求める。
 ・ナンパが三回以上連続成功した期間を求める(笑)。

ArList.java
  /**
  * start(含む)以降で指定要素がcount個数以上続いているインデックスを戻す.
  * count個数以上続いている時は、一つ進めたインデックスも格納されている.下記参照.
  * 0:a,1:b,2:b,3:b,4:b,5:cでbを二個以上の場合は1,2,3が戻される.
  */
 public static <T> List<Integer> sameValueSequence(List<T> list,ArValidator<T> validator,int count,int start)
 /**
  * end(含まない)以前でで指定要素がcount個数以上続いているインデックスを戻す.
  * count個数以上続いている時は、一つ進めたインデックスも格納されている.下記参照.
  * 0:a,1:b,2:b,3:b,4:b,5:cでbを二個以上の場合は3,2,1が戻される.
  */
 public static <T> List<Integer> sameValueSequenceReverse(List<T> list,ArValidator<T> validator,int count,int end)

会議室の空き時間を調べる

List,Set,Mapから指定された条件に従ってオブジェクトを抽出し、新しいList,Set,Mapを作成する。

・抽出条件はArValidatorで指定する。ArValidatorでtrueと判定されたオブジェクトが抽出される。
・Mapではキーと値のどちらかで指定できる。

ArList.java
  /** Listからvalidatorで選択された要素を取り出す.元のListは変化しない. */
 public static <T> List<T> select(List<T> list,ArValidator<T> validator)
ArMap.java
 /** キーをArValidatorで選択して新しいMapを作成する. */
 public static <T0, T1> Map<T0,T1> selectByKey(Map<T0,T1> map,ArValidator<T0> validator)
 /** 値をArValidatorで選択して新しいMapを作成する. */
 public static <T0, T1> Map<T0,T1> selectByValue(Map<T0,T1> map,ArValidator<T1> validator)

・[List<Person>から40歳以上の男性を抽出する]
・[Map<地域,売上データ>から東京都のデータを抽出する、売上が100個以上のデータを抽出する]

List,Set,Mapのオブジェクトを別のクラスのオブジェクトに変換し、新しいList,Set,Mapを作成する。

・別オブジェクトの生成方法はArCreatorで指定する。ArCreatorは元のオブジェクトを新しいオブジェクトに変換する。

ArLIst.java
  /** 要素をT1のオブジェクトに変換したListを戻す. */
  public static <T0,T1> List<T1> convert(List<T0> list,ArCreator<T0,T1> creator)
ArMap.java
 /** 値をT2のオブジェクトに変換したMapを戻す. */
 public static <T0, T1, T2> Map<T0,T2> convert(Map<T0,T1> map,ArCreator<T1,T2> creator)

・[List<社員>から(管理職を抽出して)List<管理職>を作成する]
★[他サンプル]

List,SetからMapを作成する

・List,Setの要素からMapのキーと値を生成してMapを作成する。
・キーと値の生成方法はArCreatorで指定する。

ArList.java
  /** CollectionをMap<T1,T2>に変換する */
  public static <T0, T1, T2> Map<T1,T2> createMap(Collection<T0> collection,ArCreator<T0,T1> creator1,ArCreator<T0,T2> creator2)

・[List<売上>から日付と売上高を抽出してMap<日付,売上高>を作成する]
★[他サンプル]

テンプレートクラス

・テンプレートパターンでList,Set,Mapの各要素を参照して処理する。

★[テンプレートクラスでSetの各要素を処理する]
★[テンプレートクラスでListの各要素を処理する]
★[テンプレートクラスでMapの各要素を処理する]

集合演算

・二つの集合(List,Set,Map)のAND,OR,XOR,EXCEPTを作成する。
 ・Mapでは、キーで集合演算を行う。キーが被る場合は、どちらかの値になる。

・[二つのListの集合演算]
★[二つのMapの集合演算]

等値判定

・二つの集合(List,Set,Map)の等値判定を行う。
 ・List,Setでは順番無視、null無視、重複無視の等値判定を行うこともできる。

★[二つのListの等値判定を行う]
★[二つのMapの等値判定を行う]
・[二つのListを順番無視、null無視、重複無視で等値判定を行う]
★[Listと配列を順番無視、null無視、重複無視で等値判定を行う]
★[ListとSetをnull無視、重複無視(List側)で等値判定を行う]

包含判定

・二つの集合の包含判定を行う。
・要素の包含判定を行う。
 ・要素の指定は、直接要素を指定するか、ArValidatorで指定することもできる。

★[List同士の包含判定を行う]
★[Map同士の包含判定を行う]

★[Listの要素の包含判定を行う]
★[Mapの値の包含判定を行う]
★[Mapのキーと値の包含判定を行う]

データ構造構築用クラス(Artery独自)(02)

以下のクラスがある。

・ArBiMap - 双方向Map
・ArMatrix - 二次元Map
・ArListOnMap - Mapの値がList
・ArMapOnMap - Mapの値がMap

・[ArBiMap(双方向Map)の簡単なサンプル]
・[ArMatrix(二次元Map)の簡単なサンプル]
・[ArListOnMap(Mapの値がList)の簡単なサンプル]
★[ArMapOnMapの簡単なサンプル]

ArBiMap,ArMapOnMap,ArMatrix,ArListOnMapから指定された条件に従ってオブジェクトを抽出し、新しいArBiMap,ArMapOnMap,ArMatrix,ArListOnMapを作成する。

・抽出条件はArValidatorで指定する。

★[ArMatrixからArValidatorに合致する値を持つ要素を抽出する]
★[他サンプル]

ArMapOnMap,ArMatrix,ArListOnMapのオブジェクトを別のクラスのオブジェクトに変換し、新しいArMapOnMap,ArMatrix,ArListOnMapを作成する。

・別オブジェクトの生成方法はArCreatorで指定する。

★[他サンプル]

テンプレートクラス

・テンプレートパターンでArMapOnMap,ArMatrix,ArListOnMapの各要素を参照して処理する。

★[ArMatrixをテンプレートパターンで処理する]

集合演算

・二つの集合(ArMatrix,ArListOnMap)のAND,OR,XOR,EXCEPTを作成する。

★[他サンプル]

等値判定

・二つの集合(ArBiMap,ArMapOnMap,ArMatrix,ArListOnMap)の等値判定を行う。

★[他サンプル]

包含判定

・要素の包含判定を行う。

★[ArMatrixにArValidatorで指定された値が存在するかを判定する]

重要なインターフェース(03)

ArValidator

ArValidator.java
/** 指定されたオブジェクトの正当性を判定するインターフェース. */
public interface ArValidator<T> {
 /**
  * 指定されたオブジェクトを判定する.
  * @param value 判定対象オブジェクト.
  */
 public boolean check(T value);
}

・オブジェクトを引数にしてtrue,falseを判定する。以下の処理に使用する。
 ・Listなどからのオブジェクトの抽出
 ・データのチェック

・提供している主な実装
 ・Stringチェック系-文字種、包含、開始、終了、バイト数、文字数など
 ・数値チェック系-等値、範囲包含、整数倍
 ・null,notNull,emtpy,notEmpty判定
 ・trueとなる選択肢を配列で指定する実装
 ・Collectionの判定
  ・すべてfalse,一個だけtrue,一個以上true,すべてtrue
 ・オブジェクトの等値判定

・特殊な実装として以下のものがある。
 ・ArValidatorGroup - 複数のArValidatorをまとめて一つのArValidatorとする。
 ・ArValidatorNot - 指定されたArValidatorの反対の判定をする。
 ・ArValidatorConvertedByCreator - ArCreatorで変換した結果を判定する。
 ・ArValidatorAlwaysTrue - 常にtrueを戻す。
 ・ArValidatorAlwaysFalse - 常にfalseを戻す。
 ・ArValidatorRandom - ランダムにtrue,falseを戻す。

・関連クラス
 ・ArValidatorUtil - ArValidator関連のユーティリティクラス。

★[Stringのチェック]
★[数値のチェック]
★[null,empty判定]
★[trueとなる選択肢を配列で指定する実装]
★[Collectionの判定]
★[ArValidatorGroup]
★[ArValidatorConvertedByCreator]

ArCreator

ArCreator.java
/** 指定されたオブジェクトから別のオブジェクトを生成するインタフェース. */
public interface ArCreator<T0,T1> {
 /** objからT1クラスのオブジェクトを生成する.実装がどのような処理をしているか分からないのでExceptionをthrowしておく. */
 public T1 convert(T0 obj) throws Exception;
}

・オブジェクトを引数にして別のクラスのオブジェクトを生成する。
 ・List,Set,Mapの値を別のクラスに変換する。

・よく使用する実装
 ・ArCreatorSelf - 引数自身を戻す。
 ・ArCreatorByFieldName - 指定された名前のフィールドを戻す。
 ・ArCreatorSameField - オブジェクトを生成し同一名フィールド間のコピーを行う。
 ・ArCreatorValueConverter - ArValueConverterを使用したArCreator。

★[他サンプル]

ArValueConverter - ArCreatorを継承

ArValueConverter.java
/** 値を(主に別のタイプの値に)変換するためのinterface. */
public interface ArValueConverter<T0,T1> extends ArCreator<T0,T1> {
 /** 値を逆変換する. */
 public T0 revert(T1 value) throws InstantiationException, IllegalAccessException;
}

・Beanの相互変換、コピー処理に使用する。

・よく使用する実装
 ・ArValueConverterForArray - 二つの配列を利用したArValueConverterの実装
 ・ArValueConverterForBiMap - ArBiMapを利用したArValueConverterの実装
 ・ArValueConverterForEnumInteger - Integer⇔enumの変換を行うArValueConverterの実装
 ・ArValueConverterForEnumString - String⇔enumの変換を行うArValueConverterの実装
 ・ArValueConverterForIntArray - int[]とオブジェクト配列を利用したint⇒オブジェクト変換のArValueConverterの実装
 ・ArValueConverterForTuple - ArTupleを利用したArValueConverterの実装

・関連クラス
 ・ArValueConverterInfo - Bean間の値のコピー時の変換情報を指定するクラス。
 ・ArAutoValueConverter - オブジェクトの変換を「できるだけ自動的に」行うクラス
  ⇒変換できない時はArValueConverterを利用する。

★[ArValueConverterForArray]
★[ArValueConverterForBiMap]
★[ArValueConverterForEnumInteger]
★[ArValueConverterForEnumString]
★[ArValueConverterForIntArray]
★[ArValueConverterForTuple]

Bean間の値のコピー(06)

・画面⇔ビジネスロジック⇔DBの間の処理で値のコピーが多出する。ここでは次のような問題がある。
 ・フィールドの名称が異なる。
 ・フィールドのタイプが異なる。
 ・同じタイプのフィールドでも、値の意味が異なる。例、(男性:0,女性:1)⇒(男性:1,女性2)。
 ・コピーしたくないフィールドがある。
 ・そのような処理を個別に記述していくと、手法がバラバラになり、バグが入りやすくなる。
 ・また同じような処理がいろいろなところに記述される。
 ・できるだけ共通化・部品化して品質を上げることが望ましい。

・[ArAutoValueConverterの使用例(フィールドタイプの変換)]
・[ArValueConverter(の実装クラス)の使用例-オブジェクトを別のオブジェクトに変換・逆変換する]
・[同じ名称のフィールドのコピー(フィールドタイプの変換-ArAutoValueConverterの使用)]
・[Bean間のコピー-違う名称のフィールドのコピー(変換情報を指定しArValueConverterで変換する)]
・(番外だけど)[Beanに作成者・作成日時、更新者・更新日時の設定を行う]

文字列処理(04)

バイト数、文字数処理

 ・文字列のバイト数を計算する。
 ・文字列を指定バイト数になるように、後ろを削除する
 ・指定バイト数になるようにスペースを追加する

 ・後に指定文字を追加して指定長さにする
 ・前に指定文字を挿入して指定長さにする

★[バイト数を計算する、指定バイト数に調整する]
★[他サンプル]

文字列のチェック処理

・文字種類のチェックを行う。
 ・英字、数字、大文字、小文字...。

★[文字種チェックのサンプル]

文字列の可変部分の置換

・文字列の中の可変部分(${xxxx}形式の変数)を置換する

★[他サンプル]

スペース関係処理

・スペース・タブ・全角スペースの判定-
・前または後のスペース・タブ・全角スペースの削除
・スペース・タブ・全角スペースの置換・集約

★[他サンプル]

左寄せ、右寄せ、センタリング

★[他サンプル]

引用符の付加、削除

★[他サンプル]

日付、時刻、曜日、休日(祝日)、和暦(05)

日付処理

・日付(時刻は扱わない)処理専用のクラスとしてArDateがある。
・現在日のArDateの生成、指定日のArDateの生成
・ArDateとDate,Calendar,longとの相互変換
・日数計算-休日も考慮できる。
 ・最終日指定で日数を求める。
 ・日数指定で最終日を求める。
・曜日、和暦へ変換する。

★[日数計算]
★[休日を考慮した日数計算]

日時処理

・日時関係の処理をまとめたものとしてArDateUtilがある。
 ・Date,Calendar,longの日時関係処理

・日数計算-休日も考慮できる。
 ・最終日指定で日数を求める。
 ・日数指定で最終日を求める。

・時刻を無視した同一日判定
 ・時刻が違っても同一日と判定する。

・時刻の切上げ、切捨て
 ・本日の0時0分、明日の0時0分にする。
 ・分・秒を切り捨てる、分・秒を切り上げる

・曜日、和暦へ変換する。

・[時刻を無視した同一日判定]
・[時刻の切上げ、切捨て]
・[日数計算]
・[休日を考慮した日数計算]

曜日処理

・ArYoubi-曜日のenum
・ArDate,Date,Calendar,longから曜日を求める
・曜日⇔文字列(日本語/英語、長形/短形)の変換
・曜日⇔数値の変換

★[他サンプル]

和暦処理

・ArWareki-和暦のクラス
・ArDate,Date,Calendar,longとの相互変換
・和暦を文字列表現する。
・曜日を取得する。

★[ArDate,Date,Calendar,longとの相互変換]

休日処理

・ArHoliday-休日処理のインターフェース。
・曜日、月の特定日、年の特定日、日本の祝日などを休日指定する実装クラスを提供。
・ArHolidayStandard-上記の実装クラスを組合わせるArHolidayの実装。
 ・一般のアプリケーションはArHolidayStandardを使用する。

★[休日の組合せ例]

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AudioTrackで正弦波の単音を再生させるためのデータ型選択

はじめに

AudioTrackを使って生成した正弦波をリアルタイムで再生するアプリを作成。再生できるようになったが再生したい周波数と違う周波数の音(雑音)も再生されていたため、修正。

正弦波生成

周波数を引数として正弦波を生成し、byte型の値の範囲-128~127で正規化。

getSound
/**
 * 正弦波生成
 * @param frequency 音の周波数
 * @return 音声データ
 */
public byte[] getSound(double frequency) {
    frequency = frequency / 2;
    // byteバッファを作成
    byte[] buffer = new byte[bufferSize];
    double max = 0;
    double[] t = new double[buffer.length];
    double hz = frequency / this.sampleRate;
    for(int i = 0; i < buffer.length; i++) {
        t[i] = Math.sin(i * 2 * Math.PI * hz);
        if(t[i] > max) {
            max = t[i];
        }
    }
    double trans = 127 / max;
    for(int i = 0; i < buffer.length; i++) {
        buffer[i] = (byte)Math.round(t[i]*trans);
    }
    return buffer;
}

結果

20kHzの音を再生した。これ自体は非可聴音だが、可聴音も再生されてしまっている。この可聴音は雑音なので除去したい。

修正したこと

音声データをbyte型ではなくshort型にしただけ。short型の値の範囲-32768~32767で正規化することで正弦波の分解能が高くなった。

getSoundShort
/**
 * 正弦波生成
 * @param frequency 音の周波数
 * @return 音声データ
 */
public short[] getSoundShort(double frequency) {
    double[] value = new double[sampleRate];
    double max = 0.0;
    for(int i = 0; i < sampleRate; i++) {
        value[i] = Math.sin(2.0 * Math.PI * frequency * i / sampleRate);
        if(value[i] > max) {
            max = value[i];
        }
    }
    short[] buffer = toShort(value, max);

    return buffer;
}

/**
 * double型をshort型に変換(-32768~32767で正規化)
 * @param val double型音声データ
 * @param max 最大値
 * @return short型音声データ
 */
public short[] toShort(double[] val, double max) {
    double trans = 32767 / max;
    short[] buf = new short[sampleRate];
    for(int i = 0; i < sampleRate; i++) {
        buf[i] = (short)Math.round(val[i] * trans);
    }
    return buf;
}

修正結果

同様に20kHzを再生したところ、可聴域の雑音がなくなり、20kHzの単音になった。

おそらく

詳しいことはわからないが、byte型の-128~127の範囲だと分解能が低く、誤差が生じて雑音が発生したと思われる。

AudioTrackの書込処理(writeメソッド)にはbyte型、short型、float型を用いることができるが、float型は値の範囲等が複雑なため試していない。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

KMS) エンベロープ暗号化でopensslで暗号化してjavaで復号化する

備忘録です

セキュリティあんまり強くないので、こんなやり方もあるよくらいの参考でお願いします

AWS CLIで KMS DataKeyを生成する

$ aws kms generate-data-key --key-spec "AES_128" --key-id "arn:aws:kms:REGION_NAME:ACCOUNT_ID:key/HOGEHOGE"

{
  "ciphertextBlob": "foo",
  "Plaintext" : "bar",
  "KeyId" : "KEY-ID"
}

返却される ciphertextBlog, PlaintextはBase64-encodedされています

Opensslで暗号化する

$ KEY=$(echo "bar" | base64 -d | od -A n -t x1 -v | sed -e 's/ //g')
$ openssl aes-128-ecb -e -in secret.txt -out encrypted.txt -base64  -K ${KEY}

KEY = KMSのDataKey を HEX文字列に変えたもの(求:もっとスマートな方法
openssl コマンドで暗号化。このとき初期化ベクトル不要なブロック処理にしています

Javaで復号化する

String encryptedTxt = "baz";
String plainDataKey = "bar";

byte[] key = DatatypeConverter.parseBase64Binary(plainDataKey);

SecretKeySpec secretKey = new SecretKeySpec(key, "AES");

Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);

byte[] encrypted = DatatypeConverter.parseBase64Binary(encryptedTxt);

String decryptedTxt = new String(cipher.doFinal(encrypted));
System.out.println("result: " + decryptedTxt);

上記の例では、plainDataKey ベタ書きしてますけど、
正しくは plainDataKeyは、AWSのドキュメントを参考にciphertextBlogをデコードして取得してください。
あと、使い終わったplainDataKeyは早々に削除するようにAWSドキュメントにも記載があります。

その他

ちゃんとするなら、鍵と初期化ベクトルを生成したほうがいいです。
楽した = セキュリティの低下 と思ってください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む