Java Spring BootでWebベースの調査プラットフォームを開発する: 初心者のためのガイド

アンケートプラットフォームとは?

アンケートとは、ある集団からデータを収集するための調査方法です。ある集団の意見、行動、その他の特徴に関する情報を収集するためのツールです。調査は、インタビュー、アンケート、オンラインフォームなど、さまざまな方法で実施することができます。

ウェブベースの調査プラットフォームは、多くの利点があるため、ますます人気が高まっています。ここでは、ウェブベースの調査プラットフォームを使用する利点のいくつかを紹介します:

  • より速く: ウェブベースのアンケートは、参加者が自分のペースで、インターネット接続があればどこからでも回答できるため、従来の方法よりも短時間で完了することができます。
  • より正確: より正確:オンライン調査では、参加者が回答を直接ウェブ調査に入力するため、誤差が大幅に減少します。回答者自身が回答を記録するため、インタビュアーが回答を誤解する機会がありません。
  • 視覚的に魅力的: ウェブベースのアンケートには、画像やビデオなどのマルチメディア要素を含めることができ、より魅力的でインタラクティブなアンケートにすることができます。 ウェブベースのアンケートプラットフォームには、効果的なアンケートを作成するのに役立つさまざまな機能や要件があります。ここでは、ウェブベースのアンケートツールで利用できる最も一般的な機能をいくつか紹介します:

  • 質問の種類: 質問タイプ:ウェブベースのアンケートプラットフォームには、多肢選択式、自由形式、評価スケールなど、さまざまな質問タイプがあります。

  • スキップロジック: スキップロジック: スキップロジックでは、回答者の回答に基づいてアンケートの流れをカスタマイズすることができます。関連性のない質問をスキップし、回答者に関連する質問のみを表示することができます。

  • リアルタイム分析: ウェブベースのアンケートプラットフォームは、リアルタイム分析を提供し、回答が寄せられるたびにその回答を追跡し、リアルタイムでデータを分析することができます。

  • モバイルとの互換性: ウェブベースのアンケートは、モバイルデバイス用に最適化することができるため、回答者はスマートフォンやタブレットで簡単にアンケートに回答することができます。

  • 他のツールとの統合: ウェブベースのアンケートプラットフォームは、メールマーケティングソフトウェアや CRM システムなど、他のツールと統合することができます。

これから構築するもの

このチュートリアルでは、Spring BootとGridDBのパワーを活用したWebベースのアンケートプラットフォームを作成します。まずは自由形式の質問を実装し、回答者が複数の回答を提出できるようにします。最後に、回答内容と質問ごとの回答率を表示するレポートを作成します。

要件

前述の概要に基づき、以下の機能要件があります: 1. アンケート、回答、および回答のリストの表示 2. 回答の記録 3. 表やグラフで回答を可視化します。

範囲外 * ユーザー管理 (登録、ログインなど) * アンケート管理 (作成、編集、削除)

このチュートリアルでは、CSV ファイルからアンケートデータを作成します。

データをどこに保存するか?

SQLデータベースとNoSQLデータベースのどちらを選択するかは、特定の要件によって異なります。しかし、このシステムでNoSQLデータベースがSQLデータベースよりも好ましい理由は以下の通りです: 1. 水平方向のスケーラビリティ: NoSQLデータベースは通常、水平方向のスケーラビリティが容易です。 2. クエリ構造: この場合、テーブル結合のような複雑なリレーショナル操作は必要ありません。 3. NoSQLデータベースは通常、分散分散アーキテクチャで構築されているため、クラスタにノードを追加することで容易にスケーリングが可能です。

オンライン調査のクラス図

次に、データベースの設計とハイレベル・アーキテクチャに取り組みましょう。アンケート・プラットフォームのクラス図を以下に示します。

オンライン調査のデータベース設計

上記のクラス図から、以下の属性を持つデータベーススキーマを作成します:

  • アンケート

    これはシステムの出発点です。このエンティティには、システムで生成される各アンケートに関するコア情報が含まれます。以下の属性があります:

    • String id: システムで生成された一意の識別子。これはプライマリキーです。
    • String title: アンケートの概要です。
    • String description: アンケートの目的についての説明です。
    • boolean isActive: 有効か無効かを示すブール値: アンケートがまだ有効かどうかを示すブール値です。
    • Date createdAt: システムが生成したアンケート作成のタイムスタンプです。
  • 質問

    このエンティティには、アンケートで利用可能な質問が含まれます。以下の属性を持ちます:

    • String id: システムが生成した一意の識別子。これはプライマリキーです。
    • String surveyId: 親アンケートを識別するための ID です。
    • String questionText: 質問のテキストです。
    • Integer position: アンケート内の質問の順番です。
  • アンケート回答

    このエンティティには、回答に関する情報と、アンケートの回答を開始した時点と終了した時点の情報が含まれます。以下の属性を持ちます:

    • String id: システムで生成された一意の識別子です。これはプライマリキーです。
    • String surveyId: 親アンケートを識別するための IDです。
    • Date startedAt: 回答者がアンケートの回答を開始した日時です。
    • Date completedAt: 回答者がアンケートの回答を終了した日時です。
  • 回答

    このエンティティには、アンケートの各質問に対する回答者の回答が含まれます。以下の属性を持ちます:

    • String id: ステムで生成された一意の識別子。これはプライマリキーです。
    • String surveyResponseId: 親アンケートの回答を識別する ID です。
    • String answer: 回答者がアンケートに回答を開始した日時です。
    • Date createdAt: 回答者がアンケートの回答を終了した日時です。

プロジェクトのセットアップ方法

Spring Bootでできること

Spring Bootはアプリケーションを高速に構築する方法を提供します。Spring Bootはファイルを編集するコードを生成しません。その代わりに、アプリケーションを起動すると、Spring BootがBeanと設定を動的に配線し、アプリケーションコンテキストに適用します。Spring Bootを使えば、インフラよりビジネス機能に集中できます。

インストールに必要なもの

Spring Bootプロジェクトを作成する

start.spring.ioに移動します。 このサービスは、アプリケーションに必要なすべての依存関係を取り込み、ほとんどのセットアップを行います。以下のスクリーンショットに従ってgenerateをクリックすると、Spring Bootプロジェクトが生成され、zip形式でダウンロードされます。

このプロジェクトを解凍し、任意のIDEにインポートします。 次に、このプロジェクトに必要なライブラリを以下のように追加する必要があります:

<dependencies>
<!-- Include log4j2 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
  
<dependency>
  <groupId>com.github.griddb</groupId>
  <artifactId>gridstore</artifactId>
  <version>5.1.0</version>
</dependency>

<dependency>
  <groupId>com.github.f4b6a3</groupId>
  <artifactId>tsid-creator</artifactId>
  <version>5.2.5</version>
</dependency>
<dependency>
  <groupId>de.siegmar</groupId>
  <artifactId>fastcsv</artifactId>
  <version>2.2.2</version>
</dependency>

</dependencies>

コーディング

GridDBによるデータ・アクセス

まず、GridDBのテーブルやコンテナを表すJava POJOクラスを作成します。Lombokの@Dataアノテーションを付けると、すべてのフィールドのゲッター、便利なtoStringメソッド、すべての非一時的なフィールドをチェックするhashCodeとequalsの実装が自動的に生成されます。また、すべての非終端フィールドのセッターとコンストラクタも生成します。

@Data
public class Survey {
    @RowKey String id;
    String title;
    String description;
    boolean isActive;
    Date createdAt;
}

@Data
public class SurveyResponse {
  @RowKey String id;
  String respondentId;
  String surveyId;
  Date startedAt;
  Date completedAt;
}

@Data
public class Answer {
  @RowKey String id;
  String surveyResponseId;
  String questionId;
  Date createdAt;
  String answer;
}

@Data
public class Question {
  @RowKey String id;
  String surveyId;
  String questionText;
  Integer position;
}

@Data
public class Respondent {
  @RowKey String id;
}

次に、データベース操作の中心的な設定として GridDBConfig クラスを作成します。このクラスは以下のことを行います: * GridDBインスタンスへのデータベース接続を管理するためのGridStoreクラスを作成します。コンテナは、リレーショナル・データベースのテーブルに相当します。* Collectionの作成・更新時には、Collectionのカラムレイアウトに対応する名前とオブジェクトを指定します。また、各コレクションに対して、頻繁に検索され、TQLのWHEREセクションの条件で使用される列のインデックスを追加します。

@Configuration
public class GridDBConfig {

  @Value("${GRIDDB_NOTIFICATION_MEMBER}")
  private String notificationMember;

  @Value("${GRIDDB_CLUSTER_NAME}")
  private String clusterName;

  @Value("${GRIDDB_USER}")
  private String user;

  @Value("${GRIDDB_PASSWORD}")
  private String password;

  @Bean
  public GridStore gridStore() throws GSException {
    // Acquiring a GridStore instance
    Properties properties = new Properties();
    properties.setProperty("notificationMember", notificationMember);
    properties.setProperty("clusterName", clusterName);
    properties.setProperty("user", user);
    properties.setProperty("password", password);
    GridStore store = GridStoreFactory.getInstance().getGridStore(properties);
    return store;
  }

  @Bean
  public Collection<String, Survey> surveyCollection(GridStore gridStore) throws GSException {
    Collection<String, Survey> collection = gridStore.putCollection("surveys", Survey.class);
    return collection;
  }

  @Bean
  public Collection<String, Respondent> respondentCollection(GridStore gridStore)
      throws GSException {
    Collection<String, Respondent> collection =
        gridStore.putCollection("respondents", Respondent.class);
    return collection;
  }

  @Bean
  public Collection<String, SurveyResponse> surveyResponseCollection(GridStore gridStore)
      throws GSException {
    Collection<String, SurveyResponse> collection =
        gridStore.putCollection("surveyResponses", SurveyResponse.class);
    collection.createIndex("surveyId");
    collection.createIndex("respondentId");
    return collection;
  }

  @Bean
  public Collection<String, Question> questionCollection(GridStore gridStore) throws GSException {
    Collection<String, Question> collection = gridStore.putCollection("questions", Question.class);
    collection.createIndex("surveyId");
    return collection;
  }

  @Bean
  public Collection<String, Answer> answerCollection(GridStore gridStore) throws GSException {
    Collection<String, Answer> urlCollection = gridStore.putCollection("answers", Answer.class);
    urlCollection.createIndex("surveyResponseId");
    return urlCollection;
  }
}

サービス・クラス

次に、ビジネスロジックを処理するための @Service アノテーションを付与したサービスクラスを作成し、Entity クラスを DTO に変換します。例えば、SurveyService.java は、GridDB 内のアンケートデータの検索と作成を行います。

  • create: 新しいアンケート行を作成します。このメソッドは Web リクエスト (SurveyWebController) からデータを受け取り、その値を新しい Survey エンティティに代入します。エンティティを GridDB データベースに保存するには、前に作成した surveyCollection Bean から put メソッドを呼び出します。
public String create(SurveyCreateRequest request) {
    Survey survey = new Survey();
    survey.setId(KeyGenerator.next("survey"));
    survey.setActive(request.isActive());
    survey.setTitle(request.getTitle());
    survey.setDescription(request.getDescription());
    survey.setCreatedAt(new Date());
    try {
      log.info("put: {}", survey);
      surveyCollection.put(survey.getId(), survey);
    } catch (GSException e) {
      e.printStackTrace();
    }
    return survey.getId();
  }
  • getSurvey: GridDB から行を検索するには、まず TQL(GridDBで使用するクエリ言語)を surveyId パラメータ付きで作成し、TQL を実行するための Query オブジェクト surveyCollection.query(tql) を作成します。行のセットを取得するには、fetch() メソッドを呼び出します。
  public SurveyDto getSurvey(String surveyId) {
    String tql = String.format("select * from surveys where id='%s'", surveyId);
    List<surveydto> surveyDtos = query(tql, true);
    if (surveyDtos == null || surveyDtos.size() == 0) {
      throw new ResponseStatusException(NOT_FOUND, "Not found");
    }
    return surveyDtos.get(0);
  }

  public List</surveydto><surveydto> getSurveys() {
    String tql = "select * from surveys limit 50";
    return query(tql, false);
  }

  private List</surveydto><surveydto> query(String tql, boolean includeQuestion) {
    List</surveydto><surveydto> result = new ArrayList<>();
    Query<survey> query;
    try {
      query = surveyCollection.query(tql);
      RowSet</survey><survey> rs = query.fetch();
      while (rs.hasNext()) {
        Survey model = rs.next();
        List<questiondto> questions = new ArrayList<>();
        if (includeQuestion) questions = questionService.getQuestions(model.getId());
        result.add(
            SurveyDto.builder()
                .id(model.getId())
                .createdAt(model.getCreatedAt())
                .description(model.getDescription())
                .title(model.getTitle())
                .isActive(model.isActive())
                .questions(questions)
                .build());
      }
    } catch (GSException e) {
      e.printStackTrace();
    }
    return result;
  }
</questiondto></survey></surveydto>

Spring MVCでWebコンテンツを扱う

SpringのWeb MVCフレームワークは、他の多くのWeb MVCフレームワークと同様に、リクエスト駆動型で、リクエストをコントローラにディスパッチし、Webアプリケーションの開発を容易にする他の機能を提供する中央のServletを中心に設計されています。Spring MVCフレームワークを使うことで、次のような利点があります: * Spring MVCは、モデルオブジェクト、コントローラ、コマンドオブジェクト、ビューリゾルバなど、それぞれの役割を特化したオブジェクトで実現できるように分離しています。* アプリケーションの開発とデプロイに軽量なサーブレットコンテナを使用します。* フレームワークとアプリケーション・クラスの両方に堅牢なコンフィギュレーションを提供し、ウェブ・コントローラからビジネス・オブジェクトへのようなコンテキストをまたいだ容易な参照を含みます。* ページを簡単にリダイレクトする特定のアノテーションを提供します。

このチュートリアルでは、標準的なMVCアーキテクチャに従います。コントローラ (SurveyWebController クラス)、ビュー (surveys.html Thymeleaf テンプレート)、そしてビューにデータを渡すためのモデル (Java マップオブジェクト) を用意します。コントローラのすべてのメソッドは URI にマップされます。

アンケートページ

以下の例では、SurveyWebController のメソッド listSurveys/surveys に対する GET リクエストを処理してビュー名 (この場合は surveys) を返し、addAttribute メソッドによって Model に属性 surveys を追加しています。属性の値 (サーベイのリスト) を提供するために、surveyService.getSurveys() メソッドを呼び出します。

@Controller
public class SurveyWebController {
    
  @GetMapping("/surveys")
  public String listSurveys(Model model) {
    model.addAttribute("surveys", surveyService.getSurveys());
    return "surveys";
  }
}

コントローラクラスを作成した後は、生成するビューのテンプレートを定義する必要があります。Thymeleafは最新のサーバサイドJavaテンプレートエンジンで、ウェブ環境とスタンドアロン環境の両方に対応しています。Thymeleafで書かれたHTMLテンプレートはHTMLのように見え、動作します。次の例 surveys.html では、モデルの属性は次の構文でアクセスできます: attributeName}、この場合のattributeNameはsurveysです。アンケートのリストを表示するためにHTMLテーブルを定義し、アンケートのコレクションを繰り返し表示するためにth:eachタグ属性を使用し、値を表示するためにth:text`タグを使用します。

  <div class="table-responsive">
  <table class="table table-bordered" id="dataTableSurvey">
    <thead>
    <tr>
      <th scope="col">#</th>
      <th scope="col">Title</th>
      <th scope="col">Description</th>
      <th scope="col">Is Active</th>
      <th scope="col">Created Date</th>
    </tr>
    </thead>
    <tbody>
    <tr th_each="survey : ${surveys}">
      <td>
        <a class="nav-link" th_href="@{/responses/{surveyId}(surveyId=${survey.id})}">Answer</a>
      </td>
      <td>
        <a class="nav-link" th_href="@{/surveys/{surveyId}/responses(surveyId=${survey.id})}" th_text="${survey.title}"></a>
      </td>
      <td th_text="${survey.description}"></td>
      <td th_text="${survey.isActive}? 'Yups': 'No'"></td>
      <td th_text="${survey.createdAt}"></td>
    </tr>
    </tbody>
  </table>
</div>
  • アンケートに回答するために、表内に回答リンクを用意しています。
  • 回答リストを表示するには、アンケートのタイトルから移動します。回答リストから回答に移動することができます。

以下はアンケート一覧ページのプレビューです。

チャートページ

チャートページのプレビューです:

Docker Composeを使ったプロジェクトの実行

プロジェクトを立ち上げるために、一般的なコンテナ・エンジンであるDockerを利用します。コンテナは、アプリケーションがどのマシンでも期待通りに動作することを保証するのに役立ちます。Dockerは、複数のコンテナ化されたアプリケーションを一緒にオーケストレーションするためのツールであるDocker Composeへのアクセスを提供してくれます。

  • dev.Dockerfile: JDK21のMaven dockerイメージを使用します。
FROM maven:3.9.5-eclipse-temurin-21-alpine
RUN mkdir /app
WORKDIR /app

COPY pom.xml ./
RUN mvn dependency:go-offline

COPY docker-entrypoint-dev.sh ./
COPY src ./src
  • docker-entrypoint-dev.sh : このスクリプトでは、コードが変更されるたびにコンパイルしたいです。
#!/bin/bash
export TERM=xterm
echo "wait 5s"
sleep 5

mvn spring-boot:run -Dspring-boot.run.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" &

while true; do
    watch -d -t -g "ls -lR . | sha1sum" && mvn compile
done
  • docker-compose-dev.yml
version: '3.3'
services:
  survey-app-dev:
    container_name: survey-app-dev
    build:
      context: ./
      dockerfile: dev.Dockerfile
    volumes:
      - ./src:/app/src
      - ./.m2:/root/.m2
    environment:
      - GRIDDB_NOTIFICATION_MEMBER=griddb-dev:10001
      - GRIDDB_CLUSTER_NAME=dockerGridDB
      - GRIDDB_USER=admin
      - GRIDDB_PASSWORD=admin
      - spring.thymeleaf.prefix=file:src/main/resources/templates/
    command: sh ./docker-entrypoint-dev.sh
    ports:
      - 8080:8080
      - 35729:35729
      - 5005:5005
    networks:
      - griddb-dev-net
    depends_on:
      - griddb-dev

  griddb-dev:
    container_name: griddb-dev
    build:
      context: ./dockergriddb
      dockerfile: Dockerfile
    volumes:
      - griddb-dev-vol:/var/lib/gridstore
    ports:
      - 10001:10001
    networks:
      - griddb-dev-net

networks:
  griddb-dev-net:
volumes:
  griddb-dev-vol:

次に以下のコマンドを実行します:

  1. docker イメージをビルドします:
  docker compose -f .docker-compose-dev.yml build
  1. docker イメージを実行します:
  docker compose -f .docker-compose-dev.yml up up

ウェブサイトはhttp://localhost:8080です。

まとめ

Spring BootとデータベースとしてGridDBを使って、Webベースのアンケートプラットフォームを作る方法を学びました。また、Docker Composeを使って開発する方法も学びました。DockerとDocker Composeを適切に使うことで、ローカルのデータベースやウェブサイトを含む最小限のローカル環境構成で、開発環境全体を他の開発者のマシンに簡単に移植することができます。次のイテレーションでは、セキュリティ、ユーザー管理、アンケート管理などの機能を追加し、より多くの分析ユースケースを提供することができます。このプロジェクトのレポは こちら にあります。

If you have any questions about the blog, please create a Stack Overflow post here https://stackoverflow.com/questions/ask?tags=griddb .
Make sure that you use the “griddb” tag so our engineers can quickly reply to your questions.

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください