【JAVA EE#2】【傳智書城·源碼閱讀】登錄模塊:c3p0連接數據庫+DButils寫Dao實現數據庫存儲+QueryRunner使用套路

本來想自己按着書本項目來做一遍的,在閱讀代碼時,無奈代碼量實在太多而且沒有詳盡的指導,層次相當複雜,自己不清楚原理,不明白架構,對我自己來講不會就不能硬着頭皮上,所以做項目變成了源碼閱讀尷尬了。

導包配置步驟:
在畫思維導圖的時候,首要的就是數據庫操作,源碼利用了c3p0的連接方式,首先要明確用到的四個類

--jdk自帶的類
javax.sql.DataSource
java.lang.ThreadLocal<Connection>

--apache dbutils工具類
org.apache.commons.dbutils.QueryRunner;
org.apache.commons.dbutils.handlers.BeanHandler

--c3p0操作庫
com.mchange.v2.c3p0.ComboPooledDataSource

用的DButils包爲commons-dbutils-1.4.jarjdk1.7c3p0-0.9.1.2.jar
參考了具體操作有大佬博客寫得詳細:https://blog.csdn.net/qq_27869123/article/details/81138638
不同於傳統的JDBC操作,這個需要指定c3p0-config.xml配置文件
具體內容如下,文件放在項目名/src根目錄下

<?xml version="1.0" encoding="UTF-8"?>
<!--該文件用於配置數據庫連接參數-->
<c3p0-config>
	<default-config>
		<property name="user">root</property>
		<property name="password">root</property>
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql://localhost:3306/itcaststore?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false</property>
	</default-config>
</c3p0-config>

同樣位置還有一個叫merchantInfo.properties的文件,但是不清楚其作用,刪掉後也無影響,暫且不管,內容是:

p1_MerId=10001126856
keyValue=69cl522AV6q613Ii4W6u8K6XuW8vM1N6bFgyv769220IuYe9u37N4y7rI4Pl
responseURL=http://localhost:8080/itcaststore/callback

DataSource層初始化步驟:
寫一個數據源工具類,完成數據源實例化、獲取數據源、通過ThreadLocal鍵值對獲取本線程對應Connection實例。

public class DataSourceUtils {
	//ComboPooledDataSource 是c3p0封裝了jdbc 對DataSource接口的實現。
	//默認讀取c3p0-config.xml配置信息,主機名端口號數據庫名和用戶名密碼等
	private static DataSource dataSource = new ComboPooledDataSource();
	//ThreadLoacl對象,用當前線程作爲key,存儲每個線程對應的一個Connection對象
	private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
	public static DataSource getDataSource() {
		return dataSource;
	}
	/**
	 * 當DBUtils需要手動控制事務時,調用該方法獲得一個連接
	 */
	public static Connection getConnection() 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 = getConnection();
		if (con != null)
			con.setAutoCommit(false);
	}
	/**
	 * 從ThreadLocal中釋放並且關閉Connection,並結束事務
	 */
	public static void releaseAndCloseConnection() throws SQLException {
		Connection con = getConnection();
		if (con != null) {
			con.commit();
			tl.remove();
			con.close();
		}
	}
	/**
	 * 事務回滾
	 */
	public static void rollback() throws SQLException {
		Connection con = getConnection();
		if (con != null) {
			con.rollback();
		}
	}
}

該類除了獲取實例外還定義了一些開啓事務關閉實務回滾事務的方法。一般寫代碼都是用手動控制的方法,這些當是初始化DataSource配置就可以了。

Dao層步驟:
底層準備完,來寫高層,本層主要QueryRunner利用DataSource進行數據庫訪問。

public class UserDao {
	// 添加用戶
	public void addUser(User user) throws SQLException {
		String sql = "insert into user(username,password,gender,email,telephone,introduce,activecode) values(?,?,?,?,?,?,?)";
		QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
		int row = runner.update(sql, user.getUsername(), user.getPassword(),
				user.getGender(), user.getEmail(), user.getTelephone(),
				user.getIntroduce(), user.getActiveCode());
		if (row == 0) {
			throw new RuntimeException();
		}
	}

	// 根據激活碼查找用戶
	public User findUserByActiveCode(String activeCode) throws SQLException {
		String sql = "select * from user where activecode=?";
		QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
		return runner.query(sql, new BeanHandler<User>(User.class), activeCode);

	}

	// 激活用戶
	public void activeUser(String activeCode) throws SQLException {
		String sql = "update user set state=? where activecode=?";
		QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
		runner.update(sql, 1, activeCode);
	}
	
	//根據用戶名與密碼查找用戶
	public User findUserByUsernameAndPassword(String username, String password) throws SQLException {
		String sql="select * from user where username=? and password=?";
		QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
		return runner.query(sql, new BeanHandler<User>(User.class),username,password);
	}
}

總體上看,實例化一個QueryRunner 都是利用一個DataSource對象
addUser()方法裏用調用了QueryRunnerupdate()方法,並且返回一個int類型返回碼,該值是操作數據庫後受影響的行數,插入數據沒有影響則報錯。
findUserByActiveCode() 方法裏面調用了QueryRunnerquery()方法中實例化一個BeanHandler<User>集合以及隨後的activeCode查詢條件通配sql語句?號,用於返回符合條件的用戶。再觀察隨後兩個方法,不難發現QueryRunner的使用套路,無論是查詢還是更新,方法裏面第一個參數必定是含有佔位符?的sql語句,如果是查詢返回的應該是結果集,所以方法第二個參數必定爲BeanHandler<T>,然後按照sql佔位符的順序排列好的參數一一放在其後。

說說BeanHandler<T>,因爲JDBC返回的是Result對象,不同於直接用Resultset結果集next()循環操作的原生JDBC,BeanHandler是直接把結果實體類封裝成了javabean,丟回去一個實體類。就該項目來說,只是用BeanHandler返回單個結果,至於怎麼用這個東西返回一堆實體類呢?應該也能實現,有空試試。

感激樂於分享技術的巨人前輩們!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章