質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
servlet

Servletとは、Webページの動的な生成やデータ処理などをサーバ上で実行するために、Javaで作成されたプログラムです。 ショッピングサイトやオンラインバンキングといった、動的なウェブサイトの構築に用いられています。

Q&A

解決済

1回答

883閲覧

Servletでのお知らせ欄実装の妥当性について

nacchan

総合スコア8

servlet

Servletとは、Webページの動的な生成やデータ処理などをサーバ上で実行するために、Javaで作成されたプログラムです。 ショッピングサイトやオンラインバンキングといった、動的なウェブサイトの構築に用いられています。

0グッド

0クリップ

投稿2021/05/31 08:38

編集2021/05/31 08:45

teratailの皆様、お世話になっております。

ServletでWEBサイトを作っていて、トップページのお知らせ欄を下記のように実装しようと考えています。
実装の方法が妥当かどうか意見をいただければ幸いです。

1)データベースに、「お知らせ日時(システム時間)」を「bigint型」、「お知らせ内容」を「text型」として定義したテーブル(以下、お知らせテーブルとします)を作成します。

2)「ServletContextListener」のサブクラスを作り、フィールド変数で「public static final ConcurrentHashMap<Long,String> INFO_MAP = new ConcurrentHashMap<>();」と宣言、初期化します。

3)上記クラスの「contextInitialized」メソッドの中で、データベースからお知らせテーブルの内容を「INFO_MAP」へ格納します。

4)「HttpServlet」のサブクラス(トップページ)を作り、「doGet」メソッドの中で、ローカル変数で「TreeMap<Long,String> infoMap = new TreeMap<>();」と宣言、初期化します。

5)「infoMap」に「INFO_MAP」の中身を全て格納し、
for(Long time:infoMap.descendingKeySet()){
//timeを日付表示に整形し、お知らせを表示するHTMLソースを出力します。
}

6)お知らせを追加する場合は管理者だけがログインできる編集画面を作り、INFO_MAPおよびお知らせテーブルに新しいお知らせ情報を追加します。

「HttpServlet」や「EventListener」のサブクラス内でフィールド変数(例では静的定数ですが)を宣言するのは良くないと良く聞きます。
上記のサブクラスはTomcatのコンテナ内でインスタンスがただひとつだけ作られるため、上記のフィールド変数が各処理単位で共有され、値の整合性が取れなくなるからと理解しています。
上記の例では、値の整合性こそ取れるものの、ひとつの静的定数が大量の処理単位から同タイミングで参照された場合にオーバーヘッドの原因になるのではないかと懸念しています。
実際はどうなのでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

まず、用語の統一

個の回答では、クラスに属する変数は「クラス変数」、インスタンスが個別に持つ変数は「インスタンス変数」と呼称します。
(フィールドとかメンバとかはどちらの意味にも使われがちなので、混乱を避けるために)

回答

サーブレットのクラス変数に特定のデータを入れて運用するのが妥当かどうかは、クラス変数の中身の使い方に依ります。

質問ではクラス変数にお知らせのデータを入れて、システム全体で使いまわすことを想定していると思います。
その場合、お知らせデータは基本的に Read only なので不特定多数のスレッドから同時に呼び出されても特に不具合は起こらないと考えられます。

ひとつの静的定数が大量の処理単位から同タイミングで参照された場合にオーバーヘッドの原因になるのではないかと懸念しています。

と書かれていますが、「読取専用」のクラス変数に限って言えば特にオーバーヘッドとなることもありません。ただ同じアドレスのデータをみんなで見ているだけなので。
ただし、その変数に入っているオブジェクトが完全にスレッドセーフであることが条件です。

スレッドセーフでないオブジェクトの最たる例が SimpleDateFormatです。
https://docs.oracle.com/javase/jp/8/docs/api/java/text/SimpleDateFormat.html

ConcurrentHashMapについては、並列で内部の値を書き換えるような処理でない限りはスレッドセーフです。(ドキュメントを読む限りでは)
しかし、書き換えを伴う動作を含む参照が複数スレッドから同時に発生すると、取得するデータの整合性は保証されません。

質問では管理者のみがお知らせテーブルを変更できるとしています。管理者による更新と他ユーザによるトップページ表示処理がかち合うと場合によっては中途半端に更新されたデータが表示される可能性があります。
また、複数の管理者が同時にお知らせを更新した場合、データの整合性が保証されません。
(お知らせテーブルを更新→INFO_MAPを洗い替え、という流れであればある程度抑止はできますが)

結論としては、質問の要件ではクラス変数にお知らせデータを保持する設計は妥当ではありません。
システム稼働中に更新の可能性がない場合に限ってサーブレットのクラス変数は使用してください。

素直に、トップページを表示するたびにお知らせテーブルからデータを読みだしてくる実装の方が賢明です。トップページなんてそんなに頻回に表示しないし、お知らせテーブルのような変動がほぼないテーブルはDBも結果をキャッシュするので、コネクション管理を適切に行っていればデータベースのオーバーヘッドもほぼ存在しません。

投稿2021/05/31 09:39

hope_mucci

総合スコア4447

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nacchan

2021/05/31 14:22

hope_mucciさん ご回答くださいまして誠にありがとうございます。 丁寧でわかりやすいです! たしかに管理者がINFO_MAPの値を書き換え、他ユーザーがINFO_MAPを参照しようとすれば、その他ユーザーには中途半端なお知らせが表示されかねないですよね。 本当にその通りと思いますし理解できましたので、ご指摘の通り逐一テーブルからデータを読み出す実装にしたいと思います。 改めてありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問