20190411のJavaに関する記事は9件です。

Gsonはデフォルトではnullなフィールドをシリアライズしない

調べたのでメモ。

題名の通り、GsonはオブジェクトをJSONに変換する際、デフォルトではnullのフィールドをシリアライズしない。

nullのフィールドをシリアライズしたい場合、 GsonBuilder#serializeNulls() を使用する。

サンプルコード

MainActivity.kt
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val data = Data("nonnull", "nullable", null)

        val gson1 = Gson()
        val json1 = gson1.toJson(data)
        Log.d(TAG, json1)
        // => {"nonNullProp":"nonnull","nullableProp1":"nullable"}

        val gson2 = GsonBuilder().serializeNulls().create()
        val json2 = gson2.toJson(data)
        Log.d(TAG, json2)
        // => {"nonNullProp":"nonnull","nullableProp1":"nullable","nullableProp2":null}
    }

    data class Data(
        val nonNullProp: String,
        val nullableProp1: String?,
        val nullableProp2: String?
    )

    companion object {
        private const val TAG = "MainActivity"
    }
}

参考URL

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

Java(OkHttp)でHTTP通信するときのメモ

目的

・Javaで実装された処理から、外部APIとHTTPでやり取りしたい
・bodyにXMLを詰めたい
・実装がなるべく簡単なものがいい

JavaのHTTPクライアントって・・・

デファクトスタンダード的なものはないのでしょうか・・・?
ネット上の記事を参考に、
OkHttp(https://square.github.io/okhttp/)
というライブラリを使ってみました。

<!-- http client -->
<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
  <version>3.7.0</version>
</dependency>

実装
例ではXMLをbodyに詰めてAPIをコール、
APIから返却されるXMLのStringを受け取ってreturnするイメージです。
basic認証もしています。

public String callApi(String xml) throws IOException {
    return new OkHttpClient().newCall(
                new Request.Builder()
                    .url("【APIのURL】")
                    // basic authentication
                    .header("Authorization",
                            Credentials.basic(
                                    "【basic認証のuser】",
                                    "【basic認証のpassword】"))
                    .post(RequestBody.create(MediaType.parse("text/xml"), xml))
                    .build()
            ).execute()
            .body()
            .string();
}

参考

JavaのHTTPクライアントはどれがいいのか?
https://qiita.com/alt_yamamoto/items/0d72276c80589493ceb4

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

【初心者向け】javaのコンストラクタについてまとめ

コンストラクタとは

コンストラクタとは、newで生み出されたインスタンスに自動で情報(フィールド)を渡してくれるものです。

public class Hero {
    String name;
    int hp;
}

このHeroクラスをインスタンス化すると、値の入っていないHero(nameがnullでhpが0)が生成されてしまいますが

public class Hero {
    String name;
    int hp;

    //コンストラクタ
    Hero(){
        this.name = "名前";
        this.hp = 100;
    }
}

こうすることによって値をフィールドに代入できます。
でもこれだと同じ名前と同じHPのHeroしか作ることができません。
魔法使いHP50とか、妖精HP70とかが作れないのです。
なので、自由にキャラクターを作れるようにするためにこのようにします。

public class Hero {
        String name;
        int hp;

        //コンストラクタ
        Hero(String name , int hp){
            this.name = name;
            this.hp = hp;
        }
}

これは何をしているかというと、Hero(String name , int hp)でnameとhpを受け取って、フィールドに代入しているのです。
どこから受け取っているのかというと、mainメソッドから受け取っています。

public static void main(String[] args) {
        Hero h = new Hero("勇者" , 100);
    }

Heroクラスで引数を2つ受け取るコンストラクタを設定したので、呼び出すときも引数を2つ設定して呼び出します。
これが引数1つだとエラーになります。

結果はこうなります。
ちゃんと値が渡されているか確認しましょう。

public static void main(String[] args) {
        Hero h = new Hero("勇者" , 100);
        System.out.println("名前は" + h.name + "です。");
        System.out.println("HPは" + h.hp + "です。");
    }

結果は

名前は勇者です
HPは100です

ちゃんと渡すことができました。

でもこれだと、名前だけを渡したい場合エラーになってしまいます。

public class Main {

    public static void main(String[] args) {
        Hero h = new Hero("勇者");//エラーになる
    }
}

こんな時はHeroメソッドにコンストラクタを追加します。

public class Hero {
        String name;
        int hp;

        //コンストラクタ
        Hero(String name , int hp){   //ひとつ目のコンストラクタ
            this.name = name;
            this.hp = hp;
        }
        Hero(String name){   //ふたつ目のコンストラクタ
            this.name = name;   
            this.hp = 50;
        }
}

コンストラクタは、名前が同じでも、引数の数が違えば複数設定できるのです。
これをオーバーロードと言います。
もしmainメソッドでnewして呼び出すとき、引数にnameとhpがあったらひとつ目のコンストラクタが呼び出されます。
引数にnameだけがあったらふたつ目のコンストラクタが呼び出されます。
このままでもエラーにはなりませんが、渡されるフィールドが増えてきたりすると、修正や可読性が悪くなってしまいます。
なので

this()

を使います。
これを使うと、同じメソッドにあるコンストラクタを呼び出すことができます。

public class Hero {
        String name;
        int hp;

        //コンストラクタ
        Hero(String name , int hp){   //ひとつ目のコンストラクタ
            this.name = name;
            this.hp = hp;
        }
        Hero(String name){   //ふたつ目のコンストラクタ
            this(name , 50);
        }
}

こうなります。
ふたつ目のコンストラクタの中でひとつ目のコンストラクタを呼び出しているのです。
ひとつ目のコンストラクタは引数が2つあるのでthis()の()の中の引数は2つです。
nameはmainメソッドから渡されたものが入り、hpは渡されないので任意のhpを入れます。ここでは50としています。
これで実行すると

public class Main {

    public static void main(String[] args) {
        Hero h = new Hero("勇者");
        System.out.println("名前は" + h.name + "です。");
        System.out.println("HPは" + h.hp + "です。");
    }
}

結果

名前は勇者です
HPは50です

ちゃんと50が渡されてますね。
this()は結構ややこしいですね。ひとつずつ書いたほうが分かりやすいよ!と思ってる方もいると思いますが、そうすると後からフィールドを変更しなければならなくなった時大変になります。

public class Hero {
        String name;
        int hp;

        //コンストラクタ
        Hero(String name , int hp){   //ひとつ目のコンストラクタ
            this.name = name;
            this.hp = hp;
        }
        Hero(String name){   //ふたつ目のコンストラクタ
            this.name = name;   
            this.hp = 50;
        }
}

こんなふうに書いていて、あとからフィールドを変更しなければならなくなると

public class Hero {
        String name1;  //ここをname1に変更しなければならない。
        int hp;

        //コンストラクタ
        Hero(String name , int hp){   //ひとつ目のコンストラクタ
            this.name = name;  //エラーになる。
            this.hp = hp;
        }
        Hero(String name){   //ふたつ目のコンストラクタ
            this.name = name;  //エラーになる。
            this.hp = 50;
        }
}

この場合2か所直さないといけなくなります。
コンストラクタが2つならまだいいですが、これが増えてくると作業が大変になります。
なん十か所も直していたら一か所くらいミスが出てもおかしくありません。

自分がつまずいた部分だったのでまとめてみました。
間違いがありましたら指摘をお願いします。

nkojimaさんご指摘ありがとうございます。
記事の一部を削除しました。
確かに読み返してみたら可読性とは違いましたね。
送っていただいた記事も参考にさせていただきます。

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

クラスタリング環境で発生した検証値エラー

クラスタリング環境で検証エラーが発生する

開発環境

  • JavaSE8
  • JavaEE7
  • Payara5
  • JSF2.2
  • Eclipse Oxygen4.7.2

事象

パラメータによって選択肢が変化するラジオボタンがある。
任意の項目を選択しポストすると「j_idt39: 検証エラー: 値が有効ではありません 」が発生。
ローカルやシングルインスタンス環境だと発生しない。

原因

プロジェクトの要件でスティッキーセッションを使用せず、通信の都度ランダムにサーバに割り振られていた。
そのためセッション内にBeanが複製されまくり、その中には初期値のない(null)のラジオボタンの選択肢リストが存在した。
ポストしたときに運悪くリストがnullのBeanに当たった時に検証エラーが発生していた。

問題のソース

XHTML

<form jsf:id="form" jsf:prependId="false">
            <h:selectOneRadio name="choiceName" value="#{choiceBean.choiceName}" styleClass="radio_list">
                <f:selectItems value="#{choiceBean.choiceList}" var="prize" itemLabel="#{item.labelName}" itemValue="#{item}" />
            </h:selectOneRadio>

        <button jsf:action="#{pageControlBean.toConfirm()}" class="btn_orange">
            次へ
        </button>
    <span jsfc="h:messages" class="form_error_text" />
</form>

Bean

@Named
@SessionScoped
public class ChoiceBean implements Serializable {

    private static final long serialVersionUID = 1L;

    @NotNull
    @Getter @Setter
    private ChoiceType choiceName;

    @Getter @Setter
    private List<ChoiceType> choiceList;
}

Enum

public enum ChoiceType {
    TYPE1("タイプ1", 0),
    TYPE2("タイプ2", 1),
    TYPE3("タイプ3", 2);

    @Getter
    private final String labelName;

    @Getter
    private final int index;

    private ChoiceType(String labelName, int index) {
        this.labelName = labelName;
        this.index = index;
    }
}

解決

コンストラクタで初期値を設定してやる。

Bean

@Named
@SessionScoped
public class ChoiceBean implements Serializable {
    private static final long serialVersionUID = 1L;

    @NotNull
    @Getter
    @Setter
    private PrizeType choiceName;

    @Getter
    @Setter
    private List<ChoiceType> choiceList;

    /**
     * Constructor
     * NotNull変数の初期値設定
     */
    public ChoiceBean() {
        choiceList = new ArrayList<>();
        choiceList.add(ChoiceType.TYPE1);
        choiceList.add(ChoiceType.TYPE2);
        choiceList.add(ChoiceType.TYPE3);
    }
}

調査のコツ

最終的にセッションの中身が見えるIDE(有料版Intellijとか)で調査してbeanの複製が分かった。
JavaEEだとNetBeansでも見れるそうですね。

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

【MySQL+Java】採番プロシージャ

はじめに

MySQL + Java な環境で採番の仕組みを用意する必要があったので、
下記の記事を参考に作ってみました。

MySQL で採番テーブル
MySQL で シーケンス 機能実現
LAST_INSERT_IDを使って採番テーブルを扱う

OracleのシーケンスがMySQLにもあればなー。

環境

MySQL 5.7
Java 8

やったこと

  • 採番用のテーブルを用意する。×2
  • 採番プロシージャを用意する。×2
  • Javaで検証する。

採番用テーブル

テーブル1
CREATE TABLE `seq1` (
  `prefix` varchar(8) NOT NULL,
  `id` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
テーブル2
CREATE TABLE `seq2` (
  `prefix` varchar(8) NOT NULL,
  `id` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8

それぞれ1レコードずつ登録しておく。

採番プロシージャ

プロシージャ1
CREATE DEFINER=`trial`@`localhost` PROCEDURE `nextval_seq1`(out seqnum varchar(12))
BEGIN
    START TRANSACTION;
    update seq1 set id = LAST_INSERT_ID(id + 1);
    select CONCAT(prefix, LPAD(LAST_INSERT_ID(id), 10, '0')) into seqnum from seq1;
    COMMIT;
END
プロシージャ2
CREATE DEFINER=`trial`@`localhost` PROCEDURE `nextval_seq2`(out seqnum varchar(12))
BEGIN
    START TRANSACTION;
    update seq2 set id = LAST_INSERT_ID(id + 1);
    select CONCAT(prefix, LPAD(LAST_INSERT_ID(id), 10, '0')) into seqnum from seq2;
    COMMIT;
END

検証Javaコード

以下の観点で検証コードを実装しました。

  • マルチスレッドでも問題ない事
  • プロシージャを利用してもコネクションが死なない事
  • 複数の採番用テーブル、採番プロシージャでちゃんと?採番される事
    private void execute(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(100);
        try {
            List<Callable<Boolean>> taskList = new ArrayList<>();
            for (int i = 0; i < 50; i++) {
                taskList.add(() -> callProcedureAndUpdatePrefix1());
                taskList.add(() -> callProcedureAndUpdatePrefix2());
            }
            pool.invokeAll(taskList);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            pool.shutdown();
        }
    }

    private boolean callProcedureAndUpdatePrefix1() {
        try (Connection conn = ConnectionManager.getConnection()) {
            conn.setAutoCommit(false);
            callProcedure1(conn);
            updateSeq1(conn);
            conn.commit();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    private boolean callProcedureAndUpdatePrefix2() {
        try (Connection conn = ConnectionManager.getConnection()) {
            conn.setAutoCommit(false);
            callProcedure2(conn);
            updateSeq2(conn);
            conn.commit();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    private void callProcedure1(Connection con) throws SQLException {
        try (CallableStatement cs = con.prepareCall("call nextval_seq1(?)")) {
            cs.executeQuery();
            System.out.println(cs.getString(1));
        }
    }

    private void callProcedure2(Connection con) throws SQLException {
        try (CallableStatement cs = con.prepareCall("call nextval_seq2(?)")) {
            cs.executeQuery();
            System.out.println(cs.getString(1));
        }
    }

    private void updateSeq1(Connection con) throws SQLException {
        boolean b = (new Random()).nextInt(99) % 2 == 0 ? true : false;
        String sql = "update seq1 set prefix = " + (b ? "'XA'" : "'XB'");
        try (Statement st = con.createStatement()) {
            st.execute(sql);
        }
    }

    private void updateSeq2(Connection con) throws SQLException {
        boolean b = (new Random()).nextInt(99) % 2 == 0 ? true : false;
        String sql = "update seq2 set prefix = " + (b ? "'YA'" : "'YB'");
        try (Statement st = con.createStatement()) {
            st.execute(sql);
        }
    }

検証結果

XA0000000001
YA0000000006
YA0000000004
XA0000000006
XA0000000005
XA0000000002
XA0000000004
XA0000000007
YA0000000003
YA0000000005
XA0000000019
XA0000000018
YA0000000012
XA0000000013
YA0000000008
YA0000000011
YA0000000007
YA0000000015
YA0000000013
XA0000000003
XA0000000012
XA0000000016
XA0000000015
YA0000000014
XA0000000014
XA0000000017
YA0000000009
YA0000000010
YB0000000022
XB0000000023
XB0000000022
YB0000000021
YB0000000019
YA0000000018
YB0000000020
YA0000000016
YB0000000023
XA0000000020
YA0000000017
XA0000000021
XA0000000025
XB0000000026
XA0000000024
XA0000000011
XA0000000008
YA0000000001
XA0000000010
XA0000000009
YB0000000029
YB0000000026
YB0000000028
YB0000000030
YA0000000002
YB0000000024
YB0000000025
YB0000000027
YA0000000034
YA0000000037
YA0000000031
YA0000000032
YA0000000033
YA0000000035
YA0000000038
YA0000000039
YA0000000036
YA0000000043
XA0000000027
XA0000000029
XA0000000035
YA0000000041
XA0000000034
YA0000000042
YA0000000040
XA0000000030
XA0000000028
XA0000000036
XA0000000033
XA0000000032
XA0000000031
XB0000000045
XB0000000046
XB0000000041
XB0000000047
XB0000000048
XB0000000038
YB0000000050
XB0000000037
XB0000000050
YA0000000045
YB0000000047
YA0000000044
XB0000000042
XB0000000043
XB0000000044
XB0000000049
XB0000000039
XB0000000040
YB0000000048
YB0000000046
YB0000000049

以上

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

android PreferenceFragmentCompatのEditTextPreferenceを改行させないようにする方法

なかなか調べても出てこなかったので、
EditTextPreferenceで改行ができてしまい、すこし操作性が悪かったので
改行をできなくするようにする方法です。

というよりはDialogで呼ばれるEditTextのレイアウトを指定する方法です。

preferences.xml

<EditTextPreference
      android:dialogLayout="@xml/prefernce_custom_edittext"
      android:key="userName"
      android:title="ユーザー名" />

android:dialogLayoutを設定します。

prefernce_custom_edittext.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <EditText
        android:id="@android:id/edit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="18dp"
        android:paddingRight="18dp"
        android:inputType="text|textNoSuggestions" />

</LinearLayout>

これで普段扱っているように自由にカスタマイズできます。

スクリーンショット 2019-04-11 14.24.58.png

改行できない!よかったよかった!

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

はじめました(•ө•)

講義用です
はじめろって言われたんではじめました

よろしくおねがいします

Qiitaタグ必須なのめんどいですね

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

JMeterのインストール方法 for Mac

MacでJMeterのインストールとアップデート - ブロックチェーンエンジニアとして生きる

記事の通り、Homebrewでインストールするのが簡単で良い。

    # インストール
    $ brew install jmeter

    # 実行
    $ jmeter

JDKをまだインストールしていない場合

Java8じゃないと動かないという記事(MacにJMeterを入れる - Qiita)があるがJava12で動いたので最新Javaで問題なさそう(2019/04/11現在)。

homebrew-cask経由でインストール

複数バージョンのJavaを設定ファイルの書き換えで切り替えできるのでおすすめ。

MacのBrewで複数バージョンのJavaを利用する - Qiita
homebrew-caskって何??? - Qiita

インストーラーでインストール

Java SE - Downloads | Oracle Technology Network | Oracle

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

switch文のフォールスルー (fall through)を活用してenumのマッピングをシンプルに書く

ある処理結果(5択)のenumをバッチ結果のenum(2択)にマッピングしたいとき

switch (result) {
  case WAITING_RETRY:
    return BatchResult.SUCCESS;
  case SUCCESS:
    return BatchResult.SUCCESS;
  case SUCCESS_WITH_ZERO:
    return BatchResult.SUCCESS;
  case REQUESTING:
    return BatchResult.FAILURE;
  case PROCESSING:
    return BatchResult.FAILURE;
  case FAILURE:
    return BatchResult.FAILURE;
  default:
    return BatchResult.FAILURE;
}

switch文を利用して上のように書ける。
しかしなんだか冗長。enum定義が増えたとき、見通しが悪くなる。

switch (result) {
  case WAITING_RETRY:
  case SUCCESS:
  case SUCCESS_WITH_ZERO:
    return BatchResult.SUCCESS;
  default:
    return BatchResult.FAILURE;
}

上記のように「2択の分岐である」ことを明示すれば、
resultのenum定義が増えて分岐漏れしても比較的安全。
さらに、増えた定義が FAILURE にあたるものだった場合、ここのコードは変更しなくて済む。

フォールスルーで危険なのは、見通しの悪さから分岐漏れが生じて予期せぬ挙動になること。
「書きやすいし見やすいなら使う」。(もちろん、現場のコーディング規約は守る)

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