コード全文
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を作ることで、非常に高機能で汎用的な検索処理が実現できます。
「たくさんの検索項目を自由に組み合わせて探したい!」
というニーズにこたえる柔軟なシステムが作れるようになりますよ。
ぜひトライしてみてください!