JDBC学习笔记(2):实现增删改查操作

423 阅读3分钟

在进行增删改操作时,不需要返回值或返回值仅仅作为操作成功与否的标志,三者可以一同实现;而在进行查操作时,需要返回查询表的内容,因此将其单独实现。

实现增删改操作

public class Function{
    public void update(String sql,Object...args){
        Connection connection = null;
        PreparedStatement ps = null;
        try{
            //利用先前封装好的JDBCUtils类建立连接
            connection = JDBCUtils.getConnection();
            //预编译sql语句
            ps = connection.preparedStatement(sql);
            //填充占位符
            for(int i = 0; i<args.length; i++){
                ps.setObject(i+1,args[i]);
            }
            //执行操作
            ps.execute();
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            JDBCUtils.close(connection, ps);//提前在JDBCUtils中封装好
        }
    }
}

总体来说增删改操作比较容易实现,需要注意的是之所以使用PreparedStatement而非Statement,因为Statement不能防止“注入”问题,不安全。

实现查操作

首先明确一点,对于查询到的表内容需要以什么样的形式保存起来,推荐使用创建相应类的对象来存储信息。例如对表customers进行查询操作,就根据表中列的名称和数据类型创建对应的JavaBean。

对特定表(customers)进行查操作

↓TABLE customers中包含的属性

↓相应的JavaBean类

注意一下因为sql和Java命名习惯不太一样,Java中的属性不再使用下划线,后面会针对这个改变进一步说明。

public List<Customer> select(String sql, Object...args) {
	
	Connection connection = null;
	PreparedStatement ps = null;
	ResultSet rs = null;
	//创建容器储存Customer对象
	List<Customer> customers = new ArrayList<>();
	
	try {
		//建立连接
		connection = JDBCUtils.getConnection();
		//预编译sql语句
		ps = connection.prepareStatement(sql);
		//填充占位符
		for(int i=0;i<args.length;i++) {
			ps.setObject(i+1, args[i]);
		}
		//执行操作并获得结果集
		rs = ps.executeQuery();
		//获得结果集元数据
		ResultSetMetaData rsmd = rs.getMetaData();
		//获得TABLE customers的列数
		int columnCount = rsmd.getColumnCount();
		while(rs.next()) {
			//创建对象,用于保存查询到的数据
			Customer cust = new Customer();
			for(int i=0;i<columnCount;i++) {
				//利用结果集元数据获取属性名
				String columnName = rsmd.getColumnLabel(i+1);
				//利用结果集获取属性值
				Object columnValue = rs.getObject(i+1);
				//利用反射将获取的属性值赋给对应的属性
				Field field = Customer.class.getDeclaredField(columnName);
				field.setAccessible(true);
				field.set(cust, columnValue);
			}
			//添加对象到容器中
			customers.add(cust);
		}
		return customers;
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		JDBCUtils.close(connection, ps, rs);
	}
	return null;
}

因为结果集中没有获取属性名和属性数的方法,必须使用结果集元数据才能获得。

针对之前提到的注意到sql和Java属性命名不一致的问题,在利用结果集元数据获取属性名时String columnName = rsmd.getColumnLabel(i+1);使用的是getColumnLabel()方法,这里意味着获取属性标签,即在sql语句中设置的标签名,标签名要与Java类中属性名一致。在sql语句中select cust_name custName, cust_address custAddress from customers通过标签把sql与Java属性名对应起来。如果这里使用getColumnName()方法,返回的就是数据库的属性名,就不能在Java类中找到对应的属性,出错。另外,如果没有设置标签名,那么getColumnLabel()默认与getColumnName()一致。

建议使用getColumnLabel()

对任意表进行查操作

操作前需要对不同的表创建对应的类,与特定表操作区别不大,在输入时需要增加操作表对应的类Class<T> clz,把对特定类Customer的操作用泛型T替代,即可。

public <T> List<T> select(Class<T> clz, String sql, Object...args) {
	
	Connection connection = JDBCUtils.getConnection();
	PreparedStatement ps = null;
	ResultSet rs = null;
	List<T> customers = new ArrayList<>();
	
	try {
		ps = connection.prepareStatement(sql);
		for(int i=0;i<args.length;i++) {
			ps.setObject(i+1, args[i]);
		}
		rs = ps.executeQuery();
		ResultSetMetaData rsmd = rs.getMetaData();
		int columnCount = rsmd.getColumnCount();
		while(rs.next()) {
			T t = clz.getConstructor().newInstance();
			for(int i=0;i<columnCount;i++) {
				String columnName = rsmd.getColumnLabel(i+1);
				Object columnValue = rs.getObject(i+1);
				Field field = clz.getDeclaredField(columnName);
				field.setAccessible(true);
				field.set(t, columnValue);
			}
			customers.add(t);
		}
		return customers;
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		JDBCUtils.close(connection, ps, rs);
	}
	return null;
}