JDBC框架
直接使用JDBC开发,非常麻烦,步骤繁琐,所以在企业开发Web程序时,连接数据库进行操作都是通过JDBC框架来完成的
知识点
1,JDBC元数据API(编写JDBC框架基础)
2,框架编写思想
3,Apache DBUtils(tomcat的JDBC框架)
数据库元数据(metadata)
数据库存储结构定义信息(库,表,记录,列的定义信息)
DataBaseMetaData数据库元数据
Connection.getMetaData();通过连接获得数据库元数据信息
getURL():返回一个String类对象,代表数据库的URL。
getUserName():返回连接当前数据库管理系统的用户名。
getDriverName():返回驱动驱动程序的名称。
getPrimaryKeys(String catalog, String schema, String table):返回指定表主键的结果集
参数元数据
ParameterMetaData
通过预编译对象PreparedStatement获得
String sql="select * from users where username=? and password=?";
PreparedStament preparedStatement=conn.preparedStatement(sql);
preparedStatement.getParameterMetaData();//通过参数元数据对象可以获得?参数的信息。
getParameterCount()
获得指定参数的个数
getParameterType(int param)
获得指定参数的sql类型
注意getParameterType方法并不是所有的数据库都支持mysql就不支持,回报异常,异常处理
Parameter metadata not available for the given statement
在数据库连接池配置文件中url后面拼接参数
?generateSimpleParameterMetadata=true,但是即使是这样该方法的返回值都是varchar
结果集元数据
ResultSetMetaData
ResultSet. getMetaData()
获得代表ResultSet对象元数据的ResultSetMetaData对象。
getColumnCount()
返回resultset对象的列数
getColumnName(int column)
获得指定列的名称
getColumnTypeName(int column)
获得指定列的类型
使用元数据简化JDBC代码
在实际业务背景下,系统中所有实体对象都涉及到基本的CRUD操作;所有的CUD操作代码基本相同,仅仅是发送给数据库的SQL语句不同而已,因此可以把操作的所有相同代码抽取到工具类的一个update方法中,并定义参数接收变化的SQL语句
当然实体的R操作除了SQL语句不同之外,根据操作的实体不同,对ResultSet的映射也各不相同,因此一个query方法,除了以参数形式接收变化的SQL语句之外,可以使用策略模式由qurey方法的调用者决定如何把ResultSet的数据映射到实体对象中。
DAO模式结合自定义JDBC框架framework
DAO模式编写JvaBean,Account类
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; } }
在未使用框架时编写AccountDAO
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); } }
通过不同对象DAO模式下的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 insertAccount(Account account) { String sql = "insert into account values(null,?,?)"; Object[] params={account.getNmae(),account.getMoney()} JDBCFramework.update(sql,params) }
通用框架的编写使用在后来可以不断重复使用,大大的简化了开发。但是上面的通用框架方法只适合CUD增删改,对于查询R来说是不能使用的,所以需要单独进行抽取编写框架方法
在未使用框架时
public Account findById(int id) { Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; Account account = null; try { conn = JDBCUtils.getConnection(); String sql = "select * from account where id = ?"; stmt = conn.prepareStatement(sql); //设置参数 stmt.setInt(1, id); rs = stmt.executeQuery(); if (rs.next()) { account = new Account(); account.setId(rs.getInt("id")); account.setName(rs.getString("name")); account.setMoney(rs.getDouble("money")); } } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.release(rs, stmt, conn); } return account; }
编写框架
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; }
要另外定义接口将rs结果集中的数据封装成一个对象
public interface MyResultSetHandler<T> { // 将rs中数据封装对象 public T handle(ResultSet rs); }
使用框架之后
public Account findById(int id) { // 使用自定义框架 String sql = "select * from account where id = ?"; MyResultSetHandler handler = new MyResultSetHandler() {//内部类实现框架的接口封装数据。但是每次都要实现 MyResultSetHandler接口,手动完成数据的封装的动作,所以此动作依然可以继续向上抽取。 @Override 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")); return account; } } catch (SQLException e) { e.printStackTrace(); } return null; } }; return (Account) JDBCFramework.query(sql, handler, id); }
内部类实现框架的接口封装数据。但是每次都要实现 MyResultSetHandler接口,手动完成数据的封装的动作,所以此动作依然可以继续向上抽取。
将封装结果集数据的动作进行向上提取,利用泛型和反射在框架中编写通用的Handler程序,☆难点。
public class MyBeanHandler<T> implements MyResultSetHandler<T> { private Class<T> domainClass; public MyBeanHandler(Class<T> domainClass) {//泛型是存在编译时期的,要创建T泛型的实例,需要将具体类的字节码文件对象传入利用反射技术去创建对象。 this.domainClass = domainClass; } @Override public T handle(ResultSet rs) {//用泛型去指代返回的数据类型。 try { ResultSetMetaData resultSetMetaData = rs.getMetaData();// 结果集元数据 int count = resultSetMetaData.getColumnCount(); BeanInfo beanInfo = Introspector.getBeanInfo(domainClass);//使用内省技术获得字节码文件对象的属性描述器。 PropertyDescriptor[] descriptors = beanInfo .getPropertyDescriptors(); if (rs.next()) { T t = domainClass.newInstance();//此处获得T的实例对象 for (int i = 1; i <= count; i++) { String columnName = resultSetMetaData.getColumnName(i); // 获得列名 --- 需要去查找匹配属性 for (PropertyDescriptor propertyDescriptor : descriptors) { if (columnName.equals(propertyDescriptor.getName())) { // 列名 存在 同名属性 ---- 列值 存到属性里 Method writeMethod = propertyDescriptor .getWriteMethod(); // setName setMoney writeMethod.invoke(t, rs.getObject(columnName)); } } } return t; } } catch (Exception e) { e.printStackTrace(); } return null; } }
最后简化出的DAO下的查询方法
public Account findById(int id) { String sql = "select * from account where id = ?"; return JDBCFramework.query(sql,new MyBeanHandler<Account>(Account.class),id) }
原文:http://www.cnblogs.com/ss561/p/4646426.html