общий DAO в Java

Я пытаюсь разработать общий DAO в java. Я пробовал следующее. Это хороший способ реализовать общий DAO? Я не хочу использовать спящий режим. Я пытаюсь сделать его как можно более универсальным, чтобы мне не приходилось повторять один и тот же код снова и снова.

public abstract class  AbstractDAO<T> {

    protected ResultSet findbyId(String tablename, Integer id){
        ResultSet rs= null;
        try {
           // the following lines are not working
            pStmt = cn.prepareStatement("SELECT * FROM "+ tablename+ "WHERE id = ?");
            pStmt.setInt(1, id);
            rs = pStmt.executeQuery();


        } catch (SQLException ex) {
            System.out.println("ERROR in findbyid " +ex.getMessage() +ex.getCause());
            ex.printStackTrace();
        }finally{
            return rs;
        }

    }

}

Теперь у меня есть:

public class UserDAO extends AbstractDAO<User>{

  public List<User> findbyid(int id){
   Resultset rs =findbyid("USERS",id) // "USERS" is table name in DB
   List<Users> users = convertToList(rs);
   return users; 
}


 private List<User> convertToList(ResultSet rs)  {
        List<User> userList= new ArrayList();
        User user= new User();;
        try {
            while (rs.next()) {
                user.setId(rs.getInt("id"));
                user.setUsername(rs.getString("username"));
                user.setFname(rs.getString("fname"));
                user.setLname(rs.getString("lname"));
                user.setUsertype(rs.getInt("usertype"));
                user.setPasswd(rs.getString("passwd"));
                userList.add(user);
            }
        } catch (SQLException ex) {
            Logger.getLogger(UserDAO.class.getName()).log(Level.SEVERE, null, ex);
        }

        return userList;

    }
}

person akshay    schedule 17.05.2010    source источник
comment
Пожалуйста, посмотрите строку, говорящую // следующие строки не работают;   -  person akshay    schedule 17.05.2010
comment
Почему вы не хотите использовать Hibernate (или другой ORM)?   -  person Tom Hawtin - tackline    schedule 17.05.2010
comment
Я могу понять, что не хочу использовать Hibernate :-)   -  person Adamski    schedule 17.05.2010
comment
Имейте в виду, что вам придется вызывать close() для объектов JDBC, таких как ResultSet, когда вы закончите с ними. Куда ты собираешься звонить close() со своего ResultSet?   -  person Jesper    schedule 17.05.2010


Ответы (7)


Если вы можете жить с Spring, я предлагаю следующие улучшения:

  • Позвольте Spring выполнить обработку исключений.
  • Используйте JdbcTemplate вместо самостоятельного создания готовых операторов.

Независимо от использования Spring, я рекомендую следующее:

  • Не отправляйте имя таблицы в качестве параметра. Это должно быть сделано на этапе инициализации.
  • Используйте строку для параметра id, так как это гораздо более общее.
  • Рассмотрите возможность возврата универсального объекта вместо коллекции, так как коллекция всегда должна содержать только один объект.

Улучшенный AbstractDao с Spring:

import java.util.Collection;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

public abstract class AbstractDao<T> {

    protected final RowMapper<T> rowMapper;

    protected final String findByIdSql;

    protected final JdbcTemplate jdbcTemplate;

    protected AbstractDao(RowMapper<T> rowMapper, String tableName,
            JdbcTemplate jdbcTemplate) {
        this.rowMapper = rowMapper;
        this.findByIdSql = "SELECT * FROM " + tableName + "WHERE id = ?";
        this.jdbcTemplate = jdbcTemplate;
    }

    public  Collection<T> findById(final String id) {
        Object[] params = {id};
        return jdbcTemplate.query(findByIdSql, params, rowMapper);
    }
}

Как видите, никакой обработки исключений или взлома с примитивными классами SQL. Эти шаблоны закрывают для вас ResultSet, чего я не вижу в вашем коде.

И UserDao:

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

public class UserDao extends AbstractDao<User> {

    private final static String TABLE_NAME = "USERS";

    public UserDao(JdbcTemplate jdbcTemplate) {
        super(new UserRowMapper(), TABLE_NAME, jdbcTemplate);
    }

    private static class UserRowMapper implements RowMapper<User> {
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setUserName(rs.getString("username"));
            user.setFirstName(rs.getString("fname"));
            user.setLastName(rs.getString("lname"));

            return user;
        }
    }
}

Обновлено:

Когда вы знаете идентификатор, а идентификатор соответствует одной строке в базе данных, вам следует подумать о возврате универсального объекта вместо коллекции.

public T findUniqueObjectById(final String id) {
    Object[] params = {id};
    return jdbcTemplate.queryForObject(findByIdSql, params, rowMapper);
}

Это делает ваш сервисный код более читаемым, так как вам не нужно извлекать пользователя из списка, а только:

User user = userDao.findUniqueObjectById("22");
person Espen    schedule 17.05.2010
comment
Я пытаюсь реализовать ваш подход, и все выглядит нормально, кроме решения, где и как инициализировать объект userDao. Перед реализацией вашего подхода я использовал userDao с автоматической аннотацией в классе userService. Но теперь в вашем примере нет пустого конструктора, и вы не можете использовать dao в качестве объекта с автоматическим подключением. Я думал, что может быть пустой конструктор класса dao, который вызывает другой конструктор с автоподключенным jdbctemplate. Это полезно? - person Ömer Faruk Almalı; 08.08.2013

Мой совет:

  • Не создавайте общий DAO; Общие классы возвращаются к вам, когда вы понимаете, что они не совсем делают то, что вам нужно в конкретной ситуации, и часто в конечном итоге усложняются, чтобы покрыть постоянно увеличивающийся набор вариантов использования. Лучше закодировать DAO для конкретного приложения, а затем попытаться сгенерировать какое-либо общее поведение позже.
  • Рассмотрите возможность использования Spring JDBC для написания DAO для конкретных приложений. но гораздо более компактным и менее подверженным ошибкам способом, чем JDBC. Кроме того, в отличие от Hibernate, Spring JDBC действует только как тонкая оболочка вокруг необработанного JDBC, предоставляя вам более точный контроль и большую наглядность.

Пример

// Create or inject underlying DataSource.
DataSource ds = ...
// Initialise Spring template, which we'll use for querying.
SimpleJdbcTemplate tmpl = new SimpleJdbcTemplate(ds);     

// Create collection of "Role"s: The business object we're interested in.
Set<Role> roles = new HashSet<Role>();

// Query database for roles, use row mapper to extract and create
// business objects and add to collection.  If an error occurs Spring
// will translate the checked SQLException into an unchecked Spring
// DataAccessException and also close any open resources (ResultSet, Connection).
roles.addAll(tmpl.query("select * from Role", new ParameterizedRowMapper<Role>() {
  public Role mapRow(ResultSet resultSet, int i) throws SQLException {
    return new Role(resultSet.getString("RoleName"));
  }
}));
person Adamski    schedule 17.05.2010
comment
Еще одно голосование за Spring JDBC. Он обрабатывает множество типовых соединений (чтобы избежать утечек ресурсов). Кроме того, он предоставляет гораздо более удобный способ сопоставления столбцов с вашими объектами. Еще один комментарий: я бы не стал жестко кодировать имя столбца первичного ключа. Как только вы это сделаете, кто-то появится и создаст таблицу со столбцом с другим именем или использует первичный ключ с несколькими столбцами. - person David; 17.05.2010

Все в порядке, но измените метод

private List<User> convertToList(ResultSet rs)  { 
        List<User> userList= new ArrayList(); 
        User user= new User();; 
        try { 
            while (rs.next()) { 
                user.setId(rs.getInt("id")); 
                user.setUsername(rs.getString("username")); 
                user.setFname(rs.getString("fname")); 
                user.setLname(rs.getString("lname")); 
                user.setUsertype(rs.getInt("usertype")); 
                user.setPasswd(rs.getString("passwd")); 
                userList.add(user); 
            } 
        } catch (SQLException ex) { 
            Logger.getLogger(UserDAO.class.getName()).log(Level.SEVERE, null, ex); 
        } 

        return userList; 

    } 

to

private List<User> convertToList(ResultSet rs)  { 
        List<User> userList= new ArrayList<User>(); 
        try { 
            while (rs.next()) { 
                User user= new User();
                user.setId(rs.getInt("id")); 
                user.setUsername(rs.getString("username")); 
                user.setFname(rs.getString("fname")); 
                user.setLname(rs.getString("lname")); 
                user.setUsertype(rs.getInt("usertype")); 
                user.setPasswd(rs.getString("passwd")); 
                userList.add(user); 
            } 
        } catch (SQLException ex) { 
            Logger.getLogger(UserDAO.class.getName()).log(Level.SEVERE, null, ex); 
        } 

        return userList; 

    } 

Пользовательский объект должен быть создан внутри цикла while.

person Ravindra Gullapalli    schedule 17.05.2010
comment
Этот код приведет к утечке ресурсов, если возникнет исключение SQLException. Вам необходимо убедиться, что ResultSet, Statement и Connection закрыты. Самый простой способ - использовать фреймворк, такой как Spring JDBC, а не необработанный JDBC. - person Adamski; 17.05.2010
comment
Это из-за этого? (пропущен пробел перед WHERE) Измените приведенное ниже SELECT * FROM + tablename+ WHERE id = ? SELECT * FROM + tablename+ WHERE id = ? - person Ravindra Gullapalli; 17.05.2010


Вам нужно добавить пробел перед предложением «WHERE», см. ниже:

pStmt = cn.prepareStatement("SELECT * FROM "+ tablename+ "WHERE id = ?");

to

 pStmt = cn.prepareStatement("SELECT * FROM "+ tablename+ " WHERE id = ?");
person Robert    schedule 17.05.2010

Если я правильно понял постановку проблемы, вы пытаетесь реализовать своего рода уровень изоляции между вашими службами и простой базой данных, доступной через интерфейс JDBC. Уровень изоляции будет служить средством отображения данных ваших объектов домена POJO в наборы данных SQL. Именно в этом заключается задача библиотеки iBATIS, над которой я рекомендую вам подумать, а не реализовывать доморощенный класс GenericDAO.

person Dmitri    schedule 21.08.2011

Несмотря на то, что все предлагают здесь Spring и его API, он использует метаданные и представляет собой плохую комбинацию кода. Поэтому вообще не используйте общие DAO или Spring.

Общий код тяжелый и увеличивает вашу нагрузку.

person srikanth N    schedule 07.04.2011
comment
Похоже, у вас был плохой опыт работы с общим кодом. Метаданные неплохи, если все сделано правильно — чем больше информации вы переместите в метаданные из кода, тем лучше должен быть ваш оставшийся код (если все сделано правильно). Общий код — действительно единственный путь во многих случаях (DRY превосходит большинство других рекомендаций/запахов и является корнем большинства хороших кодов и практик кодирования). - person Bill K; 21.09.2011