JDBC的項目開發實例

    在學習JDBC的時候,項目開發的過程中,總會遇到對數據庫表數據增刪改查的操作,而很多基本的JDBC代碼都會出現大量的重複編寫,不僅對DAO的編寫造成了繁瑣,也對以後的維護增加了困難。當然,很多開源的框架可以解決這些問題,比如Mybatis,Hibernate等這些好的框架可以很好的實現相應的效果,但處在學習基礎的過程中,這些框架不建議使用,那麼最好的方法就是可以封裝原來重複的代碼,這樣的話,一個可以適用學習的過程,其次也可以提高對Java基礎的掌握程度,再而可以通過自己的封裝適用最基本的項目開發,而且很容易,很方便的進行自己的項目維護開發。

    注:本博文所使用的代碼可以通過以下鏈接進行獲取http://download.csdn.net/detail/songdeitao/6753631

    下面就來看看最基本的JDBC輕量型封裝項目的實例。

    首先來看一下最終的效果。

    比如僅僅對數據庫中數據增加的DAO操作,代碼需要這樣寫:

    UserDao中實現User數據的新增操作

public boolean doCreate(User user) {
		// 創建標誌位
		boolean flag = false;
		// 創建sql語句
		String sql = "insert into t_user values(?,?,?,?,?)";
		// 填充參數(主鍵不需要填寫,自增)
		Object[] parameters = new Object[] { null, user.getUserName(),
				user.getAge(), user.getBirthday(), user.getIsVip() };
		flag = DaoHandle.executeDML(sql, parameters) > 0 ? true : false;
		return flag;
	}
    首先創建sql語句,這裏的sql語句需要通過預處理的方式進行操作,通過佔位符填寫相應的參數,然後將需要的參數(和佔位符個數相同)創建成Object對象數組,數組中包含將要對佔位符操作的數據,而輕量型封裝的DaoHandle這個類,就主要負責了對數據庫數據增刪改查的操作。

    調用的測試代碼這樣編寫:

/**
	 * 測試增加一條記錄到數據庫中
	 */
	public static void addUser() {
		// 創建對象
		User user = new User("steven", 23, new Date(), true);
		// 創建一條記錄
		boolean flag = userDao.doCreate(user);
		// 是否成功創建記錄
		System.out.println(flag);
	}
    這個時候就可以在數據庫表中新增一條數據。


    下面就來進行主要代碼的實現過程編寫:

    本項目中主要採用MyEclipse8.5+Mysql5.0開發環境來實現開發。首先給出項目的結構圖,如圖1所示:


              圖1

    在這個圖中,可以看到,這是一個最基本的項目搭建結構,而在這個過程中本文主要對dao進行封裝,而所使用的輕量型封裝的代碼主要是DaoHandle這個類,所以以後的功能擴展,代碼可靠性的提高,主要就是對此類進行修改。這裏就按照項目搭建的過程一步步講解。

    首先創建數據庫,而用到的數據庫代碼都在mysql.sql文件,

mysql.sql

--創建數據庫steven
create database steven;
--應用數據庫
use steven;
--創建表
create table t_user(
	userId	    int(10)	 not null primary key auto_increment,
	userName    varchar(100) not null,
	age	        int(2),
	birthday    datetime,
	isVip       boolean
);
--顯示錶結構
desc t_user;
--顯示錶中數據
select * from t_user;
--提交
commit;
    這個時候可以將這些代碼如圖2所示進行執行:


                        圖2

這個時候數據庫已經創建成功。

    注:小技巧,在windows的命令提示符中如果想如上圖所示改變字體和背景的顏色,可以通過color f4這個命令來實現,不清楚的可以動手試試。後面的f4分別指代的是背景和字體的顏色。

    然後給出User實體的java代碼:

User.java

package com.steven.entity;

import java.util.Date;

/**
 * 實體類
 * 
 * @author Steven
 * 
 */
public class User {
	// 用戶Id
	private int userId;
	// 用戶名
	private String userName;
	// 用戶年齡
	private int age;
	// 用戶生日
	private Date birthday;
	// 用戶是否是會員 true:是 false:不是
	private boolean isVip;

	public User() {
	}

	// 有參構造
	public User(String userName, int age, Date birthday, boolean isVip) {
		this.userName = userName;
		this.age = age;
		this.birthday = birthday;
		this.isVip = isVip;
	}

	public int getUserId() {
		return userId;
	}

	public void setUserId(int userId) {
		this.userId = userId;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	public boolean getIsVip() {
		return isVip;
	}

	public void setIsVip(boolean isVip) {
		this.isVip = isVip;
	}

	@Override
	public String toString() {
		return "用戶信息: [年齡是" + this.getAge() + ", 生日是" + this.getBirthday()
				+ ", 用戶編號是" + this.getUserId() + ", 用戶名是" + userName
				+ isVip(this.getIsVip()) + "]";
	}

	public String isVip(boolean isVip) {
		return ", " + (isVip == true ? "是" : "不是") + "會員";
	}

}
      這個時候給出數據庫資源文件的代碼,這個資源文件的獲取有很多種方式,感興趣的可以參照(http://blog.csdn.net/songdeitao/article/details/17447627)這篇博文進行學習。

jdbc.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/steven
user=root
password=mysqladmin

        連接Mysql數據庫的代碼,這裏的連接驅動的jar包已經附屬到項目中,

DatabaseConnection.java

package com.steven.dbc;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * 進行數據庫連接的操作
 * 
 * @author Steven
 * 
 */
public class DatabaseConnection {
	public static String driver;// 驅動
	public static String url;// url
	public static String user;// 用戶名
	public static String password;// 密碼

	public DatabaseConnection() {

	}

	static {
		File file = new File("src/jdbc.properties");
		Properties pro = new Properties();
		try {
			FileInputStream in = new FileInputStream(file);
			pro.load(in);// 讀取屬性配置文件
			driver = pro.getProperty("driver");
			url = pro.getProperty("url");
			user = pro.getProperty("user");
			password = pro.getProperty("password");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 獲取數據庫連接
	 * 
	 * @return
	 */
	public static Connection getConnection() {
		Connection conn = null;
		try {
			Class.forName(driver);
			conn = DriverManager.getConnection(url, user, password);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return conn;
	}

	/**
	 * 關閉數據庫對象
	 * 
	 * @param con
	 * @param stmt
	 * @param rs
	 */
	public static void closeAll(Connection con, Statement stmt, ResultSet rs) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			rs = null;
		}
		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			stmt = null;
		}
		if (con != null) {
			try {
				con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			con = null;
		}
	}

	/**
	 * 測試數據庫連接
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		Connection conn = DatabaseConnection.getConnection();
		System.out.println(conn);
	}
}
    注:一般情況下如果寫完了連接數據庫的代碼,都要進行一次測試,如果成功可以進行接下來的編碼,如果失敗了,就要進行錯誤糾正,這樣可以保證以後的代碼編寫不會因爲之前的錯誤而影響到,也減少了更改bug的難度。這種遞進式的代碼開發,是常用的開發模式。尤其在大的項目中,一定要對自己的每一個模塊可以進行測試,確保項目的正確性。
    測試結果:

com.mysql.jdbc.JDBC4Connection@f11404
獲取了連接對象,是正確的。 
    下面將給出博文的核心,也是輕量型JDBC代碼的封裝的具體實現,DaoHandle類的具體實現如下所示:

DaoHandle.java

package com.steven.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.steven.dbc.DatabaseConnection;

/**
 * DAO數據操作的輔助類
 * 
 * @author Steven
 * 
 */
public class DaoHandle {
	private static Connection con;
	private static PreparedStatement pstmt;
	private static ResultSet rs;

	/**
	 * 執行所有的DML操作
	 * 
	 * @param sql
	 * @param parameters
	 * @return
	 */
	public static int executeDML(String sql, Object[] parameters) {
		int count = -1;
		// 獲取連接
		con = DatabaseConnection.getConnection();

		if (con != null) {

			try {
				// 獲取處理器對象
				pstmt = con.prepareStatement(sql);
				// 注入參數
				for (int i = 0; i < parameters.length; i++) {
					// 根據參數的類型判斷調用注入方法
					// 參數類型不安全
					pstmt.setObject(i + 1, parameters[i]);
				}
				// 執行SQL語句
				count = pstmt.executeUpdate();
			} catch (SQLException e) {
				e.printStackTrace();
			} finally {
				DatabaseConnection.closeAll(con, pstmt, null);
			}
		}
		return count;
	}

	/**
	 * 查詢獲取單個對象
	 * 
	 * @param <T>
	 * @param sql
	 * @param paramters
	 * @param objClass
	 * @return
	 */
	public static <T> T executeQueryForSingle(String sql, Object[] parameters,
			Class<T> objClass) {
		T t = null;
		// 獲取連接
		con = DatabaseConnection.getConnection();

		if (con != null) {
			try {
				// 獲取處理器
				pstmt = con.prepareStatement(sql);
				// 注入參數
				if (parameters != null) {
					for (int i = 0; i < parameters.length; i++) {
						pstmt.setObject(i + 1, parameters[i]);
					}
				}
				// 執行查詢
				rs = pstmt.executeQuery();
				// 獲取結果集的元數據
				ResultSetMetaData metaData = rs.getMetaData();
				// 獲取所有列名
				String[] colNames = getColNames(metaData);
				// 獲取所有列的數據類型
				// int[] colTypes = getColTypes(metaData);
				// 使用反射獲取當前類的方法
				Method[] methods = objClass.getDeclaredMethods();
				// 獲取結果集的數據
				while (rs.next()) {
					// 獲取類的實例
					t = objClass.newInstance();
					// 循環判斷每列的數據類型
					for (int i = 0; i < colNames.length; i++) {
						// 獲取每列的結果
						Object value = null;
						// 進行所有參數類型的賦值,不能保證類型安全
						value = rs.getObject(i + 1);
						// 遍歷每個方法
						for (Method m : methods) {
							if (value != null) {
								// 如果是和該列同名的set方法,則調用該方法
								if (m.getName().equalsIgnoreCase(
										"set" + colNames[i])) {
									// 進行對set方法的調用,向其中置值
									m.invoke(t, value);
								}
							}
						}

					}
				}

			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				DatabaseConnection.closeAll(con, pstmt, rs);
			}
		}
		// 返回對象
		return t;
	}

	/**
	 * 通過元數據獲取列名數組
	 * 
	 * @param metaData
	 * @return
	 */
	private static String[] getColNames(ResultSetMetaData metaData) {
		String[] colNames = null;

		try {
			// 獲取結果集的列數
			int colCount = metaData.getColumnCount();
			// 創建數組
			colNames = new String[metaData.getColumnCount()];
			// 遍歷每列
			for (int i = 1; i <= colCount; i++) {
				// 獲取列名
				colNames[i - 1] = metaData.getColumnLabel(i);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}

		return colNames;
	}

	/**
	 * 根據元數據獲取所有列的數據類型
	 * 
	 * @param metaData
	 * @return
	 */
	private static int[] getColTypes(ResultSetMetaData metaData) {
		int[] colNames = null;

		try {
			// 獲取列數
			int colCount = metaData.getColumnCount();
			// 創建數組
			colNames = new int[colCount];
			for (int i = 1; i <= colCount; i++) {
				// colNames[i-1] = metaData.getColumnTypeName(i);
				colNames[i - 1] = metaData.getColumnType(i);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}

		return colNames;
	}

	/**
	 * 查詢多行對象
	 * 
	 * @param <T>
	 * @param sql
	 * @param paramters
	 * @param objClass
	 * @return
	 */
	public static <T> List<T> executeQueryForMultiple(String sql,
			Object[] parameters, Class<T> objClass) {
		List<T> list = new ArrayList<T>();

		// 獲取連接
		con = DatabaseConnection.getConnection();

		if (con != null) {
			try {
				// 獲取處理器
				pstmt = con.prepareStatement(sql);
				if (parameters != null) {
					// 注入參數
					for (int i = 0; i < parameters.length; i++) {
						pstmt.setObject(i + 1, parameters[i]);
					}
				}
				// 執行查詢
				rs = pstmt.executeQuery();
				// 獲取結果集的元數據
				ResultSetMetaData metaData = rs.getMetaData();
				// 獲取所有列名
				String[] colNames = getColNames(metaData);
				// 獲取所有列的數據類型
				// int[] colTypes = getColTypes(metaData);
				// 使用反射獲取當前類的方法
				Method[] methods = objClass.getDeclaredMethods();
				// 獲取結果集的數據
				while (rs.next()) {
					T t = null;
					// 獲取類的實例
					t = objClass.newInstance();
					// 循環判斷每列的數據類型
					for (int i = 0; i < colNames.length; i++) {
						// 獲取每列的結果
						Object value = null;
						// 進行所有參數類型的賦值,不能保證類型安全
						value = rs.getObject(i + 1);
						// 遍歷每個方法
						for (Method m : methods) {
							if (value != null) {
								// 如果是和該列同名的set方法,則調用該方法
								if (m.getName().equalsIgnoreCase(
										"set" + colNames[i])) {
									// 進行對set方法的調用,向其中置值
									m.invoke(t, value);
								}
							}
						}

					}
					// 向集合中添加數據
					list.add(t);
				}

			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} finally {
				DatabaseConnection.closeAll(con, pstmt, rs);
			}
		}
		// 返回集合
		return list;
	}

	/**
	 * 返回數據表中數據的條數
	 * 
	 * @param sql
	 * @param paramters
	 * @return
	 */
	public static int executeQueryForCount(String sql, Object[] parameters) {
		int count = 0;
		// 獲取連接
		con = DatabaseConnection.getConnection();
		try {
			// 獲取處理器
			pstmt = con.prepareStatement(sql);
			if (parameters != null) {
				// 注入參數
				for (int i = 0; i < parameters.length; i++) {
					pstmt.setObject(i + 1, parameters[i]);
				}
			}
			// 執行查詢
			rs = pstmt.executeQuery();
			// 遍歷查找的結果集
			while (rs.next()) {
				count = rs.getInt(1);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			DatabaseConnection.closeAll(con, pstmt, rs);
		}
		return count;
	}
}

      注:這個過程中用到了反射機制,也是java底層代碼實現的最基本應用,功能很強大,通過相應的方法,可以進行動態的方法調用,實現數據庫表元素和對象之間的互相映射。其實在數據庫框架中,也大量使用這反射機制。

    然後給出Dao層中,iface和impl的實現代碼:

IBaseDao.java

package com.steven.dao.iface;

import java.util.List;

/**
 * 通過泛型機制爲所有的實現類提供一下方法
 * 
 * @author Steven
 * 
 */
public interface IBaseDao<T> {
	/**
	 * 新增一條數據
	 * 
	 * @param t
	 * @return
	 */
	public boolean doCreate(T t);

	/**
	 * 根據編號(主鍵)進行刪除一條數據
	 * 
	 * @param id
	 * @return
	 */
	public boolean delete(int id);

	/**
	 * 修改數據
	 * 
	 * @param t
	 * @return
	 */
	public boolean update(T t);

	/**
	 * 根據ID查找到該信息
	 * 
	 * @param id
	 * @return
	 */
	public T findById(int id);
	
	/**
	 * 返回所有的信息
	 * @return
	 */
	public List<T> findAll();
}
IUserDao.java

package com.steven.dao.iface;

import com.steven.entity.User;

public interface IUserDao extends IBaseDao<User> {

}
UserDao.java

package com.steven.dao.impl;

import java.util.List;

import com.steven.dao.iface.IUserDao;
import com.steven.entity.User;
import com.steven.util.DaoHandle;

public class UserDao implements IUserDao {

	@Override
	public boolean delete(int id) {
		// 創建標誌位
		boolean flag = false;
		// 創建sql語句
		String sql = "delete from t_user where userId=?";
		// 填充參數
		Object[] parameters = new Object[] { id };
		// 執行數據庫數據刪除操作
		flag = DaoHandle.executeDML(sql, parameters) > 0 ? true : false;
		return flag;
	}

	@Override
	public boolean doCreate(User user) {
		// 創建標誌位
		boolean flag = false;
		// 創建sql語句
		String sql = "insert into t_user values(?,?,?,?,?)";
		// 填充參數(主鍵不需要填寫,自增)
		Object[] parameters = new Object[] { null, user.getUserName(),
				user.getAge(), user.getBirthday(), user.getIsVip() };
		flag = DaoHandle.executeDML(sql, parameters) > 0 ? true : false;
		return flag;
	}

	@Override
	public List<User> findAll() {
		// 創建sql語句
		String sql = "select * from t_user";
		// 查詢所有的記錄
		List<User> list = (List<User>) DaoHandle.executeQueryForMultiple(sql,
				null, User.class);
		return list;
	}

	@Override
	public User findById(int id) {
		// 創建sql語句
		String sql = "select * from t_user where userId = ?";
		// 填充參數
		Object[] parameters = new Object[] { id };
		// 查找單個記錄
		User user = DaoHandle
				.executeQueryForSingle(sql, parameters, User.class);
		return user;
	}

	@Override
	public boolean update(User user) {
		// 創建標誌位
		boolean flag = false;
		// 創建sql語句
		String sql = "update t_user set userName=?,age=?,birthday=?,isVip=? where userId=?";
		// 注入參數
		Object[] parameters = new Object[] { user.getUserName(), user.getAge(),
				user.getBirthday(), user.getIsVip(), user.getUserId() };
		flag = DaoHandle.executeDML(sql, parameters) > 0 ? true : false;
		return flag;
	}
}
接下來將給出UserDao的測試代碼,也是對輕量型封裝的DaoHandle的檢驗,

CRUDTest.java

package com.steven.test;

import java.util.Date;
import java.util.List;

import com.steven.dao.impl.UserDao;
import com.steven.entity.User;

public class CRUDTest {
	private static UserDao userDao = new UserDao();

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// addUser();
		// retrieveUser();
		// deleteUser();
		// updateUser();
		// retrieveAll();
	}

	/**
	 * 測試增加一條記錄到數據庫中
	 */
	public static void addUser() {
		// 創建對象
		User user = new User("steven", 23, new Date(), true);
		// 創建一條記錄
		boolean flag = userDao.doCreate(user);
		// 是否成功創建記錄
		System.out.println(flag);
	}

	/**
	 * 測試從數據庫中讀取一條記錄
	 */
	public static void retrieveUser() {
		// 從數據庫中讀取一條記錄
		User user = userDao.findById(1);
		// 將此記錄的相應對象輸出
		System.out.println(user);
	}

	/**
	 * 從數據庫表中刪除一條數據
	 */
	public static void deleteUser() {
		// 首先從數據庫中查找出一條記錄
		User user = userDao.findById(1);
		// 然後根據查找後的對象id從數據庫中刪除此記錄
		boolean flag = false;
		if (user != null) {
			flag = userDao.delete(user.getUserId());
		}
		// 輸出刪除的成功與否
		System.out.println(flag);
	}

	/**
	 * 更新數據操作
	 */
	public static void updateUser() {
		// 從數據表中讀取一條數據
		User user = userDao.findById(2);
		// 更改數據
		user.setIsVip(false);
		// 執行更新操作
		boolean flag = userDao.update(user);
		// 成功與否
		System.out.println(flag);
	}

	public static void retrieveAll() {
		// 獲取所有記錄的集合
		List<User> userList = userDao.findAll();
		// 輸出所有對象信息
		for (User user : userList) {
			System.out.println(user);
		}
	}
}

然後給出測試的結果,這裏僅僅給出前兩個測試效果,所有的測試都是正確的,可以自己動手試試。

addUser()

控制檯輸出

true
數據庫中的結果如圖3所示:


                       圖3

retrieveUser();

控制檯輸出:

用戶信息: [年齡是23, 生日是2013-12-22 16:10:25.0, 用戶編號是1, 用戶名是steven, 是會員]

    到這裏,輕量型的JDBC封裝的實現和測試已經全部給出,下面就開始編寫JavaSE的具體項目邏輯了,這時候可以在建一個包com.steven.business層(可以爲其他的層次)進行業務邏輯的操作,這樣一個簡答的使用封裝後的JDBC項目就可以實現了,當然這也可以用到web項目中,具體的項目編寫,還是大家實現吧,這個封裝僅僅是簡單的實現,如果有需要擴展的,比如分頁查找,搜索字段,都是可以實現的。

    在此恭祝大家學習愉快!


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