Как я могу улучшить свой DAO? - Java EE - PullRequest
2 голосов
/ 13 июня 2011

Я хочу получить доступ к своей базе данных кандидатов, поэтому я создал для нее класс 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;
}

Ответы [ 5 ]

5 голосов
/ 13 июня 2011

Чтобы дать вам представление о том, как будет выглядеть более современный 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 различных метода. Первый метод - это простое получение по основному идентификатору объекта. Именно такой метод заставляет людей задуматься, нужна ли абстракция DAO. Но читайте дальше.

Второй метод показывает метод обновления. В этом случае приложение может захотеть сделать что-то дополнительное для объекта, например, установить дату последнего изменения. DAO - вполне естественное место для этого. Да, это может быть сделано и в БД, но тогда DAO все равно будет полезен, поскольку вам, возможно, придется прочитать объект обратно, чтобы узнать об установленной дате.

Третий метод - метод удаления. Из-за специфической проблемы в спецификации JPA вы можете удалить только объект, который находится в присоединенном состоянии. Это означает некоторую дополнительную логику, чтобы проверить, присоединен ли он (содержится в контексте постоянства), а если нет, присоединить (объединить).

Четвертый метод показывает получение данных с помощью (JPQL) запроса. И имя запроса, и имя параметра не являются типобезопасными. DAO удобно скрывает это за типобезопасным Java-методом. Да, вы можете извлечь эти имена в константу, но связь между этим конкретным параметром и этим конкретным запросом все равно не будет применена.

Как правило, DAO допускает определенный объем рефакторинга. В какой-то момент я могу захотеть заменить запрос JPQL критерием запроса. Изменение этого на всех колл-сайтах может быть проблематичным. Тогда есть случай, когда менеджер сущностей как общий DAO просто силен. Я не хочу отправлять их на все клиентские сайты (в случае удаленных клиентов это даже невозможно или очень плохая практика).

Наконец, использование менеджера сущностей из клиентского кода, который сам не является транзакционным, означает, что этот клиент должен беспокоиться о транзакциях. Это добавляет много подробностей в код. С DAO клиентский код становится намного проще:

@Named
@RequestScoped
public class SomeBean {

    @EJB
    private ApplicantDAO applicantDAO;

    public void someMethod() {
        applicantDAO.delete(applicant);
    }
}
5 голосов
/ 13 июня 2011

Гигантские вопиющие проблемы, которые я вижу:

  1. DAO - это синглтон. Почему?
  2. Вы не используете EntityManager. Кроме того, почему?

Если вы на самом деле используете Java EE (так как вопрос помечен), а не J2EE (который является жалкой спецификацией, построенной на Java 1.4), то шаблон DAO совершенно не нужен. EntityManager - это новый DAO.

Посмотрите первые несколько разделов Учебное пособие по Java EE 6 - Постоянство .


мы обязаны использовать J2ee ..: (

Хорошо, вам все еще нужно исправить реализацию синглтона. Нет просто никакой причины создавать только один экземпляр этого объекта, учитывая, что он не хранит никакого внутреннего состояния. Это легко исправить:

  • Удалить private static ApplicantDAO me = null; целиком
  • Измените реализацию getInstance() на

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

Другие запахи:

  • Вы почти всегда захотите объявить List с, а не ArrayList с, поэтому измените объявления, например

    public ArrayList<Applicant> getApplicants() throws SQLException
    // to
    public List<Applicant> getApplicants() throws SQLException
    
    // and
    
    ArrayList<Applicant> applicantList = null;
    // to
    List<Applicant> applicantList = null;
    
2 голосов
/ 13 июня 2011

Ваш пример выглядит как идеальное изображение «до», чтобы показать отличное изображение «после» после использования Hibernate для отображения объектов в реляционную базу данных и функции внедрения зависимостей Spring для поддержки соединений и управления транзакциями.

1 голос
/ 13 июня 2011

Наихудший запах кода когда-либо: вы изобретаете колесо - снова. Почему бы не использовать некоторую открытую библиотеку, такую ​​как Spring JDBC ? (Примечание: вы можете прочитать код, чтобы поучиться у него тоже!)

Если вы занимаетесь здесь и / или вам не разрешено использовать внешний код, то вот несколько советов по улучшению вашего кода:

  • весь код, который не предназначен для определения оператора (с его аргументами) или обработки resultSet, может быть разложен на многократно используемые методы для уменьшения дублирования кода. Это может привести к аргументам в стиле обратного вызова или переменным для создания операторов, а также к компонентам в стиле адаптера для восстановления кандидатов (попробуйте просмотреть код Spring для его классов JdbcTemplate и RowMapper)
  • ваши подписи слишком точны: зачем возвращать ArrayList, если вы можете вернуть список?
  • почему ваш метод getInstance () синхронизирован? Вы можете избежать этого, инициализируя me во время инициализации класса: private static ApplicantDAO me = new ApplicantDAO(); public static ApplicantDAO getInstance() { return me; }
  • Ваша лог-политика не определена, кажется: я предполагаю, что она здесь специально, но имейте в виду, что это очень важно в реальных приложениях.
1 голос
/ 13 июня 2011

Вы можете использовать Java Persistence API , и большая часть этого скучного кода будет излишней.

...