谷本 心 in せろ部屋

はてなダイアリーから引っ越してきました

Dapr Advent Calendar 2日目 - DaprでHello World

こんにちは、Dapr Advent Calendar 2日目です。3日坊主まであと1日です。

Hello Worldを動かしてみよう

前回はDapr CLIをインストールして動かしてみただけですが、今回はDaprとともにWebアプリケーションを起動して呼び出すところまで説明します。

動かすアプリケーションは、もちろん、プログラミング界で最も著名なアプリケーション「Hello World」です。

f:id:cero-t:20211202051328p:plain
今回作るアプリケーション

Webアプリケーションの作成

まずはWebアプリケーションの作成です。Hello Worldをサクッと作りましょう。

Spring BootでこのようなControllerクラスを作りました。

HelloController.java(抜粋)

@RestController
public class HelloController {
    @GetMapping("/hello")
    public Map<String, String> hello() {
        return Map.of("message", "Hello, world!");
    }
}

ソースコードgithubに置いてあります。

https://github.com/cero-t/dapr-advent-2021

このソースコードの例ではMavenのマルチモジュール構成で、次のように作ってあります。

  • dapr-advent-2021
    • mvnw
    • pom.xml (親モジュール)
    • hello
      • pom.xml (子モジュール)
      • src

まずはWebアプリケーション「hello」を単体で起動します。

../mvnw spring-boot:run

アプリケーションが起動したらcurlコマンドでアクセスします。

curl localhost:8080/hello

無事に実行結果が表示されました。

{"message":"Hello, world!"}

f:id:cero-t:20211202051454p:plain
ここまでで作ったアプリケーション

Daprとともにアプリケーションを起動

続いて、Daprを使ってアプリケーションを起動します。

上で起動したアプリケーションを一旦停止させた後、次のコマンドで起動します。

dapr run --app-id hello-app --app-port 8080 --dapr-http-port 18080 ../mvnw spring-boot:run

コマンドの引数についての詳しい説明はあとで行います。

起動に成功すると、WebアプリケーションのログとDaprのログが両方ともコンソールに表示されるはずです。

(前半部分は割愛)
== APP == 2021-12-02 00:00:00.000  INFO 58739 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
== APP == 2021-12-02 00:00:00.000  INFO 58739 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
== APP == 2021-12-02 00:00:00.000  INFO 58739 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
INFO[0003] application configuration loaded              app_id=hello-app instance=macmini.local scope=dapr.runtime type=log ver=1.5.0
INFO[0003] actor runtime started. actor idle timeout: 1h0m0s. actor scan interval: 30s  app_id=hello-app instance=macmini.local scope=dapr.runtime.actor type=log ver=1.5.0
INFO[0003] dapr initialized. Status: Running. Init Elapsed 3540.333ms  app_id=hello-app instance=macmini.local scope=dapr.runtime type=log ver=1.5.0
INFO[0005] placement tables updated, version: 0          app_id=hello-app instance=macmini.local scope=dapr.runtime.actor.internal.placement type=log ver=1.5.0

ここまで表示されれば起動は完了です。

もしここで次のエラーが出た場合はDaprの初期化が済んでいない可能性が高いので dapr init コマンドを実行してください。

ℹ️  Starting Dapr with id hello-app. HTTP Port: 18080. gRPC Port: 60117
❌  fork/exec /Users/(ユーザ名)/.dapr/bin/daprd: no such file or directory

それでは起動したDapr経由で、Webアプリケーションにアクセスしてみましょう。curlコマンドでアクセスします。

curl localhost:18080/v1.0/invoke/hello-app/method/hello

URLが少しややこしいのですが、このURLの詳細についてもあとで説明します。

ふつうにアクセスした時と同じ実行結果が表示されました。

{"message":"Hello, world!"}

dapr list コマンドで、起動中のDaprアプリケーションを確認することができます。

dapr list

先ほど起動した hello-app が表示されます。

  APP ID     HTTP PORT  GRPC PORT  APP PORT  COMMAND               AGE  CREATED              PID    
  hello-app  18080      61164      8080      ../mvnw spring-bo...  1m   2021-12-02 00:00.00  59584  

なお dapr run コマンドで起動したアプリケーションは、特にコンテナの中で動いているなどではなく、普通のプロセスとして起動しているだけなので、Daprを経由せずにアクセスすることもできます。

curl localhost:8080/hello
{"message":"Hello, world!"}

Dapr経由でアクセスしようとしたけどなぜかアクセスできない、という時にはアプリケーションに直接アクセスして正常に動作しているかを確認してみてください。

f:id:cero-t:20211202051829p:plain
Daprを経由したアクセス

やったことの解説

それでは打ったコマンドなどについて説明します。

起動オプション

今回はDaprを次のコマンドで起動しました。

dapr run --app-id hello-app --app-port 8080 --dapr-http-port 18080 ../mvnw spring-boot:run

dapr run コマンドが、Daprの起動コマンドです。このコマンドでアプリケーションを起動します。ちなみに何の引数も指定せずに dapr run だけ実行してもDaprのプロセスを立ち上げることができます。

--app-id がアプリケーションのIDです。任意の名前を指定することができます。Daprはアプリケーションをこのapp-idで識別していて、アプリケーションをinvokeする時などに利用します。省略すると自動的にIDが発行されます。

--app-port がWebアプリケーションのポート番号です。Hello Worldアプリケーションは8080番ポートで起動しているため、そのポート番号を指定します。省略すると0番ポートが指定されてしまってアプリケーションにアクセスできなくなるため、必ず指定してください。

--dapr-http-port がDapr側のポート番号です。任意のポート番号を指定できます。Daprにアクセスする際にはこのポートを利用します。省略するとランダムなポート番号が指定されます。

../mvnw spring-boot:run の部分がアプリケーションの起動コマンドです。

アプリケーションの起動コマンドに引数を渡す場合は、起動コマンドの前に -- を追加する必要があります。たとえばアプリケーションの起動を java -jar コマンドで行う場合は次のようなコマンドになります。

../mvnw clean package
dapr run --app-id hello-app --app-port 8080 --dapr-http-port 18080 -- java -jar target/hello-1.0.0.jar

javaコマンドの前に -- をつけているところがポイントです。

その他のコマンド一覧

もちろんコマンド引数は他にもあり、このページに一覧が掲載されています。

https://docs.dapr.io/reference/arguments-annotations-overview/

また dapr run --help コマンドを実行すると、dapr run コマンドに対する引数の一覧を見ることができます。

Daprからアプリケーションへのアクセス

Dapr経由でWebアプリケーションにアクセスする際に、次のコマンドを用いました。

curl localhost:18080/v1.0/invoke/hello-app/method/hello

18080 がDaprのポート番号です。

/v1.0 がDaprのAPIバージョンで、今のところ v1.0 しかありません。

/invokeInvoke APIで、起動したWebアプリケーションにアクセスするAPIになります。他にもデータを保存する /state や、エンキューを行う /pubsub などのAPIがあります。

Invoke API/invoke/(app-id)/method/(アプリケーションのパス) という形式になります。今回は app-idhello-app を指定し、アプリケーションのパスは /hello を指定しました。これで localhost:8080/hello にアクセスするのと同じになります。

その他のアクセス方法

DaprのInvoke APIは、Dapr CLIを用いて実行することもできます。

dapr invoke --app-id hello-app --method hello --verb GET

このCLIはverbを指定しないと POST となるため、GETリクエストしたい場合は --verb GET を指定します。

また、Daprのv1.4からInvoke APIを別のURLでも起動できるようになりました。

curl -H 'dapr-app-id:hello-app' localhost:18080/hello
curl dapr-app-id:hello-app@localhost:18080/hello

Daprを運用する際に「パスの書き換えはできないけど任意のヘッダを付けることができる」ようなミドルウェアを利用している場合には、この形式のURLを用いてアクセスすることができるようになりそうですが、実際に使ったことがないのでどれくらい便利になるかはちょっと説明できません。てへぺろ

まとめ

それでは今回の内容を簡単に振り返りましょう。

  • dapr run コマンドでDaprとともにWebアプリケーションを起動することができます
  • Invoke APIを用いてDapr経由でWebアプリケーションにアクセスができます
  • WebアプリケーションそのものもローカルPC上で起動しているため、直接アクセスすることができます

今のところDaprの良さは何も引き出していませんが、次回からはDaprの便利な機能を利用していきます!

Dapr Advent Calendar 1日目 - Dapr CLIのインストール

こんにちは、Dapr Advent Calendar を作りました @cero_t です。このエントリーは Dapr Advent Calendar の1日目です。

Dapr Advent Calendarはじめました

Dapr (https://dapr.io/) とは、Microsoftが中心になって開発しているOSSの分散アプリケーションランタイム、Distributed Application Runtimeの略でDaprです。

様々なクラウドサービスやミドルウェアを良い感じに透過的に扱うことのできるプロダクトで、なかなか筋が良いのですが、何に使えるかよく分からないというか、そもそもどういうものか分かりづらい、発音の仕方すら分からないということもあって注目度はあまり高くなく、日本語の情報も多くありません。

ただ、このDaprはちょうど僕の感じていた課題を解決してくれそうなプロダクトだったので、わりと調べたり触ったりしていて、最近は仕事でも使い始めたところです。そんな時にちょうどAdvent Calendarの季節になりましたので、一人Advent Calendarになることを覚悟の上でカレンダーを立ててみました。

それでは12月25日まで、お付き合いいただければ、いや、きちんと25日まで継続できることを祈ってくだされば、ありがたいです!

あ、ちなみにDaprの発音ですが、英語読みすれば「だぱー」が近いと思いますが、このスペルだと日本語では「だっぱー」になるのが自然です。topを「とぷ」ではなく「とっぷ」と発音するのと同じです。とは言え英語の日本語読みなんてどうでも良いことですので、好きなように発音してもらえば良いと思います。

はじめに

このAdvent Calendarで僕が書くエントリーは、大きく分けて次の三部で構成する予定です。

  1. 基本編 - 各機能を使ってみる
  2. 分散環境編 - k8s上で各機能を使ってみる
  3. 実践編 - 実際に仕事で使ってみた感想や知見をまとめる

基本的な使い方くらい知ってらぁ! という方は12月の半ばあたりから読み始めてもらえれば良いかなと思います。

また、僕が書く記事は「macOS + Java + Spring Boot + Dapr」という構成を前提とします。DaprはOSや言語を問わず使えるサービスですので、他のOSや言語、フレームワークで読み替えてもらえれば良いと思います。

Daprの特徴

「それではDaprの使い方を見ていきましょう」

、、、と書き始めても誰もこの先を読み進めないと思うので、Daprの特徴とメリットを先に簡単に書いておきますね。長々と書いても仕方ないので、ここでは簡単に済ませます。

  1. サイドカーサービスとして動作する
    • Daprは、アプリケーションのプロセスとは別のプロセスとして起動するもので、アプリケーションとHTTP/gRPCプロトコルで通信することで機能を提供する
    • つまり、HTTPかgRPC通信さえできれば、どのような言語やフレームワークでも利用できる
  2. 様々なビルディングブロックを提供する
    • データストアや、pubsub(エンキュー、デキュー)、分散トレーシングなどの機能を提供する
    • サービスディスカバリーやルーティングに相当する機能も提供する
  3. 様々なクラウドサービスやミドルウェアを抽象化したAPIを提供する
    • たとえばメッセージングミドルウェアであれば、RabbitMQやKafka、Amazon SNS/SQSなどを独自APISDKに依存することなく同じAPIで利用できる
    • つまり、アプリケーション開発者はどのようなインフラの上で動作させるかを強く意識する必要がない
  4. ローカル環境でもk8s上でも運用できる
    • ローカル環境ではk8sやDockerイメージなどを作成することなく、分散アプリケーション開発ができる
    • 検証環境や本番環境などでk8s上で動作させる際には、ソースコードを変えることなく設定ファイルを書くだけで対応できる
    • さらにいえば、ローカル環境で開発する際にはDaprそのものを使わずに開発することもできる
  5. キューからデキューして行う非同期メッセージング処理を、通常のWeb APIとして実装できる
    • アプリケーション開発者はあくまでも「Web API」の開発にのみ注力できる
    • 開発時にキューを立てなくても良い

私見なので少し偏りがある気はしますが、総じて、開発と運用の境界にあるような部分を切り離すことで開発をシンプルにできるようになる、というところにメリットを感じています。

Dapr CLIのインストール

それではDaprの使い方を見ていきましょう。

Daprの利用にはDapr CLIが不可欠です。アプリケーションを起動する際にDaprをサイドカーとして起動したり、Dapr経由でアプリケーションを呼び出したり、起動中のアプリケーションの一覧を見たりするために利用します。

Daprを利用するためにはDockerが必要なため、まずはDockerをインストールしておきましょう

https://docs.docker.com/get-docker/

Dockerをインストールしたら、次はDapr CLIのインストールです。インストール方法はOSごとに異なるため、詳しくはドキュメントを参照してください。

https://docs.dapr.io/getting-started/install-dapr-cli/

たとえば、macOSでは次のコマンドでインストールできます。

curl -fsSL https://raw.githubusercontent.com/dapr/cli/master/install/install.sh | /bin/bash

Dapr CLIを触ってみる

Dapr CLIのコマンドを簡単に触ってみましょう。まずはバージョンの確認です。

dapr --version

出力結果はこうなります。

CLI version: 1.4.0 
Runtime version: n/a

まだ環境の初期化が済んでいないため、Runtime versionはn/aとなっています。

Daprを動作させるため、初期化を行いましょう。

dapr init

このコマンドで、ローカル環境に設定ファイルなどが作成され、またDaprの動作に必要なDockerコンテナが作成されます。

初期化が完了したら、もう一度バージョンを確認しましょう。

dapr --version

先ほどと出力結果が変わっているはずです。

CLI version: 1.4.0 
Runtime version: 1.5.0

また、Dockerのコンテナ一覧も確認してみましょう。

docker ps

dapr_placement、dapr_zipkin、dapr_redisの3つのコンテナが起動しているはずです。

CONTAINER ID   IMAGE               COMMAND                  CREATED              STATUS                        PORTS                              NAMES
f7dc118a4bbc   daprio/dapr:1.5.0   "./placement"            About a minute ago   Up About a minute             0.0.0.0:50005->50005/tcp           dapr_placement
46555ffc1d22   openzipkin/zipkin   "start-zipkin"           About a minute ago   Up About a minute (healthy)   9410/tcp, 0.0.0.0:9411->9411/tcp   dapr_zipkin
c55c6700a22c   redis               "docker-entrypoint.s…"   About a minute ago   Up About a minute             0.0.0.0:6379->6379/tcp             dapr_redis

この辺りの使い方は、後のエントリーでおいおい説明していきます。

最後にdapr関連のファイルができていることも確認しましょう。

ls ~/.dapr   

設定ファイルや実行ファイルがいくつか追加されています。

bin      components  config.yaml

ちなみにアンインストールしたい場合には、dapr unistall コマンドを利用します。

dapr uninstall --all

--all オプションを指定しなければ、~/.dapr フォルダや、zipkinとredisのDockerイメージも残ってしまいますが、--all をつけることですべて削除されます。

まとめ

今回はここまでです。今回の内容を簡単に振り返りましょう。

  • Daprはサイドカーサービスとして動作し、様々なクラウドサービスやミドルウェアを共通のAPIで利用できるようになります
  • DaprとアプリケーションはHTTP/gRPCで通信するため、開発者は好きな言語やフレームワークを利用してアプリケーション開発ができます
  • まずはDockerとDapr CLIをインストールしましょう

まだCLIをインストールしただけの小さな一歩ですが、ここから続くAdvent Calendarを読むために欠かせない作業です。

それでは、これからも引き続きDapr Advent Calendarをよろしくお願いします。継続することを祈っていてください!!(!?)

Bootiful SQL Template 2.1.0でRecord対応をしました。

年に一回くらい、突然Javaの話をブログに書き始めるJava Championの @cero_t です。

数年前、「Spring BootでもSQLファイルを使いたいな」と思って勢いで作った Bootiful SQL Template ですが、何気に仕事で使うことがたまにあるので細々とメンテナンスを続けています。

最近、APIを全面的に見直してFluent API化したバージョン2.0.0をリリースし、日本語のドキュメントも書き始めました。

sqltemplate/README_ja.md at main · cero-t/sqltemplate · GitHub

今回は、これをRecordに対応させたというお話です。Record対応されたバージョンは2.1.0になります。

TL;DR

  • JdbcTemplateがRecordに対応してないから自分で対応したよ
  • Java16から追加された Class#getRecordComponents とリフレクションを使えば良いよ
  • ライブラリをJava8でも使えるようにするために、Record関連の処理をリフレクションにしたよ

はじめに

Recordとは

Recordは、Java 16から使えるようになったデータクラスというか値オブジェクトというかエンティティクラスというかDTOというか、まぁなんかそういうヤツを良い感じに作れるようにする機能です。説明が雑。

Recordの定義はこうなります。

public record EmpRecord(Integer empno,
                        String ename,
                        String job,
                        Integer mgr,
                        LocalDate hiredate,
                        BigDecimal sal,
                        BigDecimal comm,
                        Integer deptno) {
}

このようにフィールドを列挙するだけで、そのフィールドすべてを使ったコンストラクタが自動的に生成されるため、これを使ってインスタンスを作成します。

EmpRecord emp = new EmpRecord(1000, "cero-t", "MANAGER", 7839,
        LocalDate.of(2004, 4, 1), new BigDecimal(4000), new BigDecimal(400),
        10);

Recordの値はコンストラクタでしか設定できません。そのためRecordのインスタンスはイミュータブルということになります。

値を取り出す時は、フィールド名と同じ名前のメソッドを使います。

System.out.println(emp.ename()); // cero-tが出力される

Recordを使えば、getterやsetterをたくさん書かなくて良くなりますし、toStringやhashCodeメソッドも動的に生成されるようになって生産性アゲアゲですね! まぁもともと自動生成してましたけど。

先月LTS版のJava 17がリリースされたので、このRecordを使う機会も増えることになりそうですよね。

Spring BootのRecord対応

そんなRecordですが、じゃぁSpring Bootで使えるのかって話ですよね。

まずspring-webでは、既にRecordが使えます。JSONJavaオブジェクトを相互変換するJacksonがRecordの対応をしていて、spring-webではそれを使っているためです。ちょっとどのバージョンから使えるようになったかは確認してないですけど。

ただ、DBアクセスをするライブラリであるJdbcTemplate(spring-jdbc)はRecordには対応していません。既に試された人がいらっしゃいました。

ashishtechmill.com

JdbcTemplateは新しい機能が追加されることがほとんどなく、Spring Framework 5.3でStreamを返すAPIが追加されただけでも、「クララが立った!」に匹敵するレベルで興奮しましたね。たとえ話が昭和ですね。

そんなわけで、たとえJdbcTemplateがRecordに対応しなくても、それを対応するのがBootiful SQL Templateの役割なので、今回実装してみようと思い立ちました。

Recordクラス対応の流れ

Bootiful SQL TemplateをRecordに対応させる流れは、次のようになりました。

  1. 与えられたクラスがRecordか否かを判定する
  2. Recordのフィールドの一覧を取り出す
  3. Recordのコンストラクタを取り出す
  4. Recordのコンストラクタを使ってインスタンスを作成する
  5. Recordから値を取り出す側は?
  6. Java 16以降に依存しないコードに書き換える

順を追って見ていきます。

1. 与えられたクラスがRecordか否かを判定する

まずは与えられたクラスがRecordなのか、ただのValue Objectなのかを判定する必要があります。RecordであればRecord対応の処理を行い、Recordでなければ既存の処理を行う、というような分岐をしたいためです。

Recordクラスは java.lang.Record を継承したクラスとなるため、次のようなコードで判定できます。

if (Record.class.isAssignableFrom(targetClass)) {
    return new RecordMapper<>(targetClass); // Recordを扱う処理
}

return new BeanMapper<>(targetClass); // 既存処理

ここで instanceof も使えるのですが、なんやかんや色々あって僕はいつも isAssignableFrom を使うようにしています。

2. Recordのフィールドの一覧を取り出す

次にRecordのフィールドの一覧を取得する方法を確認します。Java 16でClassクラスに新たに追加された getRecordComponents メソッドが使えるようです。

RecordComponent[] components = targetClass.getRecordComponents();

// フィールドの数を取る。最初の例に書いたEmpRecordなら 8
System.out.println(components.length); 

// 最初のフィールドの名前を取る。EmpRecordなら "empno"
System.out.println(components[0].getName());

// 最初のフィールドの型を取る。EmpRecordのempnoなら java.lang.Integer
System.out.println(components[0].getType());

説明はコメントで書いたので、割愛しますね。
どうあれRecordの情報は簡単に取れるようです。複雑な継承関係をあまり考えなくて済むので、通常のクラスよりも扱いが楽ですね。

3. Recordのコンストラクタを取り出す

続いて、コンストラクタの取得です。
今回やりたいことを簡単に言うと、データベースの検索結果である ResultSet から値を取り出して、Recordのコンストラクタにそれを渡してオブジェクトを作成する、ということになります。そのためにコンストラクタが必要なのです。

先にも書いたとおり、Recordはフィールドを列挙するだけでそのフィールドすべてを使ったコンストラクタを自動的に生成します。これはcanonical constructorと呼ばれています。コンストラクタがcanonical constructor 1つのみであれば、次のようなコードでコンストラクタを取得できます。

targetClass.getDeclaredConstructors()[0]

ただ、Recordでは自分でコンストラクタを追加することもできるのです。

public record EmpRecord(Integer empno,
                        String ename,
                        String job,
                        Integer mgr,
                        LocalDate hiredate,
                        BigDecimal sal,
                        BigDecimal comm,
                        Integer deptno) {
    public EmpRecord(String ename) {
        this(null, ename, null, null, null, null, null, null);
    }
}

こうなると、上に書いた方法でコンストラクタを取得しても、canonical constructorが取得できる保証はないですよね。そのため、フィールドの型を列挙してコンストラクタを取得することにしました。こんなコードになります。

Class<?>[] parameterTypes = Arrays .stream(EmpRecord.class.getRecordComponents())
        .map(RecordComponent::getType)
        .toArray(Class<?>[]::new);
Constructor<?> constructor = targetClass.getDeclaredConstructor(parameterTypes);

ちなみに後から知ったのですが、Class#getRecordComponentsJavadocにもこれと全く同じ実装が掲載されています。みんなやることは同じだ。

https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html#getRecordComponents%28%29

Java標準のコードと一致してたら、著作権違反だって訴えられちゃうかな? #やめなさい

4. Recordのコンストラクタを使ってインスタンスを作成する

そうやって取得したコンストラクタに、データベースの検索結果である ResultSet から取り出した値を渡せばインスタンスを生成できます。

おおむねこういうコードになります。

Object[] params = new Object[constructor.getParameterCount()];

// 何やかんやResultSetから値をきちんと取り出してparamsに代入する

return constructor.newInstance(params);

実際には「何やかんや」の部分で、大文字小文字を無視したり、スネークケースとキャメルケースを同一視するみたいなコードを入れてるのですが、あくまでもRecordに関係する処理に限ると、この部分だけになります。

これで、データベースから検索してその結果からRecordのインスタンスを作成する部分ができました。
もう少し詳しく処理を見たい人は、このコミットログを見てください。

https://github.com/cero-t/sqltemplate/commit/2910ae82fc8b3e46149635e5c237d74081cf5d98

5. Recordから値を取り出す側は?

ここまでの処理で、SELECT文に相当する処理は書けたのですが、INSERT文についても考えなくてはなりません。INSERTの処理はここまでのSELECTの処理とは逆で、Recordのインスタンスから値を取り出す必要があります。

・・・なのですが、これは既存のBootiful SQL Templateのコードでも問題なく動作しました。
というのも、JdbcTemplateでは元々データクラスのオブジェクトに対してprivateフィールドからアクセサメソッドを経由して値を取り出す仕組みがあり、その仕組みがRecordにも機能しているためです。

なのでINSERT側は実装なしで対応済みということになりました。

6. Java 16以降に依存しないコードに書き換える

今回このようにしてRecord対応を行いましたが、Java 16以降で追加されたAPIやクラスを使っているため、これをJava 15以前のバージョンで動作させようとすると、たとえば java.lang.Record#isAssignableFrom を呼んだ際などに ClassNotFoundException が発生してしまいます。

Exception in thread "main" java.lang.NoClassDefFoundError: java/lang/Record
    at ninja.cero.sqltemplate.core.mapper.MapperBuilder.mapper(MapperBuilder.java:20)
    at ninja.cero.sqltemplate.core.executor.ArrayExecutor.forList(ArrayExecutor.java:37)
    at ninja.cero.sqltemplate.example.SampleProcess.gettingStarted(SampleProcess.java:25)
    at ninja.cero.sqltemplate.example.SampleProcess.process(SampleProcess.java:123)
    at ninja.cero.sqltemplate.example.SampleApplication.main(SampleApplication.java:22)
Caused by: java.lang.ClassNotFoundException: java.lang.Record
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    ... 5 more

Bootiful SQL Template自体はJava 8でも動くようにしたいため、ここに対応する必要があります。

①局所的にリフレクションにする(ビルドはJava 17、sourceとtargetは1.8)

手っ取り早く対応するのであれば、isAssignableFrom の判定を行うところを、次のように修正すれば動きます。

try {
    if (Record.class.isAssignableFrom(targetClass)) {
        return new RecordMapper<>(targetClass); // Recordを扱う処理
    }
} catch (ClassNotFoundException e) {
    // ignore
}

return new BeanMapper<>(targetClass); // 既存処理

このようにすればJava 15以前ではClassNotFoundExceptionが発生して既存処理を実行することになるため、Record対応の処理は実行されません。そのため、これ以降でClassNotFoundExceptionが発生することもありません。

この修正を行う場合、Record対応の処理(RecordMapperの実装)のコンパイルにはJava 17が必要となるため、Bootiful SQL TemplateのビルドはJava 17で行い、sourceとtargetを1.8にすれば、「Java 8でも動く、Java 17でビルドされたライブラリ」ができます。

<properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
</properties>

これは一つの方法としてアリですが、なんかちょっとしたはずみで動かなくなりそうですし、今回はもう少し慎重な方法を選びました。

Java 16以降で追加されたAPIをすべてリフレクションにする(Java 8でビルド)

より慎重を期すために、Bootiful SQL Template自体をJava 8でビルドするようにしました。そうすれば、うっかりJava 16に以降で追加されたAPIを使ったコードを書いてしまっても、コンパイルエラーが起きて気づけるためです。

そのために、Java 16以降で追加されたAPIはリフレクションで扱うことになります。まず上に書いたisAssignableFromのコードは次のようになります。

try {
    Class<?> recordClass = Class.forName("java.lang.Record");
    if (recordClass.isAssignableFrom(targetClass)) {
        return new RecordMapper<>(targetClass); // Recordを扱う処理
    }
} catch (ClassNotFoundException e) {
    // ignore
}

return new BeanMapper<>(targetClass); // 既存処理

このコードであればJava 8でもコンパイルも実行もでき、Java 16以降で使えばRecord対応されるようになります。

また、この先の処理も同じように文字列を使ってリフレクションで操作することになります。この辺りはJacksonの実装を参考にして、次のようなコードにしました。

Method classGetRecordComponents = Class.class.getMethod("getRecordComponents");
Class<?> c = Class.forName("java.lang.reflect.RecordComponent");
Method recordComponentGetName = c.getMethod("getName");
Method recordComponentGetType = c.getMethod("getType");

Object[] components = (Object[]) classGetRecordComponents.invoke(targetClass);
paramTypes = new Class<?>[components.length];
for (int i = 0; i < components.length; i++) {
    paramTypes[i] = (Class<?>) recordComponentGetType.invoke(components[i]);
}

// この後もう少しコードが続く

リフレクションのための処理をリフレクションで行うという、なかなか妙な感じになっていますが、こういう事をやろうとする時のあるあるな気がしますね。

どうあれこれで、Bootiful SQL Template本体はJava 8でビルド、テストができるようになり、Java 16以降で利用した際にはRecordが使えるようになりました。
もう少し詳しく処理を見たい人は、このコミットログを見てください。

https://github.com/cero-t/sqltemplate/commit/2d5a7e07693a0faf5ad47841767552da78268bea

無事に完成しました

このような対応をして、Bootiful SQL TemplateのRecord対応は無事に完成しました。

ちなみに、今回は作業を始めてからMaven Centralリポジトリにアップするところまで、3時間弱と意外と短時間で済みました。リフレクションの知識さえあれば、Record関連の処理はわりと素直に書けるように思います。

さらにちなむと、このブログを書き始めてからここまで4時間くらい掛かりました。実装よりも説明のほうが大変ですよね・・・。

そんなわけで、このRecord対応が少しでも皆さんのお役に立てば、ブログを書いたかいがあるってものです。

それではね、See You!

三井住友のナンバーレスカードを作りました。

前から思ってるんですけど、コンビニってなんかワクワクするというか、購買欲をそそるような陳列をしてないですか?
あまり具体的にどうとは言えないんですけど、スーパーに比べてコンビニのほうが派手で目を引くパッケージの商品を、煽るように陳列してあるなと思うのですよね。

そういうコンビニの戦略にまんまと引っかかり、用もないのにぶらりと立ち寄ってはお菓子をたくさん買ってしまう @cero_t です。皆さんいかがお過ごしでしょうか。

今回は、コンビニとSBI証券のために三井住友のナンバーレスカードを作ったよというお話です。

陸マイラーがコンビニに行くと

僕とかって陸マイラーなので、地上で行うすべての購買行動で航空会社のマイルが効率よく貯まるようにしようとしています。

たとえばコンビニだと、

という使い分けになります。
いやこれホントにこういう風に使い分けてるんですよ、実際。

そんなにマイル(ポイント)をセコセコ貯めるならそもそもコンビニを使わずスーパー使えやって言われそうですし、まぁ実際言われてるんですけど、夜型というか深夜型の生活をしているとコンビニの利用頻度って高くなるし、あとほら、コンビニって冒頭に書いてる通りワクワクするじゃないですか。ワクワク感。

まぁでも正直、支払い方法を使い分けるのは面倒くさいなぁと思っていたところ、三井住友のナンバーレスカードだったら上のどの方法よりもマイルが貯まることに気づいたのです。

いや、なんか何をどう書いてもステマっぽくなってしまうんですけど、特にステマでもダイマでもないお得情報のつもりなのです。

コンビニでの5%還元がやばい

三井住友ナンバーレスカードは、クレジットカードに番号が書いておらず、アプリで番号を確認するタイプのカードです。なんか最近の流行っぽいですね。
別にそんなの大したメリットだと思ってなかったのであまり注目してなかったんですが、たまたま見たサイトで「コンビニ利用で5%還元」って書いてあったのを見つけてオッとなって、よく調べてみることにしました。

www.smbc-card.com

ちなみに広告に出ている中条あやみは出身中学が同じなので、それだけで好感が持てます。どうでも良い話ですね。

セブン、ファミマ、ローソン、マクドで使えばポイント2.5%還元、タッチ決済だとさらに+2.5%されて、合計5%還元。
タッチ決済はスマホでも大丈夫なので、物理カードを持ち歩く必要はありません。

還元ポイントは「Vポイント」で貯まって、ANAマイルに交換するなら2ポイント = 1マイルになるため、2.5%分のマイルが還元されるという計算になります。

これまで僕が使っていた支払い方法だと、一番マイルが貯まるファミマでも2%のマイル還元(Tポイントカード併用でも2.25%)だったのでそれを超えていますし、何よりコンビニごとに決済方法を変えなくて済むのでシンプルです。
さらにファミマのTポイントカードとか、ローソンのPontaカードも併用したら、2.75%分のマイル還元になりますね。

コンビニでの支払い方法が統一できて、マイルも効率よく貯まるので、こりゃ作るしかないなとなったわけです。
もちろんマイルに限らず、還元されたポイントをそのまま支払いに使ってもOKです。

SBI証券のためにゴールドにした

ナンバーレスカードは、一般カード(年会費無料)とゴールドカード(年会費5500円)があります。

ゴールドカードの年会費は初年度無料で、さらに1年で100万円使えば以降の年会費が永年無料になるので、ゴールドカードにすることもやぶさかではない気持ちになったのですが、とはいえ別にコンビニでの還元率は変わらないし、年会費無料の一般カードで決まりでしょ、って考えていました。

ただもう一つ、ゴールドカードのほうはSBI証券の積立投信に使うと2%のポイント還元(来年からは1%還元)になるというキャンペーンがありました。

go.sbisec.co.jp

いま僕はSBI証券ANA VISAカードを使って積立投信をしていて、月5万円の積み立てで毎月450マイル(来年からは毎月150マイル)貯まっているのですが、これをナンバーレスカードのゴールドに切り替えれば毎月500マイル(来年からは毎月250マイル)貯まることになります。微妙な差だとはいえ、来年からは年間1200マイルの差になるので、だったらゴールドカードにしようかなとなりました。

そんなわけでゴールドカードを作って、最初の1年だけ100万円使って永年無料にして、以降はコンビニとSBI証券専用にしよう、という結論になりました。

au PAYのためにマスターカードにした

三井住友カードはVISAとマスターカードの2つから選べます。この2択だと、だいたいの人は名前の通ってるVISAを選ぶと思うのですが、今回はマスターカードを選びました。*1
理由は「au PAYにチャージできるから」という一点だけです。au PAYは陸マイラー的には少し効率のよいPayなので、いまはメインで使っています。東京や神奈川では納税にも使えるようになっていて、納税額の1.25%分のマイルが貯まるとか胸熱ですよね。

そんなau PAYですが、チャージに使えるクレジットカードには制限があり、VISAやJCBでは一部の発行元のカードしか使えません。ただマスターカードならどこが発行したカードでもチャージに使えるので、今回はマスターカードを選んだのです。実際に試してみたところ問題なくチャージできました*2

ちなみにナンバーレスカードについて調べると「マスターカードでないとiPhoneでタッチ決済できない」という情報がいくつか見つかりますが、今年の5月頃にVISAのタッチ決済もiPhoneに対応したので、この件についてはVISAでもマスターカードでも構わないと思います。

まとめ

  • コンビニのヘビーユーザーは、ナンバーレスカードを作ろう
  • SBI証券でクレカ積立をやってるなら、ゴールドカードを選ぼう。そして頑張って100万円使って年会費無料にしよう
  • au PAYを使うならマスターカードを選ぼう

ちなみにいま紹介キャンペーンをやっていて、紹介した人にも紹介された人にもポイントが貯まるようになっています。
この記事を読んでナンバーレスカードを作りたくなったなという人は、このURLから発行してもらえると嬉しいです。

https://www.smbc-card.com/olentry/affiliate/online_entry.do?bno=03400708611

あ、いえ、この紹介URLのためにこのエントリーを書いたわけじゃないですからね!

よろしくね!!

*1:申請時に間違ってVISAを選んでしまったので、申し込み後に電話してマスターカードに変えてもらいました

*2:初回チャージ時に不正利用防止機能が働いてカードを止められちゃいましたけど

Mac用にUSBやThunderbolt3の外付けSSDを買った話。

最近、大きめの動画をたくさん扱うことが増えてきたので、外付けSSDを買いましたというお話です。

TL;DR

  • 格安のUSB SSDは使っていると性能が落ちる
  • 少し高めのUSB SSDなら問題なさそう
  • でも少し高いUSB SSDを買うぐらいならNVMe M.2 SSDとThuderbolt 3のエンクロージャを買うのが良い

Buffaloの格安SSDは使っていると性能が落ちる

これまではBuffaloのSSD-PG1.0U3というSSDを使っていました。2年前に買ったときは1TBで13,000円くらい、現在は10,000円を切るぐらいの値段になっています。

ただ長く使っていると、読み込みが途中で止まったり、書き込みが遅くなったりして、どうにも性能が微妙だなと思うようになりました。まさか令和のSSDもまだプチフリするのか? と思って調べてみると、どうやら格安SSDあるあるのようですね。

www.hivoki.com

www.hivoki.com

なるほど、と思って自分でも計測してみたところ、確かにSecure Eraseをしてみると性能が回復しました。 1枚目は長く使ったあとにデータをすべて削除しただけの状態。2枚目はSecure Eraseを行った後の状態です。

f:id:cero-t:20210529140657p:plain
長く使った後の性能(Secure Erase前)

f:id:cero-t:20210529140647p:plain
Secure Erase後(=購入直後の性能)

特にWrite性能で大きな差がついていますね。そりゃ遅いわ。

というわけで、このような安いSSDに手を出すのはやめようと決めました。

Samsung T7を買ってみた

それで購入候補にした製品がこの辺りです。

  • Samsung X5 (Thuderbolt 3) - 500GB 25,000円
  • Samsung T7 (USB 3.2 Gen2) - 1TB 20,000円
  • SANDISK Extreme V2 - 1TB 20,000円

上に書いたサイトを見て、Thunderbolt 3のSSDを買うのが良いのかなと思っていましたが、ちょっとお高すぎるなと思い、今回はあくまでデータ置き場としてのSSDが欲しかっただけなので、USB 3.2 (Gen2) のSSDで妥協することにしました。

www.amazon.co.jp

せっかくだから俺はこの赤いSSDを選ぶぜ、という気持ちで選ばれたのが、Samsung T7でした。

買った直後にベンチマークするのを忘れていたので、80%ほど埋まった状態でベンチマークを取ったのですが、こんな感じでした。

f:id:cero-t:20210529144300p:plain
Samsung T7を80%ほど使った状態

購入直後の状態が分からないにせよ、全く問題のない性能が出ていますね。

比較しやすいよう、BuffaloのSSDベンチマークも再掲しておきますね。

f:id:cero-t:20210529140647p:plain
(再掲)Bufflo SSD-PG1.0U3(Secure Erase後)

Samsung T7を80%使った状態でも、BuffaloのSSDを買った直後の状態よりも高速です。値段が倍ほど違う理由がここにあるんだなと思わせる性能差でした。

急転直下、もう一台SSDが必要に

それで10日ほどSamsung T7とBuffaloのSSDを併用していたのですが、突然、BuffaloのSSDが読み込み中に認識されなくなる現象が起きるようになりました。もしかして新しい子に浮気していることに腹を立てたのでしょうか。モテる男はツライネー。

どうあれSSDがもう一台必要になったので、またSamsung T7を買うか? とも思ったのですが、ふと2万円も出してUSBのSSDを買うくらいなら、NVMe M.2 SSDと、Thunderbolt 3のエンクロージャSSDケース)を買えるんじゃないか? と思い至りました。

以前にもM.2 SSDもThunderbolt 3のエンクロージャを組み合わせることを検討していて、その時にはなかなか良いお値段がして手を出しづらかったのですが、今なら値段も下がってるのでは、と思って調べてみたらこんな感じでした。

  • Samsung 980 (NVMe M.2) - 1TB 15,000円
  • Samsung 980 PRO (NVMe M.2) - 1TB 25,000円
  • Samsung 970 EVO Plus (NVMe M.2) - 1TB 19,000円
  • Wavlink Thunderbolt 3 Nvme M.2 SSDケース - 9,000円

あれ、Samsung 980、安いな? 性能はどうなのかな? と思って調べてみると、、、

pc.watch.impress.co.jp

まったく問題ない性能ですね。

DRAMがないのでランダムリードライトを多用するような、たとえばOSの起動ディスクにするには向かないかも知れないのですが、データ置き場にする分にはまったく問題ないでしょう。

また、Wavlinkのエンクロージャも以前は10,000円くらいだったのですが微妙に値段が下がってきています。

ということで、Samsung 980とWavlinkのエンクロージャを買うことにしました。

www.amazon.co.jp

www.amazon.co.jp

せっかくだからベンチマーク

SSDを買ったら最初にやることは、ベンチマークですよね!*1

比較としてMac mini本体のSSDベンチマークも取ってみました。

f:id:cero-t:20210529144144p:plain
Mac mini (2018) 本体のSSD

f:id:cero-t:20210529144205p:plain
Samsung 980 + Wavlinkのエンクロージャ

Mac本体のSSDと同等とは言えないものの、USBのSSDとは比べものにならない性能が出ていますね。 比較しやすいようSamsung T7のベンチマークも並べておきます。

f:id:cero-t:20210529144300p:plain
Samsung T7を80%ほど使った状態

Thunderbolt 3は40Gbps、USB 3.2 (Gen2) は10Gbpsというバスの性能差もあってか、性能が倍以上違いますね。また上の方で紹介したサイトによるとThunderbolt 3ならTrimコマンドが効くおかげで性能劣化しないことも期待できます。

お値段はSamsung 980 + Wavlinkのエンクロージャが24,000円、Samsung T7が20,000円であることを考えると、圧倒的に前者の方が高コスパと言えますね。なんでSamsung T7を買う前に気づけなかったんだろうか。

ただ問題点を挙げると、Thunderbolt 3のポート数が不足しがちだということですね。M1のMac miniや、MacBook AirなどはThunderbolt 3のポートが2つしかありませんし、ハブやドッキングステーションなどを経由してUSBポートを増やせても、Thuderbolt 3ポートを増やすことはなかなかできません。少し古いWindowsのPCではThunderbolt 3のポートがないかも知れません。

その辺りの取り回し面では、引き続きUSBのほうが強いでしょうね。

まとめ

ということでまとめです。

  • 格安のUSB SSDは使っていると性能が落ちる
  • 少し高めのUSB SSDなら問題なさそう
  • でも少し高いUSB SSDを買うぐらいならNVMe M.2 SSDとThuderbolt 3のエンクロージャを買うのが良い

今回買った製品たちは、こちらです。

www.amazon.co.jp

www.amazon.co.jp

www.amazon.co.jp

今年、来年からはUSB 4やThunderbolt 4の対応製品が増えてきて、また状況が変わるかも知れませんが、どうあれこの価格でこの性能が出るNVMe M.2 SSD + Thunderbolt 3エンクロージャの組み合わせは、十分に悪くないコスパだと思います。

はぁー、これで、動画編集も捗る、、、のかな?

*1:Samsung T7のベンチマークは取り忘れたくせに

ソニー銀行を住信SBIネット銀行と比べ始めて1年経ったのでメモ。

こんにちは、最近ツイッターのフリートでは株の話しかしてない @cero_t です。

もともと住信SBIネット銀行(以降SBI銀行)のユーザーだったのですが、外貨を扱うことが多いならソニー銀行の方が得かも知れないと気づき、ちょうど1年ほど前にソニー銀行の口座を開設しました。
それ以降、それぞれのメリットなどをメモしていたのですが、1年経ったのでブログにまとめておくことにしました。

先にまとめ

先に結論を書いておきますね。

  • 国内利用が中心で、振り込みをする機会が多いなら、SBI銀行の方が便利
  • 海外利用が多いか、外貨受取をする機会が多いなら、ソニー銀行の方が便利
  • 両方とも口座を作って、国内はSBI銀行、外貨はソニー銀行、という使い分けでもOK

住信SBIネット銀行のほうが良いところ

まずはSBI銀行の良いところです。

ATM手数料と振り込み手数料の無料回数が多い

SBI銀行は残高に応じて「スマプロランク」と呼ばれるランクがつき、それでATMの無料利用回数や振り込みの無料回数が変わる仕組みになっています。たとえば残高が30万円以上ならランク2、300万円以上ならランク3になります。 ランク2ならATM利用は月5回、振り込みは月3回まで無料。ランク3ならいずれも月7回まで無料です。

スマプロランクについて | スマートプログラム | NEOBANK 住信SBIネット銀行

ソニー銀行は同様に「Club S」という仕組みがあり、最初はステージなし、残高300万円以上などでシルバーになります。
ステージなしならATM利用は月4回、振り込みは月2回まで無料。シルバーならATM利用は月7回、振り込みは月4回まで無料です(SONY Bank Walletカード作成する前提)

優遇特典|優遇プログラム Club S|MONEYKit - ソニー銀行

表にまとめておきます。

残高が30万円以上、300万円未満の時
SBI銀行 ソニー銀行
ATM無料利用回数 5 4
振り込み無料回数 3 2
残高が300万円以上の時(※残高300万円未満でも他の条件を満たせばこのステージになる)
SBI銀行 ソニー銀行
ATM無料利用回数 7 7
振り込み無料回数 7 4

ATMも振り込みも、月に7回使えれば、まず困ることはないですよね。

毎月の自動振り込み機能がある

SBI銀行は、毎月決まった日に他の口座へ自動的に一定額を振り込むことができます。
たとえば給与口座がSBI銀行で、他のローンやクレジットカードなどの支払い口座があったとして、そちらの口座振り込むことができます。
僕はうっかり住宅ローンの口座に振り込むのを忘れて残高不足になることが度々あったので、この機能はかなり重宝しています。

他の銀行でもこの機能を使えるところはありますが、ソニー銀行にはありません。

米ドルへの両替が安い

SBI銀行、ソニー銀行のいずれも、円だけでなく外貨の口座を持つこともできます。
為替手数料もかなり安いため、外貨貯蓄をしたいとか、FXをするほどではないけど外貨の現物を売り買いして稼ぎたいとかの用途に使いやすいです。

ソニー銀行と比較した時のSBI銀行の強みは、円をドルに替える際の手数料が「1ドルにつき4銭」と非常に安いことです。
たとえば通常のクレジットカードで外貨支払いをする際の手数料は1.6〜2%ですから、その表現で言うと0.04%程度ということになります。文字通り桁が違いますね。
銀行窓口や空港での両替手数料はもっと高いことも多いので、さらに差がつくでしょう。

米ドルでの支払いも安い(ただし年間30回まで)

SBI銀行が発行するデビットカードである「ミライノデビット」はドルで払う際にドル口座の残高から支払うことができます。上に書いたとおり通常のクレジットカードに比べて安い手数料で両替したドルで払えるわけです。
また、円高の際にドルを買っておいて、その後に円安になっても保有しているドルで支払うことができるわけです。もちろん逆のパターンもありますけどね、ワハハハハ(笑い事ではない)

ただ少しルールが複雑で、ミライノデビットを利用してドル口座から支払う場合、1回の決済につき2.8%の手数料が掛かります。これは通常のクレジットカードの為替手数料を優に超える手数料です。
しかし、この手数料は年に30回まで、同額のポイントでキャッシュバックを受けることができます。

分かりにくいのですが、要するに年間30回までの支払いであれば掛かる手数料はあくまでも1ドルにつき4銭だけで、それを超えると1ドルにつき3円近い手数料が発生してしまうということです。

海外旅行で頻繁に使っていると30回を超えてしまうので、たまに米Amazon.comで買い物をするとか、海外旅行中でも大きめの買い物をする時に限り使うなど、用途を絞った方が良いでしょう。

JALのマイルが貯まる

SBI銀行やミライノデビットの利用で貯まるポイントは、JALのマイルと交換することができます。

スマプロポイントについて | スマートプログラム | 住信SBIネット銀行

ただマイル還元率はあまりよくないため、効率よくマイルを貯めたいのであれば、後述するJAL Global WALLETやJAL NEOBANKの利用を検討したほうが良いでしょう。

余談: JAL GLOBAL WALLETとJAL NEOBANK

SBI銀行はJALと提携していて、SBI銀行の外貨口座から円や外貨をチャージして利用できるJAL Global WALLETというプリペイドカードを発行しています。また、外貨預金や外貨積み立てでマイルが貯まるJAL NEOBANKというサービス(SBI銀行のJAL支店という位置づけ)も提供しています。
いずれも年会費などは掛からず、SBI銀行やミライノデビットに比べて効率よくマイルを貯めることができます。

ただ、いずれのサービスも現時点ではあまり使い勝手が良くないため、僕は利用していません。

ソニー銀行のほうが良いところ

続いて、ソニー銀行の良いところです。

11種類の外貨決済に対応

ソニー銀行デビットカードである「Sony Bank WALLET」は、11種類の外貨口座からの支払いに対応しています。SBI銀行のミライノデビットは米ドルにしか対応していません。 なのでヨーロッパやオーストラリアにも旅行するなら、Sony Bank WALLETのほうが便利に利用できます。

為替手数料はClub Sのステージにもよるのですが、SBI銀行の為替手数料と大きくは変わりません。通常のクレジットカードや銀行での両替に比べれば1/10くらいの手数料です。

為替コスト(手数料)・金利 | 外貨普通預金 | 住信SBIネット銀行

優遇特典|優遇プログラム Club S|MONEYKit - ソニー銀行(ページ後半の「為替コストの優遇」)

米ドルに関して言えば、Club Sのステージなしで15銭、シルバーなら10銭ですが、SBI銀行では1ドル4銭と安くなっています。
プラチナステージまで到達すれば1ドルにつき4銭になりますが、到達するのが大変ですね。

外貨での支払時に手数料が掛からない

Sony Bank WALLETでは、外貨口座から支払う際の手数料などは掛かりません。SBI銀行のミライノデビットでは、先に述べたとおり1回の決済につき2.5%の手数料が掛かります(ただし米ドルは30回まで同額をキャッシュバック)

Sony Bank WALLETのほうが、ドルも含め、普段の外貨支払いとして利用しやすいと言えます。

ANAのマイルが貯まるSony Bank WALLETがある

Sony Bank WALLETは国内で利用すると通常0.5〜2.0%のキャッシュバックがあるのですが「ANAマイレージクラブ / Sony Bank WALLET」というサービスに加入すると、キャッシュバックの代わりにANAのマイルをもらうことができます。いや、もうちょっと良い名前にできなかったんですかね。

ANAマイレージクラブ / Sony Bank WALLET 商品詳細説明書|MONEYKit - ソニー銀行

ANA版のSony Bank WALLETでは200円の利用につき1マイルの還元があり、マイル還元率0.5%になります。ちなみにANAカードではマイル還元率が1.0%くらいになるので、きちんとマイルを貯めている人にとってはあまり魅力はないですね。
また外貨預金や外貨の定期預金でもマイルが貯まり、たとえば3万ドル(≒300万円分)を1年間の定期預金にすると、年間で2520マイル貯まります。元々の為替手数料が低いことも踏まえると、この外貨貯蓄でマイルが貯まるというのはANA派にはなかなか良いですね。

外貨受取の手数料が掛からない

これは刺さる人には非常に刺さる話なのですが、ソニー銀行では、海外から送金された外貨を受け取る際の手数料が掛かりません。多くの銀行では数千円の手数料が取られます。
SBI銀行でも2500円前後の手数料が掛かるため、たとえば10000円分の外貨が送金されても7500円前後になるのですが、ソニー銀行では満額を受け取ることができます。

頻繁に外貨を受け取ることがある人は、その目的だけでもソニー銀行の開設にメリットがあると思います。

硬貨での入金ができる

これは非常にマイナーな話なのですが、ソニー銀行では三井住友か三菱UFJのATMから硬貨での入金ができます。SBI銀行ではできません。

僕はふだん小銭入れのない財布を使っていて、発生した小銭はポケットなどに入れて持ち帰って貯金箱に入れておき、たまに銀行に持って行くのですが、SBI銀行には入金できないのです。 まぁそんなマイナーな問題で困る人はあまり多くないと思いますが。

両方の銀行に共通すること

最後に、両方の銀行に共通するけど、他の都市銀行地方銀行にはあったりなかったりする特徴を整理しておきます。

毎月の自動入金機能がある

他の銀行の口座から、SBI銀行やソニー銀行の口座に毎月定額の振り込みをする機能です。
振り込みなので手数料が掛かるのかなと思いきや、手数料は掛かりません。

僕は都市銀行に給与口座を一つ持っているのですが、そちらの銀行からSBI銀行に毎月入金して、残高を集約しています。

対象外の外貨は受け取れない

SBI銀行、ソニー銀行とも、外貨送金で受け取れる通貨は決まっており、それ以外の通貨は受け取ることができません。
たとえば突然インドネシアルピアの送金を受け取ることになった場合には、都市銀行地方銀行の口座が必要になります。

住民税などの支払い口座に設定できないことがある

住民税の口座振替などのレガシーな口座振替は、都市銀行や一部の地方銀行、ゆうちょ銀行しか対応していないことがあります。
ネット銀行1本でいこうと思って他の口座をすべて閉じたりすると、そういう時に困ってしまうので注意してください。

まとめ

まとめると、振り込み回数や自動振り込みの利便性が強いSBI銀行、海外利用に強いソニー銀行、という整理になりました。
海外利用が少ないならSBI銀行のみでも良いでしょうし、振り込みをあまり使わないならソニー銀行のみでも良いでしょう。
あるいはJALのマイルを貯めているからSBI銀行、ANAのマイルを貯めているからソニー銀行、という選択もできます。

僕はJAL/ANA両方のマイルを貯めていますし、国内でも海外でも利用するので、これからも両方の銀行を併用し続けると思います。

いかがでしたか?


あ、このブログを読んで(読まなくても)ソニー銀行を開設しよっかなという気持ちになった人は、2000円 or 4000円がもらえる紹介キャンペーンに招待するのでお知らせください。

ご家族・ご友人 紹介プログラム|MONEYKit - ソニー銀行

紹介すると僕の方も1500円もらえるので嬉しいです!

4Kディスプレイのサイズと解像度の関係を改めて調べてみた。

皆さんこんにちは、調べてみましたブログです。

要約

先に本文の内容をまとめておくと、こんな感じです。

  • 4KディスプレイをPCディスプレイとして利用する場合、通常は4Kネイティブではなく拡大して利用する
    • 21〜23インチの4Kディスプレイは、1920 x 1080(200%拡大)で利用する人が多い
    • 27インチの4Kディスプレイは、2560 x 1440(150%拡大)で利用する人が多い
    • 32インチの4Kディスプレイは、Macなら3008 x 1692、Windowsなら3072 x 1728(125%拡大)で利用する人が多い
    • 41インチぐらいなら4Kネイティブで利用できる
  • Retinaディスプレイは、実解像度の1/2の疑似解像度で使うのがベスト
    • 21.5インチは、4K(3840 x 2160)を1920 x 1080(200%拡大)で使うのがベスト
    • 27インチは、5K(5120 x 2880)を2560 x 1440(200%拡大)で使うのがベスト
    • 32インチは、6K(6016 x 3384)を3008 x 1692(200%拡大)で使うのがベスト

はじまり

今年の春頃、みんながリモートワークを始めるに伴い、ディスプレイやアームを買っているのを見ていると、ついついうらやましくなってしまい、僕も27インチの4Kディスプレイを買いました。MacBook Pro 16に繋いで問題なく使ってたのですが、ふと、自分が2560 x 1440(WQHD)相当の疑似解像度で使っていることを思い出しました。
せっかく4Kディスプレイなのだから、4K解像度にしたほうが良いのかなと思って、3840 x 2160にしてみたら、いやー、文字が小さくて読めない読めない。27インチでこのサイズは無理だ。もっと大きなディスプレイが必要だ、という気持ちになりました。

どれぐらいのサイズが必要なのだろうかと思い、サッと比で計算すると40.5インチのディスプレイが必要だということになり、ちょっとデスクで使うには大きいですね、という気持ちになります。でも、本当にこの計算が合ってるのかな? と思い、ちょっと調べてみることにしました。

Macの疑似解像度と、Windowsの解像度表記

そもそも解像度とか疑似解像度と言われるものが何だか分からなくなってきたので、整理することにしました。

Macの場合、ディスプレイの解像度は「疑似解像度」(英語表記の場合は Looks like)で表現されます。たとえば僕の持っている4KディスプレイをMacBook Proに接続した場合、疑似解像度の選択肢は次の5つになりました。( )内は4Kを標準とした場合の倍率です。

  • 1920 x 1080(200%)
  • 2560 x 1440(150%)
  • 3008 x 1692(128%)
  • 3360 x 1890(114%)
  • 3840 x 2160(100%)

一方、Windowsの場合は「解像度」と「テキスト、アプリ、その他の項目のサイズを変更する」という2つで表現します。後者は、100%、125%、150%、175%、200%、225%、250%、300%、350%となっています。
MacWindowsの刻み方を比較すると、100%、125%(128%)、150%、200%の辺りは共通ですね。

つまるところ、現代の高解像度ディスプレイはDPI(インチあたりのドット数)が高すぎて、そのままだと文字が小さくて読みづらいため、文字を拡大する(その代わり、フチを滑らかに表示する)ようにしています。それでWindowsではパーセント表記で拡大率を表現する一方、Macでは疑似解像度という名前で表示の大きさを決めていて、つまるところMacでもWindowsでも、表現の仕方こそ違えどやっていることは同じでした。

Appleのサイトで製品を見てると

疑似解像度について少しググって調べてみると、こういう風に説明されていました。

上記で”2倍”と書いたが、Retina採用MaciPhoneiPadなどはMacBook無印などの例外を除けば全てネイティブ解像度はデフォルト設定の擬似解像度のちょうど縦横2倍になっている。
ではなぜデフォルトの値が2倍なのかというと、モニターというものはスケーリングした際に整数倍の表示にする時が一番ボケにくく鮮明な表示が可能になるからだ。
Macで4Kモニターを使用した際のRetina解像度 | INFORNOGRAPHY

なるほど、そういうものなのかと思い、AppleオンラインストアMacのオプション製品を調べてみることにしました。

27インチなら5K

まず、27インチRetinaディスプレイはこれが紹介されています。 www.apple.com 解像度は5Kです。前に見た時は「27インチで4Kを表示する時でも拡大するのに、5Kにしたところでさぁ・・・」と思っていましたが、このディスプレイの解像度は5120 x 2880で、ちょうど2560 x 1440の2倍になっています。
27インチディスプレイは、2560 x 1440相当で使うのがちょうど良い、という体感も照らし合わせると、そのちょうど2倍の解像度で提供するというのは納得がいきます。ちなみに27インチiMacも同じ解像度でした。

32インチなら6K

そして、値段が高いと評判の32インチRetinaディスプレイですが、こちらは6Kです。 www.apple.com この6016 x 3384という解像度は、3008 x 1692のちょうど2倍になります。つまり32インチでは、3008 x 1692の疑似解像度で使うと良いというのがAppleの考え方なのでしょう。

4Kは21.5インチ

ちなみに、iMacは21.5インチ版は4Kディスプレイ(あるいはフルHDディスプレイ)となっています。 www.apple.com 比率的にも、確かに21.5インチがちょうど良いと思います。

ただ、ディスプレイ単体としてはなぜか23.5インチの4Kディスプレイが紹介されていますね。 www.apple.com この辺りの不統一な感じはよく分かりませんが、視力が良くないなどで少し大きめに表示されて欲しい場合には、23.5インチ4Kが良さそうですよね。

4Kディスプレイユーザーの声も拾ってみる

「4K 27インチ 125% 150%」みたいな雑なキーワードでググってみて、世の中のWindowsユーザーがどういう倍率で表示しているかを調べてみました。
結果、どうやら27インチを使っている人は150%、32インチを使っている人は125%が多いようでした。

ここまでの話を整理すると、こんな感じです。

  • 21.5インチ or 23.5インチ
    • Appleストア: 4Kの200%表示で、1920 x 1080
    • Windows: 4Kの200%表示で、1920 x 1080
  • 27インチ
    • Appleストア: 5Kの200%表示で、2560 x 1440
    • Windows: 4Kの150%表示で、2560 x 1440
  • 32インチ
    • Appleストア: 6Kの200%表示で、3008 x 1692
    • Windows: 4Kの125%で、3072 x 1728

どうあれOSに関わらず、皆さんほぼ同じぐらいの(疑似)解像度で利用しているようです。

まとめ

そんなわけで、いま27インチディスプレイを2560 x 1440相当で使っていることは妥当であり、これを32インチに変えたところで程度の差はあれど拡大して使うことに変わりはないということが分かりました。

それならAppleオススメのLGの27インチ 5Kディスプレイにすれば綺麗になるかな、と思いましたが、こちらはHDMI入力がありません。Mac専用ディスプレイにするなら良いのでしょうけど、家庭用ゲーム機などを接続できなくなってしまうのは、ちょっと困ります。

じゃぁいっそ4Kネイティブで使える42インチ 4Kディスプレイを買えばネイティブ解像度で使えるから良いよね、と少し血迷いましたが、机から買い直しになるし、別にそこまで大きいものが欲しいわけじゃないしさすがにね、という気持ちになりました。

というわけで、安心して「何も買わない」という結論が出た、今回の調査でした。

いかがでしたか?