先上一個登錄代碼---判斷登錄是否成功
1、c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <default-config> <property name="user">root</property> <property name="password">123456</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql:///filter</property> </default-config> </c3p0-config>
2.C3P0Utils
package com.ithiema.utils; import com.mchange.v2.c3p0.ComboPooledDataSource; import javax.sql.DataSource; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class C3P0Utils { //import com.mchange.v2.c3p0.ComboPooledDataSource; private static DataSource dataSource = new ComboPooledDataSource(); private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); // 直接可以獲取一個連接池 public static DataSource getDataSource() { return dataSource; } public static Connection getConnection() throws SQLException{ return dataSource.getConnection(); } // 獲取連接對象 public static Connection getCurrentConnection() throws SQLException { Connection con = tl.get(); if (con == null) { con = dataSource.getConnection(); tl.set(con); } return con; } // 開啟事務 public static void startTransaction() throws SQLException { Connection con = getCurrentConnection(); if (con != null) { con.setAutoCommit(false); } } // 事務回滾 public static void rollback() throws SQLException { Connection con = getCurrentConnection(); if (con != null) { con.rollback(); } } // 提交並且 關閉資源及從ThreadLocall中釋放 public static void commitAndRelease() throws SQLException { Connection con = getCurrentConnection(); if (con != null) { con.commit(); // 事務提交 con.close();// 關閉資源 tl.remove();// 從線程綁定中移除 } } // 關閉資源方法 public static void closeConnection() throws SQLException { Connection con = getCurrentConnection(); if (con != null) { con.close(); } } public static void closeStatement(Statement st) throws SQLException { if (st != null) { st.close(); } } public static void closeResultSet(ResultSet rs) throws SQLException { if (rs != null) { rs.close(); } } }
3.UserDao.java
package com.ithiema.dao; import com.ithiema.domain.User; import com.ithiema.utils.C3P0Utils; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import java.sql.SQLException; public class UserDao { public User login(String username, String password) throws SQLException { QueryRunner runner = new QueryRunner(C3P0Utils.getDataSource()); String sql = "select * from user where username=? and password=?"; return runner.query(sql, new BeanHandler<User>(User.class), username,password); } }
QueryRunner類
QueryRunner中提供對sql語句操作的API
它主要有三個方法
query() 用於執行select
update() 用於執行insert/update/delete
batch() 批處理
1,Query語句
先來看下query的兩種形式, 我們這里主要講第一個方法, 因為我們用C3P0來統一管理connection.(QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()))
query(sql,ResultSetHandler,Object...params);
query(conn,sql,ResultSetHandler,Object...params);
第一種: 不需要params
//查詢所有圖書
public List<Book> selectAllBooks() throws SQLException {
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
return qr.query("select * from books", new BeanListHandler<Book>(Book.class));
}
第二種: 需要一個參數查詢
//根據id查詢指定的書
public Book selectBookById(String id) throws SQLException {
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
return qr.query("select * from books where id=?", new BeanHandler(Book.class),id);
}
第三種:需要多個參數查詢
//多條件查詢圖書信息
public List<Book> findBookByManyCondition(String id, String category,
String name, String minprice, String maxprice) throws SQLException {
StringBuilder sql = new StringBuilder("select * from books where 1=1");
List list = new ArrayList();
if(!"".equals(id)){
sql.append(" and id like ?");
list.add("%"+id+"%");
}
if(!"".equals(category)){
sql.append(" and category=?");
list.add(category);
}
if(!"".equals(name)){
sql.append(" and name like ?");
list.add("%"+name+"%");
}
if(!"".equals(minprice)){
sql.append(" and price > ?");
list.add(minprice);
}
if(!"".equals(maxprice)){
sql.append(" and price < ?");
list.add(maxprice);
}
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
return qr.query(sql.toString(),new BeanListHandler<Book>(Book.class),list.toArray());
}
那么我們來看下源碼的實現:
(1)QueryRunner.java
//第一種情況,無參數
public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {
Connection conn = this.prepareConnection();
return this.query(conn, true, sql, rsh, (Object[]) null);
}
//第二種和第三種使用同一方法: 需要參數
public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
Connection conn = this.prepareConnection();
return this.query(conn, true, sql, rsh, params);
}
解讀: 這里先是獲取connection, 利用this.preparaConnection() 獲取. 然后調用query()方法去執行查詢語句. 接下來看源碼是如何獲取到當前傳輸過來的connection以及query()方法的內部實現.
protected Connection prepareConnection() throws SQLException {
if (this.getDataSource() == null) {
throw new SQLException("QueryRunner requires a DataSource to be " +
"invoked in this way, or a Connection should be passed in");
}
return this.getDataSource().getConnection();
}
這里很簡單, 因為我們用的C3P0數據庫連接池獲取的DataSource, 所以這里直就可以過去到當前的Connection.接下來就看下query()方法的內部實現.
private <T> T query(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params)
throws SQLException {
if (conn == null) {
throw new SQLException("Null connection");
}
if (sql == null) {
if (closeConn) {
close(conn);
}
throw new SQLException("Null SQL statement");
}
if (rsh == null) {
if (closeConn) {
close(conn);
}
throw new SQLException("Null ResultSetHandler");
}
PreparedStatement stmt = null;
ResultSet rs = null;
T result = null;
try {
stmt = this.prepareStatement(conn, sql);
this.fillStatement(stmt, params);
rs = this.wrap(stmt.executeQuery());
result = rsh.handle(rs);
} catch (SQLException e) {
this.rethrow(e, sql, params);
} finally {
try {
close(rs);
} finally {
close(stmt);
if (closeConn) {
close(conn);
}
}
}
return result;
}
解讀: 在這里可以看出, 無論是否有傳遞參數params, 都調用的是同一個query方法, 接着來看this.fillStatement(stmt, params);是如何將參數賦予preparedStatement中的.
public void fillStatement(PreparedStatement stmt, Object... params) throws SQLException {
// check the parameter count, if we can
ParameterMetaData pmd = null;
if (!pmdKnownBroken) {
pmd = stmt.getParameterMetaData();
int stmtCount = pmd.getParameterCount();
int paramsCount = params == null ? 0 : params.length;
if (stmtCount != paramsCount) {
throw new SQLException("Wrong number of parameters: expected "
+ stmtCount + ", was given " + paramsCount);
}
}
// nothing to do here
if (params == null) {
return;
}
for (int i = 0; i < params.length; i++) {
if (params[i] != null) {
stmt.setObject(i + 1, params[i]);
} else {
// VARCHAR works with many drivers regardless
// of the actual column type. Oddly, NULL and
// OTHER don't work with Oracle's drivers.
int sqlType = Types.VARCHAR;
if (!pmdKnownBroken) {
try {
sqlType = pmd.getParameterType(i + 1);
} catch (SQLException e) {
pmdKnownBroken = true;
}
}
stmt.setNull(i + 1, sqlType);
}
}
}
這個方法就是核心所在.
第一種情況: 當params為null的時候, 直接return然后執行sql語句.
第二種第三種情況: 當params不為null時, 循環遍歷傳入的params, 然后將params賦值到preparedStatement中, 然后填充占位符進行sql查詢. 這里我們也來回顧下直接使用preparedStatement來進行查詢的方式:
@Test
public void update(){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "update users set name=?,email=? where id=?";
st = conn.prepareStatement(sql);
st.setString(1, "gacl");
st.setString(2, "gacl@sina.com");
st.setInt(3, 2);
int num = st.executeUpdate();
if(num>0){
System.out.println("更新成功!!");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
@Test
public void find(){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "select * from users where id=?";
st = conn.prepareStatement(sql);
st.setInt(1, 1);
rs = st.executeQuery();
if(rs.next()){
System.out.println(rs.getString("name"));
}
}catch (Exception e) {
}finally{
JdbcUtils.release(conn, st, rs);
}
}
2, Update語句
查看update語句:
//修改圖書
public void updateBook(Book book) throws SQLException {
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
qr.update(
"UPDATE books SET NAME=? ,price=?,bnum=?,category=?,description=? WHERE id=?",
book.getName(), book.getPrice(), book.getBnum(),
book.getCategory(), book.getDescription(), book.getId())
}
接着是QueryRunner.java中的update 方法:
public int update(String sql, Object... params) throws SQLException {
Connection conn = this.prepareConnection();
return this.update(conn, true, sql, params);
}
private int update(Connection conn, boolean closeConn, String sql, Object... params) throws SQLException {
if (conn == null) {
throw new SQLException("Null connection");
}
if (sql == null) {
if (closeConn) {
close(conn);
}
throw new SQLException("Null SQL statement");
}
PreparedStatement stmt = null;
int rows = 0;
try {
stmt = this.prepareStatement(conn, sql);
this.fillStatement(stmt, params);
rows = stmt.executeUpdate();
} catch (SQLException e) {
this.rethrow(e, sql, params);
} finally {
close(stmt);
if (closeConn) {
close(conn);
}
}
return rows;
}
到了參數賦值的時候又調用了上面的fillStatement方法, 這里就不再闡述了.
3, Batch語句
這里直接看batch方法的實例, 然后結合源碼的實現.
//批量刪除
public void delBooks(String[] ids) throws SQLException {
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
Object[][] params = new Object[ids.length][];//高維確定執行sql語句的次數,低維是給?賦值
for (int i = 0; i < params.length; i++) {
params[i] = new Object[]{ids[i]};//給“?”賦值
}
qr.batch("delete from books where id=?", params);
}
然后看QueryRunner中的batch()方法:
public int[] batch(String sql, Object[][] params) throws SQLException {
Connection conn = this.prepareConnection();
return this.batch(conn, true, sql, params);
}
private int[] batch(Connection conn, boolean closeConn, String sql, Object[][] params) throws SQLException {
if (conn == null) {
throw new SQLException("Null connection");
}
if (sql == null) {
if (closeConn) {
close(conn);
}
throw new SQLException("Null SQL statement");
}
if (params == null) {
if (closeConn) {
close(conn);
}
throw new SQLException("Null parameters. If parameters aren't need, pass an empty array.");
}
PreparedStatement stmt = null;
int[] rows = null;
try {
stmt = this.prepareStatement(conn, sql);
for (int i = 0; i < params.length; i++) {
this.fillStatement(stmt, params[i]);
stmt.addBatch();
}
rows = stmt.executeBatch();
} catch (SQLException e) {
this.rethrow(e, sql, (Object[])params);
} finally {
close(stmt);
if (closeConn) {
close(conn);
}
}
return rows;
}
解讀: 因為params是一個二維數組, 所以往preparedStatement中賦值的時候使用了for循環, 然后通過preparedstatement.addBatch() 進行批量添加, 然后執行executeBatch()進行操作.
/**
* Adds a set of parameters to this <code>PreparedStatement</code>
* object's batch of commands.
*
* @exception SQLException if a database access error occurs or
* this method is called on a closed <code>PreparedStatement</code>
* @see Statement#addBatch
* @since 1.2
*/
void addBatch() throws SQLException;

