案例:
#创建数据库
create database day22_JDBC;
#使用数据库
use day22_JDBC;
###创建分类表
CREATE TABLE `sort` (
`sid` int(11) NOT NULL AUTO_INCREMENT,
`sname` varchar(100) DEFAULT NULL,
`sprice` double(10,2) DEFAULT NULL,
`sdesc` varchar(255) DEFAULT NULL,
PRIMARY KEY (`sid`)
)
#初始化数据
INSERT INTO `sort` VALUES ( '家电', '8900.00', '家具价格上调');
INSERT INTO `sort` VALUES ('服饰', '502.99', '换季销售');
INSERT INTO `sort` VALUES ('化妆品', '50.00', '洗发水促销');
发开步骤:
- 注册驱动.
- 获得连接.
- 获得语句执行平台
- 执行sql语句
- 处理结果
- 释放资源.
public static void main(String[] args) throws Exception {
//1.注册驱动
/*
* JDBC规范定义驱动接口:java.sql.Driver,MySql驱动包提供了实现类:com.mysql.jdbc.Driver
* DriverManager工具类,提供注册驱动的方法 registerDriver(),方法的参数是java.sql.Driver,所以我们可以通过如下语句进行注册:
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
以上代码不推荐使用,存在两方面不足
1. 硬编码,后期不易于程序扩展和维护
2. 驱动被注册两次。
通常开发我们使用Class.forName() 加载一个使用字符串描述的驱动类。
如果使用Class.forName()将类加载到内存,该类的静态代码将自动执行。
通过查询com.mysql.jdbc.Driver源码,我们发现Driver类“主动”将自己进行注册
*/
//Class.forName("com.mysql.jdbc.Driver"); //(不推荐)mysql-connector-java-8.0
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获得连接.
/*
static Connection getConnection(String url, String user, String password)
返回值是Connection接口的实现类,在mysql驱动程序
url: 数据库地址 jdbc:mysql://连接主机IP:端口号//数据库名字
*/
String url = "jdbc:mysql://localhost:3306/day22_jdbc?serverTimezone = GMT";
String username = "root";
String password = "123";
Connection conn = DriverManager.getConnection(url, username, password);
//3.获得语句执行平台,过数据库连接对象,获取到SQL语句的执行者对象
Statement statement = conn.createStatement();
//4.执行sql语句
// int executeUpdate(String sql) 执行数据库中的SQL语句, insert delete update
// ResultSet executeQuery(String sql) 执行数据库中的SQL语句, select
String sql = "select * from sort";
ResultSet result = statement.executeQuery(sql);
System.out.println(result);
//5.处理结果
while (result.next()) {
System.out.println(result.getObject(1) + "" + result.getObject(2));
}
//6.释放资源.
statement.close();
conn.close();
}
API详解:获得语句执行平台
int executeUpdate(String sql); --执行insert update delete语句.
ResultSet executeQuery(String sql); --执行select语句.
boolean execute(String sql); --执行select返回true 执行其他的语句返回false.
API详解:处理结果集(执行insert、update、delete无需处理)
ResultSet实际上就是一张二维的表格,我们可以调用其boolean next()方法指向某行记录,当第一次调用next()方法时,便指向第一行记录的位置,这时就可以使用ResultSet提供的getXXX(int col)方法(与索引从0开始不同个,列从1开始)来获取指定列的数据:
rs.next();//指向第一行
rs.getInt(1);//获取第一行第一列的数据
常用方法:
- Object getObject(int index) / Object getObject(String name) 获得任意对象
- String getString(int index) / Object getObject(String name) 获得字符串
- int getInt(int index) / Object getObject(String name) 获得整形
- double getDouble(int index) / Object getObject(String name) 获得双精度浮点型
PrepareStatement
接口预编译
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/day22_jdbc?serverTimezone = GMT";
String username = "root";
String password = "123";
Connection conn = DriverManager.getConnection(url, username, password);
String sql = "select * from sort where sid=? ";
PreparedStatement pstatement = conn.prepareStatement(sql);
//调用 pstatement 对象set方法,设置问号?占位符上的参数
pstatement.setObject(1,1);
ResultSet set = pstatement.executeQuery();
while (set.next()){
System.out.println(set.getObject(1));
}
}
JDBCUtils帮助类
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* @Description: JDBC帮助类
**/
public class JDBCUtils {
private static final String DRIVERNAME = "com.mysql.cj.jdbc.Driver";
private static final String URL = "jdbc:mysql://localhost:3306/day22_jdbc?serverTimezone = GMT";
private static final String USER = "root";
private static final String PASSWORD = "123";
JDBCUtils() {
try {
Class.forName(DRIVERNAME);
} catch (Exception ex) {
System.out.println("MYSQL连接失败");
}
}
public static Connection getConnction() throws Exception {
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
return conn;
}
/**
*
* @param conn 连接
* @param statement
*/
public static void close(Connection conn, Statement statement) {
if (statement != null) {
try {
statement.close();
} catch (Exception ex) {
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception ex) {
}
}
}
public static void close(Connection conn, Statement statement, ResultSet set) {
if (set != null) {
try {
set.close();
} catch (Exception ex) {
}
}
if (statement != null) {
try {
statement.close();
} catch (Exception ex) {
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception ex) {
}
}
}
}
properties配置文件
开发中获得连接的4个参数(驱动、URL、用户名、密码)通常都存在配置文件中,方便后期维护,程序如果需要更换数据库,只需要修改配置文件即可。
通常情况下,我们习惯使用properties文件,此文件我们将做如下要求:
- 文件位置:任意,建议src下
- 文件名称:任意,扩展名为properties
- 文件内容:一行一组数据,格式是“key=value”. a) key命名自定义,如果是多个单词,习惯使用点分隔。例如:jdbc.driver b) value值不支持中文,如果需要使用非英文字符,将进行unicode转换。
IDEA 在src路径下,创建 Resouce bundle
文件 database.properties
drivername=com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/day22_jdbc?serverTimezone = GMT
user = root
pass = 123
package utils;
import java.io.FileReader;
import java.io.Reader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
/**
* @Description:
* @Package: utils
* @ClassName: JDBCUtils
* @Author: tangge
* @CreateDate: 2018年08月09 11:07
* @Version: 1.0
**/
public class JDBCUtils {
private static Connection conn;
private static String DRIVERNAME;
private static String URL ;
private static String USER ;
private static String PASSWORD ;
static {
try {
readConfig();
Class.forName(DRIVERNAME);
conn = DriverManager.getConnection(URL, USER, PASSWORD);
} catch (Exception ex) {
//throw new RuntimeException("MYSQL连接失败```");
System.out.println("MYSQL连接失败");
}
}
private static void readConfig() throws Exception{
String path = System.getProperty("user.dir")+"\\src\\";
// 1 使用Properties处理流
// 使用load()方法加载指定的流
Properties props = new Properties();
Reader in = new FileReader(path+"database.properties");
/**
* class.getClassLoader()返回类的类加载器
* public ClassLoader getClassLoader()
* ClassLoader.getResourceAsStream 返回用于读取指定资源的输入流。
* public InputStream getResourceAsStream(String name)
*/
// InputStream in = JDBCDemo.class.getClassLoader()
// .getResourceAsStream("database.properties");
props.load(in);
// 2 使用getProperty(key),通过key获得需要的值,
DRIVERNAME = props.getProperty("drivername");
URL = props.getProperty("url");
USER = props.getProperty("user");
PASSWORD = props.getProperty("pass");
}
public static Connection getConnction() {
return conn;
}
}
调用
public static void main(String[] args) {
try (Connection conn = JDBCUtils.getConnction()) {
String sql = "select * from sort ";
PreparedStatement pstatement = conn.prepareStatement(sql);
ResultSet set = pstatement.executeQuery();
List<SortInfo> list = new ArrayList<SortInfo>();
while (set.next()) {
SortInfo sort = new SortInfo(
set.getInt("sid"), set.getString("sname"),
set.getDouble("sprice"), set.getString("sdesc")
);
list.add(sort);
}
for (SortInfo sort : list
) {
System.out.println(sort);
}
} catch (Exception e) {
System.out.println("连接失败!!");
}
DBUtils 帮助类(commons-dbutils)
DBUtils是java编程中的数据库操作实用工具,小巧简单实用。
DBUtils封装了对JDBC的操作,简化了JDBC操作,可以少写代码。
Dbutils三个核心功能介绍
QueryRunner中提供对sql语句操作的API.
ResultSetHandler接口,用于定义select操作后,怎样封装结果集.
DbUtils类,它就是一个工具类,定义了关闭资源与事务处理的方法
QueryRunner 核心类
update(Connection conn, String sql, Object... params)
,用来完成表数据的增加、删除、更新操作query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params)
,用来完成表数据的查询操作
private static Connection conn = JDBCUtils.getConnction();
public static void main(String[] args) {
try {
//Insert();
//Update();
Delete();
} catch (Exception ex) {
System.out.println(ex.toString());
}
}
/**
* 添加
*/
public static void Insert() throws Exception {
QueryRunner qr = new QueryRunner();
String insertSQL = "INSERT INTO `day22_jdbc`.`sort` (`sname`, `sprice`, `sdesc`) VALUES (?,?,?);\n";
Object[] param = {"乒乓求", 289.33, "购买体育用品"};
int row = qr.update(JDBCUtils.getConnction(), insertSQL, param);
System.out.println(row);
DbUtils.closeQuietly(conn);
}
/**
* 修改
*/
public static void Update() throws Exception {
QueryRunner qr = new QueryRunner();
String updateSQL = "UPDATE `day22_jdbc`.`sort` SET `sname`=?, `sprice`=?, `sdesc`=? WHERE (`sid`=?);\n";
Object[] param = {"乒乓球", 12.22, "购买体育用品~~", 4};
int row = qr.update(JDBCUtils.getConnction(), updateSQL, param);
System.out.println(row);
DbUtils.closeQuietly(conn);
}
/**
* 删除
*/
public static void Delete() throws Exception {
QueryRunner qr = new QueryRunner();
String updateSQL = "delete from `day22_jdbc`.`sort` WHERE (`sid`=?);\n";
int row = qr.update(conn, updateSQL, 5);
System.out.println(row);
DbUtils.closeQuietly(conn);
}
ResultSetHandler接口
Handler | 说明 |
---|---|
ArrayHandler | 将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是这条记录中的每一个字段的值 |
ArrayListHandler | 将结果集中的每一条记录都封装到一个Object[]数组中,将这些数组在封装到List集合中。 |
BeanHandler | 将结果集中第一条记录封装到一个指定的javaBean中。 |
BeanListHandler | 将结果集中每一条记录封装到指定的javaBean中,将这些javaBean在封装到List集合中 |
ColumnListHandler | 将结果集中指定的列的字段值,封装到一个List集合中 |
ScalarHandler | 它是用于单数据。例如select count(*) from 表操作。 |
MapHandler | 将结果集第一行封装到Map集合中,Key 列名, Value 该列数据 |
MapListHandler | 将结果集第一行封装到Map集合中,Key 列名, Value 该列数据,Map集合存储到List集合 |
package QueryRunnerdemo;
import Models.SortInfo;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayHandler;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import utils.JDBCUtils;
/**
* @Description:
* @Package: QueryRunnerdemo
* @ClassName: QueryRunnerHandlerdemo
* @Author: tangsansan
* @CreateDate: 2018年08月10 11:35
* @Version: 1.0
**/
public class QueryRunnerHandlerdemo {
private static Connection conn = JDBCUtils.getConnction();
public static void main(String[] args) {
try {
QueryMapListHandler();
} catch (Exception ex) {
System.out.println(ex.toString());
}
}
/**
* 1.QueryRunner.query(String sql, ResultSetHandler<T> rsh)
* 执行给定的SELECT SQL而不使用任何替换参数。
* 2.QueryRunner.query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params)
* 使用替换参数执行SQL SELECT查询。
* 3.QueryRunner.query(String sql, ResultSetHandler<T> rsh, Object... params)
* 执行给定的SELECT SQL查询并返回结果对象。
*/
public static void QueryArrayHandler() throws Exception {
String sql = "select * from sort";
QueryRunner qr = new QueryRunner();
Object[] obj = qr.query(conn, sql, new ArrayHandler());
for (Object in : obj
) {
System.out.print(in+" ");
}
System.out.println();
}
/**
结果:
1 家电1 8900.0 家具价格上调
*/
/**
* @throws Exception
*/
public static void QueryArrayListHandler() throws Exception {
String sql = "select * from sort";
QueryRunner qr = new QueryRunner();
List<Object[]> obj = qr.query(conn, sql, new ArrayListHandler());
for (Object[] list : obj
) {
for (Object in : list
) {
System.out.print(in+" ");
}
System.out.println();
}
}
/**
结果:
1 家电1 8900.0 家具价格上调
2 服饰 502.99 换季销售
3 化妆品 50.0 洗发水促销
4 乒乓球 12.22 购买体育用品~~
5 乒乓求 289.33 购买体育用品
*/
/**
* ResultSetHandler将第一ResultSet行,转换为JavaBean的实现。这个类是线程安全的。
* BeanHandler(Class<? extends T> type)
* @throws Exception
*/
public static void QueryBeanHandler() throws Exception {
String sql = "select * from sort";
QueryRunner qr = new QueryRunner();
SortInfo sort = qr.query(conn, sql, new BeanHandler<SortInfo>(SortInfo.class));
System.out.println(sort.getSid()+" "+sort.getSname());
}
/**
结果:
1 家电1
*/
/**
* 传递结果集的实现类BeanListHandler
* BeanListHandler(Class<? extends T> type)
* @throws Exception
*/
public static void QueryBeanListHandler() throws Exception {
String sql = "select * from sort";
QueryRunner qr = new QueryRunner();
List<SortInfo> sort = qr.query(conn, sql, new BeanListHandler<SortInfo>(SortInfo.class));
System.out.println(sort);
for (SortInfo s:sort
) {
System.out.println(s.getSid()+" "+s.getSname());
}
}
/**
结果:
1 家电1
2 服饰
3 化妆品
4 乒乓球
5 乒乓求
*/
/**
* 将结果集中指定的列的字段值,封装到一个List集合中
* List<Object> 每个列数据类型不同
* ***********构造函数
ColumnListHandler(int columnIndex)
ColumnListHandler(String columnName)
* @throws Exception
*/
public static void QueryColumnListHandler() throws Exception {
String sql = "select * from sort";
QueryRunner qr = new QueryRunner();
Object sort = qr.query(conn, sql, new ColumnListHandler<Object>("sname"));
System.out.println(sort);
}
//结果:[家电1, 服饰, 化妆品, 乒乓球, 乒乓求]
/**
* 它是用于单数据。例如select count(*) from 表操作。
* ***********构造函数
ScalarHandler()
ScalarHandler(int columnIndex)
ScalarHandler(String columnName)
* @throws Exception
*/
public static void QueryScalarHandler() throws Exception {
String sql = "select * from sort";
QueryRunner qr = new QueryRunner();
Object sort = qr.query(conn, sql, new ScalarHandler<Object>());
System.out.println(sort);
}
//结果:1
/**
* 将结果集第一行封装到Map集合中,Key 列名, Value 该列数据
* ***********构造函数
MapHandler()
使用BasicRowProcessorfor转换创建MapHandler的新实例 。
MapHandler(RowProcessor convert)
* @throws Exception
*/
public static void QueryMapHandler() throws Exception {
String sql = "select * from sort";
QueryRunner qr = new QueryRunner();
Map<String,Object> sort = qr.query(conn, sql, new MapHandler());
System.out.println(sort); //{sid=1, sname=家电1, sprice=8900.0, sdesc=家具价格上调}
for (String key :sort.keySet()
) {
System.out.println(key+":"+sort.get(key));
}
}
/**
* {sid=1, sname=家电1, sprice=8900.0, sdesc=家具价格上调}
sid:1
sname:家电1
sprice:8900.0
sdesc:家具价格上调
*/
/**
* 将结果集第一行封装到Map集合中,Key 列名, Value 该列数据,Map集合存储到List集合
* ***********构造函数
MapListHandler()
使用BasicRowProcessorfor转换创建MapHandler的新实例 。
MapListHandler(RowProcessor convert)
* @throws Exception
*/
public static void QueryMapListHandler() throws Exception {
String sql = "select * from sort";
QueryRunner qr = new QueryRunner();
List<Map<String,Object>> sort = qr.query(conn, sql, new MapListHandler());
System.out.println(sort);
//[{sid=1, sname=家电1, sprice=8900.0, sdesc=家具价格上调}, {sid=2, sname=服饰, sprice=502.99, sdesc=换季销售},
// {sid=3, sname=化妆品, sprice=50.0, sdesc=洗发水促销}, {sid=4, sname=乒乓球, sprice=12.22, sdesc=购买体育用品~~},
// {sid=5, sname=乒乓求, sprice=289.33, sdesc=购买体育用品}]
}
}
连接池 DataSource
实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常情况我们采用连接池技术,来共享连接Connection。这样我们就不需要每次都创建连接、释放连接了,这些操作都交给了连接池
规范
Java为数据库连接池提供了公共的接口:javax.sql.DataSource
,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!
常见的连接池:DBCP、C3P0。
DBCP连接池
DBCP也是一个开源的连接池,是Apache Common成员之一,在企业开发中也比较常见,tomcat内置的连接池。
jar包
导入3个包
commons-dbcp2-2.5.0.jar
commons-pool2-2.6.0.jar
commons-logging-1.2.jar
编写工具类
连接数据库表的工具类, 采用DBCP连接池的方式来完成,Java中提供了一个连接池的规则接口 :DataSource
: 它是java中提供的连接池,作为 DriverManager
工具的替代项。在DBCP包中提供了DataSource
接口的实现类,我们要用的具体的连接池 BasicDataSource
类
//简单创建一个datasource
BasicDataSource db = new BasicDataSource();
String driverName = DBCPHelper.getDRIVERNAME();
db.setDriverClassName(DBCPHelper.getDRIVERNAME());
db.setUrl(DBCPHelper.getURL());
db.setUsername(DBCPHelper.getUSER());
db.setPassword(DBCPHelper.getPASSWORD());
try {
//调用getconnection
Connection conn= db.getConnection();
System.out.println(conn);
}catch (Exception ex){
ex.printStackTrace();
throw new RuntimeException(ex);
}
//1641808846, URL=jdbc:mysql://localhost:4040/day22_jdbc?serverTimezone = GMT, UserName=root@localhost, MySQL Connector/J
常见配置项
参考:http://commons.apache.org/proper/commons-dbcp/configuration.html
分类 | 属性 | 描述 |
---|---|---|
必须 | driverClassName | 数据库驱动名称 |
url | 数据库的地址 | |
username | 用户名 | |
password | 密码 | |
基本项(扩展) | maxActive | 最大连接数量 |
minIdle | 最小空闲连接 | |
maxIdle | 最大空闲连接 | |
initialSize | 初始化连接 |
DBCP帮助类
package utils;
import java.io.FileReader;
import java.io.Reader;
import java.util.Properties;
import org.apache.commons.dbcp2.BasicDataSource;
/**
* @Description:
* @Package: utils
* @ClassName: DBCPHelper
* @Author: tangsansan
* @CreateDate: 2018年08月10 15:56
* @Version: 1.0
**/
public class DBCPHelper {
private static BasicDataSource dbSource = new BasicDataSource();
private static String DRIVERNAME;
private static String URL;
private static String USER;
private static String PASSWORD;
static {
try {
readConfig();
} catch (Exception ex) {
System.out.println("MYSQL配置读取失败");
}
}
private static void readConfig() throws Exception {
String path = System.getProperty("user.dir") + "\\src\\";
// 1 使用Properties处理流
// 使用load()方法加载指定的流
Properties props = new Properties();
Reader in = new FileReader(path + "database.properties");
props.load(in);
// 2 使用getProperty(key),通过key获得需要的值,
DRIVERNAME = props.getProperty("drivername");
URL = props.getProperty("url");
USER = props.getProperty("user");
PASSWORD = props.getProperty("pass");
//创建一个datasource
dbSource.setDriverClassName(DRIVERNAME);
dbSource.setUrl(URL);
dbSource.setUsername(USER);
dbSource.setPassword(PASSWORD);
}
public static BasicDataSource getDataSource() {
return dbSource;
}
}
测试帮助类
package QueryRunnerdemo;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import utils.DBCPHelper;
/**
* @Description:
* @Package: QueryRunnerdemo
* @ClassName: dbcpdemo
* @Author: tangsansan
* @CreateDate: 2018年08月10 15:45
* @Version: 1.0
**/
public class dbcpdemo {
private static QueryRunner qr = new QueryRunner(DBCPHelper.getDataSource());
public static void main(String[] args) {
try {
// QueryArrayListHandler();
Insert();
}catch (Exception ex){
// ex.printStackTrace();
throw new RuntimeException(ex);
}
}
/**
* 查询集合
* @throws Exception
*/
public static void QueryArrayListHandler() throws Exception {
String sql = "select * from sort";
List<Object[]> obj = qr.query(sql, new ArrayListHandler());
for (Object[] list : obj
) {
for (Object in : list
) {
System.out.print(in + " ");
}
System.out.println();
}
}
/**
* 添加
*/
public static void Insert() throws Exception {
String insertSQL = "INSERT INTO `day22_jdbc`.`sort` (`sname`, `sprice`, `sdesc`) VALUES (?,?,?);\n";
Object[] param = {"篮球", 30.55, "购买体育用品"};
int row = qr.update(insertSQL, param);
System.out.println(row);
}
}
question:遇见的问题:
导入 mysql-connector-java-8.0.jar
出错:
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
Exception in thread "main" java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
1.加载类“com.mysql.jdbc.Driver”。这已被弃用。新的驱动程序类是com.mysql.cj.jdbc.Driver
。驾驶员通过SPI自动注册和驱动程序类的
解决方法:这个问题很简单按照它的提示把代码中com.mysql.jdbc.Driver改成com.mysql.cj.jdbc.Driver就可以了
//Class.forName("com.mysql.jdbc.Driver"); //(不推荐)mysql-connector-java-8.0
Class.forName("com.mysql.cj.jdbc.Driver");
2.线程 "main " java.sql.SQLException 中的异常: 服务器时区值 "Öйú±ê׼ʱ¼ä" 无法识别或代表多个时区。如果要利用时区支持, 则必须配置服务器或 JDBC 驱动程序 (通过 serverTimezone 配置属性), 以便使用更特定的时区值。
解决方法:
这是由于数据库和系统时区差异所造成的,在JDBC连接的URL后面加上serverTimezone = GMT即可解决问题,如果需要使用GMT + 8时区,需要写成GMT%2B8,否则会被解析为空。再一个解决办法就是使用低版本的MySQL jdbc驱动,
String url = "jdbc:mysql://localhost:3306/test?serverTimezone = GMT";
String username="root";
String password="123";
Connection conn = DriverManager.getConnection(url,username,password);