JavaScript -連想配列と配列でオブジェクトを表現-

今日は Java ではなくて JavaScript の話。

初学者向けの JavaScript の記事、ヤバいのが多くないか?

この前の記事でモデル Phone のコレクション(リスト)を含む Person を永続化する REST api を作成したが、JavaScript で Person のオブジェクトを投げたい。

その際、配列と連想配列を用いて Person の実体を作成する必要があるんだが、配列と連想配列を組み合わせて使うという発想がないらしく、そのままでは動かないサンプルが散見された。

JavaScript は詳しくないが、おそらく「動く」コードは以下のような感じ。


var phone1 ={'number':'090-111-1111'};
var phone2 ={'number':'090-222-2222'};
var phones =[phone1,phone2];
var person = {'phones':phones};
var json_text = JSON.stringify(person);
console.log(json_text);//確認用
// {"phones":[{"number":"090-111-1111"},{"number":"090-111-1111"}]}

この処理をしてから、XMLHttpRequest などを使って REST api に POST する。
ちなみにちゃんと永続化できてた。

配列の宣言・初期化は [] 、連想配列の宣言・初期化は {} 、が基本だと思うのだが、おかしな記法や説明している記事が多かった。

思うに、オブジェクトを配列・連想配列を組み合わせて JSON で表現する、という実用的な使用方法を意識してないんだろうなあ。

スクリプト系言語の初学者向けの記事には気をつけた方がいいかな?

 

 

 

もっとも簡単な hibernate のサンプル

はじめに

ある程度実用的な Java ウエブアプリを作る場合、別のクラスのインスタンスをコレクションで含むエンティティを hibernate で操作する、みたいなことをよくやる。
が、意外にこのためのサンプルが少ない。

簡単なサンプルを作成したので、ちと解説。

モデルの準備

最終的に操作したい親のクラスを Person とした。
この次に Phone クラスを登場させるが、Person と Phone の関係は1対多(OneToMany)で一方向のみ。
hibernate がどのように動作するのか確認したいなら、これで十分でしょう。


@Entity(name = "person")
public class Person implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    
    @OneToMany(cascade = CascadeType.ALL)
    private List<phone> phones = new ArrayList<>();

    (以下、ゲッター・セッター)

ただし、フィールド変数に他のクラス(小クラス phone)のリスト phones を含めた。
ここを String とかにすると実用性ないので。

で、phone はこんな感じ。


@Entity(name = "phone")
public class Phone implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    
    private String number;
    (以下、セッター・ゲッター)

1対多の関係ならば、このクラスに Person の情報はいらないので、これでいい。
mapped by とかそういうややこしいのは要らない。忘れましょう。

ここまででも、ビルドして適当なアプリケーションサーバにデプロイすると(hibernate が適切に設定されているならば)指定したデータベースにテーブルを作ってくれる。

結合テーブル person_phone ができるが、このテーブルで person と phone を関連づけているわけですね。
見た目はスッキリしていないが、確実に動くプログラムを作成するのと hibernate がどの情報を用いて動作するのか把握するのは初学者にとって大事なのでそちらを優先。
いきなり難しいことをやる必要はない。

CRUD 操作

ここまでできたら、CRUD 操作用の REST api を作成。

まず、アプリケーションパスを設定(IDEが勝手に作ってくれると思うけど)。


@ApplicationPath("resources")
public class JAXRSConfiguration extends Application {
    
}

GET

次に、GET 命令で適当なオブジェクトを永続化させてみよう。


@Path("/personphone")
public class TestPersonPhone {
    
    @PersistenceContext(unitName="適当な名前")
    private EntityManager em;
    
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Transactional
    public String testPersist() {
        
        Person person = new Person();
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        List<Phone> phones = new ArrayList<>();
        
        phone1.setNumber("123");
        phone2.setNumber("456");
        phones.add(phone1);
        phones.add(phone2);
        person.setPhones(phones);
        
        em.persist(person);
        
        return "person phone persist";
    } 
}

デプロイ後、ブラウザで

http://localhost:8080/(プログラム名)/resources/personphone

にアクセスするとブラウザ上では person phone persist が返ってくる。

一方、データベースを覗くと phone テーブルのカラム number に “123”, “456” が入っているはずだ。

POST

これだけだと面白くないので、POST での操作も試してみよう。
上記のプログラムに以下のコードを追加。


    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Transactional
    public String testPersist2(String json) throws JsonProcessingException {
        
        ObjectMapper mapper = new ObjectMapper();
        Person person = new Person();
        
        person = mapper.readValue(json, new TypeReference<>() {});
        
        em.persist(person);
        
        return "person phone persist post";
    }

デプロイ後、例えば、コマンドラインから


curl -i -X POST -H "Content-Type: application/json" --data-binary '{"phones":[{"number":"789"},{"number":"012"}]}' http://localhost:8080/(プログラム名)/resources/personphone

と叩く。
phone テーブルのカラム number に “789”, “012” が入っているはずだ。

jackson でデシリアライズ

POST の方のキモは、jackson のオブジェクトマッパーを用いて、TypeReference を指定してデシリアライズしたところ。


        person = mapper.readValue(json, new TypeReference<>() {});

ここを


person = mapper.readValue(json, Person.class);

などとしてしまうと、コレクション部分(phones)を無視してしまう。
独自の型情報を使った場合、それを使った旨を言語処理系に教えないと動かないのは当たり前なんだが、どういうわけかここら辺に言及した記事はあまり見かけなかった。

おわりに

いかがでしたでしょうか?

簡単な hibernate 〜 CRUD 操作に加えて、実用的な curl コマンドの使い方や jackson の小技を入れてみました。

ところで、ワイがこういった書き方をする理由は、Java とりわけ EE 環境の解説記事に違和感を感じているから。

以前にも JavaEE/JakartaEE ではけっこう有名とされている人の記事に間違いを見つけたが、ああいう間違え方は他の言語使用者からするとありえんと思う。

筋の悪い間違え方を続けていると通常は次第に相手にされなくなっていくと思うが、本邦の JAVAer はなぜかそういうセンスのない人の言動を「あの人が言ったから」ということで重宝がる傾向があるようだ。

権威を好んでロジックをないがしろにする文化に継続的な発展はありえませんよ。

 

Karaku 2022 冬

季節目の変わり目は Karaku のチェックw

今季、気になった商品はこれ

単品でも雰囲気あるし、以前に取り上げたこのシャツにも似合いそう。

しかし、値段が・・・。

4万近くするのはちょっと躊躇する。

ちなみに、某先生は、↑とこれこれを大人買いしたそうだ。いいなあ。
でも、コートの類はどうすんだろ?


結局、懐事情の関係でこれにした。

値段の関係でそれほど期待はしていなかったのだが、

・保温性はバッチリ

・現物デザインも悪くない

ということで、かなり気に入り、現在、絶賛ヘビロテ中だ。

ウールに比べれば、確かに重いんだが、これは生地の厚さのせいもあるだろう。

とにかくコスパ最強格。

 

 

JAX-RS と JSON と List と hibernate

おさらい

REST api を自作してると response として List を含んだ JSON を返したいということがよくあるが、簡単なサンプルがなかったりするので、簡単なやつを作ってみた。


@Path("/persons")
public class SimpleResource {
 
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Person> list() {
        
        Person person1 = new Person();
        Person person2 = new Person();
        person1.setName("秋葉");
        person2.setName("akiba");
        List<Person> persons = new ArrayList<>();
        persons.add(person1);
        persons.add(person2);
        
        return persons;
        
    }
    
    private class Person{
        
        private String name;

        public String getName() {
            return name;
        }

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

これをアプリケーションサーバにデプロイしてブラウザで http://…/persons にアクセスすると

[{"name":"秋葉"},{"name":"akiba"}]

という文字列が表示される。

だから何?って言われそうだが、やってないとけっこう忘れますw

返値の型は List<Person> なので、JSON で配列を表す [] が先にきて、インスタンスが二つあるので {} が二つ続くと。

 

この程度なら単純なのだが、ややこしくなってくるのは、Person クラス自体にコレクションを含むような場合だ。

ちょっとやってみよう。


@Path("/persons")
public class SimpleResource {
 
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Person> list() {
        
        Person person1 = new Person();
        Person person2 = new Person();
        person1.setName("秋葉");
        person2.setName("akiba");
        person1.addfriend("上野");person1.addfriend("新橋");person1.addfriend("神田");
        List<Person> persons = new ArrayList<>();
        persons.add(person1);
        persons.add(person2);
        
        return persons;
        
    }
    
    private class Person{
        
        private String name;
     List<String> friends = new ArrayList<>();

        public String getName() {
            return name;
        }

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

        public void addfriend(String name){
            friends.add(name);
        }

        public List getFriends() {
            return friends;
        }

        public void setFriends(List friends) {
            this.friends = friends;
        } 

    }
    
}

Person クラスに String の List friends を追加した。

この場合のブラウザ出力は

[{"name":"秋葉","friends":["上野","新橋","神田"]},{"name":"akiba","friends":[]}]

となる。

本題 (インスタンスの List を hibernate で操作 )

実用的なシステムを組む場合、上の friends 相当箇所が別のクラス(エンティティ)になる。

Friend というクラスがあって、List<Friend> friends を定義する、みたいな。

この場合、hibernate を組み込んで、@OneToMany だののアノテーションをつけることになると思うが、巷の教科書的な解説はアノテーションの付け方を変に複雑にしているせいで、わかりにくくなっている。

次の記事でシンプルなサンプルを提示する予定。

コンテクストルート

ところで、REST を作る最も簡単なやり方は

@ApplicationPath("resources")
public class JakartaRestConfiguration extends Application {

}

とすることだが、このとき @GET などのアノテーションをつけたクラスの URI は

http:// (サーバIP)/rest-1.0/resources/

となってなんかダサい。war のファイル名になるのを回避するには

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<context-root>/rest</context-root>
</jboss-web>
jboss-web.xml

というファイルを WEB-INF においておけばいい。この場合は、context-root で指定した文字列がコンテクストルートになる。

http:// (サーバIP)/rest/resources/

こっちの方がスッキリしますね。

 

江添 C++ 本と本家ストラウストラップ本

プロになる Java』で Java の教科書的な書籍を取り上げたので、今度は C++ で。

当初、江添 C++ 本のみ取り上げようと思ったんだが、やっぱり(世にいう)ストラウストラップ本も必要かな?と思い、最初の版よりかなり内容変わってます。

ある程度実用的に C や C++ 使うんだったら、両方必要でしょう。

ワイの場合、C はまあまあ使えていたから、ここら辺から入ったが、C 未習の人は、この前に C の基礎をやっておいた方がいいでしょう。

江添 C++ 本

しっかり書かれた本ならなんでもいいと思うのだが、取り組みやすさを考えるならば『江添亮の C++ 入門』あたりがオススメ。

取り組みやすさ、というのは、この本のイントロが GitHub で公開されている、というのもその理由の一つ。

標準ライブララリを一つのヘッダファイルでまとめる話が第 2 章で出てくるが、こんなのいちいち打ち込むわけにもいかない。ここが該当箇所。

まあ、ならサンプルコード一覧つけてくれよ、と思わないでもないが。

これはケチをつけているわけではなく、Mac でこのコードを動かそうとしても動かない場合があるから。

‘cstdalign’ file not found エラーの回避方法

ワイの環境の場合、

‘cstdalign’ file not found

というエラーが出る。

なお、これを一番簡単に回避する方法は、以下のように cstdalign をコメントアウトして

#include <cstddef>
#include <limits>
#include <climits>
#include <cfloat>
#include <cstdint>
#include <cstdlib>
#include <new>
#include <typeinfo>
#include <exception>
#include <initializer_list>
//#include <cstdalign>
#include <stdexcept>
#include <cassert>
#include <cerrno>
#include <system_error>
#include <string>
#if __has_include(<string_view>)
# include <string_view>
#endif
#include <array>
#include <deque>
#include <forward_list>
#include <list>
#include <vector>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <queue>
#include <stack>
#include <iterator>
#include <algorithm>
#include <cfenv>
#include <random>
#include <numeric>
#include <cmath>
#include <iosfwd>
#include <iostream>
#include <ios>
#include <streambuf>
#include <istream>
#include <ostream>
#include <iomanip>
#include <sstream>
#include <fstream>
#if __has_include(<filesystem>)
# include <filesystem>
#endif
#include <cstdio>
#include <cinttypes>
#include <regex>
#include <atomic>
#include <thread>
#include <mutex>
#include <shared_mutex>
#include <condition_variable>
#include <future>
using namespace std::literals ;

これをコンパイル。

g++ -std=c++17 -include all.h -o hello hello.cpp

などとする方法だろう。

こうすれば、mac でもとりあえずは動く。

 

しかし、この本、冒頭で「オススメ」とか書いちゃったが、今、読み返してみると、これは初学者向けの本ではないべ。

関数の説明でいきなりラムダ式

例えば、「関数」の説明でいきなりラムダ式が出てくるw

int main(){

    auto print = [](auto x){
        std::cout << x << "\n";
    };

    print("hello");
}

いや、手順通りにやれば確かに hello とは表示されるんですけどね。

あとで、これは本物の関数ではないと断っているんですが、お戯れ感がすごい。

実は class という言葉はなかなか出てこない

ほとんど struct で通しているw

というか今読み返している箇所では、今のところ class 出てきてねー(笑)。

C++ では関数も struct で扱えるからこれでも大した差は出ないんだが、

class のデフォルトでのアクセシビリティはデフォルトで private

struct のデフォルトでのアクセシビリティはデフォルトで public

という違いはある。
(『C++ における class と struct の違い』あたり参照)

実用的にはこれでも問題ないと思うが、多くの初学者が思う

「オブジェクト指向の特徴の一つ=カプセル化」

概念からすると違和感感じるかもしれない。

ここら辺はいわゆるストラウストラップ本の「C++ の言語機能の中核は、クラス(class)である」のような説明と一線を画す。
実際、ストラウストラップ本では、まず complex というクラス(名前から予想がつくように複素数を取り扱うクラス)の説明から入っている。

なお、たまに誤解している人もいるが struct でも継承はできます。

だから、「オブジェクト指向なので class というキーワードや使い方を期待している読者もいるかもしれないが、実用的には struct で代用できるので、ここでは struct で説明していく」と一言断っておけばいいんですよね。

また、Objective-C でも class は出てきません(@class というディレクティブはあるが、Java のようにわかりやすい形で class という単語が使われているわけではない)。NSObject を継承していて @property でメソッドが定義されているものがクラスだ、という風に理解されていると思います。

何をもってオブジェクト指向というのか?という宗教論争を引き起こしかねない話をここでする気はないので、まあ、そこら辺はスルーします。

テンプレートの説明はわかりやすい

逆にテンプレートのサンプルはわかりやすかったりする。

template <typename T>
T twice(T n){
    return n * 2;
}

int main(){
    twice(3);
    twice(3.14);
}

ああ、なるほど、こう書けば、いちいち型を気にする必要はありませんね。

プリプロセッサ

ここもわかりやすい。例えば

_cplusplus 具体的には C++17 ならば 201703L

あたりとか(40.8)。

なんというか書くのが疲れてきた(と思われる)後半に入って、わかりやすい記載になっていくのが興味深い。

ポインタ

著者が自信を持っていうだけあって、わかりやすくはあると思う。

ただ、これも初学者にはどうか?

スマートポインタ

ここもある程度慣れた人にとってはわかりやすいと思う。

スマートポインタ→生ポインタ (36章)あたりは、「へー、そういうことだったんですか!」といたく納得した。

 

(続く)