java代码重构
平时我们写的代码虽然满足了需求但往往不利于项目的开发与维护,以下面的JDBC代码为例
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
//增加学生信息
@Override
publicvoidsave(Studentstu){
Stringsql="INSERTINTOt_student(name,age)VALUES(?,?)";
Connectionconn=null;
Statementst=null;
try{
//1.加载注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接
conn=DriverManager.getConnection("jdbc:mysql:///jdbcdemo","root","root");
//3.创建语句对象
PreparedStatementps=conn.prepareStatement(sql);
ps.setObject(1,stu.getName());
ps.setObject(2,stu.getAge());
//4.执行SQL语句
ps.executeUpdate();
//5.释放资源
}catch(Exceptione){
e.printStackTrace();
}finally{
try{
if(st!=null)
st.close();
}catch(SQLExceptione){
e.printStackTrace();
}finally{
try{
if(conn!=null)
conn.close();
}catch(SQLExceptione){
e.printStackTrace();
}
}
}
}
//删除学生信息
@Override
publicvoiddelete(Longid){
Stringsql="DELETEFROMt_studentWHEREid=?";
Connectionconn=null;
Statementst=null;
try{
//1.加载注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接
conn=DriverManager.getConnection("jdbc:mysql:///jdbcdemo","root","root");
//3.创建语句对象
PreparedStatementps=conn.prepareStatement(sql);
ps.setObject(1,id);
//4.执行SQL语句
ps.executeUpdate();
//5.释放资源
}catch(Exceptione){
e.printStackTrace();
}finally{
try{
if(st!=null)
st.close();
}catch(SQLExceptione){
e.printStackTrace();
}finally{
try{
if(conn!=null)
conn.close();
}catch(SQLExceptione){
e.printStackTrace();
}
}
}
}
//修改学生信息
@Override
publicvoidupdate(Studentstu){
Stringsql="UPDATEt_studentSETname=?,age=?WHEREid=?";
Connectionconn=null;
Statementst=null;
try{
//1.加载注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接
conn=DriverManager.getConnection("jdbc:mysql:///jdbcdemo","root","root");
//3.创建语句对象
PreparedStatementps=conn.prepareStatement(sql);
ps.setObject(1,stu.getName());
ps.setObject(2,stu.getAge());
ps.setObject(3,stu.getId());
//4.执行SQL语句
ps.executeUpdate();
//5.释放资源
}catch(Exceptione){
e.printStackTrace();
}finally{
try{
if(st!=null)
st.close();
}catch(SQLExceptione){
e.printStackTrace();
}finally{
try{
if(conn!=null)
conn.close();
}catch(SQLExceptione){
e.printStackTrace();
}
}
}
上述代码中功能没问题,但是代码重复的太多,因此我们可以进行抽取,把重复的代码放到一个工具类JDBCUtil2里
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
//工具类
publicclassJDBCUtil2{
privateJDBCUtil2(){
}
static{
//1.加载注册驱动
try{
Class.forName("com.mysql.jdbc.Driver");
}catch(Exceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
publicstaticConnectiongetConnection(){
try{
//2.获取数据库连接
returnDriverManager.getConnection("jdbc:mysql:///jdbcdemo","root","root");
}catch(Exceptione){
e.printStackTrace();
}
returnnull;
}
//释放资源
publicstaticvoidclose(ResultSetrs,Statementst,Connectionconn){
try{
if(rs!=null)
rs.close();
}catch(SQLExceptione){
e.printStackTrace();
}finally{
try{
if(st!=null)
st.close();
}catch(SQLExceptione){
e.printStackTrace();
}finally{
try{
if(conn!=null)
conn.close();
}catch(SQLExceptione){
e.printStackTrace();
}
}
}
}
}
在实现类中直接调用工具类中的方法即可
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicclassStudentDAOImpl2implementsIstudentDAO{
//增加学生信息
@Override
publicvoidsave(Studentstu){
Stringsql="INSERTINTOt_student(name,age)VALUES(?,?)";
Connectionconn=null;
PreparedStatementps=null;
try{
conn=JDBCUtil2.getConnection();
//3.创建语句对象
ps=conn.prepareStatement(sql);
ps.setObject(1,stu.getName());
ps.setObject(2,stu.getAge());
//4.执行SQL语句
ps.executeUpdate();
//5.释放资源
}catch(Exceptione){
e.printStackTrace();
}finally{
JDBCUtil2.close(null,ps,conn);
}
}
//删除学生信息
@Override
publicvoiddelete(Longid){
Stringsql="DELETEFROMt_studentWHEREid=?";
Connectionconn=null;
PreparedStatementps=null;
try{
conn=JDBCUtil2.getConnection();
//3.创建语句对象
ps=conn.prepareStatement(sql);
ps.setObject(1,id);
//4.执行SQL语句
ps.executeUpdate();
//5.释放资源
}catch(Exceptione){
e.printStackTrace();
}finally{
JDBCUtil2.close(null,ps,conn);
}
}
//修改学生信息
@Override
publicvoidupdate(Studentstu){
Stringsql="UPDATEt_studentSETname=?,age=?WHEREid=?";
Connectionconn=null;
PreparedStatementps=null;
try{
conn=JDBCUtil2.getConnection();
//3.创建语句对象
ps=conn.prepareStatement(sql);
ps.setObject(1,stu.getName());
ps.setObject(2,stu.getAge());
ps.setObject(3,stu.getId());
//4.执行SQL语句
ps.executeUpdate();
//5.释放资源
}catch(Exceptione){
e.printStackTrace();
}finally{
JDBCUtil2.close(null,ps,conn);
}
}
@Override
publicStudentget(Longid){
Stringsql="SELECTFROMt_studentWHEREid=?";
Connectionconn=null;
Statementst=null;
ResultSetrs=null;
PreparedStatementps=null;
try{
conn=JDBCUtil2.getConnection();
//3.创建语句对象
ps=conn.prepareStatement(sql);
ps.setObject(1,id);
//4.执行SQL语句
rs=ps.executeQuery();
if(rs.next()){
Stringname=rs.getString("name");
intage=rs.getInt("age");
Studentstu=newStudent(id,name,age);
returnstu;
}
//5.释放资源
}catch(Exceptione){
e.printStackTrace();
}finally{
JDBCUtil2.close(rs,ps,conn);
}
returnnull;
}
@Override
publicListlist(){
Listlist=newArrayList<>();
Stringsql="SELECTFROMt_student";
Connectionconn=null;
Statementst=null;
ResultSetrs=null;
PreparedStatementps=null;
try{
conn=JDBCUtil2.getConnection();
//3.创建语句对象
ps=conn.prepareStatement(sql);
//4.执行SQL语句
rs=ps.executeQuery();
while(rs.next()){
longid=rs.getLong("id");
Stringname=rs.getString("name");
intage=rs.getInt("age");
Studentstu=newStudent(id,name,age);
list.add(stu);
}
//5.释放资源
}catch(Exceptione){
e.printStackTrace();
}finally{
JDBCUtil2.close(rs,ps,conn);
}
returnlist;
}
}
虽然完成了重复代码的抽取,但数据库中的账号密码等直接显示在代码中,不利于后期账户密码改动的维护,我们可以建立个db.propertise文件用来存储这些信息
[html]viewplaincopy在CODE上查看代码片派生到我的代码片
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///jdbcdemo
username=root
password=root
只需在工具类中获取里面的信息即可
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
privatestaticPropertiesp;
static{
//1.加载注册驱动
try{
ClassLoaderloader=Thread.currentThread().getContextClassLoader();
InputStreaminputStream=loader.getResourceAsStream("db.properties");
p=newProperties();
p.load(inputStwww.shanxiwang.netream);
Class.forName(p.getProperty("driverClassName"));
}catch(Exceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
publicstaticConnectiongetConnection(){
try{
//2.获取数据库连接
returnDriverManager.getConnection(p.getProperty("url"),p.getProperty("username"),
p.getProperty("password"));
}catch(Exceptione){
e.printStackTrace();
}
returnnull;
}
抽取到这里貌似已经完成,但在实现类中,依然存在部分重复代码,在DML操作中,除了SQL和设置值的不同,其他都相同,将相同的抽取出去,不同的部分通过参数传递进来,无法直接放在工具类中,这时我们可以创建一个模板类JDBCTemplate,创建一个DML和DQL的模板来进行对代码的重构。
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicclassJDBCTemplate{
privateJDBCTemplate(){};
//DML通用模板
publicstaticvoidupdate(Stringsql,Object...params){
Connectionconn=null;
PreparedStatementps=null;
try{
conn=JDBCUtil2.getConnection();
ps=conn.prepareStatement(sql);
//设置值
for(inti=0;i ps.setObject(i+1,params[i]);
}
ps.executeUpdate();
}catch(Exceptione){
e.printStackTrace();
}finally{
JDBCUtil.close(null,ps,conn);
}
}
//DQL同意模板
publicstaticListquery(Stringsql,Object...params){
Listlist=newArrayList<>();
Connectionconn=null;
PreparedStatementps=null;
ResultSetrs=null;
try{
conn=JDBCUtil.getConnection();
ps=conn.prepareStatement(sql);
//设置值
for(inti=0;i ps.setObject(i+1,params[i]);
}
rs=ps.executeQuery();
while(rs.next()){
longid=rs.getLong("id");
Stringname=rs.getString("name");
intage=rs.getInt("age");
Studentstu=newStudent(id,name,age);
list.add(stu);
}
//5.释放资源
}catch(Exceptione){
e.printStackTrace();
}finally{
JDBCUtil.close(rs,ps,conn);
}
returnlist;
}
}
实现类直接调用方法即可。
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
//增加学生信息
@Override
publicvoidsave(Studentstu){
Stringsql="INSERTINTOt_student(name,age)VALUES(?,?)";
Object[]params=newObject[]{stu.getName(),stu.getAge()};
JDBCTemplate.update(sql,params);
}
//删除学生信息
@Override
publicvoiddelete(Longid){
Stringsql="DELETEFROMt_studentWHEREid=?";
JDBCTemplate.update(sql,id);
}
//修改学生信息
@Override
publicvoidupdate(Studentstu){
Stringsql="UPDATEt_studentSETname=?,age=?WHEREid=?";
Object[]params=newObject[]{stu.getName(),stu.getAge(),stu.getId()};
JDBCTemplate.update(sql,params);
}
@Override
publicStudentget(Longid){
Stringsql="SELECTFROMt_studentWHEREid=?";
Listlist=JDBCTemplate.query(sql,id);
returnlist.size()>0?list.get(0):null;
}
@Override
publicListlist(){
Stringsql="SELECTFROMt_student";
returnJDBCTemplate.query(sql);
}
这样重复的代码基本就解决了,但又个很严重的问题就是这个程序DQL操作中只能处理Student类和t_student表的相关数据,无法处理其他类如:Teacher类和t_teacher表。不同表(不同的对象),不同的表就应该有不同列,不同列处理结果集的代码就应该不一样,处理结果集的操作只有DAO自己最清楚,也就是说,处理结果的方法压根就不应该放在模板方中,应该由每个DAO自己来处理。因此我们可以创建一个IResultSetHandle接口来处理结果集
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicinterfaceIResultSetHandle{
//处理结果集
Listhandle(ResultSetrs)throwsException;
}
DQL模板类中调用IResultSetHandle接口中的handle方法,提醒实现类去自己去实现handle方法
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
//DQL同意模板
publicstaticListquery(Stringsql,IResultSetHandlersh,Object...params){
Listlist=newArrayList<>();
Connectionconn=null;
PreparedStatementps=null;
ResultSetrs=null;
try{
conn=JDBCUtil.getConnection();
ps=conn.prepareStatement(sql);
//设置值
for(inti=0;i ps.setObject(i+1,params[i]);
}
rs=ps.executeQuery();
returnrsh.handle(rs);
//5.释放资源
}catch(Exceptione){
e.printStackTrace();
}finally{
JDBCUtil.close(rs,ps,conn);
}
returnlist;
}
实现类自己去实现IResultSetHandle接口的handle方法,想要处理什么类型数据在里面定义即可
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
@Override
publicStudentget(Longid){
Stringsql="SELECTFROMt_studentWHEREid=?";
Listlist=JDBCTemplate.query(sql,newStudentResultSetHandle(),id);
returnlist.size()>0?list.get(0):null;
}
@Override
publicListlist(){
Stringsql="SELECTFROMt_student";
returnJDBCTemplate.query(sql,newStudentResultSetHandle());
}
classStudentResultSetHandleimplementsIResultSetHandle{
@Override
publicListhandle(ResultSetrs)throwsException{
Listlist=newArrayList<>();
while(rs.next()){
longid=rs.getLong("id");
Stringname=rs.getString("name");
intage=rs.getInt("age");
Studentstu=newStudent(id,name,age);
list.add(stu);
}
returnlist;
}
}
好了,基本已经大功告成了,但是DQL查询不单单只有查询学生信息(List类型),还可以查询学生数量,这时就要通过泛型来完成
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
//声明泛型T作为返回类型,谁调用IResultSetHandle,谁就决定T类型
publicinterfaceIResultSetHandle{
//处理结果集
Thandle(ResultSetrs)throwsException;
}
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
//DQL同意模板
publicstaticTquery(Stringsql,IResultSetHandlersh,Object...params){
Connectionconn=null;
PreparedStatementps=null;
ResultSetrs=null;
try{
conn=JDBCUtil.getConnection();
ps=conn.prepareStatement(sql);
//设置值
for(inti=0;i ps.setObject(i+1,params[i]);
}
rs=ps.executeQuery();
returnrsh.handle(rs);
//5.释放资源
}catch(Exceptione){
e.printStackTrace();
}finally{
JDBCUtil.close(rs,ps,conn);
}
returnnull;
}
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
classStudentResultSetHandleimplementsIResultSetHandle>{
publicListhandle(ResultSetrs)throwsException{
Listlist=newArrayList<>();
while(rs.next()){
longid=rs.getLong("id");
Stringname=rs.getString("name");
intage=rs.getInt("age");
Studentstu=newStudent(id,name,age);
list.add(stu);
}
returnlist;
}
这样不仅可以查询List,还可以查询学生数量
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
//查询学生整数
@Test
publicvoidtestgetTotal()throwsException{
LongtotalCount=JDBCTemplate.query("SELECTCOUNT()tatalFROMt_student",newIResultSetHandle(){
@Override
publicLonghandle(ResultSetrs)throwsException{
LongtotalCount=null;
if(rs.next()){
totalCount=rs.getLong("tatal");
}
returntotalCount;
}
});
System.out.println(totalCount);
}
好了,重构设计已经完成,好的代码能让我们以后维护更方便,因此学会对代码的重构是很重要的
|
|