【问题标题】:How can I improve my DAO? - Java EE如何改进我的 DAO? -Java EE
【发布时间】:2011-06-13 14:26:36
【问题描述】:

我想访问我的申请人数据库,这就是我为其创建 DAO 类的原因。

我觉得我有很多代码味道,因为我不断重复一些代码。那么我可以做些什么来简化我的代码以减少代码异味呢?我违反了什么规则?我怎样才能改进我的代码?谢谢。

我的代码如下:

public class ApplicantDAO {

private static ApplicantDAO me = null;

private ApplicantDAO(){};

public static synchronized ApplicantDAO getInstance() {
    if(me == null) {
        me = new ApplicantDAO();
    }
    return me;
}

public Applicant getApplicant(int applicantNumber) throws SQLException {
    Applicant applicant = null;

    Connection conn = null; 
    Statement statement= null;
    String query = null;
    ResultSet rs = null;

    try {
        conn = ConnectionManager.getConnection();
        statement = conn.createStatement();
        query = "SELECT * FROM applicant WHERE applicant_no = '" + applicantNumber +"'";    //check applicant_number
        rs = statement.executeQuery(query);

        while(rs.next()){
            applicant = new Applicant();

            applicant.setApplicantNumber(rs.getInt("applicant_no"));
            applicant.setApplicationDate(rs.getString("applicant_date")); 
            applicant.setfName(rs.getString("first_name"));
            applicant.setlName(rs.getString("last_name"));
            applicant.setmName(rs.getString("middle_name"));
            applicant.setAge(rs.getInt("age"));
            applicant.setGender(rs.getString("gender"));
            applicant.setEmail(rs.getString("email_address"));
            applicant.setContactNumber(rs.getString("contact_no"));
            applicant.setCity(rs.getString("city"));
            applicant.setSchool(rs.getString("school"));
            applicant.setCourse(rs.getString("course"));
            applicant.setYearGraduated(rs.getInt("year_graduated"));
            applicant.setYearWorkExp(rs.getInt("year_work_exp"));
            applicant.setSourceChannel(rs.getString("source_channel"));
            applicant.setStatus_id(rs.getInt("status_id"));

        }

    } catch (SQLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }finally {
        if (rs != null) try { rs.close(); } catch (SQLException logOrIgnore) {}
        if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {}
        if (conn!= null) try { conn.close(); } catch (SQLException logOrIgnore) {}
    }


    return applicant;
}

public ArrayList<Applicant> getApplicants() throws SQLException{
    ArrayList<Applicant> applicantList = null;
    Applicant applicant = null;

    Connection conn = null; 
    Statement statement= null;
    String query = null;
    ResultSet rs = null;

    try {
        conn = ConnectionManager.getConnection();
        statement = conn.createStatement();
        query = "select * from applicant";
        rs = statement.executeQuery(query);

        while(rs.next()){
            if(applicantList == null){
                applicantList = new ArrayList<Applicant>();
            }
            applicant = new Applicant();

            applicant.setApplicantNumber(rs.getInt("applicant_no"));
            applicant.setApplicationDate(rs.getString("applicant_date")); 
            applicant.setfName(rs.getString("first_name"));
            applicant.setlName(rs.getString("last_name"));
            applicant.setmName(rs.getString("middle_name"));
            applicant.setAge(rs.getInt("age"));
            applicant.setGender(rs.getString("gender"));
            applicant.setEmail(rs.getString("email_address"));
            applicant.setContactNumber(rs.getString("contact_no"));
            applicant.setCity(rs.getString("city"));
            applicant.setSchool(rs.getString("school"));
            applicant.setCourse(rs.getString("course"));
            applicant.setYearGraduated(rs.getInt("year_graduated"));
            applicant.setYearWorkExp(rs.getInt("year_work_exp"));
            applicant.setSourceChannel(rs.getString("source_channel"));
            applicant.setStatus_id(rs.getInt("status_id"));

            applicantList.add(applicant);
        }

    } catch (SQLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }finally{
         if (rs != null) try { rs.close(); } catch (SQLException logOrIgnore) {}
         if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {}
         if (conn!= null) try { conn.close(); } catch (SQLException logOrIgnore) {}
    }
    return applicantList;
}

【问题讨论】:

    标签: jakarta-ee dao


    【解决方案1】:

    我看到的巨大而明显的问题:

    1. DAO 是一个单例。为什么?
    2. 您没有使用EntityManager。还有,为什么?

    如果您实际上使用的是 Java EE(问题已被标记)而不是 J2EE(这是一个围绕 Java 1.4 构建的可悲规范),那么 DAO 模式是完全没有必要的。 EntityManager 是新的 DAO。

    看看The Java EE 6 Tutorial - Persistence的前几节。


    我们需要使用 J2ee.. :(

    好的,所以您仍然需要修复单例实现。鉴于它不存储任何内部状态,根本没有理由只创建该对象的一个​​实例。有一个简单的解决方法:

    • 完全删除private static ApplicantDAO me = null;
    • getInstance() 实现更改为

      public static ApplicantDAO getInstance() {
          return new ApplicantDAO();
      }
      

    其他气味:

    • 您几乎总是希望声明 Lists 而不是 ArrayLists,因此请更改声明,例如

      public ArrayList<Applicant> getApplicants() throws SQLException
      // to
      public List<Applicant> getApplicants() throws SQLException
      
      // and
      
      ArrayList<Applicant> applicantList = null;
      // to
      List<Applicant> applicantList = null;
      

    【讨论】:

    • DAO 是单例的,所以我不需要每次调用它时都创建它的实例。对吗?
    • EntityManager 使用 @PersistenceContext 注入到 EJB 中。
    • 我不完全同意 EntityManager 是新的 DAO 的说法。它更像是一个帮助类来有效地实现 DAO。
    • @newb,也许(希望)您只是认为您正在使用 J2ee。您使用的服务器的名称和版本是什么?
    • 技术上 Tomcat 6 实现了 Java EE 5 的一部分 - Servlet 2.5 和 JSP 2.1。将至少 JPA 添加到 Tomcat(例如通过 Hibernate)是相当简单的。您还可以添加 EJB 或 Spring Core 和一些 JTA 事务管理器,这允许您使用更简单的编程风格。然而,在那个时候,最好使用一个轻量级的服务器,它已经拥有所有这些东西,比如 Resin 或 Glassfish。
    【解决方案2】:

    为了让您了解更现代的 DAO 会是什么样子:

    @Stateless
    public class JPAApplicantDAO implements ApplicantDAO {
    
        @PersistenceContext(unitName = "myPU")
        private EntityManager entityManager;
    
        @Override
        public Applicant getByID(Long applicantID) {
            return entityManager.find(Applicant.class, applicantID);
        }
    
        @Override
        public void update(Applicant applicant) {
            applicant.setLastModifiedDate(new Date());
            entityManager.merge(applicant);
        }
    
        @Override
        public void delete(Applicant applicant) {
            Applicant deletedApplicant = applicant;
            if (!entityManager.contains(applicant)) {
                deletedApplicant = entityManager.merge(applicant);
            }
    
            entityManager.remove(deletedApplicant);
        }
    
        @Override
        public List<Applicant> getBySomethingID(Long somethingID) {
            return entityManager.createNamedQuery("Applicant.getBySomethingID", Applicant.class)
                                .setParameter("somethingID", somethingID)
                                .getResultList();
        }
    }
    

    现在有人建议放弃整个 DAO 概念,直接在任何地方使用实体管理器。我不完全同意这一点。

    这个 DAO 示例展示了 4 种不同的方法。第一种方法是通过实体的主 ID 进行简单获取。正是这种方法让人怀疑是否还需要DAO抽象。但请继续阅读。

    第二种方法显示了一种更新方法。在这种情况下,应用程序可能想要对实体做一些额外的事情,例如设置最后修改日期。 DAO 是一个很自然的地方来做这件事。是的,它也可以在 DB 中完成,但是 DAO 仍然很方便,因为您可能必须回读实体才能了解设置的日期。

    第三种方法是删除方法。由于 JPA 规范中的一个特殊问题,您只能删除处于附加状态的实体。这意味着需要一些额外的逻辑来检查它是否已附加(包含在持久性上下文中),如果没有附加(合并它)。

    第四种方法显示通过 (JPQL) 查询进行数据检索。查询名称和参数名称都不是类型安全的。 DAO 方便地将其隐藏在类型安全的 Java 方法后面。是的,您可以将这些名称提取到一个常量中,但仍不会强制执行此特定参数与此特定查询之间的关联。

    一般来说,DAO 允许进行一定数量的重构。在某些时候,我可能想用条件查询替换 JPQL 查询。在所有呼叫站点更改此设置可能会有问题。然后是一个实体管理器作为一个通用的 DAO 简直是强大的。我不想将这些发送到所有客户端站点(如果是远程客户端,这甚至是不可能或非常糟糕的做法)。

    最后,使用客户端代码中的实体管理器本身不是事务性的,这意味着该客户端必须担心事务。这给代码增加了很多冗长。使用 DAO,客户端代码变得更加简单:

    @Named
    @RequestScoped
    public class SomeBean {
    
        @EJB
        private ApplicantDAO applicantDAO;
    
        public void someMethod() {
            applicantDAO.delete(applicant);
        }
    }
    

    【讨论】:

      【解决方案3】:

      在使用 Hibernate 将对象映射到关系数据库并使用 Spring 依赖注入功能支持连接和事务管理之后,您的示例看起来像是一张完美的“之前”图片,显示了一张漂亮的“之后”图片。

      【讨论】:

        【解决方案4】:

        你可以使用Java Persistence API,大部分无聊的代码都是不必要的。

        【讨论】:

          【解决方案5】:

          有史以来最糟糕的代码气味:你又在重新发明轮子。为什么不使用一些开放的库,例如 Spring JDBC ? (注意:你也可以阅读代码来学习!)

          如果您在这里练习,并且/或者不允许使用外部代码,那么这里有一些提示可以增强您的代码:

          • 所有与定义语句(及其参数)或处理结果集无关的代码都可以分解为可重用的方法,以减少代码重复。这可能会导致您使用回调样式的参数或可变参数来创建语句,并使用适配器样式的组件来重构申请人(尝试查看 Spring 的 JdbcTemplateRowMapper 类的代码)
          • 您的签名太精确了:当您可能返回一个 List 时,为什么还要返回一个 ArrayList?
          • 为什么你的 getInstance() 方法是同步的?您可以通过在类初始化时初始化 me 来避免这种情况:
            private static ApplicantDAO me = new ApplicantDAO();
            public static ApplicantDAO getInstance() { return me; }
            
          • 您的日志策略似乎没有定义:我认为这是故意的,但请记住,这在实际应用中非常重要。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-08-15
            • 2016-05-19
            • 2023-03-18
            • 1970-01-01
            • 1970-01-01
            • 2012-09-15
            • 2014-08-23
            相关资源
            最近更新 更多