Scoreの順位をJsonで返却するWebAPIを作成しよう。

[作成]
1.MySQLでデータベース[unity]を作成する。

CREATE DATABASE unity
DEFAULT CHARACTER SET utf8;

2.データベースunityに[scores]テーブルを作成する。

CREATE TABLE scores(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
score INT,
sex INT
);

3.https://dev.mysql.com/downloads/connector/j/にアクセスして、MySQLのJDBCをダウンロードする。

遷移したら「No thanks, just start my download.」をクリック

4.エクリプス。新規動的Webプロジェクトを選択し「ScoreAPI」を作成する。
5.WEB-INFフォルダにあるlibフォルダに先程ダウンロードしたJDBCをコピペで貼り付ける。

6.context.xmlを以下のように編集。

<?xml version="1.0" encoding="UTF-8" ?>
<Context>
  <Resource
      name="jdbc/jsp"
      auth="Container"
      type="javax.sql.DataSource"
      driverClassName="com.mysql.jdbc.Driver"
      url="jdbc:mysql://localhost:3306/unity?characterEncoding=UTF-8"
      connectionProperties="autoReconnect=true;verifyServerCertificate=false;useSSL=false;requireSSL=false"
      username="root"
      validationQuery="select 1"/>
 </Context>

上はrootユーザー、パスワードなしが前提だが、ユーザー、パスワードを設定している場合は以下

<?xml version="1.0" encoding="UTF-8" ?>
<Context>
  <Resource
      name="jdbc/jsp"
      auth="Container"
      type="javax.sql.DataSource"
      driverClassName="com.mysql.jdbc.Driver"
      url="jdbc:mysql://localhost:3306/unity?characterEncoding=UTF-8"
      connectionProperties="autoReconnect=true;verifyServerCertificate=false;useSSL=false;requireSSL=false"
      username="ユーザーネーム"
      password="パスワード"
      validationQuery="select 1"/>
 </Context>

7.作成したcontext.xmlをMETA-INFの直下に配置

8.modelの作成。modelパッケージにScoreクラスを以下のように作成
●model.Score.java


package model;

import java.io.Serializable;

public class Score implements Serializable{
	private int id;
	private String name;
	private int score;
	private int sex;
	public Score(){
		
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getScore() {
		return score;
	}
	public void setScore(int score) {
		this.score = score;
	}
	public int getSex() {
		return sex;
	}
	public void setSex(int sex) {
		this.sex = sex;
	}
	
}

9.DAOの作成。daoパッケージを作成し、その中にScoreDAO.javaを作成する。まずは1つデータを追加できるinsertOneメソッドを作成する。

package dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import model.Score;

public class ScoreDAO {
	private Connection db;
	private PreparedStatement ps;
	private ResultSet rs;

	// 接続共通処理
	private void connect() throws NamingException, SQLException {
		Context context = new InitialContext();
		DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/jsp");
		this.db = ds.getConnection();
	}

	// 切断共通処理
	private void disconnect() {
		try {
			if (rs != null) {
				rs.close();
			}
			if (ps != null) {
				ps.close();
			}
			if (db != null) {
				db.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	public void insertOne(Score score){
		try {
			this.connect();
			ps=db.prepareStatement("INSERT INTO scores(name,score,sex) VALUES(?,?,?)");
			ps.setString(1, score.getName());
			ps.setInt(2, score.getScore());
			ps.setInt(3, score.getSex());
			ps.executeUpdate();
			
		} catch (NamingException | SQLException e) {
			
			e.printStackTrace();
		}finally{
			this.disconnect();
		}
	}
	
	
}


10.Insertの確認。controllerパッケージを作成し、Insert.java(Servlet)を以下のように作成する。

package controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.ScoreDAO;
import model.Score;

@WebServlet("/Insert")
public class Insert extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
   
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		Score score=new Score();
		score.setName("山田太郎");
		score.setScore(10000);
		score.setSex(0);
		ScoreDAO dao=new ScoreDAO();
		dao.insertOne(score);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
	}

}


11.Insertサーブレットをサーバーで実行してみよう。データが1件挿入されれば成功だ。

ダミーデータの作成

12.以下から名前と性別の50人分のcsvをダウンロードし、WEB-INF/data/の中に配置する。


13.ScoreDAOを以下のように変更する。

package dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import model.Score;

public class ScoreDAO {
	private Connection db;
	private PreparedStatement ps;
	private ResultSet rs;

	// 接続共通処理
	private void connect() throws NamingException, SQLException {
		Context context = new InitialContext();
		DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/jsp");
		this.db = ds.getConnection();
	}

	// 切断共通処理
	private void disconnect() {
		try {
			if (rs != null) {
				rs.close();
			}
			if (ps != null) {
				ps.close();
			}
			if (db != null) {
				db.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	public void insertOne(Score score){
		try {
			this.connect();
			ps=db.prepareStatement("INSERT INTO scores(name,score,sex) VALUES(?,?,?)");
			ps.setString(1, score.getName());
			ps.setInt(2, score.getScore());
			ps.setInt(3, score.getSex());
			ps.executeUpdate();
			
		} catch (NamingException | SQLException e) {
			
			e.printStackTrace();
		}finally{
			this.disconnect();
		}
	}
	//ここを追記
	public void insertAll(List<Score> list){
		try {
			this.connect();
			db.setAutoCommit(false);
			ps=db.prepareStatement("INSERT INTO scores(name,score,sex) VALUES(?,?,?)");
			for(Score score:list){
				ps.setString(1, score.getName());
				ps.setInt(2, score.getScore());
				ps.setInt(3, score.getSex());
				ps.executeUpdate();	
			}
			db.commit();
		} catch (NamingException | SQLException e) {
			e.printStackTrace();
			
		}finally{
			this.disconnect();
		}
		
	}
	
	
}

14.controllerパッケージにCreateRandomData.java(Servlet)を以下のように作成する。

package controller;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.ScoreDAO;
import model.Score;

/**
 * Servlet implementation class CreateRandomData
 */
@WebServlet("/CreateRandomData")
public class CreateRandomData extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
   
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletContext application =this.getServletContext();
		String path=application.getRealPath("/WEB-INF/data/data.csv");
		FileInputStream fis=new FileInputStream(path);
		InputStreamReader isr=new InputStreamReader(fis,"UTF-8");
		BufferedReader br=new BufferedReader(isr);
		Random rand=new Random();
		List<Score> list=new ArrayList<>();
		String line;
		while((line=br.readLine()) !=null){
			String[] vals=line.split(",");
			Score score=new Score();
			score.setName(vals[0]);
			//0~99999のランダム
			score.setScore(rand.nextInt(100000));
			score.setSex(Integer.parseInt(vals[1]));
			list.add(score);
		}
		ScoreDAO dao=new ScoreDAO();
		dao.insertAll(list);
		br.close();
	}

}


15.CreateRandomData.javaをサーバーで実行してみよう。以下のよにデータが挿入されれば成功だ。

APIの作成

リクエストに応じて点数降順リストをJSONで返却するAPIを作成する。
16.ScoreDAOを以下のように変更

package dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import model.Score;

public class ScoreDAO {
	private Connection db;
	private PreparedStatement ps;
	private ResultSet rs;

	// 接続共通処理
	private void connect() throws NamingException, SQLException {
		Context context = new InitialContext();
		DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/jsp");
		this.db = ds.getConnection();
	}

	// 切断共通処理
	private void disconnect() {
		try {
			if (rs != null) {
				rs.close();
			}
			if (ps != null) {
				ps.close();
			}
			if (db != null) {
				db.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	public void insertOne(Score score){
		try {
			this.connect();
			ps=db.prepareStatement("INSERT INTO scores(name,score,sex) VALUES(?,?,?)");
			ps.setString(1, score.getName());
			ps.setInt(2, score.getScore());
			ps.setInt(3, score.getSex());
			ps.executeUpdate();
			
		} catch (NamingException | SQLException e) {
			
			e.printStackTrace();
		}finally{
			this.disconnect();
		}
	}
	
	public void insertAll(List<Score> list){
		try {
			this.connect();
			db.setAutoCommit(false);
			ps=db.prepareStatement("INSERT INTO scores(name,score,sex) VALUES(?,?,?)");
			for(Score score:list){
				ps.setString(1, score.getName());
				ps.setInt(2, score.getScore());
				ps.setInt(3, score.getSex());
				ps.executeUpdate();	
			}
			db.commit();
		} catch (NamingException | SQLException e) {
			e.printStackTrace();
			
		}finally{
			this.disconnect();
		}
		
	}
	//ここに追記
	public List<Score> find(int num){
		List<Score> list=new ArrayList<>();
		try {
			this.connect();
			ps=db.prepareStatement("SELECT * FROM scores ORDER BY score DESC LIMIT ?");
			ps.setInt(1, num);
			rs=ps.executeQuery();
			while(rs.next()){
				Score score=new Score();
				score.setId(rs.getInt("id"));
				score.setName(rs.getString("name"));
				score.setScore(rs.getInt("score"));
				score.setSex(rs.getInt("sex"));
				list.add(score);
				
			}
			
		} catch (NamingException | SQLException e) {
			
			e.printStackTrace();
		}finally{
			this.disconnect();
		}
		return list;
		
	}
	
	
}

16.下からgson.jarファイルをダウンロード


17.WEB-INF/lib/ファルダのにgson.jarをコピペで配置する。
18.controllerパッケージの中にGetData.java(Servlet)を以下のように作成する。

package controller;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.Gson;

import dao.ScoreDAO;
import model.Score;


@WebServlet("/GetData")
public class GetData extends HttpServlet {
	private static final long serialVersionUID = 1L;
         
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String count_s=request.getParameter("count");
		int count;
		if(count_s ==null){
			count=10;
		}else{
			count=Integer.parseInt(count_s);
		}
		ScoreDAO dao=new ScoreDAO();
		List<Score> list=dao.find(count);
		Gson gson = new Gson();
		response.setContentType("application/json;charset=UTF-8");
		PrintWriter out=response.getWriter();
		out.print("{\"result\": " + gson.toJson(list)+"}");
	}
}

19.GetData.javaをサーバーから実行してみよう。以下のようにJSONが取得できれば成功だ。

JSONに多くの情報を詰める

20.より多くの情報を返却するAPIを作成する。まずはmodelパッケージにRankingResultクラスを作成する。
●model.RankingResult.java

package model;

import java.io.Serializable;
import java.util.List;

public class RankingResult implements Serializable{
	private int lastId;//最後に挿入したデータのid
	private int rank;//最後に挿入したデータの順位
	private List<Score> list;//順位データを降順ソートしたリスト
	private boolean isRankingIn;//指定順位に入っているか
	public RankingResult(){}
	public int getLastId() {
		return lastId;
	}
	public void setLastId(int id) {
		this.lastId = id;
	}
	public int getRank() {
		return rank;
	}
	public void setRank(int rank) {
		this.rank = rank;
	}
	public List<Score> getList() {
		return list;
	}
	public void setList(List<Score> list) {
		this.list = list;
	}
	public boolean isRankingIn() {
		return isRankingIn;
	}
	public void setRankingIn(boolean isRankingIn) {
		this.isRankingIn = isRankingIn;
	}
	
}


21.ScoreDAOにメソッド追記する。

package dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import model.RankingResult;
import model.Score;

public class ScoreDAO {
	private Connection db;
	private PreparedStatement ps;
	private ResultSet rs;

	// 接続共通処理
	private void connect() throws NamingException, SQLException {
		Context context = new InitialContext();
		DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/jsp");
		this.db = ds.getConnection();
	}

	// 切断共通処理
	private void disconnect() {
		try {
			if (rs != null) {
				rs.close();
			}
			if (ps != null) {
				ps.close();
			}
			if (db != null) {
				db.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	public void insertOne(Score score){
		try {
			this.connect();
			ps=db.prepareStatement("INSERT INTO scores(name,score,sex) VALUES(?,?,?)");
			ps.setString(1, score.getName());
			ps.setInt(2, score.getScore());
			ps.setInt(3, score.getSex());
			ps.executeUpdate();
			
		} catch (NamingException | SQLException e) {
			
			e.printStackTrace();
		}finally{
			this.disconnect();
		}
	}
	
	public void InsertAll(List<Score> list){
		try {
			this.connect();
			db.setAutoCommit(false);
			ps=db.prepareStatement("INSERT INTO scores(name,score,sex) VALUES(?,?,?)");
			for(Score score:list){
				ps.setString(1, score.getName());
				ps.setInt(2, score.getScore());
				ps.setInt(3, score.getSex());
				ps.executeUpdate();	
			}
			db.commit();
		} catch (NamingException | SQLException e) {
			e.printStackTrace();
			
		}finally{
			this.disconnect();
		}
		
	}
	
	public List<Score> find(int num){
		List<Score> list=new ArrayList<>();
		try {
			this.connect();
			ps=db.prepareStatement("SELECT * FROM scores ORDER BY score DESC LIMIT ?");
			ps.setInt(1, num);
			rs=ps.executeQuery();
			while(rs.next()){
				Score score=new Score();
				score.setId(rs.getInt("id"));
				score.setName(rs.getString("name"));
				score.setScore(rs.getInt("score"));
				score.setSex(rs.getInt("sex"));
				list.add(score);
				
			}
			
		} catch (NamingException | SQLException e) {
			
			e.printStackTrace();
		}finally{
			this.disconnect();
		}
		return list;
		
	}
	
	//ここに追記
	public RankingResult getRankingResult(Score score,int count){
		//返却するRankingResultインスタンスを作成
		RankingResult result=new RankingResult();
		try {
			this.connect();
			//第一引数で入ってきたScoreインスタンスをデータベースに挿入
			ps=db.prepareStatement("INSERT INTO scores(name,score,sex) VALUES(?,?,?)",Statement.RETURN_GENERATED_KEYS);
			ps.setString(1, score.getName());
			ps.setInt(2, score.getScore());
			ps.setInt(3, score.getSex());
			ps.executeUpdate();
			//insertした際にオートインクリメントで付与されたid取得しresultインスタンスにセット
			rs=ps.getGeneratedKeys();
			if(rs.next()){
				result.setLastId(rs.getInt(1));
			}
			//今挿入したデータの順位を求める(今挿入したデータより点数の大きいデータの数を数えてそれに1をたす)
			ps=db.prepareStatement("SELECT COUNT(*) + 1 AS rank FROM scores WHERE score > ?");
			ps.setInt(1, score.getScore());
			rs=ps.executeQuery();
			if(rs.next()){
				result.setRank(rs.getInt("rank"));//今挿入したデータは何位かをresultにセットする。
				result.setRankingIn(rs.getInt("rank")<count);//今挿入したデータはランキングに入ったか?をresultにセット
			}
			List<Score> list=new ArrayList<>();//第二引数で指定のあった数だけ上位データを取得する。
			ps=db.prepareStatement("SELECT * FROM scores ORDER BY score DESC LIMIT ?");
			ps.setInt(1, count);
			rs=ps.executeQuery();
			//レコードの数まわるループ
			while(rs.next()){
				//レコードの情報を元にScoreインスタンスを作成
				Score s=new Score();
				s.setId(rs.getInt("id"));
				s.setName(rs.getString("name"));
				s.setScore(rs.getInt("score"));
				s.setSex(rs.getInt("sex"));
				//リストに追加
				list.add(s);
			}
			result.setList(list);//作成したリストをresultにセット
			
		} catch (NamingException | SQLException e) {
			
			e.printStackTrace();
		}finally{
			this.disconnect();
		}
		return result;//作成したRankingResultインスタンスを返却
	}
	
	
}


21.controllerパッケージに以下のようなGetRanking.java(Servlet)を作成する。

package controller;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.Gson;

import dao.ScoreDAO;
import model.RankingResult;
import model.Score;

@WebServlet("/GetRanking")
public class GetRanking extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		// parameterでcountが来てるか?
		String count = request.getParameter("count");
		// countが来ていなかったらtop10そうでなければcount件のデータを取得する。
		count = count == null ? "10" : count;
		String name = request.getParameter("name");
		name = name == null ? "未入力" : name;
		String score = request.getParameter("score");
		score = score == null ? "0" : score;
		String sex = request.getParameter("sex");
		sex = sex == null ? "0" : sex;
		// Scoreインスタンスを作成
		Score s = new Score();
		// インスタンス値をつめる
		s.setName(name);
		s.setScore(Integer.parseInt(score));
		s.setSex(Integer.parseInt(sex));
		ScoreDAO dao = new ScoreDAO();
		// RankingResultインスタンスを取得
		RankingResult result = dao.getRankingResult(s, Integer.parseInt(count));
		// パーサーをnew
		Gson gson = new Gson();
		// コンテントタイプをjsonに指定
		response.setContentType("application/json;charset=UTF-8");
		PrintWriter out = response.getWriter();
		// resultインスタンスをJSONにして出力する。
		out.print(gson.toJson(result));

	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}
}



22.テストをするためにWebContentの直下に以下のようなtest.jspを作成すする。
●test.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Insert title here</title>
</head>
<body>
<form action="GetRanking" method="post">
件数:<input type="number" name="count" value="10"><br>
名前:<input type="text" name="name"><br>
score:<input type="number" name="score"><br>
性別:<input type="radio" name="sex" value="0">男
<input type="radio" name="sex" value="1">女<br>
<button type="submit">送信</button>
</form>
</body>
</html>

23.フォームから適当にデータを入れてみよう。

24.以下のようにJSONデータが出力されれば成功だ。(ブラウザによってはファイル保存となるが、保存したのちテキストエディタで確認すればよい)

25.整形サイトを利用して見やすく表示してみる。先ほど入力したデータ30000点というのはベスト10に入っていなくて、699位で,
オートインクリメントで付与されたidが1001ということがわかる。それと合わせてトップ10のデータが表示されている。

26.このように実際のゲームに必要な情報をJSONとして出力できるようになっておくことが重要だ。この後UNITYからこのAPIにアクセスする課題があるので合わせてやってみるとよいだろう。