{"id":257,"date":"2023-10-22T05:00:54","date_gmt":"2022-11-15T02:46:17","guid":{"rendered":"https:\/\/www.silicloud.com\/zh\/blog\/index.php\/2023\/11\/30\/jdbc%e9%9d%a2%e8%af%95%e9%97%ae%e9%a2%98%e4%b8%8e%e7%ad%94%e6%a1%88\/"},"modified":"2025-08-01T03:02:00","modified_gmt":"2025-07-31T19:02:00","slug":"jdbc%e9%9d%a2%e8%af%95%e9%97%ae%e9%a2%98%e4%b8%8e%e7%ad%94%e6%a1%88","status":"publish","type":"post","link":"https:\/\/www.silicloud.com\/zh\/blog\/jdbc%e9%9d%a2%e8%af%95%e9%97%ae%e9%a2%98%e4%b8%8e%e7%ad%94%e6%a1%88\/","title":{"rendered":"JDBC\u9762\u8bd5\u7cbe\u9009\uff1a\u5e38\u89c1\u95ee\u9898\u4e0e\u6743\u5a01\u7b54\u6848\u89e3\u6790"},"content":{"rendered":"<p>\u6b22\u8fce\u9605\u8bfbJDBC\u9762\u8bd5\u95ee\u9898\u4e0e\u7b54\u6848\u7cfb\u5217\u6587\u7ae0\u3002JDBC API\u4e3b\u8981\u7528\u4e8e\u8fde\u63a5\u5173\u7cfb\u578b\u6570\u636e\u5e93\uff0c\u5e76\u4eceJava\u7a0b\u5e8f\u4e2d\u6267\u884cSQL\u67e5\u8be2\u3002\u5728\u4e4b\u524d\u7684\u6587\u7ae0\u4e2d\uff0c\u6211\u4eec\u5df2\u7ecf\u6df1\u5165\u63a2\u8ba8\u4e86JDBC API\u53ca\u5176\u91cd\u8981\u7279\u6027\u3002\u672c\u6587\u65e8\u5728\u63d0\u4f9b\u4e00\u7cfb\u5217\u5173\u952e\u7684JDBC\u9762\u8bd5\u95ee\u9898\u53ca\u89e3\u7b54\uff0c\u4ee5\u52a9\u60a8\u5728Java\u9762\u8bd5\u4e2d\u8131\u9896\u800c\u51fa\u3002<\/p>\n<h2>JDBC \u9762\u8bd5\u95ee\u9898<\/h2>\n<div><img decoding=\"async\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/65647bb7a4b2f92e6c72c322\/2-0.jpg\" class=\"post-images\" alt=\"JDBC\u9762\u8bd5\u95ee\u9898, JDBC\u9762\u8bd5\u95ee\u9898\u4e0e\u7b54\u6848\" title=\"JDBC\u9762\u8bd5\u95ee\u9898\u4e0e\u7b54\u6848\"><\/p>\n<ol>\n<li>\u4ec0\u4e48\u662fJDBC API\uff0c\u4ee5\u53ca\u4f55\u65f6\u9700\u8981\u4f7f\u7528\u5b83\uff1f<\/li>\n<li>JDBC\u9a71\u52a8\u7a0b\u5e8f\u6709\u54ea\u4e9b\u4e0d\u540c\u7684\u7c7b\u578b\uff1f<\/li>\n<li>JDBC API\u5982\u4f55\u5b9e\u73b0Java\u7a0b\u5e8f\u4e0eJDBC\u9a71\u52a8\u7a0b\u5e8fAPI\u4e4b\u95f4\u7684\u677e\u8026\u5408\uff1f<\/li>\n<li>\u4ec0\u4e48\u662fJDBC\u8fde\u63a5\uff1f\u8bf7\u7b80\u8981\u8bf4\u660e\u5728\u7b80\u5355\u7684Java\u7a0b\u5e8f\u4e2d\u83b7\u53d6\u6570\u636e\u5e93\u8fde\u63a5\u7684\u6b65\u9aa4\u3002<\/li>\n<li>JDBC DriverManager\u7c7b\u7684\u4f5c\u7528\u662f\u4ec0\u4e48\uff1f<\/li>\n<li>\u5982\u4f55\u5728Java\u7a0b\u5e8f\u4e2d\u83b7\u53d6\u6570\u636e\u5e93\u670d\u52a1\u5668\u7684\u8be6\u7ec6\u4fe1\u606f\uff1f<\/li>\n<li>\u4ec0\u4e48\u662fJDBC Statement\uff1f<\/li>\n<li>execute\u3001executeQuery\u548cexecuteUpdate\u65b9\u6cd5\u4e4b\u95f4\u6709\u4ec0\u4e48\u533a\u522b\uff1f<\/li>\n<li>\u4ec0\u4e48\u662fJDBC PreparedStatement\uff1f<\/li>\n<li>\u5982\u4f55\u5728JDBC PreparedStatement\u4e2d\u8bbe\u7f6eNULL\u503c\uff1f<\/li>\n<li>Statement\u4e2d\u7684getGeneratedKeys()\u65b9\u6cd5\u6709\u4f55\u7528\u9014\uff1f<\/li>\n<li>PreparedStatement\u76f8\u5bf9\u4e8eStatement\u6709\u54ea\u4e9b\u4f18\u52bf\uff1f<\/li>\n<li>PreparedStatement\u7684\u5c40\u9650\u6027\u662f\u4ec0\u4e48\uff0c\u4ee5\u53ca\u5982\u4f55\u514b\u670d\u8fd9\u4e9b\u5c40\u9650\uff1f<\/li>\n<li>\u4ec0\u4e48\u662fJDBC ResultSet\uff1f<\/li>\n<li>ResultSet\u6709\u54ea\u4e9b\u4e0d\u540c\u7684\u7c7b\u578b\uff1f<\/li>\n<li>Statement\u4e2d\u7684setFetchSize()\u548csetMaxRows()\u65b9\u6cd5\u7684\u4f5c\u7528\u662f\u4ec0\u4e48\uff1f<\/li>\n<li>\u5982\u4f55\u4f7f\u7528JDBC API\u8c03\u7528\u5b58\u50a8\u8fc7\u7a0b\uff1f<\/li>\n<li>\u4ec0\u4e48\u662fJDBC\u6279\u5904\u7406\uff0c\u5b83\u6709\u54ea\u4e9b\u597d\u5904\uff1f<\/li>\n<li>\u4ec0\u4e48\u662fJDBC\u4e8b\u52a1\u7ba1\u7406\uff0c\u4e3a\u4ec0\u4e48\u9700\u8981\u5b83\uff1f<\/li>\n<li>\u5982\u4f55\u56de\u6edaJDBC\u4e8b\u52a1\uff1f<\/li>\n<li>\u4ec0\u4e48\u662fJDBC Savepoint\uff1f\u5982\u4f55\u4f7f\u7528\u5b83\uff1f<\/li>\n<li>\u4ec0\u4e48\u662fJDBC DataSource\uff0c\u5b83\u6709\u54ea\u4e9b\u597d\u5904\uff1f<\/li>\n<li>\u5982\u4f55\u4f7f\u7528JDBC DataSource\u548cApache Tomcat\u670d\u52a1\u5668\u4e2d\u7684JNDI\u5b9e\u73b0JDBC\u8fde\u63a5\u6c60\uff1f<\/li>\n<li>\u4ec0\u4e48\u662fApache DBCP API\uff1f<\/li>\n<li>JDBC\u8fde\u63a5\u9694\u79bb\u7ea7\u522b\u6709\u54ea\u4e9b\uff1f<\/li>\n<li>\u4ec0\u4e48\u662fJDBC RowSet\uff1fRowSet\u6709\u54ea\u4e9b\u4e0d\u540c\u7684\u7c7b\u578b\uff1f<\/li>\n<li>ResultSet\u548cRowSet\u4e4b\u95f4\u6709\u4ec0\u4e48\u533a\u522b\uff1f<\/li>\n<li>\u5e38\u89c1\u7684JDBC\u5f02\u5e38\u6709\u54ea\u4e9b\uff1f<\/li>\n<li>JDBC\u4e2d\u7684CLOB\u548cBLOB\u6570\u636e\u7c7b\u578b\u662f\u4ec0\u4e48\uff1f<\/li>\n<li>JDBC\u4e2d\u7684\u201c\u810f\u8bfb\u201d\u662f\u4ec0\u4e48\uff1f\u54ea\u4e2a\u9694\u79bb\u7ea7\u522b\u53ef\u4ee5\u9632\u6b62\u810f\u8bfb\uff1f<\/li>\n<li>\u4ec0\u4e48\u662f\u4e24\u9636\u6bb5\u63d0\u4ea4\uff1f<\/li>\n<li>JDBC\u4e2d\u6709\u54ea\u4e9b\u4e0d\u540c\u7c7b\u578b\u7684\u9501\u5b9a\uff1f<\/li>\n<li>\u60a8\u5bf9DDL\u548cDML\u8bed\u53e5\u6709\u4ec0\u4e48\u7406\u89e3\uff1f<\/li>\n<li>java.util.Date\u548cjava.sql.Date\u4e4b\u95f4\u6709\u4ec0\u4e48\u533a\u522b\uff1f<\/li>\n<li>\u5982\u4f55\u5c06\u56fe\u50cf\u6216\u539f\u59cb\u6570\u636e\u63d2\u5165\u5230\u6570\u636e\u5e93\u4e2d\uff1f<\/li>\n<li>\u5e7b\u8bfb\u662f\u4ec0\u4e48\uff0c\u54ea\u4e2a\u9694\u79bb\u7ea7\u522b\u53ef\u4ee5\u9632\u6b62\u5e7b\u8bfb\uff1f<\/li>\n<li>\u4ec0\u4e48\u662fSQL\u8b66\u544a\uff1f\u5982\u4f55\u5728JDBC\u7a0b\u5e8f\u4e2d\u68c0\u7d22SQL\u8b66\u544a\uff1f<\/li>\n<li>\u5982\u4f55\u4f7f\u7528\u6570\u636e\u5e93\u5bf9\u8c61\u4f5c\u4e3aIN\/OUT\u53c2\u6570\u8c03\u7528Oracle\u5b58\u50a8\u8fc7\u7a0b\uff1f<\/li>\n<li>\u4f55\u65f6\u4f1a\u51fa\u73b0java.sql.SQLException: No suitable driver found\u5f02\u5e38\uff1f<\/li>\n<li>\u6709\u54ea\u4e9bJDBC\u6700\u4f73\u5b9e\u8df5\uff1f<\/li>\n<\/ol>\n<h2>JDBC \u9762\u8bd5\u95ee\u9898\u4e0e\u7b54\u6848<\/h2>\n<ol>JDBC DriverManager class is used to manage the JDBC drivers. It is responsible for registering and loading the appropriate driver class and providing the connection to the database. It acts as an intermediary between the application and the drivers, allowing the application to obtain a database connection without needing to know the specific implementation of the driver.<\/ol>\n<pre class=\"post-pre\"><code>JDBC `DriverManager` is the factory class through which we get the Database Connection object. When we load the JDBC Driver class, it registers itself to the DriverManager, you can look up the JDBC Driver classes source code to check this. Then when we call `DriverManager.getConnection()` method by passing the database configuration details, DriverManager uses the registered drivers to get the Connection and return it to the caller program.\r\n<\/code><\/pre>\n<ol>\u5728Java\u7a0b\u5e8f\u4e2d\u5982\u4f55\u83b7\u53d6\u6570\u636e\u5e93\u670d\u52a1\u5668\u7684\u8be6\u7ec6\u4fe1\u606f\uff1f<\/ol>\n<pre class=\"post-pre\"><code>We can use `DatabaseMetaData` object to get the database server details. When the database connection is created successfully, we can get the meta data object by calling `getMetaData()` method. There are so many methods in DatabaseMetaData that we can use to get the database product name, it's version and configuration details.\r\n\r\n```\r\nDatabaseMetaData metaData = con.getMetaData();\r\nString dbProduct = metaData.getDatabaseProductName();\r\n```\r\n<\/code><\/pre>\n<ol>JDBC Statement\u662f\u4ec0\u4e48\uff1f<\/ol>\n<pre class=\"post-pre\"><code>JDBC API `Statement` is used to execute SQL queries in the database. We can create the Statement object by calling Connection `createStatement()` method. We can use Statement to execute static SQL queries by passing query through different execute methods such as execute(), executeQuery(), executeUpdate() etc. Since the query is generated in the java program, if the user input is not properly validated it can lead to SQL injection issue, more details can be found at [SQL Injection Example](\/community\/tutorials\/jdbc-statement-vs-preparedstatement-sql-injection-example). By default, only one ResultSet object per Statement object can be open at the same time. Therefore, if we want to work with multiple ResultSet objects, then each must have been generated by different Statement objects. All execute() methods in the Statement interface implicitly close a statment's current ResultSet object if an open one exists.\r\n<\/code><\/pre>\n<ol>\u6267\u884c\u3001\u6267\u884c\u67e5\u8be2\u548c\u6267\u884c\u66f4\u65b0\u6709\u4ec0\u4e48\u533a\u522b\uff1f<\/ol>\n<pre class=\"post-pre\"><code>Statement `execute(String query)` is used to execute any SQL query and it returns TRUE if the result is an ResultSet such as running Select queries. The output is FALSE when there is no ResultSet object such as running Insert or Update queries. We can use `getResultSet()` to get the ResultSet and `getUpdateCount()` method to retrieve the update count. Statement `executeQuery(String query)` is used to execute Select queries and returns the ResultSet. ResultSet returned is never null even if there are no records matching the query. When executing select queries we should use the executeQuery method so that if someone tries to execute insert\/update statement it will throw java.sql.SQLException with message \"executeQuery method cannot be used for update\". Statement executeUpdate(String query) is used to execute Insert\/Update\/Delete (DML) statements or DDL statements that returns nothing. The output is int and equals the row count for SQL Data Manipulation Language (DML) statements. For DDL statements, the output is 0. You should use execute() method only when you are not sure about the type of statement else use executeQuery or executeUpdate method.\r\n<\/code><\/pre>\n<ol>JDBC PreparedStatement\u662f\u4ec0\u4e48\uff1f<\/ol>\n<pre class=\"post-pre\"><code>JDBC `PreparedStatement` object represents a precompiled SQL statement. We can use it's setter method to set the variables for the query. Since PreparedStatement is precompiled, it can then be used to efficiently execute this statement multiple times. PreparedStatement is better choice that Statement because it automatically escapes the special characters and avoid SQL injection attacks.\r\n<\/code><\/pre>\n<ol>\u5728JDBC PreparedStatement\u4e2d\u5982\u4f55\u8bbe\u7f6eNULL\u503c\uff1f<\/ol>\n<pre class=\"post-pre\"><code>We can use PreparedStatement setNull() method to bind the null variable to a parameter. The setNull method takes index and SQL Types as argument, for example `ps.setNull(10, java.sql.Types.INTEGER);`.\r\n<\/code><\/pre>\n<ol>Statement\u4e2d\u7684getGeneratedKeys()\u65b9\u6cd5\u6709\u4ec0\u4e48\u4f5c\u7528\uff1f<\/ol>\n<pre class=\"post-pre\"><code>Sometimes a table can have auto generated keys used to insert the unique column value for primary key. We can use Statement `getGeneratedKeys()` method to get the value of this auto generated key.\r\n<\/code><\/pre>\n<ol>\u4f7f\u7528PreparedStatement\u76f8\u6bd4Statement\u6709\u54ea\u4e9b\u4f18\u52bf\uff1f<\/ol>\n<pre class=\"post-pre\"><code>Some of the benefits of PreparedStatement over Statement are:\r\n-   PreparedStatement helps us in preventing SQL injection attacks because it automatically escapes the special characters.\r\n-   PreparedStatement allows us to execute dynamic queries with parameter inputs.\r\n-   PreparedStatement is faster than Statement. It becomes more visible when we reuse the PreparedStatement or use it\u2019s batch processing methods for executing multiple queries.\r\n-   PreparedStatement helps us in writing object Oriented code with setter methods whereas with Statement we have to use String Concatenation to create the query. If there are multiple parameters to set, writing Query using String concatenation looks very ugly and error prone.\r\n<\/code><\/pre>\n<ol>PreparedStatement\u6709\u54ea\u4e9b\u9650\u5236\uff0c\u5982\u4f55\u514b\u670d\u8fd9\u4e9b\u9650\u5236\uff1f<\/ol>\n<pre class=\"post-pre\"><code>One of the limitation of PreparedStatement is that we can't use it directly with IN clause statements. Some of the alternative approaches to use PreparedStatement with IN clause are;\r\n\r\n1.  **Execute Single Queries** - very slow performance and not recommended\r\n2.  **Using Stored Procedure** - Database specific and hence not suitable for multiple database applications.\r\n3.  **Creating PreparedStatement Query dynamically** - Good approach but looses the benefit of cached PreparedStatement.\r\n4.  **Using NULL in PreparedStatement Query** - A good approach when you know the maximum number of variables inputs, can be extended to allow unlimited parameters by executing in parts.\r\n\r\nA more detailed analysis can be found at [JDBC PreparedStatement IN clause alternatives](\/community\/tutorials\/java-preparedstatement-in-clause-alternatives).\r\n<\/code><\/pre>\n<ol>JDBC ResultSet\u662f\u4ec0\u4e48\uff1f<\/ol>\n<pre class=\"post-pre\"><code>JDBC `ResultSet` is like a table of data representing a database result set, which is usually generated by executing a statement that queries the database. ResultSet object maintains a cursor pointing to its current row of data. Initially, the cursor is positioned before the first row. The next() method moves the cursor to the next row. If there are no more rows, next() method returns false and it can be used in a while loop to iterate through the result set. A default ResultSet object is not updatable and has a cursor that moves forward only. Thus, you can iterate through it only once and only from the first row to the last row. It is possible to produce ResultSet objects that are scrollable and\/or updatable using below syntax.\r\n\r\n```\r\nStatement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,\r\n                                   ResultSet.CONCUR_UPDATABLE);\r\n```\r\n\r\nA ResultSet object is automatically closed when the Statement object that generated it is closed, re-executed, or used to retrieve the next result from a sequence of multiple results. We can use ResultSet getter method with column name or index number starting from 1 to retrieve the column data.\r\n<\/code><\/pre>\n<ol>ResultSet\u6709\u54ea\u4e9b\u4e0d\u540c\u7c7b\u578b\uff1f<\/ol>\n<pre class=\"post-pre\"><code>There are different types of ResultSet objects that we can get based on the user input while creating the Statement. If you will look into the Connection methods, you will see that createStatement() and prepareStatement() method are overloaded to provide ResultSet type and concurrency as input argument. There are three types of ResultSet object.\r\n\r\n1.  **ResultSet.TYPE\\_FORWARD\\_ONLY**: This is the default type and cursor can only move forward in the result set.\r\n2.  **ResultSet.TYPE\\_SCROLL\\_INSENSITIVE**: The cursor can move forward and backward, and the result set is not sensitive to changes made by others to the database after the result set was created.\r\n3.  **ResultSet.TYPE\\_SCROLL\\_SENSITIVE**: The cursor can move forward and backward, and the result set is sensitive to changes made by others to the database after the result set was created.\r\n\r\nBased on the concurrency there are two types of ResultSet object.\r\n1.  **ResultSet.CONCUR\\_READ\\_ONLY**: The result set is read only, this is the default concurrency type.\r\n2.  **ResultSet.CONCUR\\_UPDATABLE**: We can use ResultSet update method to update the rows data.\r\n<\/code><\/pre>\n<ol>Statement\u7c7b\u4e2d\u7684setFetchSize()\u548csetMaxRows()\u65b9\u6cd5\u6709\u4ec0\u4e48\u7528\u9014\uff1f<\/ol>\n<pre class=\"post-pre\"><code>We can use `setMaxRows(int i)` method to limit the number of rows that the database returns from the query. You can achieve the same thing using the SQL query itself. For example, in MySQL, we can use the [LIMIT](\/community\/tutorials\/sql-limit-mysql-limit) clause to set the max rows that will be returned by the query. Understanding **fetchSize** can be tricky, for that you should know how Statement and ResultSet works. When we execute a query in the database, the result is obtained and maintained in the database cache and ResultSet is returned. ResultSet is the cursor that has the reference to the result in the database. Let's say we have a query that returns 100 rows and we have set fetchSize to 10, so in every database trip JDBC driver will fetch only 10 rows and hence there will be 10 trips to fetch all the rows. Setting optimal fetchSize is helpful when you need a lot of processing time for each row and number of rows in the result is huge. We can set fetchSize through Statement object but it can be overridden through ResultSet object setFetchSize() method.\r\n<\/code><\/pre>\n<ol>\u5982\u4f55\u4f7f\u7528JDBC API\u8c03\u7528\u5b58\u50a8\u8fc7\u7a0b\uff1f<\/ol>\n<pre class=\"post-pre\"><code>Stored Procedures are group of SQL queries that are compiled in the database and can be executed from JDBC API. JDBC `CallableStatement` can be used to execute stored procedures in the database. The syntax to initialize CallableStatement is;\r\n\r\n```\r\nCallableStatement stmt = con.prepareCall(\"{call insertEmployee(?,?,?,?,?,?)}\");\r\nstmt.setInt(1, id);\r\nstmt.setString(2, name);\r\nstmt.setString(3, role);\r\nstmt.setString(4, city);\r\nstmt.setString(5, country);\r\n\r\n\/\/register the OUT parameter before calling the stored procedure\r\nstmt.registerOutParameter(6, java.sql.Types.VARCHAR);\r\n             \r\nstmt.executeUpdate();\r\n```\r\n\r\nWe need to register the OUT parameters before executing the CallableStatement. More details about this can be found at [JDBC CallableStatement Example](\/community\/tutorials\/callablestatement-in-java-example).\r\n<\/code><\/pre>\n<ol>JDBC\u6279\u5904\u7406\u662f\u4ec0\u4e48\uff1f\u5b83\u6709\u54ea\u4e9b\u597d\u5904\uff1f<\/ol>\n<pre class=\"post-pre\"><code>Sometimes we need to run bulk queries of a similar kind for a database. For example, loading data from CSV files to relational database tables. As we know that we have the option to use Statement or PreparedStatement to execute queries. Apart from that JDBC API provides Batch Processing feature through which we can execute the bulk of queries in one go for a database. JDBC API supports batch processing through Statement and PreparedStatement `addBatch()` and `executeBatch()` methods. Batch Processing is faster than executing one statement at a time because the number of database calls is less. Read more at [JDBC Batch Processing Example](\/community\/tutorials\/jdbc-batch-insert-update-mysql-oracle).\r\n<\/code><\/pre>\n<ol>JDBC\u4e8b\u52a1\u7ba1\u7406\u662f\u4ec0\u4e48\uff0c\u4e3a\u4ec0\u4e48\u6211\u4eec\u9700\u8981\u5b83\uff1f<\/ol>\n<pre class=\"post-pre\"><code>By default when we create a database connection, it runs in auto-commit mode. It means that whenever we execute a query and it\u2019s completed, the commit is fired automatically. So every SQL query we fire is a transaction and if we are running some DML or DDL queries, the changes are getting saved into the database after every SQL statement finishes. Sometimes we want a group of SQL queries to be part of a transaction so that we can commit them when all the queries run fine and if we get an exception, we have a choice of rollback all the queries executed as part of the transaction. JDBC API provide method `setAutoCommit(boolean flag)` through which we can disable the auto commit feature of the connection. We should disable auto commit only when it\u2019s required because the transaction will not be committed unless we call the commit() method on connection. Database servers uses table locks to achieve transaction management and it\u2019s resource intensive process. So we should commit the transaction as soon as we are done with it. Read more with example program at [JDBC Transaction Management Example](\/community\/tutorials\/java-jdbc-transaction-management-savepoint).\r\n<\/code><\/pre>\n<ol>\u5982\u4f55\u56de\u6edaJDBC\u4e8b\u52a1\uff1f<\/ol>\n<pre class=\"post-pre\"><code>We can use Connection object `rollback()` method to rollback the transaction. It will rollback all the changes made by the transaction and release any database locks currently held by this Connection object.\r\n<\/code><\/pre>\n<ol>JDBC\u4fdd\u5b58\u70b9\u662f\u4ec0\u4e48\uff1f\u5982\u4f55\u4f7f\u7528\u5b83\uff1f<\/ol>\n<pre class=\"post-pre\"><code>Sometimes a transaction can be group of multiple statements and we would like to rollback to a particular point in the transaction. JDBC Savepoint helps us in creating checkpoints in a transaction and we can rollback to that particular checkpoint. Any savepoint created for a transaction is automatically released and become invalid when the transaction is committed, or when the entire transaction is rolled back. Rolling a transaction back to a savepoint automatically releases and makes invalid any other savepoints that were created after the savepoint in question. Read more at [JDBC Savepoint Example](\/community\/tutorials\/java-jdbc-transaction-management-savepoint).\r\n<\/code><\/pre>\n<ol>\u4ec0\u4e48\u662fJDBC\u6570\u636e\u6e90\u4ee5\u53ca\u5b83\u7684\u597d\u5904\u662f\u4ec0\u4e48\uff1f<\/ol>\n<pre class=\"post-pre\"><code>JDBC DataSource is the interface defined in `javax.sql` package and it is more powerful that DriverManager for database connections. We can use DataSource to create the database connection and Driver implementation classes does the actual work for getting connection. Apart from getting Database connection, DataSource provides some additional features such as:\r\n\r\n-   Caching of PreparedStatement for faster processing\r\n-   Connection timeout settings\r\n-   Logging features\r\n-   ResultSet maximum size threshold\r\n-   Connection Pooling in servlet container using JNDI support\r\n\r\nRead more about DataSource at [JDBC DataSource Example](\/community\/tutorials\/java-datasource-jdbc-datasource-example).\r\n<\/code><\/pre>\n<ol>\u5982\u4f55\u5728Apache Tomcat\u670d\u52a1\u5668\u4e0a\u4f7f\u7528JDBC DataSource\u548cJNDI\u5b9e\u73b0JDBC\u8fde\u63a5\u6c60\uff1f<\/ol>\n<pre class=\"post-pre\"><code>For web applications deployed in a servlet container, creating JDBC connection pool is very easy and involve only few steps.\r\n1.  Creating JDBC JNDI resource in the container configuration files, usually server.xml or context.xml. For example `server.xml`\r\n    \r\n    ```\r\n    &lt;Resource name=\"jdbc\/MyDB\"\r\n          global=\"jdbc\/MyDB\"\r\n          auth=\"Container\"\r\n          type=\"javax.sql.DataSource\"\r\n          driverClassName=\"com.mysql.jdbc.Driver\"\r\n          url=\"jdbc:mysql:\/\/localhost:3306\/UserDB\"\r\n          username=\"scdev\"\r\n          password=\"scdev123\"\r\n           \r\n          maxActive=\"100\"\r\n          maxIdle=\"20\"\r\n          minIdle=\"5\"\r\n          maxWait=\"10000\"\/&gt;\r\n    ```\r\n    \r\n    `context.xml`\r\n    \r\n    ```\r\n    &lt;ResourceLink name=\"jdbc\/MyLocalDB\"\r\n                    global=\"jdbc\/MyDB\"\r\n                    auth=\"Container\"\r\n                    type=\"javax.sql.DataSource\" \/&gt;\r\n    ```\r\n    \r\n2.  In web application, using InitialContext to look up the JNDI resource configured in the first step and then get the connection.\r\n    \r\n    ```\r\n    Context ctx = new InitialContext();\r\n    DataSource ds = (DataSource) ctx.lookup(\"java:\/comp\/env\/jdbc\/MyLocalDB\");\r\n    ```\r\n    \r\n    For a complete example, read [Tomcat DataSource JNDI Example](\/community\/tutorials\/tomcat-datasource-jndi-example-java).\r\n<\/code><\/pre>\n<ol>Apache DBCP API\u662f\u4ec0\u4e48\uff1f<\/ol>\n<pre class=\"post-pre\"><code>If you use `DataSource` to get the Database connection, usually the code to get the connection is tightly coupled with the Driver specific DataSource implementation. Also most of the code is boiler-plate code except the choice of the DataSource implementation class. Apache DBCP helps us in getting rid of these issues by providing DataSource implementation that works as an abstraction layer between our program and different JDBC drivers. Apache DBCP library depends on Commons Pool library, so make sure they both are in the build path. For a complete example, read [Apache DBCP Example](\/community\/tutorials\/java-datasource-jdbc-datasource-example).\r\n<\/code><\/pre>\n<ol>JDBC\u8fde\u63a5\u9694\u79bb\u7ea7\u522b\u662f\u4ec0\u4e48\uff1f<\/ol>\n<pre class=\"post-pre\"><code>When we use JDBC Transactions for data integrity, DBMS uses locks to block access by others to the data being accessed by the transaction. DBMS uses locks to prevent Dirty Read, Non-Repeatable Reads and Phantom-Read issue. JDBC transaction isolation level is used by DBMS to use the locking mechanism, we can get the isolation level information through Connection getTransactionIsolation() method and set it with the setTransactionIsolation() method.\r\n\r\n| Isolation Level | Transaction | Dirty Read | Non-Repeatable Read | Phantom Read |\r\n| --- | --- | --- | --- | --- |\r\n| TRANSACTION\\_NONE | Not Supported | Not Applicable | Not Applicable | Not Applicable |\r\n| TRANSACTION\\_READ\\_COMMITTED | Supported | Prevented | Allowed | Allowed |\r\n| TRANSACTION\\_READ\\_UNCOMMITTED | Supported | Allowed | Allowed | Allowed |\r\n| TRANSACTION\\_REPEATABLE\\_READ | Supported | Prevented | Prevented | Allowed |\r\n| TRANSACTION\\_SERIALIZABLE | Supported | Prevented | Prevented | Prevented |\r\n<\/code><\/pre>\n<ol>JDBC RowSet\u662f\u4ec0\u4e48\uff1f\u6709\u54ea\u4e9b\u4e0d\u540c\u7c7b\u578b\u7684RowSet\uff1f<\/ol>\n<pre class=\"post-pre\"><code>JDBC `RowSet` holds tabular data in more flexible ways that ResultSet. All RowSet objects are derived from ResultSet, so they have all the capabilities of ResultSet with some additional features. RowSet interface is defined in `javax.sql` package. Some additional features provided by RowSet are:\r\n\r\n-   Functions as Java Beans with properties and their getter-setter methods. RowSet uses JavaBeans event model and they can send notifications to any registered component for events such as cursor movement, update\/insert\/delete of a row and change to RowSet contents.\r\n-   RowSet objects are scrollable and updatable by default, so if DBMS doesn't support scrollable or updatable ResultSet, we can use RowSet to get these features.\r\n\r\nRowSet are broadly divided into two types:\r\n1.  **Connected RowSet Objects** - These objects are connected to database and are most similar to ResultSet object. JDBC API provides only one connected RowSet object `javax.sql.rowset.JdbcRowSet` and it's standard implementation class is `com.sun.rowset.JdbcRowSetImpl`\r\n2.  **Disconnected RowSet Objects** - These RowSet objects are not required to connected to a database, so they are more lightweight and serializable. They are suitable for sending data over a network. There are four types of disconnected RowSet implementations.\r\n    -   CachedRowSet - They can get the connection and execute a query and read the ResultSet data to populate the RowSet data. We can manipulate and update data while it is disconnected and reconnect to database and write the changes.\r\n    -   WebRowSet derived from CachedRowSet - They can read and write XML document.\r\n    -   JoinRowSet derived from WebRowSet - They can form SQL JOIN without having to connect to a data source.\r\n    -   FilteredRowSet derived from WebRowSet - We can apply filtering criteria so that only selected data is visible.\r\n<\/code><\/pre>\n<ol>ResultSet\u548cRowSet\u4e4b\u95f4\u6709\u4f55\u4e0d\u540c\uff1f<\/ol>\n<pre class=\"post-pre\"><code>RowSet objects are derived from ResultSet, so they have all the features of ResultSet with some additional features. One of the huge benefit of RowSet is that they can be disconnected and that makes it lightweight and easy to transfer over a network. Whether to use ResultSet or RowSet depends on your requirements but if you are planning to use ResultSet for longer duration, then a disconnected RowSet is better choice to free database resources.\r\n<\/code><\/pre>\n<ol>\u5e38\u89c1\u7684JDBC\u5f02\u5e38\u6709\u54ea\u4e9b\uff1f<\/ol>\n<pre class=\"post-pre\"><code>Some of the common JDBC Exceptions are:\r\n1.  java.sql.SQLException - This is the base exception class for JDBC exceptions.\r\n2.  java.sql.BatchUpdateException - This exception is thrown when Batch operation fails, but it depends on the JDBC driver whether they throw this exception or the base SQLException.\r\n3.  java.sql.SQLWarning - For warning messages in SQL operations.\r\n4.  java.sql.DataTruncation - when a data values is unexpectedly truncated for reasons other than its having exceeded MaxFieldSize.\r\n<\/code><\/pre>\n<ol>JDBC\u4e2d\u7684CLOB\u548cBLOB\u6570\u636e\u7c7b\u578b\u662f\u4ec0\u4e48\uff1f<\/ol>\n<pre class=\"post-pre\"><code>Character Large OBjects (CLOBs) are character string made up of single-byte characters with an associated code page. This data type is appropriate for storing text-oriented information where the amount of information can grow beyond the limits of a regular VARCHAR data type (upper limit of 32K bytes). Binary Large Objects (BLOBs) are a binary string made up of bytes with no associated code page. This data type can store binary data larger than VARBINARY (32K limit). This data type is good for storing image, voice, graphical, and other types of business or application-specific data.\r\n<\/code><\/pre>\n<ol>\u5728JDBC\u4e2d\uff0c\u201cdirty read\u201d\u662f\u4ec0\u4e48\u610f\u601d\uff1f\u54ea\u79cd\u9694\u79bb\u7ea7\u522b\u53ef\u4ee5\u9632\u6b62\u201cdirty read\u201d\u53d1\u751f\uff1f<\/ol>\n<pre class=\"post-pre\"><code>When we work with transactions, there is a chance that a row is updated and at the same time, another query can read the updated value. This results in a dirty read because the updated value is not permanent yet, the transaction that has updated the row can rollback to a previous value resulting in invalid data. Dirty Read is prevented by isolation levels TRANSACTION\\_READ\\_COMMITTED, TRANSACTION\\_REPEATABLE\\_READ, and TRANSACTION\\_SERIALIZABLE.\r\n<\/code><\/pre>\n<ol>2\u9636\u6bb5\u63d0\u4ea4\u662f\u4ec0\u4e48\u610f\u601d\uff1f<\/ol>\n<pre class=\"post-pre\"><code>When we work in distributed systems where multiple databases are involved, we are required to use 2 phase commit protocol. 2 phase commit protocol is an atomic commitment protocol for distributed systems. In the first phase, the transaction manager sends commit-request to all the transaction resources. If all the transaction resources are OK, the transaction manager commits the transaction changes for all the resources. If any of the transaction resources responds as Abort, then the transaction manager can rollback all the transaction changes.\r\n<\/code><\/pre>\n<ol>\u5728JDBC\u4e2d\u6709\u54ea\u4e9b\u4e0d\u540c\u7c7b\u578b\u7684\u9501\u5b9a\uff1f<\/ol>\n<pre class=\"post-pre\"><code>On a broad level, there are two types of locking mechanism to prevent data corruption because of more than one user working with the same data.\r\n1.  Optimistic Locking - This locking is achieved with code. An extra column is introduced in the table to keep a count of updates. When you select the row, you read this column too, say 'version'. Now when you are trying to update\/delete the row, you pass this 'version' in the where clause. So if there are updates from other threads performed in between, the update will fail. It's a good way to avoid data corruption but it can be error prone if someone missed updating the 'version' in their update statement. The update query looks something like below in this way of locking.\r\n    \r\n    ```\r\n    mysql&gt; update emp SET name = 'David', version = 5 WHERE id = 10 and version = 4;\r\n    ```\r\n    \r\n2.  Pessimistic Locking - Locking the record from the select to read, update and commit phase. This is usually done by database vendor software and triggered by the use of `SELECT FOR UPDATE` query. This way of locking the row can lead to slow performance and deadlock if threads are handling the lock for longer time.Apart from that some DBMS systems provide locking mechanism to lock single row, table or database.\r\n<\/code><\/pre>\n<ol>\u4f60\u5bf9DDL\u548cDML\u8bed\u53e5\u7684\u7406\u89e3\u662f\u4ec0\u4e48\uff1f<\/ol>\n<pre class=\"post-pre\"><code>Data Definition Language (DDL) statements are used to define the database schema. Create, Alter, Drop, Truncate, Rename statements comes under DDL statements and usually they don't return any result. Data Manipulation Language (DML) statements are used to manipulate data in the database schema. Select, Insert, Update, Delete, Call etc are example of DML statements.\r\n<\/code><\/pre>\n<ol>java.util.Date\u548cjava.sql.Date\u6709\u4ec0\u4e48\u533a\u522b\uff1f<\/ol>\n<pre class=\"post-pre\"><code>java.util.Date contains information about the date and time whereas java.sql.Date contains information only about the date, it doesn't have time information. So if you have to keep time information in the database, it is advisable to use Timestamp or DateTime fields.\r\n<\/code><\/pre>\n<ol>\u5982\u4f55\u5c06\u56fe\u7247\u6216\u539f\u59cb\u6570\u636e\u63d2\u5165\u5230\u6570\u636e\u5e93\u4e2d\uff1f<\/ol>\n<pre class=\"post-pre\"><code>We can use BLOB to insert image or raw binary data into database.\r\n<\/code><\/pre>\n<ol>\u5e7b\u8bfb\u662f\u6307\u5728\u4e8b\u52a1\u4e2d\uff0c\u5f53\u4e00\u4e2a\u4e8b\u52a1\u8bfb\u53d6\u53e6\u4e00\u4e2a\u4e8b\u52a1\u5df2\u66f4\u65b0\u4f46\u672a\u63d0\u4ea4\u7684\u6570\u636e\u65f6\u53d1\u751f\u7684\u73b0\u8c61\u3002\u963b\u6b62\u5e7b\u8bfb\u7684\u9694\u79bb\u7ea7\u522b\u662f\u54ea\u4e2a\uff1f<\/ol>\n<pre class=\"post-pre\"><code>A phantom read is the situation where a transaction executes a query multiple times and get different data. Suppose a transaction is executing a query to get data based on a condition and then another transaction inserts a row that matches the condition. Now when same transaction will execute the query again, a new row will be part of the result set. This new row is referred as Phantom Row and this situation is termed as Phantom Read. Phantom read can be prevented only with TRANSACTION\\_SERIALIZABLE isolation level.\r\n<\/code><\/pre>\n<ol>SQL Warning\u662f\u4ec0\u4e48\uff1f\u5982\u4f55\u5728JDBC\u7a0b\u5e8f\u4e2d\u68c0\u7d22SQL\u8b66\u544a\uff1f<\/ol>\n<pre class=\"post-pre\"><code>SQLWarning is the subclass of SQLException and we can retrieve it by calling getWarnings() method on Connection, Statement, and ResultSet objects. SQL Warnings doesn't stop the execution of the script but alerts the user about the warning.\r\n<\/code><\/pre>\n<ol>\u5982\u4f55\u4f7f\u7528\u6570\u636e\u5e93\u5bf9\u8c61\u4f5c\u4e3aIN\/OUT\u53c2\u6570\u8c03\u7528Oracle\u5b58\u50a8\u8fc7\u7a0b\uff1f<\/ol>\n<pre class=\"post-pre\"><code>If Oracle Stored Procedure has IN\/OUT parameters as DB Objects then we need to create an Object array of the same size in the program and then use it to create Oracle STRUCT object. Then we can set this STRUCT object for the database object by calling setSTRUCT() method and work with it.\r\n<\/code><\/pre>\n<ol>\u6211\u4eec\u662f\u5728\u4ec0\u4e48\u65f6\u5019\u9047\u5230java.sql.SQLException\uff1a\u627e\u4e0d\u5230\u5408\u9002\u7684\u9a71\u52a8\u7a0b\u5e8f\uff1f<\/ol>\n<pre class=\"post-pre\"><code>You get No suitable driver found exception when the SQL URL String is not properly formatted. You can get this exception in both simple java application using DriverManager or with JNDI resource using DataSource. The exception stack trace looks like below.\r\n\r\n```\r\norg.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create JDBC driver of class 'com.mysql.jdbc.Driver' for connect URL ''jdbc:mysql:\/\/localhost:3306\/UserDB'\r\n\tat org.apache.tomcat.dbcp.dbcp.BasicDataSource.createConnectionFactory(BasicDataSource.java:1452)\r\n\tat org.apache.tomcat.dbcp.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1371)\r\n\tat org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)\r\n\r\n\r\njava.sql.SQLException: No suitable driver found for 'jdbc:mysql:\/\/localhost:3306\/UserDB\r\n\tat java.sql.DriverManager.getConnection(DriverManager.java:604)\r\n\tat java.sql.DriverManager.getConnection(DriverManager.java:221)\r\n\tat com.Olivia.jdbc.DBConnection.getConnection(DBConnection.java:24)\r\n\tat com.Olivia.jdbc.DBConnectionTest.main(DBConnectionTest.java:15)\r\nException in thread \"main\" java.lang.NullPointerException\r\n\tat com.Olivia.jdbc.DBConnectionTest.main(DBConnectionTest.java:16)\r\n```\r\n\r\nWhile debugging this exception, just check the URL getting printed in the logs, as in above logs the URL String is 'jdbc:mysql:\/\/localhost:3306\/UserDB whereas it should be jdbc:mysql:\/\/localhost:3306\/UserDB.\r\n<\/code><\/pre>\n<ol>JDBC\u7684\u6700\u4f73\u5b9e\u8df5\u662f\u4ec0\u4e48\uff1f<\/ol>\n<pre class=\"post-pre\"><code>Some of the JDBC Best Practices are:\r\n-   Database resources are heavy, so make sure you close it as soon as you are done with it. Connection, Statement, ResultSet and all other JDBC objects have close() method defined to close them.\r\n-   Always close the result set, statement and connection explicitly in the code, because if you are working in connection pooling environment, the connection might be returned to the pool leaving open result sets and statement objects resulting in resource leak.\r\n-   Close the resources in the finally block to make sure they are closed even in case of exception scenarios.\r\n-   Use batch processing for bulk operations of similar kind.\r\n-   Always use PreparedStatement over Statement to avoid SQL Injection and get pre-compilation and caching benefits of PreparedStatement.\r\n-   If you are retrieving bulk data into result set, setting an optimal value for fetchSize helps in getting good performance.\r\n-   The database server might not support all isolation levels, so check it before assuming.\r\n-   More strict isolation levels result in slow performance, so make sure you have optimal isolation level set for your database connections.\r\n-   If you are creating database connections in a web application, try to use JDBC DataSource resources using JNDI context for re-using the connections.\r\n-   Try to use disconnected RowSet when you need to work with ResultSet for a long time.\r\n<\/code><\/pre>\n<p>\u4ee5\u4e0a\u5c31\u662fJDBC\u9762\u8bd5\u95ee\u9898\u548c\u7b54\u6848\u7684\u5168\u90e8\u5185\u5bb9\uff0c\u5e0c\u671b\u80fd\u5bf9\u4f60\u5728JDBC\u9762\u8bd5\u4e2d\u6709\u6240\u5e2e\u52a9\u3002\u5982\u679c\u6211\u6f0f\u6389\u4e86\u4efb\u4f55\u91cd\u8981\u7684\u95ee\u9898\uff0c\u8bf7\u544a\u8bc9\u6211\uff0c\u6211\u4f1a\u5c06\u5176\u6dfb\u52a0\u5230\u5217\u8868\u4e2d\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u6b22\u8fce\u9605\u8bfbJDBC\u9762\u8bd5\u95ee\u9898\u4e0e\u7b54\u6848\u7cfb\u5217\u6587\u7ae0\u3002JDBC API\u4e3b\u8981\u7528\u4e8e\u8fde\u63a5\u5173\u7cfb\u578b\u6570\u636e\u5e93\uff0c\u5e76\u4eceJava\u7a0b\u5e8f\u4e2d\u6267\u884cSQL [&hellip;]<\/p>\n","protected":false},"author":9,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[761,941,940,937,942],"class_list":{"0":"post-257","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"hentry","6":"category-uncategorized","7":"tag-java","9":"tag-jdbc","10":"tag-937","11":"tag-942"},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v21.5 (Yoast SEO v21.5) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>JDBC\u9762\u8bd5\u7cbe\u9009\uff1a\u5e38\u89c1\u95ee\u9898\u4e0e\u6743\u5a01\u7b54\u6848\u89e3\u6790 - Blog - Silicon Cloud<\/title>\n<meta name=\"description\" content=\"\u51c6\u5907JDBC\u9762\u8bd5\uff1f\u672c\u6587\u4e3a\u60a8\u7cbe\u9009\u4e86JDBC\u5e38\u89c1\u9762\u8bd5\u95ee\u9898\uff0c\u5e76\u63d0\u4f9b\u6743\u5a01\u3001\u8be6\u7ec6\u7684\u7b54\u6848\u89e3\u6790\uff0c\u52a9\u60a8\u8f7b\u677e\u5e94\u5bf9Java\u6570\u636e\u5e93\u8fde\u63a5\u6280\u672f\u9762\u8bd5\uff0c\u63d0\u5347\u6c42\u804c\u7ade\u4e89\u529b\u3002\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.silicloud.com\/zh\/blog\/jdbc\u9762\u8bd5\u95ee\u9898\u4e0e\u7b54\u6848\/\" \/>\n<meta property=\"og:locale\" content=\"zh_CN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"JDBC\u9762\u8bd5\u7cbe\u9009\uff1a\u5e38\u89c1\u95ee\u9898\u4e0e\u6743\u5a01\u7b54\u6848\u89e3\u6790\" \/>\n<meta property=\"og:description\" content=\"\u51c6\u5907JDBC\u9762\u8bd5\uff1f\u672c\u6587\u4e3a\u60a8\u7cbe\u9009\u4e86JDBC\u5e38\u89c1\u9762\u8bd5\u95ee\u9898\uff0c\u5e76\u63d0\u4f9b\u6743\u5a01\u3001\u8be6\u7ec6\u7684\u7b54\u6848\u89e3\u6790\uff0c\u52a9\u60a8\u8f7b\u677e\u5e94\u5bf9Java\u6570\u636e\u5e93\u8fde\u63a5\u6280\u672f\u9762\u8bd5\uff0c\u63d0\u5347\u6c42\u804c\u7ade\u4e89\u529b\u3002\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.silicloud.com\/zh\/blog\/jdbc\u9762\u8bd5\u95ee\u9898\u4e0e\u7b54\u6848\/\" \/>\n<meta property=\"og:site_name\" content=\"Blog - Silicon Cloud\" \/>\n<meta property=\"article:published_time\" content=\"2022-11-15T02:46:17+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-07-31T19:02:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/65647bb7a4b2f92e6c72c322\/2-0.jpg\" \/>\n<meta name=\"author\" content=\"\u6e05, \u626c\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u4f5c\u8005\" \/>\n\t<meta name=\"twitter:data1\" content=\"\u6e05, \u626c\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4\" \/>\n\t<meta name=\"twitter:data2\" content=\"2 \u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/jdbc%e9%9d%a2%e8%af%95%e9%97%ae%e9%a2%98%e4%b8%8e%e7%ad%94%e6%a1%88\/\",\"url\":\"https:\/\/www.silicloud.com\/zh\/blog\/jdbc%e9%9d%a2%e8%af%95%e9%97%ae%e9%a2%98%e4%b8%8e%e7%ad%94%e6%a1%88\/\",\"name\":\"JDBC\u9762\u8bd5\u7cbe\u9009\uff1a\u5e38\u89c1\u95ee\u9898\u4e0e\u6743\u5a01\u7b54\u6848\u89e3\u6790 - Blog - Silicon Cloud\",\"isPartOf\":{\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/#website\"},\"datePublished\":\"2022-11-15T02:46:17+00:00\",\"dateModified\":\"2025-07-31T19:02:00+00:00\",\"author\":{\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/cb5556d2501da73d864cac945e8d9461\"},\"description\":\"\u51c6\u5907JDBC\u9762\u8bd5\uff1f\u672c\u6587\u4e3a\u60a8\u7cbe\u9009\u4e86JDBC\u5e38\u89c1\u9762\u8bd5\u95ee\u9898\uff0c\u5e76\u63d0\u4f9b\u6743\u5a01\u3001\u8be6\u7ec6\u7684\u7b54\u6848\u89e3\u6790\uff0c\u52a9\u60a8\u8f7b\u677e\u5e94\u5bf9Java\u6570\u636e\u5e93\u8fde\u63a5\u6280\u672f\u9762\u8bd5\uff0c\u63d0\u5347\u6c42\u804c\u7ade\u4e89\u529b\u3002\",\"breadcrumb\":{\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/jdbc%e9%9d%a2%e8%af%95%e9%97%ae%e9%a2%98%e4%b8%8e%e7%ad%94%e6%a1%88\/#breadcrumb\"},\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.silicloud.com\/zh\/blog\/jdbc%e9%9d%a2%e8%af%95%e9%97%ae%e9%a2%98%e4%b8%8e%e7%ad%94%e6%a1%88\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/jdbc%e9%9d%a2%e8%af%95%e9%97%ae%e9%a2%98%e4%b8%8e%e7%ad%94%e6%a1%88\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u9996\u9875\",\"item\":\"https:\/\/www.silicloud.com\/zh\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"JDBC\u9762\u8bd5\u7cbe\u9009\uff1a\u5e38\u89c1\u95ee\u9898\u4e0e\u6743\u5a01\u7b54\u6848\u89e3\u6790\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/#website\",\"url\":\"https:\/\/www.silicloud.com\/zh\/blog\/\",\"name\":\"Blog - Silicon Cloud\",\"description\":\"\",\"inLanguage\":\"zh-Hans\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/cb5556d2501da73d864cac945e8d9461\",\"name\":\"\u6e05, \u626c\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/32a4239de8ff29adace466261d309424a1e5fe9f7e3036bf89fe03f2e3dbe717?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/32a4239de8ff29adace466261d309424a1e5fe9f7e3036bf89fe03f2e3dbe717?s=96&d=mm&r=g\",\"caption\":\"\u6e05, \u626c\"},\"url\":\"https:\/\/www.silicloud.com\/zh\/blog\/author\/qingyang\/\"},{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/jdbc%e9%9d%a2%e8%af%95%e9%97%ae%e9%a2%98%e4%b8%8e%e7%ad%94%e6%a1%88\/#local-main-organization-logo\",\"url\":\"\",\"contentUrl\":\"\",\"caption\":\"Blog - Silicon Cloud\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"JDBC\u9762\u8bd5\u7cbe\u9009\uff1a\u5e38\u89c1\u95ee\u9898\u4e0e\u6743\u5a01\u7b54\u6848\u89e3\u6790 - Blog - Silicon Cloud","description":"\u51c6\u5907JDBC\u9762\u8bd5\uff1f\u672c\u6587\u4e3a\u60a8\u7cbe\u9009\u4e86JDBC\u5e38\u89c1\u9762\u8bd5\u95ee\u9898\uff0c\u5e76\u63d0\u4f9b\u6743\u5a01\u3001\u8be6\u7ec6\u7684\u7b54\u6848\u89e3\u6790\uff0c\u52a9\u60a8\u8f7b\u677e\u5e94\u5bf9Java\u6570\u636e\u5e93\u8fde\u63a5\u6280\u672f\u9762\u8bd5\uff0c\u63d0\u5347\u6c42\u804c\u7ade\u4e89\u529b\u3002","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.silicloud.com\/zh\/blog\/jdbc\u9762\u8bd5\u95ee\u9898\u4e0e\u7b54\u6848\/","og_locale":"zh_CN","og_type":"article","og_title":"JDBC\u9762\u8bd5\u7cbe\u9009\uff1a\u5e38\u89c1\u95ee\u9898\u4e0e\u6743\u5a01\u7b54\u6848\u89e3\u6790","og_description":"\u51c6\u5907JDBC\u9762\u8bd5\uff1f\u672c\u6587\u4e3a\u60a8\u7cbe\u9009\u4e86JDBC\u5e38\u89c1\u9762\u8bd5\u95ee\u9898\uff0c\u5e76\u63d0\u4f9b\u6743\u5a01\u3001\u8be6\u7ec6\u7684\u7b54\u6848\u89e3\u6790\uff0c\u52a9\u60a8\u8f7b\u677e\u5e94\u5bf9Java\u6570\u636e\u5e93\u8fde\u63a5\u6280\u672f\u9762\u8bd5\uff0c\u63d0\u5347\u6c42\u804c\u7ade\u4e89\u529b\u3002","og_url":"https:\/\/www.silicloud.com\/zh\/blog\/jdbc\u9762\u8bd5\u95ee\u9898\u4e0e\u7b54\u6848\/","og_site_name":"Blog - Silicon Cloud","article_published_time":"2022-11-15T02:46:17+00:00","article_modified_time":"2025-07-31T19:02:00+00:00","og_image":[{"url":"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/65647bb7a4b2f92e6c72c322\/2-0.jpg"}],"author":"\u6e05, \u626c","twitter_card":"summary_large_image","twitter_misc":{"\u4f5c\u8005":"\u6e05, \u626c","\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4":"2 \u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.silicloud.com\/zh\/blog\/jdbc%e9%9d%a2%e8%af%95%e9%97%ae%e9%a2%98%e4%b8%8e%e7%ad%94%e6%a1%88\/","url":"https:\/\/www.silicloud.com\/zh\/blog\/jdbc%e9%9d%a2%e8%af%95%e9%97%ae%e9%a2%98%e4%b8%8e%e7%ad%94%e6%a1%88\/","name":"JDBC\u9762\u8bd5\u7cbe\u9009\uff1a\u5e38\u89c1\u95ee\u9898\u4e0e\u6743\u5a01\u7b54\u6848\u89e3\u6790 - Blog - Silicon Cloud","isPartOf":{"@id":"https:\/\/www.silicloud.com\/zh\/blog\/#website"},"datePublished":"2022-11-15T02:46:17+00:00","dateModified":"2025-07-31T19:02:00+00:00","author":{"@id":"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/cb5556d2501da73d864cac945e8d9461"},"description":"\u51c6\u5907JDBC\u9762\u8bd5\uff1f\u672c\u6587\u4e3a\u60a8\u7cbe\u9009\u4e86JDBC\u5e38\u89c1\u9762\u8bd5\u95ee\u9898\uff0c\u5e76\u63d0\u4f9b\u6743\u5a01\u3001\u8be6\u7ec6\u7684\u7b54\u6848\u89e3\u6790\uff0c\u52a9\u60a8\u8f7b\u677e\u5e94\u5bf9Java\u6570\u636e\u5e93\u8fde\u63a5\u6280\u672f\u9762\u8bd5\uff0c\u63d0\u5347\u6c42\u804c\u7ade\u4e89\u529b\u3002","breadcrumb":{"@id":"https:\/\/www.silicloud.com\/zh\/blog\/jdbc%e9%9d%a2%e8%af%95%e9%97%ae%e9%a2%98%e4%b8%8e%e7%ad%94%e6%a1%88\/#breadcrumb"},"inLanguage":"zh-Hans","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.silicloud.com\/zh\/blog\/jdbc%e9%9d%a2%e8%af%95%e9%97%ae%e9%a2%98%e4%b8%8e%e7%ad%94%e6%a1%88\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.silicloud.com\/zh\/blog\/jdbc%e9%9d%a2%e8%af%95%e9%97%ae%e9%a2%98%e4%b8%8e%e7%ad%94%e6%a1%88\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u9996\u9875","item":"https:\/\/www.silicloud.com\/zh\/blog\/"},{"@type":"ListItem","position":2,"name":"JDBC\u9762\u8bd5\u7cbe\u9009\uff1a\u5e38\u89c1\u95ee\u9898\u4e0e\u6743\u5a01\u7b54\u6848\u89e3\u6790"}]},{"@type":"WebSite","@id":"https:\/\/www.silicloud.com\/zh\/blog\/#website","url":"https:\/\/www.silicloud.com\/zh\/blog\/","name":"Blog - Silicon Cloud","description":"","inLanguage":"zh-Hans"},{"@type":"Person","@id":"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/cb5556d2501da73d864cac945e8d9461","name":"\u6e05, \u626c","image":{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/32a4239de8ff29adace466261d309424a1e5fe9f7e3036bf89fe03f2e3dbe717?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/32a4239de8ff29adace466261d309424a1e5fe9f7e3036bf89fe03f2e3dbe717?s=96&d=mm&r=g","caption":"\u6e05, \u626c"},"url":"https:\/\/www.silicloud.com\/zh\/blog\/author\/qingyang\/"},{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/www.silicloud.com\/zh\/blog\/jdbc%e9%9d%a2%e8%af%95%e9%97%ae%e9%a2%98%e4%b8%8e%e7%ad%94%e6%a1%88\/#local-main-organization-logo","url":"","contentUrl":"","caption":"Blog - Silicon Cloud"}]}},"_links":{"self":[{"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/posts\/257","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/users\/9"}],"replies":[{"embeddable":true,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/comments?post=257"}],"version-history":[{"count":3,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/posts\/257\/revisions"}],"predecessor-version":[{"id":109740,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/posts\/257\/revisions\/109740"}],"wp:attachment":[{"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/media?parent=257"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/categories?post=257"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/tags?post=257"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}