可配置的DAO设计模式

来源:互联网 发布:国外注册域名 编辑:程序博客网 时间:2024/06/06 12:27


DAO(Data Access Object)模式实际上是两个模式的组合,即Data Accessor 模式和 Active Domain Object 模式。
其中 Data Accessor 模式实现了数据访问和业务逻辑的分离,而Active Domain Object 模式实现了业务数据的对象化封装,一般我们将这两个模式组合使用。

DAO模式是标准的J2EE设计模式之一。开发人员使用这个模式把底层的数据访问和上层的业务逻辑分开,并对数据进行了抽象。
一个典型的DAO实现有下列几个组件:
  1. 一个DAO工厂类
  2. 一个DAO接口
  3. 实现DAO接口的具体类(可能有几个,通过DAO工厂来创建选择的类实例)
  4. 数据传递对象(有时叫做值对象,value object也就是上面说的Active Domain Object)

例如实现向数据库增加,修改,读取,删除一个user类数据。可以有以下部分:
  1. 数据库连接管理
        提供静态方法获得数据库连接,和关闭数据库连接。简化代码,不需要重复书写。
        其中free部分需要特别小心,因为数据库连接是很宝贵的资源,在关闭过程中任何错误,都需要保证连接关闭。
        同时在加载数据库的Driver库时,使用了Class.forName,减少了对具体的Driver库的依赖。这样就算是换成其他非mysql数据库,也只需要传入字符串。
        这个属于数据层,所以会根据不同的部署环境来改变,有的可能是mysql数据库,有的可能是平面文件等。
package com.haoyifen.DB.util;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
 
public class DBConnectionManager {
private static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
private static final String DB_URL = "jdbc:mysql://localhost:3306/test";
 
private static final String USER = "root";
private static final String PASS = "*****";
private static Connection conn = null;
 
// load Driver
static {
try {
Class.forName(JDBC_DRIVER);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
 
// private constructor
private DBConnectionManager() {
 
}
 
//get database connection
public static Connection getConnection() throws SQLException {
 
conn = DriverManager.getConnection(DB_URL, USER, PASS);
return conn;
}
 
public static void free(ResultSet rs, Statement st, Connection conn) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (st != null) {
st.close();
}
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
}
}
}
    2.值对象
    这个值对象是在传输过程中对数据的封装,在数据库中也有其对应的表。
    同时提供了各个属性的get和set方法。
package com.haoyifen.DB.Domain;
 
import java.util.Date;
 
public class User {
private int id;
private String name;
private Date birthday;
private float 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 Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public float getMoney() {
return money;
}
public void setMoney(float money) {
this.money = money;
}
 
}

    3.DAO接口
    DAO接口定义对数据库的操作,比如增加用户,修改用户等等。通过接口使得业务逻辑与具体实现类解耦合。
这样,就可以将项目分为业务逻辑层和数据访问层,不同人可以实现不同部分。
package com.haoyifen.DB.dao;
 
import com.haoyifen.DB.Domain.User;
 
public interface UserDao {
public void addUser(User user);
public User getUser(int UserId);
public User findUser(String loginName,String password);
public void update(User user);
public void delete(User user);
}

4.DAO具体实现类
对于不同的数据库,或者磁盘文件,实现DAO接口的方式不同。那么就需要不同的类来实现DAO接口,或者根据部署的环境来选择相应的实现类。
比如这个例子中就是mysql数据库。
在这个过程中,异常处理很重要。
以其中的添加用户方法为例。
public void addUser(User user) {
// TODO Auto-generated method stub
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
 
conn = DBConnectionManager.getConnection();
String sql = "insert into user(name,birthday,money) values(?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, user.getName());
ps.setDate(2, new Date(user.getBirthday().getTime()));
ps.setFloat(3, user.getMoney());
ps.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
throw new DaoException(e.getMessage(), e);
} finally {
DBConnectionManager.free(rs, ps, conn);
}
}
这些异常是check异常,必须要被抛出或者catch。
如果我们不处理而直接抛出异常,那么就需要修改DAO接口以声明抛出异常,但是对于不同的数据层实现方式,抛出的异常是不同的,
数据库抛出的是SQL异常,磁盘文件抛出的IO异常,这样接口就不统一了。
但是如果将异常catch,直接打印出来,那么程序还会继续执行后面的操作,这样会导致程序的逻辑错误,而且根本找不到错误在哪里。
我们可以选择实现一个RuntimeException的子类,catch数据库的异常后,直接抛出这个子类的实例。
那么上层逻辑可以选择处理这个异常,或者在运行时直接终止程序。

5.DAO工厂
如果我们把实现DAO接口的具体类放到业务逻辑中,那么就会产生耦合。
所以我们通过一个单例的工厂来返回DAO具体类实例,工厂返回的类实例可以通过配置文件来选择。
在16-20行通过使用daoconfig.properties文件来配置具体实现的类,这样就可以实现动态加载。
在21行生成相应的实例。
文件中的内容为"userDaoClass=com.haoyifen.DB.dao.impl.UserDaoJdbcImpl"。
在加载文件时,使用的是ClassLoader来加载,这样只要在ClassPath目录中的文件都可以被加载。而使用File类来加载会出现在相对目录不同的情况。

package com.haoyifen.DB.dao;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
 
public class DaoFactory {
private static UserDao userDao = null;
private static DaoFactory daoFactory = new DaoFactory();
 
private DaoFactory() {
// TODO Auto-generated constructor stub
try {
Properties prop = new Properties();
InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("daoconfig.properties");
// "daoconfig.properties"
prop.load(in);
String userDaoclass=prop.getProperty("userDaoClass");
userDao=(UserDao)Class.forName(userDaoclass).newInstance();
} catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}
 
public static DaoFactory getInstacne() {
return daoFactory;
}
 
public UserDao getUserDao() {
return userDao;
}
}

0 0