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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_42568655/article/details/102725876

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]

猜你喜欢

转载自blog.csdn.net/weixin_42568655/article/details/102725876
今日推荐