コード全文

package com.example.demo.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.model.Message;

@RestController
@RequestMapping("/chat")
public class ChatController {

    private static final Queue<Message> messages = new ConcurrentLinkedQueue<>();

    @GetMapping
    public List<Message> getMessages() {
        return new ArrayList<>(messages);
    }

    @PostMapping
    public void postMessage(@RequestParam("user") String user,
                            @RequestParam("message") String message) {
        messages.add(new Message(user, message));
    }
}

package com.example.demo.model;

public class Message {
    private String user;
    private String content;

    public Message() {}  // JSON変換用

    public Message(String user, String content) {
        this.user = user;
        this.content = content;
    }

    public String getUser() {
        return user;
    }

    public String getContent() {
        return content;
    }
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>LINE風複数人チャット</title>
  <style>
    body {
      font-family: sans-serif;
      background-color: #e5ddd5;
      margin: 0;
      padding: 0;
      display: flex;
      flex-direction: column;
      height: 100vh;
    }
    #chatBox {
      flex: 1;
      padding: 10px;
      overflow-y: auto;
      background-color: #f7f7f7;
    }
    .message {
      max-width: 70%;
      padding: 8px 12px;
      margin: 6px 0;
      border-radius: 15px;
      clear: both;
    }
    .sent {
      background-color: #dcf8c6;
      float: right;
      text-align: right;
    }
    .received {
      background-color: #fff;
      float: left;
      text-align: left;
    }
    #inputArea {
      display: flex;
      padding: 10px;
      background-color: #fff;
      border-top: 1px solid #ccc;
    }
    #message, #username {
      padding: 8px;
      font-size: 1em;
      border: 1px solid #ccc;
      border-radius: 20px;
      margin-right: 10px;
    }
    #message {
      flex: 1;
    }
    #sendMessage {
      padding: 8px 20px;
      border: none;
      background-color: #34b7f1;
      color: white;
      border-radius: 20px;
      font-weight: bold;
      cursor: pointer;
    }
    .username-label {
      font-size: 0.75em;
      color: #555;
      display: block;
    }
    .clearfix::after {
      content: "";
      display: table;
      clear: both;
    }
  </style>
</head>
<body>

<div id="chatBox"></div>

<div id="inputArea">
  <input type="text" id="username" placeholder="名前" />
  <input type="text" id="message" placeholder="メッセージ" />
  <button id="sendMessage">送信</button>
</div>

<script>
  document.addEventListener("DOMContentLoaded", function () {
    const chatBox = document.getElementById("chatBox");
    const username = document.getElementById("username");
    const messageInput = document.getElementById("message");
    const sendButton = document.getElementById("sendMessage");

    function renderMessages(messages) {
      chatBox.innerHTML = '';
      messages.forEach(msg => {
        const div = document.createElement('div');
        const nameLabel = document.createElement('span');
        nameLabel.className = "username-label";
        nameLabel.textContent = msg.user;

        div.className = "message clearfix " + 
          (msg.user === username.value.trim() ? "sent" : "received");
        div.appendChild(nameLabel);
        div.appendChild(document.createTextNode(msg.content));
        chatBox.appendChild(div);
      });
      chatBox.scrollTop = chatBox.scrollHeight;
    }

    sendButton.addEventListener("click", function () {
      const user = username.value.trim();
      const message = messageInput.value.trim();
      if (!user || !message) return;

      fetch("/chat", {
        method: "POST",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded"
        },
        body: `user=${encodeURIComponent(user)}&message=${encodeURIComponent(message)}`
      }).then(() => {
        messageInput.value = "";
      });
    });

    setInterval(() => {
      fetch("/chat")
        .then(res => res.json())
        .then(data => renderMessages(data));
    }, 1500);
  });
</script>

</body>
</html>

JavaでAND条件・OR条件に対応した動的検索DAOの作り方

今回は、検索条件を動的に変更できるDAOの作り方を、Javaと研修中に説明したSuperDaoクラスを使って詳しく解説します。

しかも今回は、「AND条件」だけでなく「OR条件」にも対応できる柔軟な実装方法を紹介します!

たとえばこんな検索、やりたくなったことはありませんか?

  • 「年齢が30歳以上かつ性別が女性」
  • 「名前が『田中』または『佐藤』」
  • 「部署が『営業部』『企画部』かつ年齢が40歳以下」

こうした複雑な条件を自由に組み立てる方法を、例えを交えながら丁寧に説明します。


AND/OR条件に対応した検索DAOの設計

ここでは社員情報を検索するEmployeeDaoクラスの例で、AND・OR両方に対応する方法を解説します。

どう実現する?

検索条件を格納するために、以下のような構造を使います。

public class SearchCondition {
    public enum Operator { AND, OR }
    private String field;
    private String operator;  // 例: =, >=, LIKEなど
    private Object value;
    private Operator logic;   // ANDかOR

    // コンストラクタやgetter/setterは省略
}

これを使えば、複数の条件をANDやORでつなぐことができます。


実装コード例(EmployeeDao)

public class EmployeeDao extends SuperDao {

    public List<Employee> searchEmployees(List<SearchCondition> conditions) {
        List<Employee> resultList = new ArrayList<>();
        connect();

        StringBuilder sql = new StringBuilder("SELECT * FROM employee WHERE ");
        List<Object> params = new ArrayList<>();

        for (int i = 0; i < conditions.size(); i++) {
            SearchCondition cond = conditions.get(i);

            if (i > 0) {
                sql.append(" ").append(cond.getLogic().name()).append(" ");
            }

            sql.append(cond.getField()).append(" ").append(cond.getOperator()).append(" ?");

            // 値の加工(LIKE用の%追加など)
            if ("LIKE".equalsIgnoreCase(cond.getOperator())) {
                params.add("%" + cond.getValue() + "%");
            } else {
                params.add(cond.getValue());
            }
        }

        try (PreparedStatement stmt = con.prepareStatement(sql.toString())) {
            for (int i = 0; i < params.size(); i++) {
                stmt.setObject(i + 1, params.get(i));
            }

            ResultSet rs = stmt.executeQuery();
            while (rs.next()) {
                Employee emp = new Employee();
                emp.setId(rs.getInt("id"));
                emp.setName(rs.getString("name"));
                emp.setAge(rs.getInt("age"));
                emp.setGender(rs.getString("gender"));
                resultList.add(emp);
            }

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

        return resultList;
    }
}


利用例 – ANDとORを組み合わせる

List<SearchCondition> conditions = new ArrayList<>();

conditions.add(new SearchCondition("name", "LIKE", "田中", SearchCondition.Operator.OR));
conditions.add(new SearchCondition("name", "LIKE", "佐藤", SearchCondition.Operator.OR));
conditions.add(new SearchCondition("age", ">=", 30, SearchCondition.Operator.AND));

EmployeeDao dao = new EmployeeDao();
List<Employee> employees = dao.searchEmployees(conditions);

この例では次のようなSQLが実行されます:

SELECT * FROM employee WHERE name LIKE ? OR name LIKE ? AND age >= ?

ちなみにこれは次のように解釈されます:

(名前が「田中」に類似 または 名前が「佐藤」に類似) かつ 年齢が30歳以上

AND/OR条件の整理(表で理解)

条件SQLの書き方説明
AND条件1 AND 条件2すべての条件を満たす
OR条件1 OR 条件2いずれかの条件を満たす
グループ化(条件1 OR 条件2) AND 条件3複数条件をまとめてAND/ORで比較


メリットとデメリット

項目内容
メリット複雑な検索条件に柔軟に対応可能
デメリットSQLが長くなりやすく、括弧の使い方に注意が必要

まとめ

動的にAND/OR条件を組み合わせて検索できるDAOを作ることで、非常に高機能で汎用的な検索処理が実現できます。

「たくさんの検索項目を自由に組み合わせて探したい!」
というニーズにこたえる柔軟なシステムが作れるようになりますよ。

ぜひトライしてみてください!


最後までお読みいただきありがとうございます。