JakartaEE 各仕様まとめ

JakartaEE 関係の記事が増えてきたような気がするので、ここら辺でまとめ。

JakartaEE 各仕様

各仕様毎に軽めの記事を書いてきたつもりだったが、読み返したら微妙にオーバーラップしてる。

JAX-RS

ものぐさ Jersey』WildFly は RestEasy だが、GlassFish, Payara は Jersey なので取り上げた。が、最後の方で Jackson 成分も混じっている。

JAX-RS と JSON と List と hibernate』これも、REST の話から始まって、JPA(hibernate) につながっている。

JPA

実は JPA の仕様は資料にあたったことはない。実務的には hibernate でデータベース操作ができればいいでしょう、というわけで書いたのが

最も簡単な hibernate のサンプル

ここら辺から、「読みやすい」と言われるようになってきた。

CDI

初学者泣かせの CDI 。

CDI と weld の関係

で取り上げたが、なくてもなんとかなるので、無理して @Inject とかしなくていいかも。

つか、参考書の類で CDI の説明でわかりやすいのがないと思う。初学者にいきなり「依存性を注入して…」とか言ってもなんのことかさっぱりわからないと思う。

JAXB

Java と XML -JAXB の話-』理解があやふやだったので、自分の勉強のために書いた記事。実際、勉強になった。


他にもあるが、代表的なのはこんなところでしょうか。

 

 

Java と XML -JAXB の話-

これまで、Java ⇄ JSON みたいな話はしてきた。

このご時世、JSON 大流行りなので、大抵はこれで方が付くんだが、もちろん情報交換のフォーマットとして今でも XML フォーマットは使われている。

ここら辺は「さすが Java」という感じなのだが、Java ネイティブ型 ⇄ XML の変換を取り決めている仕様が存在する。

それが Java Architecture for XML Binding というやつで、JAXB などと呼称されている。
読み方は「ジャックスビー」と思い込んいたが、正式には「ジェイエックスビー」らしいです。

必要性は低くなってきているが、無視はできない

ところで、正直言わせてもらえれば、JAXB の話をするのはあまり気乗りがしていない。

というのは、これまで、この仕様を使わなくてもなんとかなってきたから。

XML を解析するだけであれば、

・jdom(2) を使って解析→コレクション(list や map)を用いて手動で適宜後処理

という手法でも実務上あまり困らないと思う。

これはワイに限ったことではなく、JAXB が Java の正式仕様から外されたり復活したりしている理由は背景にこういった状況があるからだろう。

時代は良くも悪くも JSON 形式なのだ。

ただ、少々汚い形式の XML 文書を Java のオブジェクトとしてプログラム内に取り込んで自由自在に扱うというのは「映え」はする。

なんか釈然としないが、気を取り直して・・・。

準備

マッピングすべきクラスのフィールド変数が基本データ型だけで構成されていればそんなに難しいことはないんだが、配列や list が含まれていると難易度が上がる。
印象としては xml では、要素のまとまりなどは配列的に使われていることが多いと思うが、Java などではこれを list として取り扱いたいということが多いと思う。

配列⇄List の相互変換

Java では、配列と List を相互変換できる。

・配列(Array)→ List のときは Arrays.asLIST

・List → 配列(Array)のときは toArray

を使うのが原則。

List<String> items (= [item1, item2, item3]) があったとき、これを配列 String[] array にしたければ

String[] array = items.toArray(new String[items.size()]);

とする。

逆に array を items に戻したければ

List<String> items = Arrays.asList(array);

とする。

ただし、以下のサンプルコードが示すようにこの小技はあまり使う必要がないかも。

サンプルコード

えいやっと書いてしまった。

Parent.java
public class Parent {

private int id;
private String name;
private List<Child> children;
(以下、セッターゲッター)

せっかく配列や List の話もしたので、Child.java も設定。


public class Child {

private String name;
(以下、セッターゲッター)

この状態でオブジェクトに適当な値を設定。

JAXB.marshal(parent, System.out);

などとすると標準出力に以下のような xml 文書を吐き出してくれる。


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<parent>
<id>1</id>
<name>アグネスタキオン</name>
<children>
<name>ダイワスカーレット</name>
</children>
<children>
<name>ディープスカイ</name>
</children>
</parent>

逆に XML→ Java オブジェクトにしたければ、

JAXB.unmarshal(new StringReader(xml), Parent.class);

などとすればいい。

食わず嫌いでした

実際に手を動かすと意外に使いやすい。

ジャクソンの場合、手を入れる必要がある箇所でも marshal/unmarshal で済んでしまう。

これは食わず嫌いだったかな。

 

参考記事

https://qiita.com/opengl-8080/items/f7112240c72d61d4cdf4

 

 

もっとも簡単な 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 はなぜかそういうセンスのない人の言動を「あの人が言ったから」ということで重宝がる傾向があるようだ。

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

 

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/

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

 

「プロになる Java」を読んでみる

けっこう評判がいいようなので『プロになる Java』という本を読んでみた。

悪い本ではないが、これでプロになれるかというと「あれ?」という感じ。

どちらかといえば初学者向けの本だと思う。あるいは(Java8)以前に Java をかじったことはあるが、最近触ってない人向け。

内容も

文法
→swing を使ったデスクトップアプリのサンプル
→spring によるウェブアプリのサンプル

という風に進んでいくので、どちらかといえば教科書的実習書みたいな構成になっている。

一昔前に栄華を放った Java8 と現在標準になっている Java17 では、文法面で変更・追加されたところがあるので、そこらへんのチェックにはいいと思う。
Java17 に対応した網羅的な教科書はほとんどないので。

都市部でフリーランス

ところで「秋葉ちゃん」アカの中の人は複数人いる(複数人による共同管理。ただし、記事書いたりするのは特定の人に限られる。まあ慣れた人が書くのが一番)。
比較的若い年代が多いと思うが、関与したプロジェクト数はけっこうあると思う。

で、ここらへんのキャリアの人が思い浮かべる「プロ」というのは

プロジェクトにフリーで参加して
それなりにアウトプットを出し
トラブルなく対価もらえる人

が定義になるだろうか。

実際、これができれば、都市圏なら食うには困らない。

その一方で、プロジェクトの途中で失踪する人、メンタル病んで戦線離脱する人・・・も数多く見てきたw

その差はどこで生まれるのだろうか?

生き残れる人とそうでない人の差は、プログラミングに関する知識というよりは「ロジックを生み出せるかどうか」によるんではないかというのがワシらのおおよその見解だ。

(こちらは了)

延長戦的雑談

ところで、この書評?は、本意とは全然違うのだが、最後の方の業界話の方が評判はいいようだ。

というわけで、続きを書こう。

確かに、あまり能力に恵まれているとはいえないが、それでもこの業界にしがみつくしかない人たち、というのは一定数いる。

個人的には(特に意識してやっているわけではないが)ネット上でそういった人たちの言動などをチェックしたりもしている。たぶん、ここここで取り上げられている人たちの一人だと思うんだが、そういった監視の対象になっている人もいる。
ここでは、特定されると嫌なので、そういった人たち複数人の特徴を混ぜて書く。

なんでそんなことをするかといえば、理由は、単純にその言動が馬鹿馬鹿しくて興味深いから。
それと、今すぐというわけではないが、将来的に、そういった人たちが絡んできた時にうまくかわしたいから、というのもある。

まず、こういった一種の炎上商法に加担しているような人たちだから、この業界でやっていくための本質的に何かが欠けている。

なのだが、体面を保つためなのか、すごく自慢げに「こんなことできました」、「今は、これをやっている」、「だから、俺、スゴいでしょ」的な発信がうざいくらい多い。

けれど、よく読むと、出来合のソフトをインスコしましたってだけなんだよね(笑)。

どこがスゴいのだろうか?

そんなんだから、誰も注目していない。

閲覧数やシェア数が悲惨なまでに少ない。
閲覧が 20 にも満たない時がほとんどで、悪い時は一桁。
たぶん、本人しか見ていないwww

通常の感覚なら、ネット上での活動を止めるか抑え気味にして、リアルでの活動を頑張るところだろうが、本人は一向に止める気配がない。

リアルで承認欲求が満たされないから、ネットで、というよくある話なんだろうが、それだけでは収まらない部分もある。

他罰的な性格傾向

他者からの認められたいと思う時、健康な職業人ならば「他人ができないような+α を出す」とか自分を高めるように努力するものだと思うが、もちろん、こういった人たちはそんな努力はしない。

想像はつくとは思うが「周囲を下げる」といういかにもな行動をとる。しかも過剰なまでに。

また、こういう行動パターンが続けていることから分かるように、地方在住者が多い。
ちなみに都市部だと、まず、この行動パターンは取れないと思う。下げてしまった「周囲」からの直接的な反発は当然あるし、プロジェクトのリーダークラスがこういった人をメンバーから外すので、仕事が途切れて淘汰されてしまうから。

 

(こちらは続く)