写在最前
由于疫情原因,学习也很懈怠,这个事情三天打鱼两天晒网也做了一个月。得反思一下自己…
功能描述
在原有贪吃蛇的基础上,当游戏结束之后,在控制台提示输入玩家名字,如果输入的是“查询”,则将之前所有玩家的成绩打印在控制台上,如果不是,则把贪吃蛇游戏分数、玩家名字、游戏时间上传到服务器的数据库(MySQL)。
(至于我为什么要在控制台上实现这个功能,而不是在写一个窗口实现查询和上传成绩呢?我也这么想过,试过一段时间,可是Java的swing我真的玩不太明白,干脆简单的来,原理都是一样的!)
视频介绍
Java贪吃蛇数据库连接版
素材准备
贪吃蛇的实现参考我之前博客,此次完全在之前的基础上添加功能,游戏主体代码不变:贪吃蛇传送门
服务器是用电脑虚拟机下的ubuntu(之前用过centos7,坑很多,最后放弃了,还是ubuntu方便一点)
值得注意的是在安装ubuntu后要选择好阿里的下载服务器,否则速度慢的不行(我原来等过一下午下载MySQL,现在想想傻得不行…):解决ubuntu下载慢的问题
解决这个问题以后,咱们就得要完成ubuntu下的MySQL安装:下载安装传送门
这样我们就完成了一个用电脑作为服务器的搭建,下面就需要完成贪吃蛇与服务器数据之前的交换。
思路介绍
MySQL是用SQL语句实现对数据库的所有操作,什么是SQL语句?
结构化查询语言(Structured Query Language)简称SQL,结构化查询语言是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统;sql 语句就是对数据库进行操作的一种语言。(在我的理解是和Linux命令差不多功能的语言)
我们会用到哪些SQL语句?
因为是实现记录的上传和查询。自然就是对数据库的增和查。(如需详细学习SQL语句,请看最后的资源下载板块,下载MySQL笔记来学习MySQL)
用Java怎么操作数据库?
Java对数据库的操作叫做JDBC,有一系列的类帮助我们完成对数据库的操作(如需详细学习,可以下载最后的资源下载板块,来下载JDBC笔记)
所以我们应该怎么实现我们这个功能?
分为三个步骤:
1,加载JDBC驱动
2,获取与数据库的链接
3,执行相关SQL语句
4,处理数据库的返回结果(如果有)
代码实现
JDBCUtils
这是我自己写的类,功能是完成对数据库连接,资源的释放等一些与数据库相关的操作,将其封装起来,代码更具有可读性。
值得说一下的是:将数据库的URL(URL怎么写传送门),用户名密码可以放在一个配置文件当中,以后要更改只需要修改配置文件就可以了,而读取配置文件有两种方法:
- 1、Properties方法:Properties文件是java中很常用的一种配置文件,文件后缀为“.properties”,属文本文件,文件的内容格式是“键=值”的格式,可以用“#”作为注释,java编程中用到的地方很多,运用配置文件,可以便于java深层次的解耦。
- 2、类加载器的方法:ClassLoader 类加载器
因为本文没有采用这种放啊,所以详细介绍不多说,想学习的进行传送门:类加载器传送门
实现方法是:
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL res = classLoader.getResource("jdbc.properties");
String path = res.getPath();//获取路径
System.out.println(path);
pro.load(new FileReader(path));
/**
* 注意!!!!!!
* 利用类加载器不能使用的原因是文件夹有中文字符,导致乱码
*/
另外在对数据库进行操作的时候,会使用到很多个类,这些类要释放资源,所以在JDBCUtils写一个对资源管理的方法很有必要。
使用多态的形式释放资源
public static void close(Statement stmt, Connection conn) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 释放资源
*
* @param stmt
* @param conn
*/
public static void close(ResultSet rs, Statement stmt, Connection conn) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
JDBCUtils完整代码。
package util;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/**
* JDBC工具类
*/
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
/**
* 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块
*/
static {
//读取资源文件,获取值。
try {
//1. 创建Properties集合类。
Properties pro = new Properties();
/**
* Properties文件是java中很常用的一种配置文件,文件后缀为“.properties”,属文本文件,
* 文件的内容格式是“键=值”的格式,可以用“#”作为注释,java编程中用到的地方很多,运用配置文件,可以便于java深层次的解耦。
*/
//获取src路径下的文件的方式--->ClassLoader 类加载器
// ClassLoader classLoader = JDBCUtils.class.getClassLoader();
// URL res = classLoader.getResource("jdbc.properties");
// String path = res.getPath();//获取路径
// System.out.println(path);
// pro.load(new FileReader(path));
/**
* 注意!!!!!!
* 利用类加载器不能使用的原因是文件夹有中文字符,导致乱码
*/
pro.load(new FileReader("src/jdbc.properties"));
//3. 获取数据,赋值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
//4. 注册驱动
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取连接
*
* @return 连接对象
*/
public static Connection getConnection() {
try {
return DriverManager.getConnection(url, user, password);
} catch (SQLException throwables) {
System.out.println("获取连接失败!");
throwables.printStackTrace();
return null;
}
}
/**
* 释放资源
*
* @param stmt
* @param conn
*/
public static void close(Statement stmt, Connection conn) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 释放资源
*
* @param stmt
* @param conn
*/
public static void close(ResultSet rs, Statement stmt, Connection conn) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
Connection类
首先,要加载JDBC的驱动(最后资源下载板块会提供),要注意的是,要选择好MySQL版本和驱动版本。这里使用MySQL8实验。
先上代码(看不懂没关系)
public static Connection getConnection() {
try {
return DriverManager.getConnection(url, user, password);
} catch (SQLException throwables) {
System.out.println("获取连接失败!");
throwables.printStackTrace();
return null;
}
}
我们先写一个方法来获取到数据库的链接对象Connection,该对象的数据库能够提供描述其表,其支持的SQL语法,其存储过程,此连接的功能等的信息。
DriverManager.getConnection是尝试建立与给定数据库URL的连接。三个参数分别是jdbc的URL,数据库的用户名,数据库的密码。 DriverManager尝试从一组已注册的JDBC驱动程序中选择适当的驱动程序。 连接成功将返回Connection对象。
PreparedStatement类
这个类是干什么的?
PreparedStatement类详解传送门
SQL语句已预编译并存储在一个PreparedStatement对象中。 然后可以使用该对象多次有效地执行此语句。
怎么获取这个类?
我们将已经获取好的Connection对象下的Connection.prepareStatement(String sql)方法创建一个PreparedStatement对象,用于将参数化的SQL语句发送到数据库。
怎么执行SQL语句呢?
我们将已经获取好的PreparedStatement对象下的PreparedStatement.executeUpdate()方法可以执行增删改的SQL语句。这样我们就完成了对数据库的添加代码。
public static void insert(String name, int sore) {
Connection con = null;//利用JDBC工具类获得连接对象
PreparedStatement psta = null;
try {
String insert_sql = "INSERT INTO record ( name ,sore,insert_time) VALUES (?,?,CURRENT_TIMESTAMP )";
con = JDBCUtils.getConnection();
// psta = con.createStatement();//用于执行静态SQL语句并返回其生成的结果的对象。
/**
使用prepareStatement而不是原来的Statement目的是解决sql注入问题。
*/
con.setAutoCommit(false);//打开事务
psta = con.prepareStatement(insert_sql);//用于执行静态SQL语句并返回其生成的结果的对象。
// int res = psta.executeUpdate(insert_sql
psta.setString(1, name);//给sql第一个?赋值
psta.setInt(2, sore);//给sql第二个?赋值
int res = psta.executeUpdate();
/**
* 在prepareStatement里面执行sql,先给sql的参数赋值(字符串里面的?)
* 然后执行这条语句(注意这条语句没有参数)
*/
/*
如果执行成功,会返回影响的行数。
为0则数据库没有进行任何变化。
*/
if (res > 0) {
System.out.println("添加成功!");
} else {
System.out.println("添加失败!");
}
con.commit();//如果没有异常的发生,证明操作正常,则提交事务,改变数据库的值
} catch (Exception throwables) {
if (con != null) {
try {
con.rollback();
/**
* 如果发生了异常,则需要把事务回滚,防止数据库意外发生
* 需要注意的是,在catch里面,要选择Exception,只要出现了异常就要进行回滚操作
*/
System.out.println("添加失败!");
} catch (SQLException e) {
e.printStackTrace();
}
}
throwables.printStackTrace();
} finally {
JDBCUtils.close(psta, con);//释放资源
}
}
这样我们就完成了对数据库的增添功能,那么查找怎么办?
查找和添加思路一样,都是获取对象,执行SQL语句,唯一不一样的是,查询会返回一个结果集,而增添返回就是影响的行数。所以查询你就要求我们对结果集进行操作。
这个结果集是什么?
这个结果集是ResultSet类,是PreparedStatement下的executeQuery()方法的放回结果,这个结果集类似于一个指针,每次指向一行结果,只对一行结果进行操作,如果换行,需要在使用方法。具体内容请传送门:ResultSet类详解传送门
这样就是查询方法的完整代码。
public static void select() {
ResultSet result = null;
Connection con = null;
PreparedStatement psta = null;
try {
String select_sql = "SELECT * FROM record";
con = JDBCUtils.getConnection();
psta = con.prepareStatement(select_sql);//用于执行静态SQL语句并返回其生成的结果的对象。
result = psta.executeQuery();
while (result.next()) {
System.out.println(result.getString(1) + " " + result.getInt(2) + " " + result.getString(3));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.close(result, psta, con);
}
}
}
资源下载
代码:(代码当中MySQLl的IP地址为空,驱动在lib文件夹下)
链接:https://pan.baidu.com/s/1s_Wesv-w4DSZcmtIEh33gg
提取码:btaa
MySQL笔记:点击下载
JDBC笔记:点击下载