Java连接、操作MySQL数据库
MySQL数据库简介
此处先给出MySQL官网?
Java对于数据库常用操作的封装
Java通过提供统一API定义的方式来实现对各种数据库进行操作时Java代码编码层面的统一,有效减少了Java代码在跨数据库时——Java项目在数据库需要变更时需要修改代码的问题。这样做的好处是显而易见的,①使操作不同的数据库在Java层面得到了统一,有利于Java项目实现跨数据库,当然变更数据库时,项目中要导入对应数据库的jar包②淡化不同数据库版本在Java代码中的差异,从而进一步突出Java项目中的业务逻辑。在使用Java的时候,不用关心底层使用什么数据库,只需要关心业务逻辑即可。API的具体实现由各数据库厂商自己提供(jar包,以驱动的形式)。在使用的时候,只需在自己的项目中导入对应数据库驱动的jar包即可,示意图如下所示。
目前常用的数据库种类很多,比如常见的MySQL、Oracle、SqlServer、Sqlite… 这些数据库都有自己的特性,如果Java为每一种数据库开发一套API,进行数据库操作,那么将是很头疼的,原因如下:
1、每一种数据库都有差异,那么就代表每一套API的使用方法可能都不同;
2、不便于数据库迁移,比如,前期使用MySQL,到后期发现Oracle更加适合项目,于是将数据从MySQL迁移到Oracle中了,因为API不同,所以需要重新修改所有的SQL操作。
3、除此之外,这么多套API,维护起来也是很麻烦的;
连接、操作MySQL数据库用到的类
连接操作数据库过程中会用到的类,如下图所示。
Java工程 连接、操作数据库
- 首先将jar包
mysql-connector-java-8.0.30.jar
导入Java工程的正确位置,通常在lib目录下。Java工程导入jar包的方法详见这篇博客。 - 设置JDBC驱动名和数据库URL,JDBC连接mysql数据库时默认使用IP地址
127.0.0.1
和端口号3306
,所以当连接数据库与此一致时,可省略数据库地址和端口为jdbc:mysql:///test
。
①MySQL 8.0 以下版本 - JDBC 驱动名(即jar包中的名称),注册JDBC驱动及数据库 URL
static final String JDBC_DRIVER = “com.mysql.jdbc.Driver”;
static final String DB_URL = “jdbc:mysql://localhost:3306/test”;
②// MySQL 8.0 以上版本 - JDBC 驱动名(即jar包中的名称),注册JDBC驱动及数据库 URL
static final String JDBC_DRIVER = “com.mysql.cj.jdbc.Driver”;
static final String DB_URL = “jdbc:mysql://localhost:3306/test?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC”;
因为本次使用的是
mysql-connector-java-8.0.30.jar
,如第一步中所述。若使用①中配置url,在第3步使用JDBC_DRIVER注册JDBC驱动时,会报如下错误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.
。
- 加载数据库驱动(即jar包中的名称),注册JDBC驱动:
Class.forName(JDBC_DRIVER);
。
Class.forName(JDBC_DRIVER);来注册加载驱动的原因
Class.forName(类名)
是让JVM查找并加载该类名对应的类中的静态代码块,而本文示例中使用的是MySQL数据库,所以选择com.mysql.jdbc.Driver
类(MySQL数据库厂商提供的 JDBC 驱动,并借助于这些驱动连接数据库)中正好只有一段静态代码——使用Jdbc程序中的DriverManager来加载驱动,由此创建与数据库的链接,如下所示。
所以删除本句话及catch中本句,可能抛出的异常。
在实际开发中之所以推荐使用
Class.forName(“com.mysql.jdbc.Driver”);
,而非registerDriver方法,来注册驱动,这是因为:
如果代码中直接使用mysql的jar包中的DriverManager类中的方法,会使程序依赖mysql jar包的api并与所使用的数据库产生耦合,当项目程序要更换底层数据库时,因为脱离mysql的jar包导致程序无法编译等非常麻烦的问题。
但此方式有问题,因为若更换数据库了,Class.forName(“com.mysql.jdbc.Driver”);
中的参数“com.mysql.jdbc.Driver”
不需要变更吗???自认待确认
Class.forName(“com.mysql.jdbc.Driver”);
的内容详见此博客。
- 创建【并打开?(有博客提到,待确认)】到数据库的链接会用到的Connection对象,(其实就是创建了一个和数据库服务器进行连接的Socket(传输层,使用TCP)。当Java项目和数据库不在同一台机器上,还涉及到了网络IO,网络IO的速度并不快:
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC", "root", "");
“jdbc:mysql://localhost:3306/test?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC”:是mySql数据库的url,其中的
test
是数据库的名称,可通过net start mysql
启动数据库、mysql -u root -p
登录数据库后,在mysql>
下(自认,即数据库内)使用show databases;
查看当前电脑上现有数据库的所有名称。
- 创建【执行sql语句、负责对数据库进行操作】的对象:
statement = connection.生成各类statement方法();
,如下方备注所示。
statement = connection.createStatement();
//返回Statement接口对象。此时查询结果java.sql.resultset只能一条条向下读,如果在代码中使用resultSet.beforeFirst();
试图回滚到resultSet第一条时,会报错java.sql.resultset is type_forward_only
。此时就需要使用带参数的构造方法,statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
以设置ResultSet回滚查询,但是sqlite数据库不支持回退模式,会报错提示。
//
statement = connection.prepareStatement(...);
//返回PreparedStatement接口对象,PreparedStatement继承自Statement。
statement = connection.prepareCall(...);
//返回CallableStatement接口对象,CallableStatement继承自PreparedStatement。
Statement接口有两个子类,分别是PreparedStatement和CallableStatement:connection.prepareStatement(...)
返回PreparedStatement对象和connection.prepareCall(...);
返回CallableStatement对象。
- 编写要执行的sql语句,比如查询,请注意sql语句要以分号结尾:
String sqlString = "select * from hogwarts_user;";
。
使用
show tables;
来查看当前数据库test
中的表(Tables_in_test
)有哪些,如下图所示。
- 使用第5步中创建的“执行sql语句的对象”statement的对应方法来执行对应sql操作:比如本次选择使用
statement.executeQuery(sqlString)
来执行第6步中编写的SQL语句。
- execute(String sql):方法声明为
boolean java.sql.Statement.execute(String sql)
。本方法可用来执行所有的SQL语句,本方法的返回值表示方法执行的SQL语句的类型——执行DML的SQL语句则返回false,执行DQL的SQL语句则返回true。 - executeQuery(sqlString):方法声明为
ResultSet executeQuery(String sql) throws SQLException;
。本方法用来执行DQL的SQL语句,本方法的返回值是执行DQL的SQL查询的结果集。查询结果返回给“java.sql.ResultSet”:ResultSet resultSet = statement.executeQuery(sqlString);
。 - executeUpdate(sqlString):方法声明为
int executeUpdate(String sql) throws SQLException;
。本方法用来执行DML的SQL语句,本方法的返回值是本次SQL操作中受影响的记录条数。
- 输出resultSet中的查询结果:如下所示。需要注意的是,
java.sql.ResultSet
也需要close()关闭。
//输出查询结果
while (resultSet.next()) {
int id = resultSet.getInt("id");//resultSet.getInt用于获取int类型的结果
String nameString = resultSet.getString("name");//resultSet.getString用于获取String类型的结果
String ageString = resultSet.getString("age");
System.out.print("id是"+id);
System.out.print(",name是"+nameString);
System.out.println(",age是"+ageString);
System.out.println("---------------");
}
遍历输出MySQL查询结果java.sql.ResultSet
方法详见此博客。
完整代码示例
完整代码,如下所示。
import java.sql.*;
Connection connection = null;
SavePoint sp = null;
Statement statement = null;
ResultSet resultSet = null;//java.sql.ResultSet也需要close关闭!
// MySQL 8.0 以下版本 - JDBC 驱动名及数据库 URL
// static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
// static final String DB_URL = "jdbc:mysql://localhost:3306/test";
// MySQL 8.0 以上版本 - JDBC 驱动名及数据库 URL
static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost:3306/test?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
try{
//通过Class.forName加载“com.mysql.cj.jdbc.Driver”类中的静态代码,来使用mysql数据库jar包中代码的方式,加载数据库驱动(即jar包中的名称),注册JDBC驱动。
Class.forName(JDBC_DRIVER);
//打开链接
connection = DriverManager.getConnection(DB_URL, "root", "");
//关闭事务自动提交
//connection.setAutoCommit(false);//待确认提交事务的位置后,再解除此注释。
//设置事务回滚点,java.sql.Connection 在创建回滚点时 需要 设置回滚点的名称。
sp = connection.setSavepoint("begin");
//创建sql查询
statement = connection.createStatement();
//sql语句
String sqlString = "select * from hogwarts_user;";
//执行查询,获取sql查询结果
resultSet = statement.executeQuery(sqlString);
//输出查询结果
while (resultSet.next()) {//此循环条件自测无误,不会导致错过一条
int id = resultSet.getInt("id");
String nameString = resultSet.getString("name");
String ageString = resultSet.getString("age");
System.out.print("id是"+id);
System.out.print(",name是"+nameString);
System.out.println(",age是"+ageString);
System.out.println("---------------");
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
//发生异常时,回滚事务
try {
connection.rollback(sp);
} catch (SQLException e1) {
e1.printStackTrace();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
//发生异常时,回滚事务
try {
connection.rollback(sp);
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally {
//此部分虽然保证了数据库的安全开启、关闭,但造成了大量时间开销,所以建议使用连接池实现对数据库的连接和操作
if (resultSet!=null) {//java.sql.ResultSet也需要close!
try {
resultSet.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (statement!=null) {
try {
statement.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (connection!=null) {
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Java工程 通过连接池实现连接、操作数据库
JDBC中使用连接时都要创建一个Connection对象,使用完毕后将其销毁,这种重复创建、销毁、创建、销毁…的过程是特别耗费计算机性能以及计算时间的,而数据库如果使用了数据库连接池,就能达到Connection对象的复用效果。
Java工程使用连接池实现连接、操作数据库详见此博客。