JDBC框架


在實際的開發中,如果直接使用JDBC開發,是非常繁瑣且麻煩的,所以現在的企業在開發web程序時,連接數據庫一定會使用一些JDBC的框架。
在學習框架之前,得先掌握一些基礎知識。

  • JDBC元數據(編寫JDBC框架的基礎)

首先就來學習一下JDBC元數據。
元數據就是數據庫、表、列的定義信息。
元數據相關類(DataBaseMetaData),可以通過Connetion接口中的getMetaData()方法來獲得一個DataBaseMetaData對象。
通過實例感受一下:
新建一個web項目,名為demo
因為在之前已經學習了數據庫連接池技術,所以之后有關數據庫操作的部分都可以使用連接池,推薦使用c3p0,因為它相較於別的連接池更加簡單和人性化。
重新編寫工具類JDBCUtils

/**
 * JDBC 工具類,抽取公共方法
 * 
 * @author seawind
 * 
 */
public class JDBCUtils {
	//獲得數據庫的連接	通過c3p0連接池
	private static DataSource dataSource = new ComboPooledDataSource();
	
	public static Connection getConnection() throws SQLException{
		return dataSource.getConnection();
	}
	
	// 釋放資源
	public static void release(ResultSet rs, Statement stmt, Connection conn) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			rs = null;
		}

		release(stmt, conn);
	}

	public static void release(Statement stmt, Connection conn) {
		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			stmt = null;
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			conn = null;
		}
	}
}

新建測試類MetaDataTest,編寫測試代碼

@Test
public void demo1() throws SQLException{
	//通過Connection對象獲得DataBaseMetaData
	Connection conn = JDBCUtils.getConnection();
	DatabaseMetaData databaseMetaData = conn.getMetaData();
	
	//通過DatabaseMetaData可以獲得JDBC連接的參數信息
	System.out.println(databaseMetaData.getURL());
	System.out.println(databaseMetaData.getDriverName());
	System.out.println(databaseMetaData.getUserName());
	
	//獲得某張表的主鍵信息
	ResultSet rs = databaseMetaData.getPrimaryKeys(null, null, "account");
	while(rs.next()){
		//獲得表名
		System.out.println(rs.getString("TABLE_NAME"));
		//獲得列名
		System.out.println(rs.getString(4));
	}
}

都是一些十分簡單的API,沒有什么可說的,自己看看API文檔應該就能明白。

接下來是第二個內容,參數元數據(ParameterMataData),可以獲得預編譯sql語句中?的信息。
通過PreparedStatement調用getParameterMataData()得到。
演示一下。
編寫測試代碼

@Test
public void demo2() throws SQLException{
	Connection conn = JDBCUtils.getConnection();
	String sql = "select * from account where name = ?";
	PreparedStatement stmt = conn.prepareStatement(sql);
	//通過ParameterMataData獲得?的相關信息
	ParameterMetaData parameterMetaData = stmt.getParameterMetaData();
	//獲得參數個數
	int count = parameterMetaData.getParameterCount();
	System.out.println(count);
	//獲得參數類型	該方法並不是所有的數據庫都支持	MySQL不支持
	int type = parameterMetaData.getParameterType(1);
	System.out.println(type);
}

其中的getParameterType()方法並不是所有的數據庫都支持,而恰好MySQL數據就不支持該方法。那如何讓該方法在MySQL數據庫被支持呢?可以在配置Url的時候在后面加上?generateSimpleParameterMetadata=true,然后運行測試代碼,該方法返回的值是12,通過查看源碼,得知12代表的是數據庫中的varchar類型。

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>VARCHAR</code>.
 */
  public final static int VARCHAR         =  12;

但是,請注意了,其實不管你的預編譯sql語句的參數是什么類型,在MySQL數據庫用該方法查詢得到的數據類型永遠都會是12,也就是varchar類型。

接下來是第三個元數據,叫做結果集元數據(ResultSetMetaData)
編寫測試代碼

@Test
public void demo3() throws SQLException{
	//測試結果集元數據ResultSetMetaData
	Connection conn = JDBCUtils.getConnection();
	String sql = "select * from account";
	PreparedStatement stmt = conn.prepareStatement(sql);
	ResultSet rs = stmt.executeQuery();
	//獲得結果集元數據
	ResultSetMetaData resultSetMetaData = rs.getMetaData();
	//獲得列數
	int count = resultSetMetaData.getColumnCount();
	//打印數據表的第一行
	for(int i = 1;i <= count;i++){
		System.out.print(resultSetMetaData.getColumnName(i) + "\t");
	}
	System.out.println();
	
	//打印每列類型
	for(int i = 1;i <= count;i++){
		System.out.print(resultSetMetaData.getColumnTypeName(i) + "\t");
	}
	
	System.out.println();
	
	//打印表數據
	while(rs.next()){
		for(int i = 1;i <= count;i++){
			System.out.print(rs.getObject(i) + "\t");
		}
		System.out.println();
	}
}

有了元數據的基礎之后,我們就可以自己來編寫JDBC框架了。
先創建一個張表,並初始化數據

create table account(
	id int primary key not null auto_increment,
	name varchar(40),
	money double
);

insert into account values(null,'aaa',1000);
insert into account values(null,'bbb',1000);
insert into account values(null,'ccc',1000);

現在就來為該表編寫DAO程序。
新建com.wang.domain包,然后在該包下創建實體類Account。


/**
 * 屬性和列對應
 * @author Administrator
 *
 */
public class Account {
	private int id;
	private String name;
	private double money;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public double getMoney() {
		return money;
	}

	public void setMoney(double money) {
		this.money = money;
	}
}


然后新建com.wang.dao包,在該包下創建類AccountDao。

/**
 * 插入一個賬戶的數據
 * @param account
 */
public void insertAccount(Account account){
	Connection conn = null;
	PreparedStatement stmt = null;
	
	try {
		conn = JDBCUtils.getConnection();
		String sql = "insert into account values(null,?,?)";
		
		stmt = conn.prepareStatement(sql);
		//設置參數
		stmt.setString(1, account.getName());
		stmt.setDouble(2, account.getMoney());
		
		stmt.executeUpdate();
	} catch (SQLException e) {
		e.printStackTrace();
	}finally{
		JDBCUtils.release(stmt, conn);
	}
}

編寫測試代碼,調用執行

public static void main(String[] args) {
		Account account = new Account();
		account.setName("ddd");
		account.setMoney(1000);
		new AccountDao().insertAccount(account);
}

查詢數據庫,數據被成功添加到數據庫。
在這里插入圖片描述
更新和刪除的方法和插入類似,不作過多贅述。

假設這個時候有很多的數據庫表,那我們都要給每一個數據庫表編寫相對應的方法,會發現,重復代碼非常的多,怎么樣能夠簡化它呢?我們應該抽取通用的方法代碼。
新建包com.wang.framework,在該包下新建類JDBCFramework。


/**
 * 自定義的JDBC框架
 * @author Administrator
 *
 */
public class JDBCFramework {
	
	/**
	 * 通用的insert,update和delete方法
	 * @param sql	預編譯需要的sql
	 * @param args	根據sql中的?准備的參數
	 */
	public static void update(String sql,Object... args){
		Connection conn = null;
		PreparedStatement stmt = null;
		
		try {
			conn = JDBCUtils.getConnection();
			stmt = conn.prepareStatement(sql);
			
			//設置參數		根據?設置參數
			ParameterMetaData parameterMetaData = stmt.getParameterMetaData();
			//獲得參數個數
			int count = parameterMetaData.getParameterCount();
			for(int i = 1;i <= count;i++){
				stmt.setObject(i, args[i - 1]);
			}
			stmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.release(stmt, conn);
		}
	}
}

然后回到類AccountDao中,編寫一個刪除的方法。

public void deleteAccount(Account account) {
		String sql = "delete from account where id = ?";
		JDBCFramework.update(sql, account.getId());
}

因為現在有了自己編寫好的框架,所以實現一個刪除方法是非常簡單的。
編寫測試代碼

public static void main(String[] args) {
	Account account = new Account();
	account.setId(3);
	new AccountDao().deleteAccount(account);
}

運行測試代碼
在這里插入圖片描述
id為3的用戶數據被成功刪除。
插入、修改方法和刪除類似。但是該框架無法用於查詢,因為沒有查詢所對應的結果集。
我們來抽取一個查詢方法,用於數據庫表的通用查詢。
在com.wang.framework包下新建接口MyResultSetHandler。

public interface MyResultSetHandler {
	
	/**
	 * 將結果集中的數據封裝成對象
	 * @param rs
	 * @return
	 */
	public Object handle(ResultSet rs);
}

在JDBCFramework類中添加方法。

/**
 * 通用的查詢方法
 */
public static Object query(String sql,MyResultSetHandler handler,Object... args){
	Object obj = null;
	
	Connection conn = null;
	PreparedStatement stmt = null;
	ResultSet rs = null;
	
	try {
		conn = JDBCUtils.getConnection();
		stmt = conn.prepareStatement(sql);
		
		//設置參數
		ParameterMetaData parameterMetaData = stmt.getParameterMetaData();
		int count = parameterMetaData.getParameterCount();
		for(int i = 1;i <= count;i++){
			stmt.setObject(i, args[i - 1]);
		}
		rs = stmt.executeQuery();
		obj = handler.handle(rs);
	} catch (SQLException e) {
		e.printStackTrace();
	}finally{
		JDBCUtils.release(rs, stmt, conn);
	}
	
	return obj;
}

這樣查詢框架就寫好了,當調用者調用該方法時需要自己實現接口,完成查詢。


    public Account findById(int id){
		//使用自定義框架查詢
		String sql = "select * from account where id = ?";
		MyResultSetHandler handler = new MyResultSetHandler() {
			
			public Object handle(ResultSet rs) {
				try {
					if(rs.next()){
						Account account = new Account();
						account.setId(rs.getInt("id"));
						account.setName(rs.getString("name"));
						account.setMoney(rs.getDouble("money"));
					}
				} catch (SQLException e) {
					e.printStackTrace();
				}
				return null;
			}
		};
		return (Account) JDBCFramework.query(sql, handler, id);
	}

編寫測試代碼。

    public static void main(String[] args){
    	Account account = new AccountDao().findById(1);
		System.out.println(account.getName());
	}

運行測試代碼,成功查詢到表數據。

會發現,要想實現查詢,調用者就必須實現接口,接下來我們利用泛型和反射幫助調用者實現接口。

這塊內容有點復雜,就直接貼代碼了。
修改MyResultSetHandler接口

public interface MyResultSetHandler<T> {
	
	/**
	 * 將結果集中的數據封裝成對象
	 * @param rs
	 * @return
	 */
	public T handle(ResultSet rs);
}

新建類MyBeanHandler


    public class MyBeanHandler<T> implements MyResultSetHandler<T>{

	private Class<T> domainClass;

	public MyBeanHandler(Class<T> domainClass){
		this.domainClass = domainClass;
	}
	
	public T handle(ResultSet rs) {
		try{
			//獲得結果集元數據
			ResultSetMetaData resultSetMetaData = rs.getMetaData();
			int count =resultSetMetaData.getColumnCount();
			
			BeanInfo beanInfo = Introspector.getBeanInfo(domainClass);
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			if(rs.next()){
				//獲得實例
				T t = domainClass.newInstance()	;
				for(int i = 1;i <= count;i++){
					String columnName = resultSetMetaData.getColumnName(i);
					//獲得列名		需要去查找匹配屬性
					for(PropertyDescriptor descriptor : propertyDescriptors){
						if(columnName.equals(descriptor.getName())){
							//列名存在一個同名的屬性
							//將列的值存入屬性
							Method method = descriptor.getWriteMethod();
							method.invoke(t,rs.getObject(columnName));
						}
					}
				}
				return t;
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}

修改類JDBCFramework的查詢方法


/**
 * 通用的查詢方法
 */
	public static <T>T query(String sql,MyResultSetHandler<T> handler,Object... args){
		T obj = null;
		
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		
		try {
			conn = JDBCUtils.getConnection();
			stmt = conn.prepareStatement(sql);
			
			//設置參數
			ParameterMetaData parameterMetaData = stmt.getParameterMetaData();
			int count = parameterMetaData.getParameterCount();
			for(int i = 1;i <= count;i++){
				stmt.setObject(i, args[i - 1]);
			}
			rs = stmt.executeQuery();
			obj = handler.handle(rs);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.release(rs, stmt, conn);
		}
		
		return obj;
	}

在AccountD類中修改查詢方法

public Account findById(int id){
		String sql = "select * from account where id = ?";
		return JDBCFramework.query(sql, new MyBeanHandler<Account>(Account.class),id);
}

重新運行測試代碼,查詢成功。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM