2010-05-17 53 views
7

Estoy tratando de desarrollar DAO genérico en Java. He probado lo siguiente. ¿Es esto una buena forma de implementar DAO genérico? No quiero usar Hibernate. Estoy tratando de hacerlo lo más genérico posible para que no tenga que repetir el mismo código una y otra vez.DAO genérico en Java

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; 
     } 

    } 

} 

Ahora tengo:

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; 

    } 
} 
+0

¿cuál es el problema? – Bozho

+0

por favor vea la línea que dice // las siguientes líneas no funcionan; – akshay

+0

¿Por qué no quieres usar Hibernate (u otro ORM)? –

Respuesta

0

No reinventar la rueda, ya se puede encontrar buenos proyectos para hacer esto, ejemplo generic-dao proyecto en Google.

EDIT: respondió muy rápido, probablemente, el proyecto de Google se basa APP pero sin embargo se puede utilizar algunos de los conceptos en su interior.

+0

quiero saber por qué mi línea se menciona en el código no funciona – akshay

1

Es bien, pero cambiar el método

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; 

    } 

a

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; 

    } 

objeto usuario debe ser creado dentro de bucle while.

+0

por favor vea la línea // las siguientes líneas no funcionan; – akshay

+0

Este código perderá recursos si se lanza una excepción SQLException. Debe asegurarse de que ResultSet, Statement y Connection estén cerrados. La forma más sencilla es utilizar un marco como Spring JDBC en lugar de JDBC sin formato. – Adamski

+0

¿Cuál es el error que estás obteniendo? –

5

Mi consejo:

  • ¿No escribir DAO genérico; Las clases genéricas vuelven para morderte cuando te das cuenta de que no hacen exactamente lo que necesitas en una situación específica y a menudo terminan creciendo en complejidad para cubrir la gama cada vez mayor de casos de uso. Es mejor codificar los DAO específicos de la aplicación y luego intentar generar cualquier comportamiento común más adelante.
  • Considere usar Spring JDBC para escribir DAO específicos de la aplicación, pero de una forma mucho más compacta y menos propensa a errores que JDBC. Además, a diferencia de Hibernate, Spring JDBC solo actúa como una capa delgada alrededor de JDBC sin procesar, lo que le proporciona un control más preciso y una mayor visibilidad.

Ejemplo

// 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")); 
    } 
})); 
+0

Otro voto para Spring JDBC. Maneja muchas cosas de la conexión estándar (para evitar fugas de recursos). Además, proporciona una forma mucho más agradable de asignar columnas a sus objetos. Otro comentario, no codificaría el nombre de la columna de la clave principal.En cuanto lo haces, alguien aparece y crea una tabla con una columna llamada otra cosa, o utiliza una clave principal de múltiples columnas. – David

6

Si se puede vivir con la primavera, voy a sugerir las siguientes mejoras:

  • Vamos Primavera hacer el manejo de excepciones.
  • Use JdbcTemplate en lugar de crear declaraciones preparadas usted mismo.

independiente de la utilización de la primavera, voy a recomendar lo siguiente:

  • no envía el nombre de tabla como parámetro. Eso debería hacerse en la fase de inicialización.
  • Use una cadena en el parámetro id, ya que eso es mucho más genérico.
  • Considere devolver un objeto genérico en lugar de una colección, ya que la colección siempre debe contener un solo objeto.

una mejor AbstractDao con la primavera:

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); 
    } 
} 

Como se ve, sin manejo de excepciones o la piratería con las clases SQL primitivas. Esta plantilla cierra el ResultSet para usted, que no puedo ver en su código.

Y el 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; 
     } 
    } 
} 

Actualizado:

Cuando se conoce el ID y la ID corresponde a una sola fila en la base de datos, se debe considerar que devuelve un objeto genérico en lugar de una colección.

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

Esto hace que su código de servicio más fácil de leer, ya que no es necesario para recuperar el usuario de una lista, pero sólo:

User user = userDao.findUniqueObjectById("22"); 
+0

Estoy tratando de implementar su enfoque y todo parece estar bien, excepto para decidir dónde y cómo inicializar el objeto userDijo. Antes de implementar su enfoque, estaba usando userDao con anotación con autowired en la clase userService. Pero ahora en su ejemplo no hay ningún constructor vacío y no puede usar dao como objeto autocableado. Pensé que podría haber un constructor vacío de clase dao que llamara a otro constructor con jdbctemplate autoadherido. ¿Eso es útil? –

0

Es necesario añadir un espacio antes de su "WHERE" cláusula ver más abajo:

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

a

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

Aunque todos sugieren Spring y su API aquí, usa metadatos y es una mala combinación de código. Entonces no use DAO's genéricos o Spring en absoluto.

El código genérico es pesado y multiplica la carga.

+0

Parece que tuvo una mala experiencia con el código genérico. Los metadatos no son malos si se realizan correctamente: mientras más información mueva a los metadatos del código, mejor será el código restante (si se hace correctamente). El código genérico es realmente el único camino a seguir en muchos casos (DRY triunfa sobre la mayoría de las otras pautas/olores y es la raíz de la mayoría de los buenos códigos y prácticas de codificación). –

0

Si he entendido correctamente la declaración del problema, está tratando de implementar un tipo de capa de aislamiento entre sus servicios y una base de datos sin formato expuesta a través de una interfaz JDBC. La capa de aislamiento serviría como un mapeador de datos de sus objetos de dominio POJO a conjuntos de datos SQL. Esa es precisamente la tarea de iBATIS library, que le recomiendo que considere en lugar de implementar la clase homegene GenericDAO.