Java Web系列:JDBC 基礎

剛哥521發表於2016-01-09

ADO.NET在Java中的對應技術是JDBC,企業庫DataAccessApplicationBlock模組在Java中的對應是spring-jdbc模組,EntityFramework在Java中對應的ORM是Hibernate。關聯式資料庫、SQL、資料庫事務、分散式事務的概念都是通用的。

1.JDBC

JDBC程式碼和ADO.NET程式碼一樣,除了學習時寫demo來掌握核心物件外,不適合在專案中直接使用。另外Java中萬年不變的學術派抽象介面給我目前看到的大多數容器和框架帶來了極大的不便,如Tomcat和Spring中定義的一些抽象型別無論是屬性和方法都和.NET中的很相似,但又不得不和Java中的基礎介面適配。抽象不能像Java中一樣只關注操作介面而不關注基礎的資料結構,正確的型別的抽象比方法的抽象更重要,但Java中抽象的做法好像還有一個原則就是與眾不同,而不只是關注操作。

(1)核心物件

ADO.NET中的4個核心抽象類是DbConnectionDbCommandDbParameterDbTransaction.DataAdapter和DataSet構建在核心物件之上。

JDBC的4個核心介面DataSourceConnectionStatementResultSetPreparedStatement是Statement的子型別。

雖然兩者不能完全匹配,對資料庫操作的核心操作是一致的,都是開啟連結、傳送命令和關閉連線的過程。

(2)引數處理

 引數的核心是索引和引數值。ADO.NET中提供了DbDataParameter型別用於引數的抽象。JDBC中沒有提供引數的抽象型別,而是通過PreparedStatement定義的方法來設定引數,可以通過Connection物件獲取該型別物件。

(3)事務處理

事務的核心操作是提交和回滾。ADO.NET中提供了DbTransaction型別用於事務的抽象,可以通過DbConnection的BeginTransaction方法開啟顯式事務.JDBC中沒有提供事務的抽象型別,而是通過Connection定義的setAutoCommitcommitrollback方法來操作事務。隱性事務的自動提交和顯性事務的手動控制方面兩者是一致的。

(4)資料來源

JDBC中的Connection不具備連線池等高階功能。DataSource定義了資料來源介面,用於連線管理、連線池和分散式事務等高階功能,連線池的重要性不用強調了。我們可以使用Apache Commons DBCP 2或C3P0(Hibernate)等第三方DataSource實現,Spring框架也有內建的一些簡單的DataSource實現,如SimpleDriverDataSource、SingleConnectionDataSource、DriverManagerDataSource等。

我們分別使用DriverManager、commons-dbcp2(參考1)、c3p0(參考2)和h2database(參考3)演示無連線池、dbcp2連線池、c3p0連線池的使用:

 1 package test.jdbc;
 2 
 3 import java.beans.PropertyVetoException;
 4 import java.sql.Connection;
 5 import java.sql.DriverManager;
 6 import java.sql.PreparedStatement;
 7 import java.sql.ResultSet;
 8 import java.sql.SQLException;
 9 import javax.sql.DataSource;
10 
11 import org.apache.commons.dbcp2.BasicDataSource;
12 import com.mchange.v2.c3p0.ComboPooledDataSource;
13 
14 public class Jdbc {
15     public static void main(String[] args) throws ClassNotFoundException, SQLException, PropertyVetoException {
16         String ddl = "create table user(id integer not null primary key,username varchar(64))";
17         String dml = "insert into user (id,username) values(1,'tom')";
18         String query = "select id,username from user where username=?";
19         String className = "org.h2.Driver";
20         String url = "jdbc:h2:mem:test";
21         // Connection conn = DriverManager.getConnection(url);
22         Connection conn = get_dbcp2_dataSource(className, url).getConnection();
23         // Connection conn = get_c3p0_dataSource(className,//
24         // url).getConnection();
25         conn.setAutoCommit(false);
26         conn.prepareStatement(ddl).execute();
27         conn.createStatement().execute(dml);
28         conn.commit();
29         conn.setAutoCommit(true);
30         PreparedStatement statement = conn.prepareStatement(query);
31         statement.setString(1, "tom");
32         ResultSet rs = statement.executeQuery();
33         User user = new User();
34         while (rs.next()) {
35             user.setId(rs.getInt(1));
36             user.setUsername("username");
37             break;
38         }
39         conn.close();
40         System.out.println(String.format("id:%d,username:%s", user.getId(), user.getUsername()));
41     }
42 
43     public static Connection getConnection(String driverClassName, String url) {
44         try {
45             Class.forName(driverClassName);
46         } catch (ClassNotFoundException e1) {
47             e1.printStackTrace();
48         }
49         Connection conn = null;
50         try {
51             conn = DriverManager.getConnection(url);
52         } catch (SQLException e) {
53             e.printStackTrace();
54         }
55         return conn;
56     }
57 
58     public static DataSource get_dbcp2_dataSource(String clssName, String url) {
59 
60         BasicDataSource dataSource = new BasicDataSource();
61         dataSource.setDriverClassName(clssName);
62         dataSource.setUrl(url);
63         return dataSource;
64     }
65 
66     public static DataSource get_c3p0_dataSource(String clssName, String url) throws PropertyVetoException {
67 
68         ComboPooledDataSource dataSource = new ComboPooledDataSource();
69         dataSource.setDriverClass(clssName);
70         dataSource.setJdbcUrl(url);
71         return dataSource;
72     }
73 }

 

其中2dbcp、c3p0和h2database的Maven依賴如下:

 1         <dependency>
 2             <groupId>com.h2database</groupId>
 3             <artifactId>h2</artifactId>
 4             <version>1.4.190</version>
 5         </dependency>
 6         <dependency>
 7             <groupId>org.apache.commons</groupId>
 8             <artifactId>commons-dbcp2</artifactId>
 9             <version>2.1.1</version>
10         </dependency>
11         <dependency>
12             <groupId>com.mchange</groupId>
13             <artifactId>c3p0</artifactId>
14             <version>0.9.5.2</version>
15         </dependency>

2.Spring Jdbc

JDBC和ADO.NET的API類似,都不適合我們直接使用,如果不適用ORM框架,在.NET中我們使用DataAccessApplicationBlock,在Java中我們可以使用Spring JDBC。Spring JDBC的核心類是JdbcTemplate

(1)Sprnig的委託實現

要使用Spring Jdbc的功能,首先要理解Spring中委託的使用方式,Spring定義一個包含該委託簽名的介面(這些介面通常都是沒有內建實現的),當需要委託引數時,使用該介面作為引數型別,利用Java中的匿名類功能,可以在內聯程式碼中實現介面定義的委託,下面列舉幾個常見的委託介面:

Func<Connection,PreparedStatement>:PreparedStatement介面的createPreparedStatement方法。

1 public interface PreparedStatementCreator {
2 
3     PreparedStatement createPreparedStatement(Connection con) throws SQLException;
4 
5 }

Func<PreparedStatement,T>:PreparedStatementCallback泛型介面的doInPreparedStatement方法。

1 public interface PreparedStatementCallback<T> {
2 
3     T doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException;
4 
5 }

Func<TransactionStatus,T>:TransactionCallback泛型介面的T doInTransaction(TransactionStatus status)方法。

1 public interface TransactionCallback<T> {
2 
3     T doInTransaction(TransactionStatus status);
4 
5 }

Fcun<ResultSet,T>:ResultSetExtractor泛型方法的T extractData(ResultSet rs)方法。

1 public interface ResultSetExtractor<T> {
2 
3     T extractData(ResultSet rs) throws SQLException, DataAccessException;
4 
5 }

(1)JdbcTemplate

 JdbcTemplate需要配合DataSource一起使用。JdbcTemplate的程式碼中很多都是通過IDE自動生成匿名類導致的,真正手寫的程式碼量並不多。

 1     public static void main(String[] args) throws ClassNotFoundException, SQLException, PropertyVetoException {
 2         String ddl = "create table user(id integer not null primary key,username varchar(64))";
 3         String dml = "insert into user (id,username) values(1,'tom')";
 4         String query = "select id,username from user where username=?";
 5         String className = "org.h2.Driver";
 6         String url = "jdbc:h2:mem:test";
 7 
 8         DataSource dataSource = get_dbcp2_dataSource(className, url);
 9         JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
10         PlatformTransactionManager tm = new DataSourceTransactionManager(dataSource);
11         TransactionTemplate tt = new TransactionTemplate(tm);
12         tt.execute(new TransactionCallback() {
13 
14             @Override
15             public Object doInTransaction(TransactionStatus status) {
16                 jdbcTemplate.execute(ddl);
17                 jdbcTemplate.execute(dml);
18                 return status;
19             }
20         });
21 
22         User user = jdbcTemplate.query(query, new Object[] { "tom" }, new ResultSetExtractor<User>() {
23             @Override
24             public User extractData(ResultSet rs) throws SQLException, DataAccessException {
25                 User user = new User();
26                 while (rs.next()) {
27                     user.setId(rs.getInt(1));
28                     user.setUsername(rs.getString("username"));
29                     break;
30                 }
31                 return user;
32             }
33         });
34         System.out.println(String.format("id:%d,username:%s", user.getId(), user.getUsername()));
35     }

(3)TransactionTemplate

在上面的程式碼中出現了TransactionTemplate用於事務處理,TransactionTemplate依賴PlatformTransactionManager,Spring Jdbc中定義的DataSourceTransactionManager實現類用於處理資料庫事務。在Spring的orm、jsm、tx等模組中還有其他的實現。

(4)JdbcDaoSupport

JdbcDaoSupport是一個在內部使用JdbcTemplate欄位並且實現了DaoSupport介面的抽象類,可供我們自定義DAO類時參考或用作基類。

 1 class MyDao extends JdbcDaoSupport {
 2     public MyDao(DataSource dataSource) {
 3         this.setJdbcTemplate(new JdbcTemplate(dataSource));
 4     }
 5 
 6     @SuppressWarnings("unchecked")
 7     public void init(String ddl, String dml) {
 8         PlatformTransactionManager tm = new DataSourceTransactionManager(this.getDataSource());
 9         TransactionTemplate tt = new TransactionTemplate(tm);
10 
11         tt.execute(new TransactionCallback() {
12 
13             @Override
14             public Object doInTransaction(TransactionStatus status) {
15                 getJdbcTemplate().execute(ddl);
16                 getJdbcTemplate().execute(dml);
17                 return status;
18             }
19         });
20     }
21 
22     public User getUser(String query, String username) {
23         return this.getJdbcTemplate().query(query, new Object[] { username }, new ResultSetExtractor<User>() {
24             @Override
25             public User extractData(ResultSet rs) throws SQLException, DataAccessException {
26                 User user = new User();
27                 while (rs.next()) {
28                     user.setId(rs.getInt(1));
29                     user.setUsername(rs.getString("username"));
30                     break;
31                 }
32                 return user;
33             }
34         });
35     }
36 }

參考

(1)http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi

(2)http://sourceforge.net/projects/c3p0/

(3)http://www.h2database.com/html/cheatSheet.html

(4)http://commons.apache.org/proper/commons-dbcp/guide/jndi-howto.html

相關文章