怎么做重构:
思想:大家来找茬.
两段功能相同的代码,发现他们的共同点,提取出去(把操作模板提出取出).
把共同的代码提取到:
1:可以提取到父类中--->不推荐,容易导致子类爆炸.
2:使用组合方式,推荐.
抽取JdbcTemplate的重要性:
1):凡是搞过JDBC,2~3年的程序员,都可以轻松的写出JdbcTemplate.
2):学习,如何做重构.
3):类似于dbutils组件/Spring JdbcTemplate.
4):之后做的高级查询+分页+深入重构设计,都会基于JdbcTemplate来.
5):模拟Hibernate.
没有操作模板的写法
DML操作模板
JdbcTemplate
public class JdbcTemplate {
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;
}
}
test
public class JdbcTemplateTest {
public void save(Student stu){
String sql = "INSERT INTO t_student (NAME, age) VALUES (?,?);";
Object[] params={stu.getName(),stu.getAge()};
JdbcTemplate.update(sql, params);
}
public void delete(Long id){
String sql = "DELETE FROM t_student WHERE id = ?";
JdbcTemplate.update(sql, id);
}
public void update(Student stu){
String sql = "UPDATE t_student SET name = ?,age= ? WHERE id =?";
Object[] params={stu.getName(),stu.getAge(),stu.getId()};
JdbcTemplate.update(sql, params);
}
}
DQL操作模板
第一个版本
public static List<Student> query(String sql,Object... params){
List<Student> list = new ArrayList<Student>();
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();
while(rs.next()){
Student stu = new Student();
stu.setName(rs.getString("name"));
stu.setAge(rs.getInt("age"));
stu.setId(rs.getLong("id"));
list.add(stu);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
JdbcUtil.close(conn, ps, null);
}
return list;
}
public Student getSingle(Long id){
String sql = "SELECT * FROM t_student WHERE id = ?";
List<Student> list=JdbcTemplate.query(sql, id);
return list.size()==1?list.get(0):null;
}
public List<Student> list(Long id){
String sql = "SELECT * FROM t_student WHERE";
return JdbcTemplate.query(sql);
}
上面的代码:从查询学生对象上来说,没有一点问题.
但是:因为在JdbcTemplate的query方法中,写死了Student和t_student表的三个列---->该query方法只能操作当前的Student对象和t_student表.是不能操作Teacher对象/t_teacher表的.
第二个版本
不同的对象(不同的表),不同的表有不同的列,列都不同了,处理结果集的代码就不相同.
只能说:每一个DAO的查询方法都应该要处理结果集,但是怎么处理,只有各自的DAO才知道.
也就是说:处理结果集的行为,不应该作为模板中的代码,而是应该交给给自的DAO来完成,因为给自的DAO才知道各自表的列有哪一些.
规定:所有的处理结果集的方法都叫做handle(在Java中做规范就是接口).
IResultSetHandler
public interface IResultSetHandler {
List handle(ResultSet rs) throws SQLException;
}
StudentResultSetHandler
public class StudentResultSetHandler implements IResultSetHandler {
public List handle(ResultSet rs) throws SQLException {
List<Student> list=new ArrayList<Student>();
while(rs.next()){
Student stu = new Student();
stu.setName(rs.getString("name"));
stu.setAge(rs.getInt("age"));
stu.setId(rs.getLong("id"));
list.add(stu);
}
return list;
}
}
查询模板
public static List query(String sql,IResultSetHandler handler,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 handler.handle(rs);
} catch (Exception e) {
e.printStackTrace();
}finally {
JdbcUtil.close(conn, ps, null);
}
return new ArrayList();
}
具体查询调用
public List<Student> list(Long id){
String sql = "SELECT * FROM t_student";
List<Student> list=JdbcTemplate.query(sql,new StudentResultSetHandler());
return list;
}
第三个版本
第二个操作模板:query方法,还不太高级.如果要查询结果总数:
SELECT COUNT(id) FROM t_student;
应该返回一个整数,而不是集合。也就说:处理结果只会,到底应该返回什么类型,其实是不确定的,不同调用者的返回类型是不一样的.不能使用List就以偏概全.
解决方案:使用泛型.
public interface IResultSetHandler2<T> {
T handle(ResultSet rs) throws SQLException;
}
public class StudentResultSetHandler2 implements IResultSetHandler2<List<Student>>{
public List<Student> handle(ResultSet rs) throws SQLException {
List<Student> list=new ArrayList<Student>();
while(rs.next()){
Student stu = new Student();
stu.setName(rs.getString("name"));
stu.setAge(rs.getInt("age"));
stu.setId(rs.getLong("id"));
list.add(stu);
}
return list;
}
}
//查询全部对象
public List<Student> list2(){
String sql = "SELECT * FROM t_student";
List<Student> list=JdbcTemplate.query2(sql,new StudentResultSetHandler2());
System.out.println("count:"+list.size());
return list;
}
//查询数量
public void list3(){
String sql = "SELECT COUNT(id) FROM t_student";
Long count=JdbcTemplate.query2(sql,new IResultSetHandler2<Long>(){
public Long handle(ResultSet rs) throws SQLException {
if(rs.next()){
return rs.getLong(1);
}
return 0L;
}
});
System.out.println("count:"+count);
}
第四个版本
在第三个版本的基础上,但是冗余了具体的databean,可以再抽象:
模拟Hibernate
public class Hibernate {
public static void save(Object obj){
try {
//获取对象对应的表名
String tableName=obj.getClass().getSimpleName();
Table table=obj.getClass().getAnnotation(Table.class);
if(table!=null){
tableName=table.getValue();
}
StringBuilder sql=new StringBuilder();
sql.append("INSERT INTO").append(tableName).append("(");
StringBuilder columnSql=new StringBuilder(); //拼接需要插入哪些列表的sql:
StringBuilder placeHolderSql=new StringBuilder();//拼接占位符的SQL:??
List<Object> params=new ArrayList<Object>(); //参数
BeanInfo beanInfo=Introspector.getBeanInfo(obj.getClass(),Object.class);
PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
//对象中的属性名
String propertyName=pd.getName();
if("id".equals(propertyName)){
columnSql.append(propertyName).append(",");
placeHolderSql.append("?").append(",");
//获取属性的值,调用属性的getter方法
Object val=pd.getReadMethod().invoke(obj);
params.add(val);
}
}
//删除最后一个
columnSql.deleteCharAt(columnSql.length()-1);
placeHolderSql.deleteCharAt(placeHolderSql.length()-1);
sql.append(columnSql);
sql.append(") VALUES (");
sql.append(placeHolderSql);
sql.append(")");
System.out.println("SQL= "+sql);
System.out.println("params= "+params);
JdbcTemplate.update(sql.toString(), params.toArray());
} catch (Exception e) {
// TODO: handle exception
}
}
}