Java通过代理类实现数据库DAO操作
下面的所有代码示例都取自李兴华的《Java Web开发实战经典》的随书源码,因为觉得设计得很好,所以将代码摘录下来作成笔记。
首先,我们在一个java文件中定义要存储的结构类型:
import java.util.Date ; /** * * @author Nero */ public class Emp { private int empno ; private String ename ; private String job ; private Date hiredate ; private float sal ; public void setEmpno(int empno){ this.empno = empno ; } public void setEname(String ename){ this.ename = ename ; } public void setJob(String job){ this.job = job ; } public void setHiredate(Date hiredate){ this.hiredate = hiredate ; } public void setSal(float sal){ this.sal = sal ; } public int getEmpno(){ return this.empno ; } public String getEname(){ return this.ename ; } public String getJob(){ return this.job ; } public Date getHiredate(){ return this.hiredate ; } public float getSal(){ return this.sal ; } }
下面我们定义一个数据库连接类,负责向数据库发起连接。java连接数据库需要驱动包,我们可以自行下载,测试的时候我使用的是mysql-connector-java-5.0.5-bin.jar。在运行程序的时候Eclipse会提示需要加载的数据库驱动包,一些是类似于"org.gjt.mm.mysql.Driver" 之类的标准包,一般来说我们选择工作目录里的驱动包就可以了。
import java.sql.Connection ; import java.sql.DriverManager ; /** * * @author Nero */ public class DatabaseConnection { private static final String DBDRIVER = "org.gjt.mm.mysql.Driver" ; private static final String DBURL = "jdbc:mysql://localhost:3306/mldn" ; private static final String DBUSER = "root" ; private static final String DBPASSWORD = "root" ; private Connection conn ; public DatabaseConnection() throws Exception { Class.forName(DBDRIVER) ; this.conn = DriverManager.getConnection(DBURL,DBUSER,DBPASSWORD) ; } public Connection getConnection(){ return this.conn ; } public void close() throws Exception { if(this.conn != null){ try{ this.conn.close() ; }catch(Exception e){ throw e ; } } } }
接下来我们定义一个接口,这个接口能够帮助我们轻松地实现代理方法。接口内的方法只有三个:插入、查找全部和通过ID查找。
import java.util.* ; /** * * @author Nero */ public interface IEmpDAO { public boolean doCreate(Emp emp) throws Exception ; public List<Emp> findAll(String keyWord) throws Exception ; public Emp findById(int empno) throws Exception ; }
然后呢,我们继承这个接口,实现具体数据库的操作类,都是一些很基本的数据库操作,没啥好说的。主要要注意的是构造函数那里,参数使用Connection对象,后面使用这个类的时候要传入前面定义的数据库连接类DatabaseConnection中的函数getConnection()返回的Connection对象。
import java.sql.* ; /** * * @author Nero */ public class EmpDAOImpl implements IEmpDAO{ private Connection conn = null ; private PreparedStatement pstmt = null ; public EmpDAOImpl(Connection conn) { this.conn = conn; } public boolean doCreate(Emp emp) throws Exception{ boolean flag = false; String sql = "INSERT INTO emp(empno,ename,job,hiredate,sal) VALUES(?,?,?,?,?)"; this.pstmt = this.conn.prepareStatement(sql); this.pstmt.setInt(1, emp.getEmpno()); this.pstmt.setString(2,emp.getEname()) ; this.pstmt.setString(3,emp.getJob()) ; this.pstmt.setDate(4,new java.sql.Date(emp.getHiredate().getTime())) ; this.pstmt.setFloat(5,emp.getSal()) ; if(this.pstmt.executeUpdate() > 0) { flag = true; } this.pstmt.close(); return flag; } public List<Emp> findAll(String keyWord) throws Exception{ List<Emp> all = new ArrayList<Emp>(); String sql = "SELECT empno,ename,job,hiredate,sal FROM emp WHERE ename LIKE ? OR job LIKE ?"; this.pstmt = this.conn.prepareStatement(sql); this.pstmt.setString(1,"%"+keyWord+"%"); //转义字符 this.pstmt.setString(2,"%"+keyWord+"%"); ResultSet rs = this.pstmt.executeQuery(sql); Emp emp = null; while(rs.next()) { emp = new Emp(); emp.setEmpno(rs.getInt(1)); emp.setEname(rs.getString(2)) ; emp.setJob(rs.getString(3)) ; emp.setHiredate(rs.getDate(4)) ; emp.setSal(rs.getFloat(5)) ; all.add(emp); } this.pstmt.close(); return all; } public Emp findById(int empno) throws Exception{ Emp emp = null ; String sql = "SELECT empno,ename,job,hiredate,sal FROM emp WHERE empno=?" ; this.pstmt = this.conn.prepareStatement(sql) ; this.pstmt.setInt(1,empno) ; ResultSet rs = this.pstmt.executeQuery() ; if(rs.next()){ emp = new Emp() ; emp.setEmpno(rs.getInt(1)) ; emp.setEname(rs.getString(2)) ; emp.setJob(rs.getString(3)) ; emp.setHiredate(rs.getDate(4)) ; emp.setSal(rs.getFloat(5)) ; } this.pstmt.close() ; return emp ; } }
下面我们看看代理类的实现,个人觉得到这里就比较有意思了。在这个类里面,声明了一个数据库连接类DatabaseConnection的对象,一个数据库应用类EmpDAOImpl对象,用DatabaseConnection对象初始化EmpDAOImpl对象,然后在代理类的每个函数中都使用EmpDAOImpl对象去调用从同一接口继承而来的方法,这样即对具体实现方法进行了一定程度的隐藏。
import java.sql.* ; /** * * @author Nero */ public class EmpDAOProxy implements IEmpDAO{ private DatabaseConnection dbc = null ; private EmpDAOImpl dao = null ; public EmpDAOProxy() throws Exception{ this.dbc = new DatabaseConnection(); this.dao = new EmpDAOImpl(this.dbc.getConnection()); } public boolean doCreate(Emp emp) throws Exception{ boolean flag = false; try{ if(this.dao.findById(emp.getEmpno()) == null) { flag = this.dao.doCreate(emp); } }catch(Exception e) { throw e; }finally{ this.dbc.close(); } return flag; } public List<Emp> findAll(String keyWord) throws Exception{ List<Emp> all = null ; try{ all = this.dao.findAll(keyWord) ; }catch(Exception e){ throw e ; }finally{ this.dbc.close() ; } return all ; } public Emp findById(int empno) throws Exception{ Emp emp = null ; try{ emp = this.dao.findById(empno) ; }catch(Exception e){ throw e ; }finally{ this.dbc.close() ; } return emp ; } }
这还不是全部,我们可以再加一个工厂类来实现工厂模式:
/** * * @author Nero */ public class DAOFactory { public static IEmpDAO getIEmpDAOInstance() throws Exception{ return new EmpDAOProxy() ; } }
这个工厂类有什么用呢?最后我们在主类文件中进行调用,可以看看工厂类有什么作用:
/** * * @author Nero */ public class TestDAOInsert { public static void main(String args[]) throws Exception{ Emp emp = null ; for(int x=0;x<5;x++){ emp = new Emp() ; emp.setEmpno(1000 + x) ; emp.setEname("中文显示测试 - " + x) ; emp.setJob("程序员 - " + x) ; emp.setHiredate(new java.util.Date()) ; emp.setSal(500 * x) ; DAOFactory.getIEmpDAOInstance().doCreate(emp) ; } } }
可见具体的实现方法隐藏得比较好,通过工厂类调用get方法获取代理类,代理类调用特定方法,然后由代理类内的具体操作对象去调用具体的操作方法。
其实这些东西看起来会觉得很简单,但是自己设计的时候往往会忘记。主要是因为用得不多吧,一开始总是要强迫自己想起来去做,然后慢慢地才能变成一种习惯。