【初心者のSpringBoot+Mybatis】色々なSELECT文のまとめ!IN句、LIKE句やInsert文も!

どうも!ヒグッティ(X→ヒグッティ@システムエンジニア)です!
今回は、SpringBootとMybatisで様々なselect文を実行して結果をまとめたいと思います!Mybatisを触ったことのない人やselect文を勉強したい方向けの記事です。初歩的な内容ですが、本記事を理解できれば実務にも十分役立つと思います!!

環境

以下の記事で作成した環境と同じです。過去の記事も参考にしてください!ソースも以下の記事から流用していますので是非読んでみて下さい。

https://www.higutthiengineer.com/2024/06/05/springbootmybatismysqldocker/

まずはINSERT文の準備

SELECT文を解説する前にデータを登録するためのAPIを準備します。以下、ソースです。addDataのメソッドで登録しています。Userhi.javaなどのクラスの詳細は過去の記事を参照願います。

package com.example.demo;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.domain.Userhi;
import com.example.demo.repo.UserhiRepo;

@RestController
@RequestMapping("/api")
public class Hello {
	@Autowired
	UserhiRepo userRepo;

	@GetMapping("/hello")
    public Map<String, String> getData() {
        Map<String, String> map = new HashMap<String, String>();
        // 全件検索
        List<Userhi> list = userRepo.findAll();

        for(Userhi user: list) {
        	map.put(user.getUserName(), user.getUserEmail());
        }
        return map;
    }
	@GetMapping("/add")
    public Map<String, String> addData() {
        Map<String, String> map = new HashMap<String, String>();
        LocalDateTime now = LocalDateTime.now();
        Userhi u = new Userhi();
        u.setUserEmail("aaaa@aa.aa");
        u.setUserName("higuhigu");
        u.setDelFlg(0);
        u.setUserRegDatetime(now);
        u.setUserUpdateStamp(now);
        userRepo.create(u);
        map.put("No:" + u.getUserId(), u.getUserName());
        return map;
    }

}

以下はリポジトリです。createメソッドの@Insertアノテーションにinsert文を記載しています。

package com.example.demo.repo;

import java.util.List;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;

import com.example.demo.domain.Userhi;

@Mapper
public interface UserhiRepo {

    @Select("SELECT user_id, user_name, user_email, user_reg_datetime,user_update_stamp,del_flg FROM userhi")
    List<Userhi> findAll();

    @Insert("INSERT INTO userhi ("
    		+ "user_name, "
    		+ "user_email, "
    		+ "user_reg_datetime,"
    		+ "user_update_stamp,"
    		+ "del_flg"
    		+ ") VALUES ("
    		+ " #{userName}, "
    		+ "#{userEmail}, "
    		+ "#{userRegDatetime},"
    		+ "#{userUpdateStamp},"
    		+ "#{delFlg})")
    @Options(useGeneratedKeys = true, keyProperty = "userId") // (3)
    void create(Userhi user);

}

これで「http://localhost:8080/api/add」にアクセスすればブラウザに登録したデータのIDと名前が表示されます。{“No:3″:"higuhigu"}のように表示されました。データを登録する処理を作成しておけばこの後のSELECT文の結果を確認する時に用意するデータを自由に作成できるのでInsertの処理を先に記載しました!

WHERE句でIDを指定したSELECT文

まずは普通のSELECT文を記載します。WHERE句でテーブルのIDに紐づくデータを検索するようにします。
getSelectIdメソッドに記載しています。ここではIDが2になるデータを検索しています。

package com.example.demo;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.domain.Userhi;
import com.example.demo.repo.UserhiRepo;

@RestController
@RequestMapping("/api")
public class Hello {
	@Autowired
	UserhiRepo userRepo;

	@GetMapping("/hello")
    public Map<String, String> getData() {
        Map<String, String> map = new HashMap<String, String>();
        // 全件検索
        List<Userhi> list = userRepo.findAll();

        for(Userhi user: list) {
        	map.put("No:" + user.getUserId(), user.getUserEmail() + "_" + user.getUserName());
        }
        return map;
    }
	@GetMapping("/selectid")
    public Map<String, String> getSelectId() {
        Map<String, String> map = new HashMap<String, String>();
        // WHERE検索
        Optional<Userhi> u = userRepo.findById(2);
        
        if(u.isEmpty()) {
            map.put("No:-1","No data");
        } else {
            Userhi user = u.get();
            map.put("No:" + user.getUserId(),user.getUserEmail());
        }

        return map;
    }
	@GetMapping("/add")
    public Map<String, String> addData() {
        Map<String, String> map = new HashMap<String, String>();
        LocalDateTime now = LocalDateTime.now();
        Userhi u = new Userhi();
        u.setUserEmail("aaaa@aa.aa");
        u.setUserName("higuhigu");
        u.setDelFlg(0);
        u.setUserRegDatetime(now);
        u.setUserUpdateStamp(now);
        userRepo.create(u);
        map.put("No:" + u.getUserId(), u.getUserName());
        return map;
    }

}

以下はSQL文を発行しているリポジトリクラスです。UserhiRepo.javaのfindByIdメソッドでuserIdを引数に検索しています。

package com.example.demo.repo;

import java.util.List;
import java.util.Optional;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;

import com.example.demo.domain.Userhi;

@Mapper
public interface UserhiRepo {

    @Select("SELECT user_id, user_name, user_email, user_reg_datetime,user_update_stamp,del_flg FROM userhi")
    List<Userhi> findAll();

	@Select("SELECT user_id, user_name, user_email, user_reg_datetime,user_update_stamp,del_flg "
			+ "FROM userhi "
			+ "WHERE user_id = #{userId}")
    Optional<Userhi> findById(int userId);

    @Insert("INSERT INTO userhi ("
    		+ "user_name, "
    		+ "user_email, "
    		+ "user_reg_datetime,"
    		+ "user_update_stamp,"
    		+ "del_flg"
    		+ ") VALUES ("
    		+ " #{userName}, "
    		+ "#{userEmail}, "
    		+ "#{userRegDatetime},"
    		+ "#{userUpdateStamp},"
    		+ "#{delFlg})")
    @Options(useGeneratedKeys = true, keyProperty = "userId") // (3)
    void create(Userhi user);

}

ブラウザで「http://localhost:8080/api/selectid」を入力したらuserIdが2のデータが画面に表示されるはずです!

IN句を用いたSELECT文

Hello.javaにgetSelectInメソッドを作成しましょう!IDをList<Integer>にして検索したいIDをListにセットしてfindInメソッドの引数に設定します。

package com.example.demo;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.domain.Userhi;
import com.example.demo.repo.UserhiRepo;

@RestController
@RequestMapping("/api")
public class Hello {
	@Autowired
	UserhiRepo userRepo;

	@GetMapping("/hello")
    public Map<String, String> getData() {
        Map<String, String> map = new HashMap<String, String>();
        // 全件検索
        List<Userhi> list = userRepo.findAll();

        for(Userhi user: list) {
        	map.put("No:" + user.getUserId(), user.getUserEmail() + "_" + user.getUserName());
        }
        return map;
    }
	@GetMapping("/selectid")
    public Map<String, String> getSelectId() {
        Map<String, String> map = new HashMap<String, String>();
        // WHERE検索
        Optional<Userhi> u = userRepo.findById(2);
        
        if(u.isEmpty()) {
            map.put("No:-1","No data");
        } else {
            Userhi user = u.get();
            map.put("No:" + user.getUserId(),user.getUserEmail());
        }

        return map;
    }
	@GetMapping("/selectin")
    public Map<String, String> getSelectIn() {
        Map<String, String> map = new HashMap<String, String>();
        // IN検索
        List<Integer> inParam = new ArrayList<Integer>();
        inParam.add(1);
        inParam.add(2);
        inParam.add(3);
        List<Userhi> inList = userRepo.findIn(inParam);

        for(Userhi user :inList) {
            map.put("No:" + user.getUserId(),user.getUserEmail());
        }

        return map;
    }
	@GetMapping("/add")
    public Map<String, String> addData() {
        Map<String, String> map = new HashMap<String, String>();
        LocalDateTime now = LocalDateTime.now();
        Userhi u = new Userhi();
        u.setUserEmail("aaaa@aa.aa");
        u.setUserName("higuhigu");
        u.setDelFlg(0);
        u.setUserRegDatetime(now);
        u.setUserUpdateStamp(now);
        userRepo.create(u);
        map.put("No:" + u.getUserId(), u.getUserName());
        return map;
    }
}

UserhiRepo.javaのfindInメソッドを追加しました。

package com.example.demo.repo;

import java.util.List;
import java.util.Optional;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;

import com.example.demo.domain.Userhi;

@Mapper
public interface UserhiRepo {

    @Select("SELECT user_id, user_name, user_email, user_reg_datetime,user_update_stamp,del_flg FROM userhi")
    List<Userhi> findAll();

	@Select("SELECT user_id, user_name, user_email, user_reg_datetime,user_update_stamp,del_flg "
			+ "FROM userhi "
			+ "WHERE user_id = #{userId}")
    Optional<Userhi> findById(int userId);

	@Select("<script>"
			+ "SELECT "
			+ "user_id, "
			+ "us_name, "
			+ "us_email, "
			+ "us_reg_datetime,"
			+ "us_update_stamp,"
			+ "del_flg"
			+ " FROM userhi "
			+ "WHERE user_id in "
			+ "<foreach item=\"id\" collection=\"idList\" open=\"(\" separator=\",\" close=\")\">"
			+ "(#{id})"
			+ "</foreach>"
			+ "</script>")
    List<Userhi> findIn(List<Integer> idList);

    @Insert("INSERT INTO userhi ("
    		+ "user_name, "
    		+ "user_email, "
    		+ "user_reg_datetime,"
    		+ "user_update_stamp,"
    		+ "del_flg"
    		+ ") VALUES ("
    		+ " #{userName}, "
    		+ "#{userEmail}, "
    		+ "#{userRegDatetime},"
    		+ "#{userUpdateStamp},"
    		+ "#{delFlg})")
    @Options(useGeneratedKeys = true, keyProperty = "userId") // (3)
    void create(Userhi user);

}

まずは@Selectアノテーションの中で<script>タグを囲み、その中にSQLを記載します。IN句は以下の書き方をすることで引数で渡したidListをループして(1,2,3,4,~)のようなIN句を作成してくれます。

			+ "<foreach item=\"id\" collection=\"idList\" open=\"(\" separator=\",\" close=\")\">"
			+ "(#{id})"
			+ "</foreach>"

「http://localhost:8080/api/selectin」にアクセスしたらブラウザに指定したIDのデータが表示されるはずです!

LIKE句を用いたSELECT文

Hello.javaでgetSelectInメソッドを追加しましょう!第一引数はuser_nameの値を指定しています。ここでは「~test」に一致するレコードを検索します。 第二引数はuser_emailの値を指定しています。 「~aa」に一致するレコードを検索します。

package com.example.demo;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.domain.Userhi;
import com.example.demo.repo.UserhiRepo;

@RestController
@RequestMapping("/api")
public class Hello {
	@Autowired
	UserhiRepo userRepo;

	@GetMapping("/hello")
    public Map<String, String> getData() {
        Map<String, String> map = new HashMap<String, String>();
        // 全件検索
        List<Userhi> list = userRepo.findAll();

        for(Userhi user: list) {
        	map.put("No:" + user.getUserId(), user.getUserEmail() + "_" + user.getUserName());
        }
        return map;
    }
	@GetMapping("/selectid")
    public Map<String, String> getSelectId() {
        Map<String, String> map = new HashMap<String, String>();
        // WHERE検索
        Optional<Userhi> u = userRepo.findById(2);
        
        if(u.isEmpty()) {
            map.put("No:-1","No data");
        } else {
            Userhi user = u.get();
            map.put("No:" + user.getUserId(),user.getUserEmail());
        }

        return map;
    }
	@GetMapping("/selectin")
    public Map<String, String> getSelectIn() {
        Map<String, String> map = new HashMap<String, String>();
        // IN検索
        List<Integer> inParam = new ArrayList<Integer>();
        inParam.add(1);
        inParam.add(2);
        inParam.add(3);
        List<Userhi> inList = userRepo.findIn(inParam);

        for(Userhi user :inList) {
            map.put("No:" + user.getUserId(),user.getUserEmail());
        }

        return map;
    }
	@GetMapping("/selectlike")
    public Map<String, String> getSelectLike() {
        Map<String, String> map = new HashMap<String, String>();
        // LIKE検索
        List<Userhi> likeList = userRepo.findLike("test","aa");

        for(Userhi user :likeList) {
            map.put("No:" + user.getUserId(),user.getUserEmail());
        }

        return map;
    }

	@GetMapping("/add")
    public Map<String, String> addData() {
        Map<String, String> map = new HashMap<String, String>();
        LocalDateTime now = LocalDateTime.now();
        Userhi u = new Userhi();
        u.setUserEmail("aaaa@aa.aa");
        u.setUserName("higuhigu");
        u.setDelFlg(0);
        u.setUserRegDatetime(now);
        u.setUserUpdateStamp(now);
        userRepo.create(u);
        map.put("No:" + u.getUserId(), u.getUserName());
        return map;
    }

}

UserhiRepo.javaにfindLikeメソッドを追加します。ポイントはWHERE user_name like CONCAT('%’, #{word})です!!引数の値( #{word} )と「%」をCONCATで結合することでLIKE句を実現できます! 「WHERE user_name like %#{word}」のような書き方はエラーになるので注意して下さい!

package com.example.demo.repo;

import java.util.List;
import java.util.Optional;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;

import com.example.demo.domain.Userhi;

@Mapper
public interface UserhiRepo {

    @Select("SELECT user_id, user_name, user_email, user_reg_datetime,user_update_stamp,del_flg FROM userhi")
    List<Userhi> findAll();

	@Select("SELECT user_id, user_name, user_email, user_reg_datetime,user_update_stamp,del_flg "
			+ "FROM userhi "
			+ "WHERE user_id = #{userId}")
    Optional<Userhi> findById(int userId);

	@Select("<script>"
			+ "SELECT "
			+ "user_id, "
			+ "user_name, "
			+ "user_email, "
			+ "user_reg_datetime,"
			+ "user_update_stamp,"
			+ "del_flg"
			+ " FROM userhi "
			+ "WHERE user_id in "
			+ "<foreach item=\"id\" collection=\"idList\" open=\"(\" separator=\",\" close=\")\">"
			+ "(#{id})"
			+ "</foreach>"
			+ "</script>")
    List<Userhi> findIn(List<Integer> idList);

	@Select("SELECT user_id, user_name, user_email, user_reg_datetime,user_update_stamp,del_flg "
			+ "FROM userhi "
			+ "WHERE user_name like CONCAT('%', #{word}) or user_email like CONCAT('%', #{word2})")
    List<Userhi> findLike(String word,String word2);

    @Insert("INSERT INTO userhi ("
    		+ "user_name, "
    		+ "user_email, "
    		+ "user_reg_datetime,"
    		+ "user_update_stamp,"
    		+ "del_flg"
    		+ ") VALUES ("
    		+ " #{userName}, "
    		+ "#{userEmail}, "
    		+ "#{userRegDatetime},"
    		+ "#{userUpdateStamp},"
    		+ "#{delFlg})")
    @Options(useGeneratedKeys = true, keyProperty = "userId") // (3)
    void create(Userhi user);
}

ブラウザに「http://localhost:8080/api/selectlike」を入力したら検索にヒットしたデータが表示されます。私は「{“No:2″:"aaaa@aa.aa","No:3″:"aaaa@aa.aa"}」と表示されました。

カラムと対応する変数を独自に指定するSELECT文

Hello.javaにgetSelectColumnメソッドを追加します。

package com.example.demo;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.domain.Userhi;
import com.example.demo.repo.UserhiRepo;

@RestController
@RequestMapping("/api")
public class Hello {
	@Autowired
	UserhiRepo userRepo;

	@GetMapping("/hello")
    public Map<String, String> getData() {
        Map<String, String> map = new HashMap<String, String>();
        // 全件検索
        List<Userhi> list = userRepo.findAll();

        for(Userhi user: list) {
        	map.put("No:" + user.getUserId(), user.getUserEmail() + "_" + user.getUserName());
        }
        return map;
    }
	@GetMapping("/selectid")
    public Map<String, String> getSelectId() {
        Map<String, String> map = new HashMap<String, String>();
        // WHERE検索
        Optional<Userhi> u = userRepo.findById(2);
        
        if(u.isEmpty()) {
            map.put("No:-1","No data");
        } else {
            Userhi user = u.get();
            map.put("No:" + user.getUserId(),user.getUserEmail());
        }

        return map;
    }
	@GetMapping("/selectin")
    public Map<String, String> getSelectIn() {
        Map<String, String> map = new HashMap<String, String>();
        // IN検索
        List<Integer> inParam = new ArrayList<Integer>();
        inParam.add(1);
        inParam.add(2);
        inParam.add(3);
        List<Userhi> inList = userRepo.findIn(inParam);

        for(Userhi user :inList) {
            map.put("No:" + user.getUserId(),user.getUserEmail());
        }

        return map;
    }
	@GetMapping("/selectColumn")
    public Map<String, String> getSelectColumn() {
        Map<String, String> map = new HashMap<String, String>();
        // カラムと変数のバインドを指定
        List<Userhi> columnList = userRepo.findOrgColumn();

        for(Userhi user :columnList) {
            map.put("No:" + user.getUserId(),user.getUserEmail());
        }

        return map;
    }
	@GetMapping("/selectlike")
    public Map<String, String> getSelectLike() {
        Map<String, String> map = new HashMap<String, String>();
        // LIKE検索
        List<Userhi> likeList = userRepo.findLike("test","aa");

        for(Userhi user :likeList) {
            map.put("No:" + user.getUserId(),user.getUserEmail());
        }

        return map;
    }

	@GetMapping("/add")
    public Map<String, String> addData() {
        Map<String, String> map = new HashMap<String, String>();
        LocalDateTime now = LocalDateTime.now();
        Userhi u = new Userhi();
        u.setUserEmail("aaaa@aa.aa");
        u.setUserName("higuhigu");
        u.setDelFlg(0);
        u.setUserRegDatetime(now);
        u.setUserUpdateStamp(now);
        userRepo.create(u);
        map.put("No:" + u.getUserId(), u.getUserName());
        return map;
    }

}

Userhi.javaでsampleNameとsampleEmailの変数を追加します。

package com.example.demo.domain;

import java.time.LocalDateTime;

import lombok.Data;

@Data
public class Userhi {
	private Integer userId;
	private String userName;
	private String userEmail;
	private LocalDateTime userRegDatetime;
	private LocalDateTime userUpdateStamp;
	private int delFlg;
	private String sampleName;
	private String sampleEmail;
}

UserhiRepo.javaでfindOrgColumnメソッドを追加します。

package com.example.demo.repo;

import java.util.List;
import java.util.Optional;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import com.example.demo.domain.Userhi;

@Mapper
public interface UserhiRepo {

    @Select("SELECT user_id, user_name, user_email, user_reg_datetime,user_update_stamp,del_flg FROM userhi")
    List<Userhi> findAll();

	@Select("SELECT user_id, user_name, user_email, user_reg_datetime,user_update_stamp,del_flg "
			+ "FROM userhi "
			+ "WHERE user_id = #{userId}")
    Optional<Userhi> findById(int userId);

	@Select("<script>"
			+ "SELECT "
			+ "user_id, "
			+ "user_name, "
			+ "user_email, "
			+ "user_reg_datetime,"
			+ "user_update_stamp,"
			+ "del_flg"
			+ " FROM userhi "
			+ "WHERE user_id in "
			+ "<foreach item=\"id\" collection=\"idList\" open=\"(\" separator=\",\" close=\")\">"
			+ "(#{id})"
			+ "</foreach>"
			+ "</script>")
    List<Userhi> findIn(List<Integer> idList);

	@Select("SELECT user_id, user_name, user_email, user_reg_datetime,user_update_stamp,del_flg "
			+ "FROM userhi "
			+ "WHERE user_name like CONCAT('%', #{word}) or user_email like CONCAT('%', #{word2})")
    List<Userhi> findLike(String word,String word2);

	@Select("SELECT user_id, user_name, user_email, user_reg_datetime,user_update_stamp,del_flg FROM userhi ")
	@Results(id = "userhi", value = {
            @Result(column = "user_id", property = "userId"),
            @Result(column = "user_name", property = "sampleName"),
            @Result(column = "user_email", property = "sampleEmail") })
    List<Userhi> findOrgColumn();

    @Insert("INSERT INTO userhi ("
    		+ "user_name, "
    		+ "user_email, "
    		+ "user_reg_datetime,"
    		+ "user_update_stamp,"
    		+ "del_flg"
    		+ ") VALUES ("
    		+ " #{userName}, "
    		+ "#{userEmail}, "
    		+ "#{userRegDatetime},"
    		+ "#{userUpdateStamp},"
    		+ "#{delFlg})")
    @Options(useGeneratedKeys = true, keyProperty = "userId") // (3)
    void create(Userhi user);

}

以下の書き方をすることで、Userhi.javaのsampleNameにuser_nameの値を格納できますsampleEmailも同じ理由ですね!逆にUserhi.javaのuserName、userEmailの値はnullが格納されます!テーブルのカラムと格納したい変数は必ずしも名前が一致するわけではないので。この機能があるのは便利すぎますね!Mybatisを既存のプロジェクトに追加しやすい理由の一つですね!

            @Result(column = "user_name", property = "sampleName"),
            @Result(column = "user_email", property = "sampleEmail") })

まとめ

SpringBootとMybatisで色々なSELECT文を試してみました!MybatisはFWを利用していない既存のアプリなどに対しても導入しやすい設計になっている感じました。多少の命名規則はありますが、命名規則が無くても導入できる仕組みがあるのはありがたいですね!今回は、javaファイルにSQLを直書きしましたが、xmlの設定ファイルにSQLを外だしできるので、javaとSQLを分離できるのもメリットの一つです!やはり需要が高いだけのことはありますね!現場で利用できる日が来るといいのだが、、おそらく一生来ないなww

スポンサーリンク