在 Java SE 中使用 DAO 有何改进?

What is the improvement of using DAO in Java SE?

我的项目经理要我使用 DAO/DTO 对象来访问和检索数据库中的数据。项目是用 Java SE 编写的,没有使用任何框架或 ORM。他的论点是使代码更易于测试并改进代码设计。有道理吗?

初始化DAO对象怎么样?有DAO字段的class实例创建时是否应该初始化:

private PersonDao personDao = new PersonDaoImpl();

或者更确切地说是在必要时初始化?

public class A {
  private PersonDao person;

  public List<Person> findAll() {
    person = new PersonDaoImpl();
    return person.getAll();
  }
}

它允许很容易地模拟这个接口,但是它是否符合 DAO 模式的使用约定?

您使用它的方式,它仍然与您的 class A 紧密耦合。

您应该使用构造函数或 setter 提供您的 DAO 作为依赖项。可能最可取的方法是使用某种控制反转(例如依赖注入框架)。

你的 class A 应该是这样的:

public class A {
  private PersonDao personDao;

  // possibly an @Inject annotation 
  public A(PersonDao personDao) {
     this.personDao = personDao;
  }

  public List<Person> findAll() {

    return personDao.getAll();
  }
}

实际上,我认为这是一种反模式。这取决于您将如何使用 class A。 如果它包含不同的业务逻辑 - 很好。 如果它只是发送对 DAO 的调用(我不喜欢这个名字,也许可以使用 Repository 来代替 ;))那么它只是不必要的抽象层。

另一件事 - 你提到了 DTO。那么在这种情况下,Person class 只是一个 DTO 吗?在这里我们可以有另一个反模式。例如,如果您需要将业务对象转换为屏幕上可见的内容,DTO 就很好。或者将持久性模型与业务模型分开。

我想说的是:不要让 Person class 只是一个数据结构。给它一些行为。

DAO 模式不是 "enterprise" 模式。它主要出现在 "enterprise" 应用程序中,但您绝对可以在仅用 SE 编写的应用程序中使用。

这并不是因为您正在编写不需要测试的 SE 应用程序,所以实际上,使用 DAO 模式和 IOC 而不是在您的代码中直接使用 JDBC,您的代码将更易于测试申请。

您使用 DAO 实现 class 的方式存在问题,因为您的 class 由于 class A 与 DAO 实现之间的紧密耦合而无法正确测试.你最好使用 IOC 模式以及像 Guice 或 Dagger 这样的框架(两者都是为 SE 设计的)。

有关代码示例,请查看 slnowak 的回答。

为了最大限度地发挥可测试性和关注点分离的优势,您应该引入控制反转 (IoC) 的概念。将 IoC 应用于对象生命周期的管理时,使用术语依赖注入。这意味着您的 class A 应该完全不知道在什么时候实例化了哪个实现。

为了实现这一点,您需要一个额外的组件来 bootstrap 您的应用程序,并使用正确的实现注入所有 classes。

你可以像这样设置你的依赖接收class(setter注入,你也可以使用构造函数)

public class PersonServiceImpl implements PersonService {

    private PersonDao personDao;

    public List<Person> findAll() {
        return personDao.getAll();
    }

    public setPersonDaoA(PersonDao personDao) {
        this.personDao = personDao;
    }
}

和一个组件来进行依赖注入:

public class ApplicationContext {

    private PersonService personService;
    private PersonDao personDao ;

    public PersonService getPersonService() {
        if (personService == null) {
            personService= new PersonServiceImpl();
            personService.setPersonDao(getPersonDao());
        }
        return personService;
    }

    public PersonDao getPersonDao() {
        if (personDao == null) {
            personDao = new PersonDaoIml();
        }
        return personDao ;
    }
}

那么应用程序启动会涉及到:

public class Main {
    public static void main(String[] args) {
        ApplicationContext ctx = new ApplicationContext();
        PersonService personService = ctx.getPersonService();
        personService.findAll();
    }
}

如您所见,ApplicationContext 封装了以下知识:

  • 要使用哪些实现
  • 设置依赖链的顺序
  • 哪些依赖已经实例化,哪些还没有实例化

PersonServiceImpl class 现在是完全可测试的,所有关于对象生命周期管理的问题都已从中提取出来。

在现实生活中,如果经常使用 Spring 或 CDI(最近越来越流行)这样的框架来完成此操作。但在你的情况下,从上面的方法开始可能是一个很好的第一步。它将获得您的项目经理提到的直接好处,而不会产生引入 Spring 的开销,也可能改变您的构建并且必须学习它是如何工作的(例如,在 XML 上下文中,源代码上下文 and/or注解)。

在稍后阶段引入 Spring 会很容易,因为所有 classes 都已经为依赖注入做好了准备。请记住,您的工厂(在我的示例中为 ApplicationContext)不应承担任何额外的责任,例如配置管理。

还要记住,上面的 ApplicationContext 示例不是单例。您自己应该确保在您的应用程序启动时只创建它的一个实例,并且所有注入都由它处理。创建重复的实例可能会导致令人困惑的错误。

数据访问对象基本上是一个对象或接口,提供对底层数据库或任何其他持久性存储的访问。

定义来自:http://en.wikipedia.org/wiki/Data_access_object

也许一个简单的例子可以帮助你理解这个概念:

假设我们有一个代表员工的实体:

public class Employee {
private int id;
private String name;
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;
}
}

员工实体将保存到数据库中相应的员工 table 中。一个简单的 DAO 接口来处理操作员工实体所需的数据库操作,如下所示:

interface EmployeeDAO {
List<Employee> findAll();
List<Employee> findById();
List<Employee> findByName();
boolean insertEmployee(Employee employee);
boolean updateEmployee(Employee employee);
boolean deleteEmployee(Employee employee);
}

接下来我们必须为该接口提供一个具体的实现来处理 SQL 服务器,另一个接口来处理平面文件等...

希望对您有所帮助