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にアクセスする課題があるので合わせてやってみるとよいだろう。