抽取JDBCTemplate 为了解决DAO实现类中代码的重复问题,另外使得代码更通用,所以抽取一个公共的模板,使其更具有通用性。
DAO实现类的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 public class StudentDAOImpl implements IStudentDAO { public void save (Student stu) { String sql = "INSERT INTO s_student(name,age) VALUES(?,?)" ; Connection conn = null ; PreparedStatement ps = null ; try { conn = JdbcUtil.getConn(); ps = conn.prepareStatement(sql); ps.setString(1 , stu.getName()); ps.setInt(2 , stu.getAge()); ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtil.close(conn, ps, null ); } } public void delete (Long id) { String sql = "DELETE FROM s_student WHERE id = ?" ; Connection conn = null ; PreparedStatement ps = null ; try { conn = JdbcUtil.getConn(); ps = conn.prepareStatement(sql); ps.setLong(1 , id); ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtil.close(conn, ps, null ); } } public void update (Student newStu) { String sql = "UPDATE s_student SET name = ?,age = ? WHERE id = ?;" ; Connection conn = null ; PreparedStatement ps = null ; try { conn = JdbcUtil.getConn(); ps = conn.prepareStatement(sql); ps.setString(1 , newStu.getName()); ps.setInt(2 , newStu.getAge()); ps.setLong(3 , newStu.getId()); ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtil.close(conn, ps, null ); } } public Student get (Long id) { String sql = "SELECT * FROM s_student WHERE id = ? " ; Connection conn = null ; PreparedStatement ps = null ; ResultSet rs = null ; try { conn = JdbcUtil.getConn(); ps = conn.prepareStatement(sql); ps.setLong(1 , id); rs = ps.executeQuery(); if (rs.next()) { Student stu = new Student(); Long sid = rs.getLong("id" ); String name = rs.getString("name" ); Integer age = rs.getInt("age" ); stu.setId(sid); stu.setName(name); stu.setAge(age); return stu; } } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtil.close(conn, ps, rs); } return null ; } public List<Student> list () { List<Student> list = new ArrayList<>(); String sql = "SELECT * FROM s_student" ; Connection conn = null ; PreparedStatement ps = null ; ResultSet rs = null ; try { conn = JdbcUtil.getConn(); ps = conn.prepareStatement(sql); rs = ps.executeQuery(sql); while (rs.next()){ Student stu = new Student(); Long id = rs.getLong("id" ); String name = rs.getString("name" ); Integer age = rs.getInt("age" ); stu.setName(name); stu.setId(id); stu.setAge(age); list.add(stu); } } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtil.close(conn, ps, rs); } return list; } }
发现save,delete,update方法的代码只有sql不同,所以,把公共的代码提出来放在模板类中的update方法中,把sql作为参数传到方法中。因为存在sql语句中的占位符并且站位符的个数并不确定,可以采用可变参数进行传参,把需要给站位符设置的站放在一个Object数组中,作为参数传到update方法中,在update方法中又把数组中的值迭代出来赋给特定的sql语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void save (Student stu) { String sql = "INSERT INTO s_student(name,age) VALUES(?,?)" ; JdbcTemplate.update(sql,stu.getName(),stu.getAge()); } public void delete (Long id) { String sql = "DELETE FROM s_student WHERE id = ?" ; JdbcTemplate.update(sql, id); } public void update (Student newStu) { String sql = "UPDATE s_student SET name = ?,age = ? WHERE id = ?;" ; JdbcTemplate.update(sql, newStu.getName(),newStu.getAge(),newStu.getId()); }
模板中的update方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public static int update (String sql,Object...params) { Connection conn = null ; PreparedStatement ps = null ; try { conn = JdbcUtil.getConn(); ps = conn.prepareStatement(sql); for (int i = 0 ; i < params.length; i++){ ps.setObject(i + 1 , params[i]); } return ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtil.close(conn, ps, null ); } return 0 ; }
query的抽取有些麻烦,因为它会返回一个结果集,处理结果集的行为,不应该作为模板中的代码,而是应该交给给自的DAO来完成,因为给自的DAO才知道各自表的列有哪一些,作为模板肯定只有在模板中调用DAO中处理完成后的结果集,但是为了保证传入query方法的参数是一致的,肯定需要定义一个规范,在程序中也就是定义一个接口,把这个接口就叫做结果集处理器,每一个DAO的实现类中要有一个处理结果的内部类,这个内部类去实现结果处理器接口,返回一个结果集供模板调用. 也就是,模板中query方法表面调用接口中的方法,实际调用的是各个DAO实现类中的结果集.这就是多态思想
1 2 3 4 public interface IResultSetHandler <T > { T handle (ResultSet rs) throws Exception ; }
学生结果集处理器 实现了结果处理器接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class StudentHandler implements IResultSetHandler <List <Student >> { @Override public List<Student> handle (ResultSet rs) throws Exception { List<Student> list = new ArrayList<>(); while (rs.next()){ Student stu = new Student(); stu.setAge(rs.getInt("age" )); stu.setId(rs.getLong("id" )); stu.setName(rs.getString("name" )); list.add(stu); } return list; } }
DAO实现类中的get方法和list方法变成了:
1 2 3 4 5 6 7 8 9 10 public Student get (Long id) { String sql = "SELECT * FROM s_student WHERE id = ? " ; List<Student> list = JdbcTemplate.query(sql,new StudentHandler(), id); return list.size() == 1 ?list.get(0 ) : null ; } public List<Student> list () { String sql = "SELECT * FROM s_student" ; return JdbcTemplate.query(sql,new StudentHandler()); }
模板中的qurey方法 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public static <T> T query (String sql,IResultSetHandler<T> ih,Object...params) { Connection conn = null ; PreparedStatement ps = null ; ResultSet rs = null ; try { conn = JdbcUtil.getConn(); ps = conn.prepareStatement(sql); for (int i = 0 ; i < params.length; i++){ ps.setObject(i + 1 , params[i]); } rs = ps.executeQuery(); return ih.handle(rs); } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtil.close(conn, ps, rs); } return null ; }
上述qurey方法抽取还存在一个问题就是,每个DAO实现类都得实现接口编写各自的结果处理器,如果DAO比较多,这个也就很麻烦了,能不能抽取值抽取一个更加通用的呢,那就得满足一定的规范,比如可以通过内省机制可以完成,但是列名必须和JavaBean的属性名相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class BeanHandler <T > implements IResultSetHandler <T > { private Class<T> classType = null ; public BeanHandler (Class<T> classType) { this .classType = classType; } @Override public T handle (ResultSet rs) throws Exception { T obj = classType.newInstance(); if (rs.next()){ BeanInfo info = Introspector.getBeanInfo(classType,Object.class); PropertyDescriptor[] pds = info.getPropertyDescriptors(); for (PropertyDescriptor ps : pds) { String column = ps.getName(); Object val = rs.getObject(column); ps.getWriteMethod().invoke(obj,val); } } return obj; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class BeanListHandler <T > implements IResultSetHandler <List <T >> { private Class<T> classType = null ; public BeanListHandler (Class<T> classType) { this .classType = classType; } @Override public List<T> handle (ResultSet rs) throws Exception { List<T> list = new ArrayList<>(); while (rs.next()){ T obj = classType.newInstance(); BeanInfo info = Introspector.getBeanInfo(classType,Object.class); PropertyDescriptor[] pds = info.getPropertyDescriptors(); for (PropertyDescriptor ps : pds) { String columnName = ps.getName(); Object val = rs.getObject(columnName); ps.getWriteMethod().invoke(obj, val); } list.add(obj); } return list; } }
这时候的DAO实现类中方法可以这样来:
1 2 3 4 5 6 7 8 9 public Student get (Long id) { String sql = "SELECT * FROM s_student WHERE id = ? " ; return JdbcTemplate.query(sql,new BeanHandler<>(Student.class), id); } public List<Student> list () { String sql = "SELECT * FROM s_student" ; return JdbcTemplate.query(sql, new BeanListHandler<>(Student.class)); }
<
javaweb环境搭建
JDBC的操作步骤
>