20200115のJavaに関する記事は10件です。

Javaプログラマ(4年目)が今まで買った技術書の棚卸しとオススメ度

初投稿です

前々から投稿したかったけど、怖くてできませんでした。慣らすためにとりあえず書こうと思いました。

買った本の棚卸し

買った本の個人の棚卸し的目的も含むため、特定の用途でまとめての紹介ではございません。
ほぼ全て紙の本です。自分は技術書って電子書籍だと読みにくい
また完全に個人の感想です。

あとなんか画像勝手に持ってくるのはいやだし、Amazonのリンク貼るのもいやだし、自分の撮った写真は汚いしで面倒になったので本の画像はなしでお送りします。

★=1点
☆=0.5点

1.リーダブルコード

オススメ度:★★★

言わずと知れた超有名な本。
新人の頃に初めて先輩に勧められて、とりあえず買ってワケ分からなくて途方にくれた思い出深い逸品。もはや御守り的にカバンに入れて持ち歩くような存在。

中身に関しては新人にはハードルが少し高く感じた。「このように書くと良いよ」を紹介してくれるのはすごく良いのだが、「もっとJavaとして書くときの書き方を教えてくれよ!」と当時は思っていた。

ただなんだかんだ書いてある内容は今も実践しているつもりだし、かなり良い本。

2.ベタープログラマ ―優れたプログラマになるための38の考え方とテクニック

オススメ度:★★

プログラマの自己啓発書。
まじでなんで買ったのか忘れたけど、良い買い物だった。

いろんな観点でこういう心構え・考え方だと良いという話が大半で、「どうしてテストするの?」だとか「どうしてコードの見た目に気を使うのか?」とか「システム開発ってなんだろう」みたいなのもある

個人的に一番気に入っているのは「第3部 個人的なこと」のところで、モチベーション維持だとか、学び方・停滞するなよって話の部分
「現在働いている場所では成長しておらず、チャレンジもないなら転職した方が良い(要約)」という部分はなんかすごく刺さった。

ただまぁものすごく翻訳が独特。リーダブルコード以上に英語っぽい言い回しで、ぶっちゃけ読んでて疲れるのだけはいただけない。
そういう意味で星2つ。なんかモヤモヤするときに読むといいかも

3.初めての自動テスト ―Webシステムのための自動テスト基礎

オススメ度:★★☆

現場やベタープログラマを読んで、自動テストやりたいマンになったため購入

現状生かせてないけど、内容は丁寧かつ読みやすかった。
紹介できる内容を書けない理由は「ほぼ覚えてないから」
やっぱり実践しないと忘れますね。

ILoveBossっていうのがサンプルコードでよく出てきたのだけは異様に頭に残っている。
Amazonのレビュー見た方が良い。

4.はじめよう!要件定義

オススメ度:★★☆

はじめよう!シリーズの1冊目。
他に「はじめよう!システム設計」「はじめよう!プロセス設計」も買ったけど、まだ読んでない。

「要件定義とかシステム設計ってなんだろうね(どうやって説明すればいいんだろうね)」って先輩に聞かれて答えられなかったので購入。

絵本チック(絵が柔らかくて可愛い)で説明も読みやすい。そして、「現実的にはこの本に書いてある内容全然やってないよね?」とめっちゃ思った。
やれば良いことはわかったけど、現実に反映するのが難しい。っていうかやってくれる気がしない。
自分が要件定義もやるかーという気分になってくきている。

新人・ど素人(こっちに仕事依頼する側)に読んで欲しいと思った。

5.カイゼン・ジャーニー たった1人からはじめて、「越境」するチームをつくるまで

オススメ度:★

先輩に「まさみち君この本に出てくる人に似てる」みたいなこと言われたので購入

あんまり興味のある領域ではなかった。けど、刺さる人には刺さる。Amazonレビューで圧倒的に好評
リーダーなどを目指す人は買って良いと思う

チームメンバーを巻き込んでチームそのものを改善する。みたいな内容で、ストーリー仕立てで改善の仕方を紹介・提案されているのはわかりやすかった。

ただまぁちょっと読んでてゾワゾワした。武勇伝読んでる気分(笑

あまり今の給料でチームを改善したいとは思わないタイプなので、自分一人でできる一人朝会とタスクの見える化だけ行った。

1〜2ヶ月やったけど、よくなったという実感がもてなかったため今はやめた。でもやっぱりやめたらやめたでやった方がいけてるんじゃ・・・という気分にもなってきている。

6.Java言語で学ぶデザインパターン入門

オススメ度:★★

デザインパターンを学ぶ上ですごい参考になった。
XXBuilderとかXXFactoryとかってそう言うことだったのか!って理解できたので疑問が解消されたのと、この本のおかげで抽象クラスやインターフェースの使い方、ジェネリクスがすごく使えるようになったと思える。
ただまぁBuilderとFactory系以外あんまみたことない。Adapterくらいかな
(Singletonは出てくる頻度がダンチだからおいといて)

7.オラクル認定資格教科書 Javaプログラマ Gold、 Silver

オススメ度:★★(受験するなら:★★★)

いわゆる紫本。

Javaの人間なら買った方が良いとは思うが、すでに持っていて読み終わったり資格とり終わった人がいるなら借りて済ませても良い。
自分は最終的には辞書がわりに使うことになってきたので人に貸せない。

内容自体はAmazonのレビューにある通りよくて星4だけど、「どこまで覚えたらどのレベルか」を把握できるのは非常に良い。
ただし必ず版数は確認して最新版を買うこと

Silverは11も出ている。Silver 8と11のページ数比べたら100ページくらい違った(笑

8.徹底攻略 Java SE 8 Gold,Silver 問題集

オススメ度:(受験するなら:★★★)

いわゆる黒本。

Goldとるときにはお世話になった。紫本+黒本2週ずつでGold取れたよ。
Silverの時はほぼ一切使わなかったからGoldも使わずに行けるかと思ったけど流石にそんなことはなかった。

微妙に紫本で出てない内容が出てきたりするから面食らう時もあった。

9.Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発

オススメ度:★★

先輩が持ってた本。読ませて見てもらって良かったので購入

ただし入門というには分厚すぎるし難しい
Springを触ったことがある。かつ少し深入りする必要が出たときに辞書的に使える。
浅く広くの辞書的な意味合いでは非常に良いと思えた。
Springを使ったプロジェクトに1冊あると安心できるかな?

以下参考
令和時代に「Spring入門」「Spring徹底入門」を読むとき気をつけるべきN個のこと
https://qiita.com/suke_masa/items/392976749fce94a8ef1f

やっぱ今時コンストラクタインジェクションだよねぇ・・・

コンストラクタインジェクションが推奨されている理由を自分なりにまとめてみる
https://qiita.com/jackazu/items/aead50c699fefe56c120

10.現場至上主義 Spring Boot2徹底活用

オススメ度:★★

Spring Bootの本。
うまく伝えられないが、実際に個人で忘年会用のwebアプリを作るときには参考になった。
ただまぁ、現場至上主義かどうかは疑問。というよりも新人の研修資料として優秀かも

先輩が新人向け研修資料の参考にする。もしくはSpring Boot触ったことない人間が参考にする。という場面で使うものに見える。
Amazonの少ないレビューが賛否両論すぎて反応に困る逸品。

11.Javaで学ぶ自然言語処理と機械学習

オススメ度:なし

Javaで!自然言語処理かつ機械学習!?ということでタイトル書い

ぶっちゃけむっずい!チラッと読んでキッツゥ!ってなった。
あとほぼ全部手実装で紹介してる。すごい。
Javaとはいえ、ゼロから作るDeepLearning読んでる気分になった。

12.Deep Learning Javaプログラミング 深層学習の理論と実装

オススメ度:なし〜★
JavaでDeepLearningを理解するための本かつDeepLearning4jの入門チックな内容が乗っている本

Deep Learningまじでやりたいけど、Java以外の言語に手を出すとアレルギーで死んじゃう!って人・・・いる?

ただしそもそもDeep Learning系の本自体が難しいのでその例に漏れない。(ゼロから作るDeepLearningでわかりやすいってまじかよ的な人間の感想)

13.Java データ構造とアルゴリズム基礎講座

オススメ度:なし
「プログラマたるものアルゴリズムわかってないよだめよ」的なことをどっかで聞いたので購入

アルゴリズムとはなんぞやからわかったので良かったけど、「Javaだと既にAPIで実装されているからここで紹介したコードを実際に使うことはないと思うよ(要約)」って書いてあって「えっ」ってなった。

ある意味そのレベルの人間だったというのがわかった意味では非常に良かったが、今調べたら出版してるの2009年でビビる。古書じゃないですかー

14.作って動かすALife ―実装を通した人工生命モデル理論入門

オススメ度:判定不能
ALifeって何?人工生命って漫画に出てきたかっこいいやつじゃん!実装できるの!?
ってなったので購入

漫画に出てくるような人工生命の実装の紹介ではなく、「コンピュータを使った生命の本質とは何かを追い求める歴史と実装の紹介」がメイン。

初めて読み終わったあとは「DeepLearningとかよりも圧倒的にすごいじゃん!人類の未来は明るい!みんなにオススメしたい!」と凄まじくハイテンションだったのだが、もう一度読み直したら全くそんなテンションにならなかったため、オススメ度は判別不能とします。まじでなんだったんだ

この技術と機械学習とかをうまく組み合わせたらすごいのできそうとは思わなくはないけど、ちょっと先取りしすぎ感はあるかなぁと

15.ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

オススメ度:★
流行ってるから買おう!興味あるし→絶望

数学苦手な人は割と発狂すると思います。(体験談
読み返したら書き込みしてて哀愁を誘いました。

入門書ではあるがそもそもの内容が難しい。その点だけしっかりと頭に入れて買うと良い。

この本のおかげで「さっさと適当にGithubにあるプロジェクト持ってきて動かしてから理解しよう」という気分になれて、かつ多少は概念的なものとか調べ方とかの検討は付けられるようになったので買って無駄ではなかった。

16.SQL逆引き大全363の極意

オススメ度:判定不能
もらいもののため判定なし

SQLを覚える気が皆無のため、SQL書くときは常にこれを片手にやっている。
なんでこんなに覚える気ないんだろうか。自分でも不思議。
他のSQLの本がないから比較できないけど、自分的には必須。
いい加減覚えろと言われると反論できない。

17.深層学習教科書 ディープラーニング G検定(ジェネラリスト) 公式テキスト

オススメ度:★
勘違い?して購入

知識としては面白いけど、実装方面の話は載ってない。
というかG検定が実装とかじゃなくて事業への活かせるようにと言う目的の試験らしい
みなさんは買う前にちゃんと調べましょう

まじで教科書。読み物としては面白かった。暗記したいかは微妙

18.プログラミング言語図鑑

電子書籍のため写真なし
オススメ度:★★
息抜きの読み物として購入。面白かった。
そもそもこんなにプログラミング言語なんてあるんだと言うことで感動。
後、Rubyが日本産の言語だなんて知らなかったよ。ごめんなさい。

特にお気に入りなのが「Brainf○ck」。もはや暗号である。

買ってちゃんと読んだのは以上

以下買ったけど読んでないもの

1.「Docker/Kubernetes 実践コンテナ開発入門」
Docker使ってはみた。本は読んでない。Docker使えない奴はヤベーヨって記事を見かけたので購入。
会社のPCでなぜかbiosの起動がどうやったらいいかわからないため日の目をみない。ヤベーヨ
Spring FestでGoogleの人がjibを紹介してくれたので、Docker使ってみる気になれた。jibすごい
2.「テスト駆動開発」
テスト駆動開発自体は宗教みたいな意見もあったので買ってみたけど、そもそも自動テストを自体をまもに勉強できてないから先にそっち勉強しないと
3.「アジャイルサムライ−達人開発者への道−」
カイゼンジャーニーに出てきたので買っといた。暇ができたら読む。

その他紹介するのもめんどいのが計15冊。主にオライリーの機械学習関係と資格の本。
一時期オライリーの本を集めるのにハマってた遺産

読んでない本多すぎる!

間違ってるところあったら言ってください。

以上、よろしくお願いいたします。

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

JUnitでprivate scopeなものをテストする方法

private scopeなものをテストする

Javaでprivate scopeのフィールド、メソッド、内部クラスを外部から呼び出し、
JUnitでテストしてみます。
※何番煎じか、出涸らしかという記事です。

まず、JUnitでテストしようとしてみます。

error.png

コンパイルエラーです。仕様です。実行できません。
privateなので、publicと同じようには呼び出せません。

そこでReflectionを使ってprivate scopeなものを呼び出します。

Reflectionとは

Reflectionとは、Javaの標準ライブラリの一種で
クラスそのものの情報を扱うことです。
具体的な例として、
クラス名、メソッド名、フィールド名の文字列で
そのクラスを生成、メソッド実行、フィールドへのアクセスが
できたりします。

以下、外部サイトですが、例えばこんな感じです。
http://java.keicode.com/lang/reflection.php

「こんなに便利なら積極的に使おう」
と思う方もいるかもしれません。
ですが、Reflectionは諸刃の剣です。取扱厳重注意です。
しかし、敢えて紹介します。

Reflectionの問題点

Reflectionは使い方次第で

  • クラス設計が崩壊する
  • コードが書きにくいし読みにくくなる
  • パフォーマンスが普通にメソッドを呼び出すより悪い

など、問題点もあります。
しかし、敢えて紹介します。

「JUnitでprivate scopeなものをテストする」
という限定した目的で使うためです。

本題に戻って

さっきのエラーだらけのJUnitのテストクラスを直してみます。

package jp.co.illmatics.sample;
/**
 * テスト対象のクラス
 */
@SuppressWarnings("unused")
public class PrivateSample {

    private String field = "Hello field!";

    private final String finalField = "Hello final field!";

    private String doIt()    {
        return "Hello method!";
    }

    private class InnerClass {
        public InnerClass() {

        }
        public String getInnerMethod() {
            return "Hello inner method!";
        }
    }
}
package jp.co.illmatics.sample;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.junit.Test;

import junit.framework.TestCase;

/**
 * テストクラス
 */
public class PrivateSampleTest extends TestCase {

    PrivateSample privateSample = new PrivateSample();
    String expected = "";
    String actual = "";

    @Test
    public void testField() throws Exception {
        final String fieldName = "field";
        expected = "Hello field!";
        actual = (String) getFieldValue(privateSample, fieldName);
        assertEquals(expected, actual);
    }

    @Test
    public void testFinalField() throws Exception {
        final String finalFieldName = "finalField";
        expected = "Hello updated final Field!";
        actual = (String) getUpdatedFinalFieldValue(privateSample, expected, finalFieldName);
        assertEquals(expected, actual);
    }

    @Test
    public void testMethod() throws Exception {
        final String methodName = "doIt";
        expected = "Hello method!";
        actual = (String) getMethodValue(privateSample, methodName);
        assertEquals(expected, actual);
    }

    @Test
    public void testInnerClassMethod() throws Exception {
        final String innerClassName = "jp.co.illmatics.sample.PrivateSample$InnerClass";
        final String innerMethodName = "getInnerMethod";
        expected = "Hello inner method!";
        actual = (String) getInnerClassMethod(privateSample, innerClassName, innerMethodName);
        assertEquals(expected, actual);
    }

    private Object getFieldValue(PrivateSample privateSample, String fieldName)
            throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException,
            SecurityException {
        return getPrivateField(privateSample, fieldName).get(privateSample);
    }

    private Field getPrivateField(PrivateSample privateSample, String fieldName)
            throws NoSuchFieldException, SecurityException, IllegalArgumentException,
            IllegalAccessException {
        Class<?> clazz = privateSample.getClass();
        Field field = clazz.getDeclaredField(fieldName);
        field.setAccessible(true);
        return field;
    }

    private Object getUpdatedFinalFieldValue(PrivateSample privateSample, String newValue,
            String fieldName) throws NoSuchFieldException, SecurityException,
                    IllegalArgumentException, IllegalAccessException {
        Field finalField = getPrivateField(privateSample, fieldName);
        finalField.set(privateSample, newValue);
        return finalField.get(privateSample);
    }

    private Object getMethodValue(PrivateSample privateSample, String name)
            throws NoSuchMethodException, SecurityException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException {
        Method doIt = PrivateSample.class.getDeclaredMethod(name);
        doIt.setAccessible(true);
        return doIt.invoke(privateSample);
    }

    private Object getInnerClassMethod(PrivateSample parent, String classFullName,
            String methodName) throws ClassNotFoundException, NoSuchMethodException,
                    SecurityException, InstantiationException, IllegalAccessException,
                    IllegalArgumentException, InvocationTargetException {
        ClassLoader loader = ClassLoader.getSystemClassLoader();
        Class<?> innerClazz = loader.loadClass(classFullName);
        Constructor<?> constructor = innerClazz.getDeclaredConstructor(parent.getClass());
        constructor.setAccessible(true);
        Object innerObj = constructor.newInstance(parent);
        return innerClazz.getDeclaredMethod(methodName).invoke(innerObj);
    }
}

resolved.png
直せました。

testStart.png
テストします。

testSucceeded.png
できました。

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

NLP4J [007] で Kuromoji を利用する Annotator を作成する

形態素解析のモジュールを使い分ける

NLP4J では標準(nlp4j-core)においてYahoo!デベロッパーネットワークの形態素解析処理を利用しています。

テキスト解析:日本語形態素解析 - Yahoo!デベロッパーネットワーク
https://developer.yahoo.co.jp/webapi/jlp/ma/v1/parse.html

Yahoo!デベロッパーネットワークのAPIはHTTPで呼べるので便利ではありますが、回数制限があるという弱点もあります。
そこでローカルでも使える kuromoji を利用するライブラリを作成することにします。

Annotator の作成

今回は nlp4j プロジェクトのサブモジュール(sub module)として nlp4j-kuromoji を作成しました。

nlp4j-kuromoji
https://github.com/oyahiroki/nlp4j/tree/master/nlp4j/nlp4j-kuromoji

Code

NLP4J が提供する nlp4j.DocumentAnnotator インターフェイスを継承(implement)します。
kuromoji で抽出したキーワードをNLP4Jで用意しているキーワードにマップしています。

package nlp4j.krmj.annotator;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.atilika.kuromoji.ipadic.Token;
import com.atilika.kuromoji.ipadic.Tokenizer;
import nlp4j.AbstractDocumentAnnotator;
import nlp4j.Document;
import nlp4j.DocumentAnnotator;
import nlp4j.impl.DefaultKeyword;

/**
 * Kuromoji Annotator
 * @author Hiroki Oya
 * @since 1.2
 */
public class KuromojiAnnotator extends AbstractDocumentAnnotator implements DocumentAnnotator {
    static private final Logger logger = LogManager.getLogger(KuromojiAnnotator.class);
    @Override
    public void annotate(Document doc) throws Exception {
        Tokenizer tokenizer = new Tokenizer(); // kuromoji のインスタンス
        for (String target : targets) {
            Object obj = doc.getAttribute(target);
            if (obj == null || obj instanceof String == false) {
                continue;
            }
            String text = (String) obj;
            List<Token> tokens = tokenizer.tokenize(text);
            int sequence = 1;
            for (Token token : tokens) {
                logger.debug(token.getAllFeatures());
                DefaultKeyword kwd = new DefaultKeyword(); // 新しいキーワード
                kwd.setLex(token.getBaseForm());
                kwd.setStr(token.getSurface());
                kwd.setReading(token.getReading());
                kwd.setBegin(token.getPosition());
                kwd.setEnd(token.getPosition() + token.getSurface().length());
                kwd.setFacet(token.getPartOfSpeechLevel1());
                kwd.setSequence(sequence);
                doc.addKeyword(kwd);
                sequence++;
            }
        }
    }
}

使い方

Annotator のクラス指定を変更する以外はYahoo!デベロッパーネットワークと同じになります。
別々の自然言語処理である kuromoji と Yahoo!デベロッパーネットワークの自然言語処理をWRAPしていることになります。

    public void testAnnotateDocument001() throws Exception {
        // 自然文のテキスト
        String text = "私は学校に行きました。";
        Document doc = new DefaultDocument();
        doc.putAttribute("text", text);
        KuromojiAnnotator annotator = new KuromojiAnnotator(); // ここだけ変更してモジュールを差し替え可能
        annotator.setProperty("target", "text");
        annotator.annotate(doc); // throws Exception
        System.err.println("Finished : annotation");
        for (Keyword kwd : doc.getKeywords()) {
            System.err.println(kwd);
        }
    }

結果

結果は以下のようになりました。
自然言語処理ライブラリの実装を意識することなく利用することができました。

Finished : annotation
私 [sequence=1, facet=名詞, lex=私, str=私, reading=ワタシ, count=-1, begin=0, end=1, correlation=0.0]
は [sequence=2, facet=助詞, lex=は, str=は, reading=ハ, count=-1, begin=1, end=2, correlation=0.0]
学校 [sequence=3, facet=名詞, lex=学校, str=学校, reading=ガッコウ, count=-1, begin=2, end=4, correlation=0.0]
に [sequence=4, facet=助詞, lex=に, str=に, reading=ニ, count=-1, begin=4, end=5, correlation=0.0]
行く [sequence=5, facet=動詞, lex=行く, str=行き, reading=イキ, count=-1, begin=5, end=7, correlation=0.0]
ます [sequence=6, facet=助動詞, lex=ます, str=まし, reading=マシ, count=-1, begin=7, end=9, correlation=0.0]
た [sequence=7, facet=助動詞, lex=た, str=た, reading=タ, count=-1, begin=9, end=10, correlation=0.0]
。 [sequence=8, facet=記号, lex=。, str=。, reading=。, count=-1, begin=10, end=11, correlation=0.0]

まとめ

NLP4J を使うと、Javaで簡単に自然言語処理ができますね!

プロジェクトURL

https://www.nlp4j.org/
NLP4J_N_128.png


Indexに戻る

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

NLP4J [007] で kuromoji を利用する Annotator を作成する

形態素解析のモジュールを使い分ける

NLP4J では標準(nlp4j-core)においてYahoo!デベロッパーネットワークの形態素解析処理を利用しています。

テキスト解析:日本語形態素解析 - Yahoo!デベロッパーネットワーク
https://developer.yahoo.co.jp/webapi/jlp/ma/v1/parse.html

Yahoo!デベロッパーネットワークのAPIはHTTPで呼べるので便利ではありますが、回数制限があるという弱点もあります。
そこでローカルでも使える kuromoji を利用するライブラリを作成することにします。

Annotator の作成

今回は nlp4j プロジェクトのサブモジュール(sub module)として nlp4j-kuromoji を作成しました。

nlp4j-kuromoji
https://github.com/oyahiroki/nlp4j/tree/master/nlp4j/nlp4j-kuromoji

Maven には kuromoji を利用するためのdependencyを追加しています。

<!-- https://mvnrepository.com/artifact/com.atilika.kuromoji/kuromoji -->
<dependency>
 <groupId>com.atilika.kuromoji</groupId>
 <artifactId>kuromoji</artifactId>
 <version>0.9.0</version>
 <type>pom</type>
</dependency>
<dependency>
 <groupId>com.atilika.kuromoji</groupId>
 <artifactId>kuromoji-ipadic</artifactId>
 <version>0.9.0</version>
</dependency>

Class Diagram

クラス図としてはこんな感じです。
形態素解析エンジンとしては同じようなことをしているので兄弟関係ということになります。
一度インプリしてしまえば差分を意識することはなくなるので、kuromojiのインプリを意識するのもおそらく今回限りということになります。

SoWkIImgAStDuShBAJ39qdF9JoxDJSqhSSpBooz9BCalKh2fqTLLYFGgy4s4Y-5NwrrQb9-RdvM94EPoICrB0Ta10000.png

@startuml
nlp4j.DocumentAnnotator <|-- YJpMaAnnotator
nlp4j.DocumentAnnotator <|-- KuromojiAnnotator 
@enduml

Code

NLP4J が提供する nlp4j.DocumentAnnotator インターフェイスを継承(implement)します。
kuromoji で抽出したキーワードをNLP4Jで用意しているキーワードにマップしています。

package nlp4j.krmj.annotator;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.atilika.kuromoji.ipadic.Token;
import com.atilika.kuromoji.ipadic.Tokenizer;
import nlp4j.AbstractDocumentAnnotator;
import nlp4j.Document;
import nlp4j.DocumentAnnotator;
import nlp4j.impl.DefaultKeyword;

/**
 * Kuromoji Annotator
 * @author Hiroki Oya
 * @since 1.2
 */
public class KuromojiAnnotator extends AbstractDocumentAnnotator implements DocumentAnnotator {
    static private final Logger logger = LogManager.getLogger(KuromojiAnnotator.class);
    @Override
    public void annotate(Document doc) throws Exception {
        Tokenizer tokenizer = new Tokenizer(); // kuromoji のインスタンス
        for (String target : targets) {
            Object obj = doc.getAttribute(target);
            if (obj == null || obj instanceof String == false) {
                continue;
            }
            String text = (String) obj;
            List<Token> tokens = tokenizer.tokenize(text);
            int sequence = 1;
            for (Token token : tokens) {
                logger.debug(token.getAllFeatures());
                DefaultKeyword kwd = new DefaultKeyword(); // 新しいキーワード
                kwd.setLex(token.getBaseForm());
                kwd.setStr(token.getSurface());
                kwd.setReading(token.getReading());
                kwd.setBegin(token.getPosition());
                kwd.setEnd(token.getPosition() + token.getSurface().length());
                kwd.setFacet(token.getPartOfSpeechLevel1());
                kwd.setSequence(sequence);
                doc.addKeyword(kwd);
                sequence++;
            }
        }
    }
}

同じ「原形」でもbaseForm と lex の違いがあったり、用語が微妙に違うことがみて取れると思います。

使い方

Annotator のクラス指定を変更する以外はYahoo!デベロッパーネットワークと同じになります。
別々の自然言語処理である kuromoji と Yahoo!デベロッパーネットワークの自然言語処理をWRAPしていることになります。

    public void testAnnotateDocument001() throws Exception {
        // 自然文のテキスト
        String text = "私は学校に行きました。";
        Document doc = new DefaultDocument();
        doc.putAttribute("text", text);
        KuromojiAnnotator annotator = new KuromojiAnnotator(); // ここだけ変更してモジュールを差し替え可能
        annotator.setProperty("target", "text");
        annotator.annotate(doc); // throws Exception
        System.err.println("Finished : annotation");
        for (Keyword kwd : doc.getKeywords()) {
            System.err.println(kwd);
        }
    }

結果

結果は以下のようになりました。
自然言語処理ライブラリの実装を意識することなく利用することができました。

Finished : annotation
私 [sequence=1, facet=名詞, lex=私, str=私, reading=ワタシ, count=-1, begin=0, end=1, correlation=0.0]
は [sequence=2, facet=助詞, lex=は, str=は, reading=ハ, count=-1, begin=1, end=2, correlation=0.0]
学校 [sequence=3, facet=名詞, lex=学校, str=学校, reading=ガッコウ, count=-1, begin=2, end=4, correlation=0.0]
に [sequence=4, facet=助詞, lex=に, str=に, reading=ニ, count=-1, begin=4, end=5, correlation=0.0]
行く [sequence=5, facet=動詞, lex=行く, str=行き, reading=イキ, count=-1, begin=5, end=7, correlation=0.0]
ます [sequence=6, facet=助動詞, lex=ます, str=まし, reading=マシ, count=-1, begin=7, end=9, correlation=0.0]
た [sequence=7, facet=助動詞, lex=た, str=た, reading=タ, count=-1, begin=9, end=10, correlation=0.0]
。 [sequence=8, facet=記号, lex=。, str=。, reading=。, count=-1, begin=10, end=11, correlation=0.0]

まとめ

NLP4J を使うと、Javaで簡単に自然言語処理ができますね!

プロジェクトURL

https://www.nlp4j.org/
NLP4J_N_128.png


Indexに戻る

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

初心者に優しいAndroidアプリの公開の手順、初めてのリリースに大苦戦⁈

初めてのAndroidアプリ公開

著作権の壁を乗り越え(前回のAndroidに関する記事参照)、やっとのことでAndroidアプリの制作が終わりました。
さっそくアプリを公開する準備を進めることに...
しかし初めて公開したので、様々な困難が待ち受けていた:fearful:

公開の手順と初心者が引っかかりやすい部分、また、どうやって解決したかを紹介していきます。私が公開する際に参考にした分かりやすい記事も紹介しています。
しっかり準備し、手順をしっかりわかっていれば問題なくできるはずです:thumbsup_tone2:
これからアプリの公開を考えてる方、特に初めての方ぜひ参考にしてください。

AndroidアプリをGoole Play Storeに公開する手順

アプリ公開には以下の3つのステップが必要です。

  1. Googleデベロッパー登録
  2. aabファイル(Android App Bundle)の作成
  3. Google Play Consoleへログイン

しかし、このステップに入る前にアプリをリリース用に設定しておくとよいでしょう。

リリース用アプリの設定

アプリ公開ステップに入る前にリリース用アプリに設定します。

Androidデベロッパー(リリースの準備 | Android デベロッパー | Android Developers)を読み進めてやっていくのがベストですが...
初心者の私にはたくさん色々なことが書いてあり、結局何をすればいいのかわからない状態でした:cold_sweat:
ですので、私がリリース前にしたことをここでは紹介します。

  • ロギングとデバッグをオフ
    image.png
    Logメソッドというのは、開発者が調査目的に記録するためのメソッドなので、ユーザー向けアプリでは必要がないので、削除します。

  • アプリのバージョン情報を設定する
    コメント 2020-01-15 144318.png

1.Googleデベロッパー登録

ここでやっと公開するためのステップ1.Googleへのデベロッパー登録です。
デベロッパー登録自体は難しくないです

私は以下の記事を参考に登録しました:point_down_tone2:
Android開発者登録方法 2019年最新版 かんたんな方法

デベロッパー登録に必要なもの
  • Googleのアカウント(ない方は作る必要があります。 普段使っているアカウントでも大丈夫ですが、私は、開発用の新しいアカウントを作成しました。)
  • クレジットカード(登録料として25ドル支払うため)

2.aabファイル(Android App Bundle)の作成

登録が完了の後は、aabファイルの作成です。

Qiitaの記事に作成手順が書かれていたので、参考にしてくだい:point_down_tone2:
Google Play Storeにアプリを公開する

ここのaabファイルを作るところで1番苦労しました。

どこの部分で引っかかったのか以下にまとめました↓

  • Android App Bundleではなく、APKを選んでしまった

コメント 2020-01-14 145008.png

APKの方でもファイルは作れますが、Goole Play Consoleでリリースする際に以下の警告が出ます。
コメント 2020-01-14 165132.png

Android App Bundleの方を選ぶようにしましょう。

  • Key store pathって何? image.png

進めていくとこのような画面が出てきます。私はここでなにを設定しているかよくわからなかったので、説明をつけておきます。
image.png

Alias=アップロード鍵:Google Play アプリ署名用にアップロードする前に、App Bundle または APK への署名に使用する鍵です。

  • キーストアに関しての警告が出てくる

必要事項を記入し、キーを作成しました。OKを押し、完了させると、以下のような警告が出てきました。
コメント 2020-01-10 161102.png

警告を消してそのままつくることができましたが、解決方法が知りたい方は以下の記事を参考にしてください:point_down_tone2:
https://qiita.com/koichi-ozaki/items/c515ba6711811aac44a2

  • Buildができない

ついにFinishボタンを押せばaabファイル完成のところをまできたのですが…最後の最後にBuildができない...
コメント 2020-01-14 144809.png

原因はGitHubで共同作業していたため、SDKのpathが他人のものになっていました。Android Studioの右上の像のマークを押して、解決しました。
image.png

1人でアプリを作成していれば、問題ないと思いますが、GitHubで共同作業してる方は注意が必要です。

3. Google Play Consoleへログイン

aabファイルが完成した方...おめでとうございます:clap_tone2:
あと1歩です。最後のステップはGoogle Play Consoleへログインです。
ここで、ストアに公開します。

先ほど、aabファイルを作った際に参考にした記事に続きが載っています:point_down_tone2:
Google Play Storeにアプリを公開する

アプリ公開にに必要なもの

  • 先程作成したaabファイル
  • スクリーンショット(4枚~8枚)
  • フィーチャーグラフィック(横1024×縦500の画像)

コメント 2020-01-14 160518.png

フィーチャーグラフィック(ユーザーがアプリのストアの掲載情報を開くと最初に目に留まるもの)という横長の画像が必要でした。事前に用意しておくと、スムーズに公開ができると思います。

ここで最後の苦労が…
私の場合最初にAPKファイルで作ってしまったので、アップロードする際に警告が出てしまいました。(aabファイルで作れば問題ないです。)
コメント 2020-01-14 165132.png
ですので、aabファイルで作り直し、アップロードすると新たな問題が…
コメント 2020-01-14 172137.png
APKでバージョン1を作ってしまっているので、バージョンを変えなければいけませんでした。
image.png

build.gradle(Moudule:app)を選択し、
image.png
VersionCodeを上げることで解決しました。

Google Play Consoleに基本情報の登録が終わると、公開完了です:bangbang:
後は、アプリの公開を待つだけです。わくわくしますね:relaxed:

記念すべき初アプリ公開されました!!

私は、アプリの公開に3日ほどかかりました…
アプリ公開までいくと一人前になった気がします:laughing:
色々改善点はあるのですが...アップデートもしていけたらいいなと思います。
指1本:point_up_tone2:キーボード打ちから半年で制作、公開まで行けました。

公開したアプリ:point_down_tone2:

:star:アプリ名:Time is money:star:
無駄な時間をお金に換算するアプリです。
image.png

今日1日の自分の無駄な行動を振り返るために作りました。
私自身とても時間を無駄に過ごしていることが多かったので...:sweat_drops:
お金に換算することでより現実的になり、時間に対しての考え方を改めることができるという思いから、お金に換算というアイディアが浮かびました:money_mouth:

時間をもっと有効に使えるといいですね!!

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

JSP-サーブレットのMVCモデルとリダイレクト・フォワード、スコープについて

MVCモデル

 Model,View,Controllerモデルの略で、それぞれ「処理」「結果表示」「統括」を担当する。
 ユーザ側のリクエストをコントローラが受け、コントローラはモデルにデータ処理を依頼し、モデルは処理したデータをコントローラに返し、そのデータはビューに渡され、ビューは成形してユーザにレスポンスとして返す、といった形になる。

 サーブレット・JSP環境では、コントローラをサーブレットクラスが、ビューをJSPが、モデルを通常のJavaのクラスが行うことが多い。
 こうする利点の一つに、複数分野にまたがる知識がなくても分担して開発することができる、という利点がある。

01.png

フォワードとリダイレクト

 サーブレットクラスからJSPやほかのサーブレットクラスにページを遷移する方法として、フォワードとリダイレクトの2つがあげられる。
 フォワードは同一のアプリケーション内でしか移動できない代わりに早く、またブラウザのアドレスバーには最初のリクエスト先の情報が残るといった特徴がある。
 対してリダイレクトはファイルや別アプリケーション(別サイト)にもアクセスできる代わりに遅く、ブラウザのアドレスバーにある情報も変更されるといった特徴がある。

02.png

処理用のメソッド

フォワード

ServletRequest#getRequestDispatcher(String) … String で指定した先にデータを転送するためのオブジェクトを生成する。
RequestDispatcher#forward(ServletRequest,ServletResponse) … Dispatcher のもつ転送先が代わりにレスポンスを行うフォワードを行う。このメソッドは通常の Response が返る前に行われる必要がある。

リダイレクト

HttpServletResponse#sendRedirect(String) … String で指定したURLに転送する指示をResponceとしてクライアントに出す。

jspファイルの置き場所

 Webcontent/WEB-INF/下に保存したjspファイルは、ブラウザからの指示を受けて開くことはできないので、直接リンクされたくない場合に利用できる。
 また、ブラウザから直接開けないという性質から、フォワードからの表示は可能だがリダイレクトはできなくなっている。

スコープ

フォワードやリダイレクトを使っても、そのままではデータを保存・送信することはできない。
スコープと呼ばれる領域にデータを保存することで、サーブレットやJSPの間でデータを共有することができる。

共有のための条件

保存できるデータはインスタンスのみで、
 ・java.io.Serializableを実装して直列化可能であること
 ・クラスはパッケージに属していること
 ・デフォルトコンストラクタ(引数なしのコンストラクタ)をpublicで持つこと
 ・フィールドはカプセル化され、かわりに命名規則に従ったgetter/setterを持つこと
が共有するためのルールとして設定してある。

共有する範囲

 スコープは保存範囲で4つに分けられ、それぞれ「ページスコープ」「リクエストスコープ」「セッションスコープ」「アプリケーションスコープ」と呼ばれている。

ページスコープ

 範囲はそのページ内のみ。今回は割愛する。

リクエストスコープ

 範囲はリクエストをされてからレスポンスが返るまで。
 入力されたデータをもとに計算結果を表示、といったWebアプリケーションに使える。

セッションスコープ

 範囲はセッションの間。
 登録フォームなどで一時的に入れた値を何度か使うときに必要。

アプリケーションスコープ

 投票ボタンなど、ユーザ全体で共有・参照するデータを扱うときに使う。
 アプリケーション終了時に破棄される。

保存・取り出し方法

 スコープごとに異なるクラスの、名称と引数が共通したメソッドで保存・取り出しができる。

 保存メソッド
 setAttribute(String,Object) … String で指定した名前で認識されるObjectインスタンスを保存する。同じ名前のものを入れると上書き、 Object が null ならその名前のものの削除になる。

 取得メソッド
 getAttribute(String) … String で指定した名前で認識されるインスタンスを取り出す。その名前のデータがない場合、 null が返ってくる。その中からデータをさらに取り出す場合はキャストを行う必要がある。

リクエストスコープの場合

 利用するクラス:ServletRequestクラス

 JSPには暗黙オブジェクトという明示的に宣言しなくても変数として利用できる変数がいくつかあり、ServletRequest型で参照される「現在のリクエスト」はrequestで暗黙的に扱うことができる。

セッションスコープの場合

 利用するクラス:HttpSessionクラス

 データを保存する場合も取り出す場合も「使用しているセッション」のインスタンスを取り出す必要がある。
 HttpServletRequest#getSession() で取り出すことができ、それを HttpSession 型の参照変数に入れて扱う。
 こちらも暗黙オブジェクトがあり、「現在利用中のセッション」をsessionで暗黙的に扱うことができる。

セッションスコープ利用時の注意点

 複数人のセッションスコープを同時に管理するために、クライアント側とサーバ側でセッションIDを共有し、サーバ側でスコープとセッションIDを紐付けておく必要がある。
 そうすると、クライアントの数だけスコープを用意することになり、メモリのパンクが考えられるため、適宜スコープの中身やセッションの破棄を行う必要がある。一定時間利用されていないセッションをガベージコレクションの対象として破棄する、セッションタイムアウトがその一例である。

また、短時間のリクエスト集中に耐えやすいようにするために、それらの破棄を自主的に行うメソッドが用意されていて、開発者が適切に利用する必要がある。

アプリケーションスコープの場合

 利用するクラス:ServletContextクラス

 こちらもセッションスコープと同様に、「使用しているアプリケーション」をのインスタンス取得する必要がある。
 これの暗黙オブジェクトはapplicationの変数名で扱われる。メソッドとして取得する場合は HttpServlet#getServletContext() で取り出すことができる。

アプリケーションスコープ利用時の注意点

 アプリケーションスコープには全クライアント共通の領域としてデータを保存できる。ただし、サーバ側にデータが残るのは一時的なものでしかないので、サーバの終了と同時に終了したアプリケーションの持つスコープは破棄される。
 再起動後にもそのデータを扱いたい場合は、ファイルなどの形にして残す必要がある。

 

スコープの種類 リクエストスコープ セッションスコープ アプリケーションスコープ
スコープ範囲 リクエストがレスポンスされるまで セッションが破棄されるまで アプリケーションが終了するまで
利用クラス ServletRequest HttpSession ServletContext
クラスの暗黙オブジェクト request session application
主な用途 入力フォーム ログイン情報 共通データ
その他注意 リダイレクトでは一度レスポンスを返すため利用できない データが溜まるとサーバに負荷がかかるので適宜破棄する データを再利用したい場合はファイルなどに出力する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Mockito】ランダム生成の文字列を使ったdo whileループを検証する

問題

Mockitoを使って単体テストを作成中
以下のようなコードのテストに悩んでいた…

  • ユーザーIDを生成
  • 既存ユーザとIDが重複していたら生成し直す
    • 生成したIDでSELECTしてEntityが取得できた場合は重複しているとする。
private String generateUserID() {
    String userId;
    do {
        // ランダム8桁の文字列を作成
        userId = RandomUtil.getRandomString(8);
        // 作成したuserIDが既に使用されてなければループを抜ける
    } while (userRepository.findByUserId(userId) != null);
    return userId;
}

Repository部をスタブ化してSELECTの戻り値を設定することでテストする。

// Entityが取得できるパターン
when(userRepository.findByUserId(anyString())).thenReturn(new FUserList());

// Entityが取得できないパターン
when(userRepository.findByUserId(anyString())).thenReturn(null);

この2つをテストできたらOK。

ん?
でもこれって、Entityが取得できるパターンは無限ループになる…

解決

調べると、こんな書き方ができました

// 一回目の関数呼び出しでは取得できて、二回目では取得できない。
        when(userRepository.findByUserId(anyString())).thenReturn(new FUserList()).thenReturn(null);

これなら、取得できるパターンとできないパターン両方いっぺんにできるし無限ループにならないですね。

参考

Mockito (Mockito 3.2.4 API)
テックノート – Mockitoのメソッド呼び出し回数によって返却値を変更する方法

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

Java + MySQLで簡易掲示板を作る

board.PNG

mysqlを使った簡易的な掲示板を作りました。

概要

・ID、名前、コメント、タイムスタンプを掲示板に表示する。
・名前かコメントが未入力だとエラーメッセージがポップアップする。

苦労したところ

・DBに格納したデータを取り出す方法がわからなかった。
最初は配列で別々にID、名前、コメント、タイムスタンプを取り出そうとしたが上手くできなかった。
ArrayListならインスタンスまるごと格納できる!

・ArrayListの中身をjspで取り出す方法がわからなかった。
EL式で取り出そうとしたが上手くできなかった。
JSTLのcoreタグライブラリでforEachを使って取り出すことができた。

・mysql接続時にtime zoneエラーが出る。
下記を参考に接続URLを変更。日本時間にしたい場合は末尾をJSTに変更する。
Grails 3 + MySQLでrun-app時に「The server time zone value」でエラーが出る場合の対処

ソースコード

model

Board.java
package model;

import java.io.Serializable;
import java.sql.Timestamp;

public class Board implements Serializable {
    private int id;
    private String name;
    private String comment;
    private Timestamp time;


    public Timestamp getTime() {
        return time;
    }
    public void setTime(Timestamp time) {
        this.time = time;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getComment() {
        return comment;
    }
    public void setComment(String comment) {
        this.comment = comment;
    }

}

FindCommentDAO.java
package model;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class FindCommentDAO {

    public List<Board> findcomment() {

        // id,name,commentを格納するリスト
        List<Board> list = new ArrayList<>();

        final String jdbcId = "id";
        final String jdbcPass = "password";
        final String jdbcUrl = "jdbc:mysql://localhost:3306/dbname?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=JST";

        Connection con = null;

        try {

            con = DriverManager.getConnection(jdbcUrl, jdbcId, jdbcPass);

            System.out.println("Connected....");

            try {
                Statement st = con.createStatement();
                String sql = "select * from board";

                try {
                    // sqlを送信
                    ResultSet rs = st.executeQuery(sql);

                    while (rs.next()) {
                        // DBから取り出したid,name,commentをJavaBeansにset
                        Board bo = new Board();
                        bo.setId(rs.getInt("id"));
                        bo.setName(rs.getString("name"));
                        bo.setComment(rs.getString("comment"));
                        bo.setTime(rs.getTimestamp("time"));

                        // リストに1個ずつ格納。末尾に要素が追加されていく。
                        list.add(bo);
                    }

                    rs.close();
                    st.close();
                    con.close();

                } catch (SQLException e) {
                    e.printStackTrace();
                }

            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                // データベース接続の切断
                if (con != null) {
                    try {
                        con.close();

                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
            System.out.println("Connection Failed.");
            return null;
        }
        return list;

    }

}

FindCommentLogic.java
package model;

import java.util.List;

public class FindCommentLogic {
    public List<Board> executeFindComment() {
        FindCommentDAO fcdao = new FindCommentDAO();
        List<Board> list = fcdao.findcomment();
        return list;
    }

}
AddCommentDAO.java
package model;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class AddCommentDAO {

    // DBにid,name,commentを加えるメソッド
    public AddCommentDAO(Board bo) {

        if(bo.getName().isEmpty()) {
            bo.setName( "名無し");
        }
        if(bo.getComment().isEmpty()) {
            bo.setComment( "コメント無し");
        }


        final String jdbcId = "id";
        final String jdbcPass = "password";
        final String jdbcUrl = "jdbc:mysql://localhost:3306/dbname?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=JST";

        Connection con = null;

        try {

            con = DriverManager.getConnection(jdbcUrl, jdbcId, jdbcPass);

            System.out.println("Connected....");

            try {

                PreparedStatement ps = con.prepareStatement("INSERT INTO board (name, comment) VALUES (?, ?)");

                ps.setString(1, bo.getName());
                ps.setString(2, bo.getComment());

                // ひな形を送信
                int r = ps.executeUpdate();

                if (r != 0) {
                    System.out.println(r + "件の書き込みを追加しました。");
                } else {
                    System.out.println("書き込みできませんでした。");
                }

                ps.close();
                con.close();

            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                // データベース接続の切断
                if (con != null) {
                    try {
                        con.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (SQLException e) {

            e.printStackTrace();
            System.out.println("Connection Failed.");

        }

    }

}
AddCommentLogic
package model;

public class AddCommentLogic {
    public void executeAddComment(Board bo) {
        AddCommentDAO acdao = new AddCommentDAO(bo);
    }
}

view

main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">


<script type="text/javascript">

function check(){
    var flag = 0;

    if(document.form1.name.value == ""){
        flag = 1;
    }
    else if(document.form1.comment.value == ""){
        flag = 1;
    }

    if(flag){
        window.alert('名前とコメントを入力してください'); 
        return false; // 送信を中止
    }
    else{
        return true; // 送信を実行
    }
}
</script>

<title>掲示板</title>
</head>
<body>
<form action="/board/BoardServlet" method="post" name="form1" onSubmit="return check()">
<p>名前:<input type="text" name="name"></p>
<p>コメント:<br>
<textarea name="comment" rows="5" cols="40"></textarea>
</p>
<p><input type="submit" value="送信"><input type="reset" value="リセット">
</p>
</form>

<c:forEach var="list" items="${listAttribute}">
<p>ID:<c:out value="${list.id}"/> 名前:<c:out value="${list.name}"/> 日付:<c:out value="${list.time}"/><br>
<c:out value="${list.comment}"/></p>
</c:forEach>

</body>
</html>

controller

BoardServlet.java
package servlet;

import java.io.IOException;
import java.util.List;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import model.AddCommentLogic;
import model.Board;
import model.FindCommentLogic;

/**
 * Servlet implementation class BoardServlet
 */
@WebServlet("/BoardServlet")
public class BoardServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public BoardServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //リスナークラスに移動したい
        request.setCharacterEncoding("UTF-8");

        // 既存のコメントを確認
        FindCommentLogic fcl = new FindCommentLogic();
        List<Board> list = fcl.executeFindComment();

        // セッションスコープにコメントリストを保存
        HttpSession session = request.getSession();
        session.setAttribute("listAttribute", list);

        RequestDispatcher rd =request.getRequestDispatcher("/WEB-INF/jsp/main.jsp");
        rd.forward(request, response);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        request.setCharacterEncoding("UTF-8");

        // 入力された値を取得
        String name = request.getParameter("name");
        String comment = request.getParameter("comment");

        //JavaBeansに格納
        Board bo = new Board();
        bo.setName(name);
        bo.setComment(comment);

        // mysqlに格納
        AddCommentLogic acl = new AddCommentLogic();
        acl.executeAddComment(bo);

        // 今入力されたコメントと既存のコメントをmysqlから取得
        FindCommentLogic fcl = new FindCommentLogic();
        List<Board> list = fcl.executeFindComment();

        // セッションスコープにコメントリストを保存
        HttpSession session = request.getSession();
        session.setAttribute("listAttribute", list);

        RequestDispatcher rd =request.getRequestDispatcher("/WEB-INF/jsp/main.jsp");
        rd.forward(request, response);

    }

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

Javaでいいね機能の実装

iineko.PNG

アウトプットはどんどんした方がいいらしいので、初めて作ったいいねアプリの公開をします。
MVCモデルで作りました。

概要

画像をクリックするといいねこ!が増えます。
値はアプリケーションスコープに保存しています。
https://iineko.herokuapp.com

苦労したところ

1、HTMLから画面遷移せずに値を送る方法がわからずに四苦八苦しました。
画面遷移せずにHTMLで値を送る方法

2、いいねを押しても1以上にならない。
いいねされる度に毎回newしていたので初回起動判定を入れた。

3、クラスをまたいでインスタンスを渡す方法がわからなかった。
YoineServelet.javaでYoine y = new Yoine();して
YoineLogic.javaでYoine yにいいねを1加算して
Yoine.javaにsetしたかったが
YoineServeletでnewしてYoineLogicでもnewしたら別のインスタンスになっちゃうしどうするんだと悩んでしまった。

YoineLogicでYoine型を引数として受け取るようにした。

ソースコード

model

Yoine.java
package model;

import java.io.Serializable;

public class Yoine implements Serializable {
    private int yoineCount = 0;

    public void setYoineCount(int yoineCount) {
        this.yoineCount = yoineCount;
    }

    public int getYoineCount() {
        return yoineCount;
    }
}
YoineLogic.java
package model;

public class YoineLogic {

    public void yoinePlus(Yoine y) {
        int count = y.getYoineCount();
        count++;
        y.setYoineCount(count);
    }
}

View

yoineView.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style type="text/css">
<!--
   div.ta0 {text-align: left;}
   div.ta1 {text-align: center;}
   div.ta2 {text-align: right;}
   div.ta3 {text-align: justify;}
-->
</style>
<title>いいねこ!!!</title>
</head>
<body>
<div class="ta1">
<p><a href="/iine/YoineServlet?action=yoine">
<img src="/iine/finger-163689_640.jpg" alt="いいね!" width="300" height="200"></a>
<a href="/iine/YoineServlet?action=yoine">
<img src="/iine/2cat-323262_1280.jpg" alt="いいね!" width="300" height="200"></a>
</p>
<p><font size="5"><b>いいねこ!:${yoine.yoineCount}</b></font></p>
</div>
</body>
</html>

Controller

YoineServlet.java
package servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import model.Yoine;
import model.YoineLogic;

/**
 * Servlet implementation class YoineServlet
 */
@WebServlet("/YoineServlet")
public class YoineServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public YoineServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 初回起動を判定するための処理
        // アプリケーションスコープから値を取得
        ServletContext sc = this.getServletContext();
        Yoine y = (Yoine) sc.getAttribute("yoine");

        // 初回起動判定の続き
        // アプリケーションスコープに値がなければnewする
        if(y == null) {
            y = new Yoine();
            sc.setAttribute("yoine", y);
        }

        // リクエストパラメーターの取得
            request.setCharacterEncoding("UTF-8");
            String yoine = request.getParameter("action");


        // いいねボタン押されたら
        if (yoine != null) {

            // YoineLogicでいいねを加算
            YoineLogic yl = new YoineLogic();
            yl.yoinePlus(y);

            // いいねの数をアプリケーションスコープに保存
            sc.setAttribute("yoine", y);
        }

        // フォワード
        RequestDispatcher rd = request.getRequestDispatcher("/yoineView.jsp");
        rd.forward(request, response);

    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

    }

}

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

画面遷移せずにHTMLで値を送る方法

画面遷移せずにGETで値を送る方法のメモです。

HTMLで値をGET送信

a href="URL?属性名=変数名"

<body>
<a href="TestServlet?action=sample"></a>
</body>

Javaで値を受け取る

request.getParameter("属性名")
sは文字列「sample」を受け取ります。

protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

            String s = request.getParameter("action");
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む