1.JDBC与数据库的交互过程
概括性来说,JDBC与数据库交互有以下这些步骤:
1、建立一个到数据库的连接。
2、在数据库中对表执行检索、创建,或修改的SQL查询。
3、关闭到数据库的连接。
JDBC的类和接口主要在java.sql和javax.sql两个包中。
1.1.JDBC组件
下面一张图清楚的描述了JDBC架构。
JAVA应用程序使用JDBC来与数据库进行交互。JDBC的API和JDBC驱动管理器进行交互,JDBC驱动管理器连接到不同种类的数据库,JDBC驱动管理器使用不同的驱动来针对其特定的数据库。
1.2.连接数据库
与数据库通讯的第一步就是在你的应用程序与数据库服务器之间建立一个连接。建立数据库连接首先要理解数据库URL。
连接数据库主要步骤:
1、引用数据库相关的jdbc包
2、创建代码连接
1.2.1.JDBC URL
下面是一般JDBC URL的格式:
jdbc:<subprotocol>:<subname>
示例剖析:mysql数据库的URL的示例:
jdbc:mysql://localhost:3306/:
对于所有数据库来说jdbc(协议)都是一样的。
对于每一种数据库<subprotocol>是不同的,在本案例中是mysql。
<subname>的格式则依赖于数据库,但一般来说其格式为//<server>:<port>/database。其中<server>是由数据库主机所在的位置确定的。每个数据库使用一个特定的<port>号码(在本例中的MySQL中是3306)。最后是所连接的数据库名字。
更多示例:
jdbc:postgresql://localhost/test jdbc:oracle://127.0.0.1:44000/test jdbc:microsoft:sqlserver://himalaya:1433
1.2.2.创建连接
示例代码
import java.sql.Connection; import java.sql.DriverManager; // 这个类尝试获取一个与数据库的连接 public class DbConnect { public static void main(String[] args) { // URL 指出了所需要使用的JDBC协议: mysql子协议 // localhost 表示数据库所在服务器的地址 // 3306 表示所需要连接数据库的端口号 String url = "jdbc:mysql://localhost:3306/"; // 连接到film数据库 String database = "film"; // 使用帐号 "root" 以及密码 "1234" String userName = "root"; String password = "1234"; try( Connection connection = DriverManager.getConnection (url + database, userName, password)){ System.out.println("Database connection: Successful"); } catch (Exception e) { System.out.println("Database connection: Failed"); e.printStackTrace(); } } }
这个URL jdbc:mysql://localhost:3306/表名协议是jdbc,子协议是mysql;localhost是表示我们所安装的数据库服务器位置,3306则是用来连接数据库的端口。
(注意,你的机器上的数据库的端口可能与这个不一样,并且不是所有MySQL数据库的端口都是一样的,这个取决于你安装时的配置。
然后是通过执行DriverManager.getConnection()方法来获取连接对象。这个方法接受数据库连接的URL以及用户名和密码。
在退出程序之前应该关闭连接。这个示例使用try-with-resource语句,因此connection的close()方法会被自动调用。
当你运行这个例子的时候,可能会抛出一个异常:
Database connection: Failed java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/film at java.sql.DriverManager.getConnection(Unknown Source) at java.sql.DriverManager.getConnection(Unknown Source) at com.oolong.javase.jdbc.DbConnect.main(DbConnect.java:23)
当你尝试去使用JDBC连接数据库时,DriverManager会搜索MySQL驱动。而这个驱动是不包含在JDK中的,因此你需要安装相关的驱动。
那么如何安装MySQL的驱动呢?
你可以到MySQL官网去下载JDBC连接的JAR包,然后将其加入到项目的classpath中即可。然后再次运行这个程序就会得到:
Database connection: Successful
当你看到这段输出时,就说明你能够正常的建立一个到数据库的连接了。
1.3.查询
当你对目标数据库建立了一个连接之后,你就可以对其执行不同种类的操作了。一般的操作包括CRUD(创建、读取、更新和删除)。
查询数据库
连接数据库
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; // 提供方法 connectToDb() 的工具类,用于被其他类调用 public class DbConnector { public static Connection connectToDb() throws SQLException { String url = "jdbc:mysql://localhost:3306/"; String database = "film"; String userName = "root"; String password = "1234"; return DriverManager.getConnection(url + database, userName, password); } }
查询数据库
import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; // 表明如何进行一个查询的示例程序 public class DbQuery { public static void main(String[] args) { String sql = "SELECT * FROM FILM"; // 获取连接、执行查询、获取结果并打印结果集中的实体 try(Connection connection = DbConnector.connectToDb(); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql)){ System.out.println("wid title score area description"); while(resultSet.next()) { System.out.println(resultSet.getInt("wid") + " " + resultSet.getString("title") + " " + resultSet.getInt("score") + " " + resultSet.getString("area") + " " + resultSet.getString("description") + " "); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); System.exit(-1); } } }
1.4.更新
更新数据库有两种方式,第一种是使用SQL查询来直接更新数据库,另一种则是通过SQL获取resultset,修改之后再写回数据库。
import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; // To illustrate how we can update a database public class DbUpdate { public static void main(String[] args){ String sql = "SELECT * FROM FILM WHERE TITLE = "盗火线""; try (Connection connection = DbConnector.connectToDb(); Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet resultSet = statement.executeQuery(sql)) {
// 首先在更新操作之前读取并显示数据 System.out.println("Before the update"); System.out.println("wid title score area description"); while(resultSet.next()) { System.out.println(resultSet.getInt("wid") + " " + resultSet.getString("title") + " " + resultSet.getInt("score") + " " + resultSet.getString("area") + " " + resultSet.getString("description") + " "); } // 然后更新结果集,并显示修改后的数据 resultSet.absolute(1); resultSet.updateString("description", "很不错的一部电影"); // 通过调用 updateRow() 方法将这些修改写回数据库 resultSet.updateRow(); System.out.println("After the update"); System.out.println("wid title score area description");
resultSet.beforeFirst(); while(resultSet.next()) { System.out.println(resultSet.getInt("wid") + " " + resultSet.getString("title") + " " + resultSet.getInt("score") + " " + resultSet.getString("area") + " " + resultSet.getString("description") + " "); } } catch (SQLException e) { e.printStackTrace(); System.exit(-1); } } }
1.5.插入
import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; // 演示如何通过 ResultSet 来插入数据 public class DbInsert { public static void main(String[] args) { String sql = "SELECT * FROM FILM"; try ( Connection connection = DbConnector.connectToDb(); Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet resultSet = statement.executeQuery(sql)) { System.out.println("Before the insert"); System.out.println("wid title score area description"); while(resultSet.next()) { System.out.println(resultSet.getInt("wid") + " " + resultSet.getString("title") + " " + resultSet.getInt("score") + " " + resultSet.getString("area") + " " + resultSet.getString("description") + " "); } resultSet.moveToInsertRow(); resultSet.updateString("title", "伸冤人"); resultSet.updateInt("score", 5); resultSet.updateString("area", "美国"); resultSet.insertRow(); System.out.println("After the insert"); System.out.println("wid title score area description"); resultSet.beforeFirst(); while(resultSet.next()) { System.out.println(resultSet.getInt("wid") + " " + resultSet.getString("title") + " " + resultSet.getInt("score") + " " + resultSet.getString("area") + " " + resultSet.getString("description") + " "); } } catch (SQLException e) { e.printStackTrace(); System.exit(-1); } } }
2.解析
2.1.Connection接口
java.sql包中的Connection接口表示一个从应用程序到数据库的连接。它是一个应用程序与数据库进行通讯的管道。下面是接口Connection中重要的方法。所有这些方法都会抛出SQLExceptions异常。
重要的方法
Statement createStatement() throws SQLException;
创建一个Statement对象,可以用来向数据库发送SQL语句。
PreparedStatement prepareStatement(String sql) throws SQLException;
创建一个PreparedStatement包含SQL语句的对象。这个SQL语句可以包含参数,使用半角的“?”符号,作为稍后会传递的实际参数的占位符。
CallableStatement prepareCall(String sql) throws SQLException;
创建一个CallableStatement对象,用来在数据库中调用存储过程。这个SQL语句可以包含参数,也是使用半角“?”作为稍后传递的实际参数的占位符。
DatabaseMetaData getMetaData() throws SQLException;
获取DatabaseMetaData对象。这个元数据包含数据库的创建信息,表信息等等。如果你不知道底层的数据库,那么这些信息会非常有用。
Clob createClob() throws SQLException;
返回一个Clob对象(Clob是接口的名字)。Character Large Object(CLOB)是SQL中内建的一种类型,它是数据库表中的一种列的类型。
Blob createBlob() throws SQLException;
返回一个Blob对象(Blob是接口的名字)。Binary Large Object(BLOB)是SQL中的一种内建类型。
void setSchema(String schema) throws SQLException;
当传递了一个schema名字时,就是设置Connection对象连接到这个数据库。
String getSchema() throws SQLException;
返回当前的Connection对象所连接的这个数据库的schema名称,如果当前没有连接到具体的哪一个数据库,则返回null。
2.2.DriverManager类
这个DriverManager类用来帮助我们在应用程序和JDBC驱动之间建立连接。自动的处理不同的数据源和JDBC驱动。因此不需要显式的加载JDBC驱动:DriverManager会自动搜索合适的驱动,如果找到了,当调用getConnection()方法时就自动加载相关驱动。
Connection connection = DriverManager.getConnection (url + database, userName, password)
这行代码用于获取连接。
重要的方法
static Connection getConnection(String url) static Connection getConnection(String url, java.util.Properties info) static Connection getConnection(String url, String user, String password)
根据给定的数据库URL尝试建立一个连接。此外,你还可以提供额外的信息,可以以字符串参数的形式传递用户名和密码,或者可以通过一个Properties文件传递。如果建立连接失败,这些方法会抛出一个SQLException异常。
static Driver getDriver(String url)
搜索已经注册过的JDBC驱动,如果找到了,则返回匹配数据库URL的Driver对象。
import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; public class GetDriver { public static void main(String[] args) throws SQLException { String url = "jdbc:mysql://localhost:3306/"; Driver driver = DriverManager.getDriver(url); System.out.println(driver.getClass().getName()); } }
com.mysql.jdbc.Driver
我们可以注意这个代码前面引用的:
import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException;
都是java.sql包下的,也就是JDK中的接口,而我们打印出通过DriverManager.getDriver()获取到的实际的Driver对象之后,打印它的类名,发现是com.mysql.jdbc.Driver,这说明DriverManager类会根据传入的参数去搜索针对这个URL的实际的数据库驱动。
static synchronized void registerDriver(java.sql.Driver driver)
将驱动对象加入到DriverManager的注册列表中。
static synchronized void deregisterDriver(Driver driver)
从DriverManager的注册列表中取消驱动对象。
2.3.Statement接口
如名称表示的那样,Statement是一个用来向已连接的数据库传递SQL语句并接受从数据库返回的结果的对象。你可以使用Statement构成SQL查询,并使用Statement接口提供的API执行SQL。
2.3.1.三种Statement接口
Statement有三种形式,它们的关系如下所示:
如何在三种Statement接口中进行选择
对于一个给定的情景,如何在三种Statement接口中进行选择呢?
Statement
发送一个无参数的SQL语句到数据库。对于一般的使用,就需要这个接口。可以使用Connection接口的createStatement()方法获取到一个Statement的实例。
PreparedStatement
表示一个预编译的SQL语句,可以传递IN参数。通常来说,它比Statement更有效率,因此,它用来改善性能,特别是对于一个需要执行多次的SQL语句。可以通过调用Connection接口的preparedStatement()方法获取PrepareStatement的实例。
CallableStatement
用于执行存储过程。CallableStatement实例可以处理IN、OUT和INOUT参数。你可以通过调用Connection接口的prepareCall()方法获取这个接口的实例。
一旦创建完成合适的Statement对象之后,就可以执行SQL语句了。Statement接口提供了三个执行方法:executeQuery(),executeUpdate(),和execute()。
如果你的SQL语句是一个SELECT查询,那么使用executeQuery()方法,它会返回一个ResultSet。
如果你的SQL是用来更新数据库的INSERT,UPDATE,或DELETE语句,你应该使用executeUpdate()方法,它会返回一个整数,表明更新影响到的数据行数。
如果你不清楚这个SQL语句的类型,你可以使用execute()方法,它会返回一个ResultSet或这一个更新的数据行数。
2.3.2.重要的方法
boolean execute(String sql)
这个方法会执行给定的SQL查询。
如果返回true,则表示这个SQL查询返回的结果为一个ResultSet。你可以通过调用getResultSet()方法来检索这个ResultSet对象。
如果返回false,则表示这个SQL查询没有结果或者结果是更新的记录条数。你可以通过调用getUpdateCount()方法来获取更新的条数。
还有一种可能性,就是这个方法可能会返回多个ResultSet,在这种情况下,可以调用getMoreResults()方法获取结果。
ResultSet executeQuery(String sql)
执行查询并返回ResultSet对象作为结果。如果没有结果,这个方法不会返回null,而是返回一个空的ResultSet对象。当调用ResultSet对象的next()方法时,会返回false。
int executeUpdate(String sql)
执行CREATE、INSERT、UPDATE或DELTE的SQL查询。它返回更新数据的行数,如果没有更新数据则返回0。
Connection getConnection()
返回创建这个Statement对象的Connection对象。
void close()
关闭与这个Statement对象相关的数据库或其他JDBC资源。对已经关闭的Statement对象调用close()方法没有任何影响。
2.3.3.PreparedStatement接口
setString() 等方法的index是从1开始
2.4.ResultSet接口
关系型数据库中包含表。每个表中含有一些属性,这些属性就是列,此外除了第一行是属性,其余的行则是数据,也就是属性的值。当查询数据库时,其结果为一个数据表,包含一定的列和行,这个数据表被称为resultset。resultset中包含标题栏和查询相关的数据。
resultset中有一个游标,指向当前这一行数据。在同一时间内只能读取当前这一行的内容,因此需要通过移动游标来读取其他行。通过调用next()方法可以将游标往前移动一行。这个方法会返回一个布尔值,因此,可以使用while循环来迭代这个resultset。
2.4.1.重要的方法
void beforeFirst()
将ResultSet中的游标设置到第一行之前。
void afterLast()
将ResultSet中的游标设置到最后一行之后。
boolean absolute( int row )
将ResultSet中的游标设置到参数中的行数(参数是ResultSet中的绝对位置,而不是相对于当前游标的位置)。
boolean relative( int rows )
根据参数将游标设置为相对于当前位置的行数。参数可以是正负值,整数表示相对于当前位置往后移动的距离;负数表示相对于当前位置往前移动的距离。
boolean next()
将游标往下移动一行
boolean previous()
将游标往上移动一行
获取数据的方法
double getDouble(int columnNumber)
根据列的坐标获取
double getDouble(String columnName)
根据列的名字获取
更新数据的方法
void updateDouble(int columnIndex, double x)
根据列的坐标更新
void updateDouble(String columnLabel, double x)
根据列的名字获取