JDBC的通用增刪改查操作(MySQL, 反射)

JDBC的通用增刪改查操作(MySQL, 反射)

獲取數據庫連接

  • 將jdbc先加入到項目工程的build path裏面

  • 書寫配置文件, 數據與代碼分離.

url=jdbc:mysql://localhost:3306/database
user=jdbctest
password=jdbctest123
className=com.mysql.cj.jdbc.Driver"
  • 讀取配置文件封裝獲取連接方法
  • 因爲每次都要用到獲取數據庫連接的這個操作, 我們將其封裝到一個類中作爲靜態方法出現.
public static Connection getConnection(){
	Connection conn = null;
	try {
        // 1. Load properties file 
		InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
		Properties props = new Properties();
		props.load(is);
        // 2. Get four elements
		String url = props.getProperty("url");
		String user = props.getProperty("user");
		String password = props.getProperty("password");
		String className = props.getProperty("className");
        // 3. Register Driver class
		Class.forName(className);
        // 4. Get connector
		conn = DriverManager.getConnection(url, user, password);
	} catch (Exception e) {
		e.printStackTrace();
	}		
	return conn;
}

數據庫的更新: 增刪改

  • 使用PreparedStatement進行數據庫的增刪改
  • 使用PreparedStatement預編譯SQL語句, 使得Statement的SQL注入問題修復
  • 對於增刪改來說, 在我們執行delete, insert, update 之後只有返回true/false, 所以我們可以將這三種寫在同一個方法中, 可以帶返回值, 也可以不帶返回值, 這裏我們不帶返回值
/**
 * Update: update, insert, delete
 * 
 * @param sql your SQL statement
 * @param objs parameters in SQL replace placeholder
 */
public void update(String sql, Object... objs) {
	Connection conn = null;
	PreparedStatement ps = null;
	try {
       	// Get connector
		conn = JDBCUtils.getConnection();
		// PreparedStatement for precompiling sql statement
        ps = conn.prepareStatement(sql);
        // Replace the placeholders
		for (int i = 0; i < objs.length; i++) {
			ps.setObject(i + 1, objs[i]);
		}
		// Execute sql statement
		ps.execute();
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
        // Close resources
		JDBCUtils.closeResources(conn, ps);
	}
}

數據庫查詢(反射)

  • 數據庫查詢操作不同於, 增刪改, 查詢操作會有一個返回結果的操作, 所以我們與增刪改分開來寫
  • 除了有返回結果之外, 對於不同的查詢, 我們返回的結果集的類型處理方式也不盡相同
  • 我們這裏使用反射, 來動態的處理我們返回的結果集, 具體操作見代碼.
/**
 * Using it for SQL query, and returns a list including objects
 * @param <T> Generics class
 * @param clazz pojo for your table
 * @param sql sql statement
 * @param objs parameter used by replacing placeholder
 * @return a list including all objects you get
 */
public <T> List<T> query(Class<T> clazz, String sql, Object... objs) {
	Connection conn = null;
	PreparedStatement ps = null;
	ResultSet rs = null;
	List<T> list = null;
	try {
        // 1. Get connector
		conn = JDBCUtils.getConnection();
        // 2. PreparedStatement to precompile SQL statement
		ps = conn.prepareStatement(sql);
        // 3. Repalce all placeholder
		for (int i = 0; i < objs.length; i++) {
			ps.setObject(i + 1, objs[i]);
		}
        // 4. Excute SQL statement
		rs = ps.executeQuery();
        // 5. Meta data is easier to process reslut set 
		ResultSetMetaData rsmd = rs.getMetaData();
		int ccount = rsmd.getColumnCount();
		list = new ArrayList<T>();
		while (rs.next()) {
			T t = clazz.newInstance();
			for (int i = 0; i < ccount; i++) {
				Object columnValue = rs.getObject(i + 1);
				String columnLabel = rsmd.getColumnLabel(i + 1);
				// Using reflect to set the value for every column
				Field field = clazz.getDeclaredField(columnLabel);
				field.setAccessible(true);
				field.set(t, columnValue);
			}
			list.add(t);
		}
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		JDBCUtils.closeResources(conn, ps, rs);
	}
	return list;
}
  • 對於上述方法的一些說明
    1. 在上述方法中我們使用了泛型方法, 主要是爲了處理對於不同的數據表的Java Bean, 產生的不同的結果集的情況. 注意: 不要忘了返回值中的通配符<T>哦
    2. 在處理結果集的時候我們使用了結果集的元數據, 更方便的處理數據, 也方便下邊的反射動態的設置值
    3. 在處理結果集中的數據的時候我們使用了反射動態設置新對象的各個值. 值得注意的是, 我們的SQL語句的佔位符和結果集中的各個數據的第一數據的下標均爲1, 所以,我們在進行循環的時候, 使用的index爲i + 1

總結

  1. 數據庫的增刪改查, 分爲兩大類, 一類是沒有產生結果集的增刪改, 另一類是有結果集的查詢操作
  2. 替換佔位符和處理查詢的結果集的時候, 下邊從1開始, 不同於以往的從0開始(同一個老外, 不同的下標)
  3. 在我們jdk1.8的新特性出現了try_with_resources的方法來替換了原來的try_catch_finally以達到自動關閉流的操作. 極大的方便了我們的使用. 但是我們的這個Connection 和 PreparedStatement以及ResultSet不可以再被分配/指定, 因此我們關閉的時候還是按照傳統的關閉流的方法.
  4. 另外, 使用try_with_resources自動關閉流的操作必須實現java.lang.AutoCloseable接口

完整代碼

JDBCUtils

  • JDBC工具類, 實現了獲取連接以及關閉流的操作

/**
 * 
 * @author Master_Joe
 *
 */
public class JDBCUtils {
	/**
	 * @return SQL Connectors
	 */
	public static Connection getConnection(){
		Connection conn = null;
		try {
			InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
			Properties props = new Properties();
			props.load(is);
			
			String url = props.getProperty("url");
			String user = props.getProperty("user");
			String password = props.getProperty("password");
			String className = props.getProperty("className");
			
			Class.forName(className);
			
			conn = DriverManager.getConnection(url, user, password);
		} catch (Exception e) {
			e.printStackTrace();
		}		
		return conn;
	}
	
	/**
	 * Close resources connector and statement
	 * @param conn SQL connector
	 * @param statement
	 */
	public static void closeResources(Connection conn, Statement statement) {
		try {
			if (conn != null) {
				conn.close();
			}
			if (statement != null) {
				statement.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Close resources including Connectors, Statement and ResultSet
	 * @param conn SQL connector 
	 * @param statement
	 * @param rs ResultSet 
	 */
	public static void closeResources(Connection conn, Statement statement, ResultSet rs)  {
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if (statement!=null) {
			try {
				statement.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if(rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
	}
}

Update & Query

實現了更新以及查詢操作

/**
 * Update & Query for SQL
 * 
 * @author Master_Joe
 *
 */
public class Operating {

	@Test
	public void testOperating() {
		String sql = null;
		Object[] objs = new Object[] {};

		update(sql, objs);
		List<Operating> list = query(Operating.class, sql, objs);

		list.forEach(System.out::println);
	}

	/**
	 * Update: update, insert, delete
	 * 
	 * @param sql
	 * @param objs
	 */
	public void update(String sql, Object... objs) {
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			conn = JDBCUtils.getConnection();
			ps = conn.prepareStatement(sql);
			for (int i = 0; i < objs.length; i++) {
				ps.setObject(i + 1, objs[i]);
			}

			ps.execute();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResources(conn, ps);
		}
	}

	/**
	 * Using it for SQL query, and returns a list including objects
	 * 
	 * @param <T>   Generics class
	 * @param clazz pojo for your table
	 * @param sql   sql statement
	 * @param objs  parameter used by replacing placeholder
	 * @return a list including all objects you get
	 */
	public <T> List<T> query(Class<T> clazz, String sql, Object... objs) {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		List<T> list = null;

		try {
			conn = JDBCUtils.getConnection();
			ps = conn.prepareStatement(sql);
			for (int i = 0; i < objs.length; i++) {
				ps.setObject(i + 1, objs[i]);
			}

			rs = ps.executeQuery();
			ResultSetMetaData rsmd = rs.getMetaData();
			int ccount = rsmd.getColumnCount();
			list = new ArrayList<T>();
			while (rs.next()) {
				T t = clazz.newInstance();
				for (int i = 0; i < ccount; i++) {
					Object columnValue = rs.getObject(i + 1);
					String columnLabel = rsmd.getColumnLabel(i + 1);
					// reflect to set the value for every column
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columnValue);
				}
				list.add(t);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return list;
	}
}
  • 說明:
    • 以上的測試方法中, 請自定義sql以及佔位符的替換

如有錯誤, 請聯繫我

作者郵箱: [email protected]

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