JUnit で、YAML を読む処理をテストする時、@EnableAutoConfiguration を忘れずに!

YAML を読む処理を JUnit テストする時、( Spring Batch での話 )
@ContextConfiguration で、イニシャライザ指定
 initializers = ConfigFileApplicationContextInitializer.class
を付けるだけでなく、
@EnableAutoConfiguration が必要

例)
application.yml に書いた、設定

address:
  group:
    info1: A
    info2: 120
aaa:
  bbb: ABC

読みだすクラス Setting

import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@ConfigurationProperties(prefix="address")
@Component
public class Setting{
   public Map<String, Object> group;

   @Value("${aaa.bbb}")
   public String name;

   public void setGroup(Map<String, Object> m) {
      group = m;
   }
   public Map<String, Object> getGroup(){
      return group;
   }
}

↑ を使用する Tasklet

@Component
public class SampleTasklet implements Tasklet {
   @Autowired
   private Setting setting;
   @Override
   public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
      // TODO 
      return RepeatStatus.FINISHED;
   }
   public Setting getSetting() {
      return setting;
   }
}

この Tasklet をテストする時に、YAML を読んできてる Setting が
想定どおり読み込ませるかテストする JUnitソース

import java.util.Map;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.ConfigFileApplicationContextInitializer;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.test.context.junit4.SpringRunner;

import org.sample.Setting;
/**
 * SampleTaskletTest
 */
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {Setting.class}, initializers = ConfigFileApplicationContextInitializer.class)
@SpringJUnitConfig
@EnableAutoConfiguration
class SampleTaskletTest{
   @Autowired
   public ApplicationContext ctx;
   private SampleTasklet tasklet;

   @BeforeEach
   void setUp() throws Exception{
      tasklet = ctx.getAutowireCapableBeanFactory().createBean(SampleTasklet.class);
   }

   @Test
   void testExecute(){
      assertTrue(true);
      Setting setting = tasklet1.getSetting();
      assertEquals("ABC", setting.name);
      Map<String, Object> map = setting.group;
      assertEquals("A", map.get("info1"));
      assertEquals(120, map.get("info2"));

      // TODO
   }
}

この @EnableAutoConfiguration を付け忘れると、1階層 Nest のYAML書式が読めても、
@ConfigurationProperties(prefix="xxxxx") で指定するMap が読まれなかったりする。

@EnableAutoConfiguration

mybatis + HikariCP

コネクションPOOL に HikariCP を使用した mybatis の接続
【再興】mybatis XML設定を使わない場合 - Oboe吹きプログラマの黙示録 のパターンで書いてみる。

HikariCP 3.4.5

<dependency>
  <groupId>com.zaxxer</groupId>
  <artifactId>HikariCP</artifactId>
  <version>3.4.5</version>
</dependency>

mysql-connector-java 8.0.21

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.21</version>
</dependency>

gradle の場合、

dependencies {
    implementation 'mysql:mysql-connector-java:8.0.21'
    implementation 'com.zaxxer:HikariCP:3.4.5'
}

サンプル
HikariDataSource に接続設定をセットして SqlSessionFactoryBuilder → SqlSessionFactory

public static SqlSession getSqlSession(Class<?> mapperclass){
   HikariDataSource dataSource = new HikariDataSource();
   dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
   dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/testDB?serverTimezone=JST");
   dataSource.setUsername("root");
   dataSource.setPassword("pass");
   dataSource.setAutoCommit(false);
   dataSource.setMaximumPoolSize(10);
   dataSource.setMinimumIdle(4);
   dataSource.setConnectionTestQuery("SELECT 1");
   Environment environment = new Environment("deployment", new JdbcTransactionFactory(), dataSource);
   Configuration config = new Configuration(environment);
   // snake Case → camel Case
   config.setMapUnderscoreToCamelCase(true);
   config.addMapper(mapperclass);
   SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
   return factory.openSession();
}

auto commit は、false を指定しないと default は true

接続開始時(立ち上げ時)、次のようにコネクションPOOLの状況を
標準出力にログを出してくれるので設定を確認できる。

22:57:18.285 [main] DEBUG com.zaxxer.hikari.HikariConfig - Driver class com.mysql.cj.jdbc.Driver found in Thread context class loader sun.misc.Launcher$AppClassLoader@73d16e93
22:57:18.436 [main] DEBUG org.apache.ibatis.logging.LogFactory - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
22:57:18.460 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
22:57:18.460 [main] DEBUG com.zaxxer.hikari.HikariConfig - HikariPool-1 - configuration:
22:57:18.465 [main] DEBUG com.zaxxer.hikari.HikariConfig - allowPoolSuspension.............false
22:57:18.465 [main] DEBUG com.zaxxer.hikari.HikariConfig - autoCommit......................false
22:57:18.465 [main] DEBUG com.zaxxer.hikari.HikariConfig - catalog.........................none
22:57:18.465 [main] DEBUG com.zaxxer.hikari.HikariConfig - connectionInitSql...............none
22:57:18.465 [main] DEBUG com.zaxxer.hikari.HikariConfig - connectionTestQuery............."SELECT 1"
22:57:18.466 [main] DEBUG com.zaxxer.hikari.HikariConfig - connectionTimeout...............30000
22:57:18.466 [main] DEBUG com.zaxxer.hikari.HikariConfig - dataSource......................none
22:57:18.466 [main] DEBUG com.zaxxer.hikari.HikariConfig - dataSourceClassName.............none
22:57:18.466 [main] DEBUG com.zaxxer.hikari.HikariConfig - dataSourceJNDI..................none
22:57:18.467 [main] DEBUG com.zaxxer.hikari.HikariConfig - dataSourceProperties............{password=<masked>}
22:57:18.467 [main] DEBUG com.zaxxer.hikari.HikariConfig - driverClassName................."com.mysql.cj.jdbc.Driver"
22:57:18.467 [main] DEBUG com.zaxxer.hikari.HikariConfig - exceptionOverrideClassName......none
22:57:18.467 [main] DEBUG com.zaxxer.hikari.HikariConfig - healthCheckProperties...........{}
22:57:18.467 [main] DEBUG com.zaxxer.hikari.HikariConfig - healthCheckRegistry.............none
22:57:18.467 [main] DEBUG com.zaxxer.hikari.HikariConfig - idleTimeout.....................600000
22:57:18.467 [main] DEBUG com.zaxxer.hikari.HikariConfig - initializationFailTimeout.......1
22:57:18.467 [main] DEBUG com.zaxxer.hikari.HikariConfig - isolateInternalQueries..........false
22:57:18.468 [main] DEBUG com.zaxxer.hikari.HikariConfig - jdbcUrl.........................jdbc:mysql://localhost:3306/testDB?serverTimezone=JST
22:57:18.468 [main] DEBUG com.zaxxer.hikari.HikariConfig - leakDetectionThreshold..........0
22:57:18.468 [main] DEBUG com.zaxxer.hikari.HikariConfig - maxLifetime.....................1800000
22:57:18.468 [main] DEBUG com.zaxxer.hikari.HikariConfig - maximumPoolSize.................10
22:57:18.468 [main] DEBUG com.zaxxer.hikari.HikariConfig - metricRegistry..................none
22:57:18.468 [main] DEBUG com.zaxxer.hikari.HikariConfig - metricsTrackerFactory...........none
22:57:18.468 [main] DEBUG com.zaxxer.hikari.HikariConfig - minimumIdle.....................4
22:57:18.468 [main] DEBUG com.zaxxer.hikari.HikariConfig - password........................<masked>
22:57:18.468 [main] DEBUG com.zaxxer.hikari.HikariConfig - poolName........................"HikariPool-1"
22:57:18.468 [main] DEBUG com.zaxxer.hikari.HikariConfig - readOnly........................false
22:57:18.468 [main] DEBUG com.zaxxer.hikari.HikariConfig - registerMbeans..................false
22:57:18.468 [main] DEBUG com.zaxxer.hikari.HikariConfig - scheduledExecutor...............none
22:57:18.468 [main] DEBUG com.zaxxer.hikari.HikariConfig - schema..........................none
22:57:18.469 [main] DEBUG com.zaxxer.hikari.HikariConfig - threadFactory...................internal
22:57:18.469 [main] DEBUG com.zaxxer.hikari.HikariConfig - transactionIsolation............default
22:57:18.469 [main] DEBUG com.zaxxer.hikari.HikariConfig - username........................"root"
22:57:18.469 [main] DEBUG com.zaxxer.hikari.HikariConfig - validationTimeout...............5000
22:57:18.469 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
22:57:18.693 [main] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@51931956
22:57:18.695 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.

mybatis-guice – MyBatis Guice | DataSources | HikariCP

【再興】mybatis XML設定を使わない場合

過去に、
mybatis XML設定を使わない場合 - Oboe吹きプログラマの黙示録

mybatis xml設定ファイル使わない場合の補足 - Oboe吹きプログラマの黙示録

と書いたが、SQLMap の XML も書かないで、SQL文もアノテーションで済ませたい。

mybatis 3.5.2
mysql-connector-java Version 8.0.21
を例に、

接続 DataSource の作成を、
GenericBuilder
https://github.com/yipuran/yipuran-core/blob/master/src/main/java/org/yipuran/util/GenericBuilder.java
で、リテラルにコーディングしたい。接続情報の設定は、xml で用意するか、YAML で用意するか、JSONで用意するか
いずれにしても下記のように、DataSource にセットすることに変わりはない。

public static SqlSession getSqlSession(Class<?> mapperclass){
   UnpooledDataSource dataSource = GenericBuilder.of(UnpooledDataSource::new)
      .with(UnpooledDataSource::setDriver, "com.mysql.cj.jdbc.Driver")
      .with(UnpooledDataSource::setUrl, "jdbc:mysql://localhost:3306/testDB?serverTimezone=JST")
      .with(UnpooledDataSource::setUsername, "root")
      .with(UnpooledDataSource::setPassword, "pass")
      .build();
   Environment environment = new Environment("deployment", new JdbcTransactionFactory(), dataSource);
   Configuration config = new Configuration(environment);
   // snake Case → camel Case
   config.setMapUnderscoreToCamelCase(true);
   config.addMapper(mapperclass);
   SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
   return factory.openSession();
}

後は、@Select , @Update , @Insert を書いた SQLMapper の インターフェースを
取得する SqlSession から getMapper をするだけで、
SQLMapper の インターフェースの SQLMap のXMLを必ずしも用意する必要はない。

でもこの方法で使用するのは、ちょっと接続してSQL実行したい時の
限定的な時だけだろう

SpringBoot MySQL接続でserver time zone エラー??

久々のSpringBoot バージョン は、2.3.1
mybatis-spring-boot-starter Version : 2.1.3 を使い、
mysql-connector-java Version: 8.2.20 を使って、MySQL に接続する。

Spring の アプリケーション起動していきなり、
java.sql.SQLException: The server time zone value '���� (�W����)' is unrecognized or represents more than one time zone.
You must configure either the server or JDBC driver (via the 'serverTimezone' configuration property)
to use a more specifc time zone value if you want to utilize time zone support.

サーバタイムゾーンを接続情報として接続せよ?
mysql-connector-java のバージョンの影響かな?

解決するために、application.yml の設定、
JDBC url で、serverTimezone を書く。

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/testDB?serverTimezone=JST
    username: root
    password: xxxxxxxxxxx

全角文字のみの正規表現と半角文字のみの正規表現

半角カナ文字を含む半角文字のみの正規表現を否定形にすれば、
全角文字のみの正規表現になる。


半角文字のみの正規表現(半角カナ文字を含む) ^[ -~。-゚]+$

全角文字のみの正規表現  ^[^ -~。-゚]+$

【再考】escape がある区切り文字による split

先日書いた、
oboe2uran.hatenablog.com

これでは、ダメだ
エスケープする文字列が残らない。
だいたい java.util.StringTokenizer なんて古いものは、すでに奨励されていない。
char 型で処理する

public static List<String> tokenToList(String str, char sep, char escape){
   List<String> list = new ArrayList<>();
   StringBuilder sb = new StringBuilder();
   boolean isEscape = false;
   for(char c:str.toCharArray()) {
      if (isEscape) {
         isEscape = false;
      }else if(c==escape){
         isEscape = true;
         sb.append(c);
         continue;
      }else if(c==sep){
         list.add(sb.toString());
         sb.setLength(0);
         continue;
      }
      sb.append(c);
   }
   list.add(sb.toString());
   return list;
}

InputStream から生成

public static List<String> tokenToList(InputStream in, char sep, char escape) throws IOException{
   return tokenToList(new InputStreamReader(in, StandardCharsets.UTF_8), sep, escape);
}

public static List<String> tokenToList(InputStreamReader in, char sep, char escape) throws IOException{
   List<String> list = new ArrayList<>();
   StringBuilder sb = new StringBuilder();
   try{
      boolean isEscape = false;
      int i;
      while((i=in.read()) > 0){
         char c = (char)i;
         if (isEscape){
            isEscape = false;
         }else if(c==escape){
            isEscape = true;
            sb.append(c);
            continue;
         }else if(c==sep){
            list.add(sb.toString());
            sb.setLength(0);
            continue;
         }
         sb.append(c);
      }
      list.add(sb.toString());
   }catch(IOException e){
      throw e;
   }
   return list;
}

Stream<String> が欲しい

public static Stream<String> tokenToStream(String str, char sep, char escape){
   Stream.Builder<String> b = Stream.builder();
   StringBuilder sb = new StringBuilder();
   boolean isEscape = false;
   for(char c:str.toCharArray()) {
      if (isEscape) {
         isEscape = false;
      }else if(c==escape){
         isEscape = true;
         sb.append(c);
         continue;
      }else if(c==sep){
         b.add(sb.toString());
         sb.setLength(0);
         continue;
      }
      sb.append(c);
   }
   b.add(sb.toString());
   return b.build();
}

InputSteam から Stream<String> が欲しい

public static Stream<String> tokenToStream(InputStream in, char sep, char escape) throws IOException{
   return tokenToStream(new InputStreamReader(in, StandardCharsets.UTF_8), sep, escape);
}

public static Stream<String> tokenToStream(InputStreamReader in, char sep, char escape) throws IOException{
   Stream.Builder<String> b = Stream.builder();
   StringBuilder sb = new StringBuilder();
   try{
      boolean isEscape = false;
      int i;
      while((i=in.read()) > 0){
         char c = (char)i;
         if (isEscape){
            isEscape = false;
         }else if(c==escape){
            isEscape = true;
            sb.append(c);
            continue;
         }else if(c==sep){
            b.add(sb.toString());
            sb.setLength(0);
            continue;
         }
         sb.append(c);
      }
      b.add(sb.toString());
   }catch(IOException e){
      throw e;
   }
   return b.build();
}

これらで、注意すべきは、エスケープする文字、char で、'\0' を指定した時は、
String の spilit メソッド実行した時に取得できる配列と同じであることだ。


おそらく上のメソッドでそのような指定の実行はしないであろう。

この メソッドは、
GitHub - yipuran/yipuran-core: Java application framework
で、
org.yipuran.util.Tokenstring として作成した。

メモ

https://dev.classmethod.jp/articles/aws_s3_getobject_error/

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/RetrievingObjectUsingJava.html

S3Object object = s3Client.getObject(new GetObjectRequest(bucketName, key));
InputStream objectData = object.getObjectContent();


https://reasonable-code.com/spring-boot-s3/


Gradle のタスク定義のあれこれ - Qiita

                          • -

@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("dev")
@ContextConfiguration(classes = { PojoConfiguration.class }, initializers = ConfigFileApplicationContextInitializer.class)


https://github.com/spring-projects/spring-boot/issues/6120