java中的通用DAO

Posted

技术标签:

【中文标题】java中的通用DAO【英文标题】:generic DAO in java 【发布时间】:2011-02-20 09:05:37 【问题描述】:

我正在尝试在 java 中开发通用 DAO。我尝试了以下方法。这是 实现通用 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;

    

【问题讨论】:

请看这行说 // 以下 lins in not working; 为什么不想使用 Hibernate(或其他 ORM)? 我可以理解不想使用 Hibernate :-) 请注意,当您处理完 JDBC 对象(例如 ResultSet)后,您必须调用 close()。你打算在你的ResultSet 上给close() 打电话? 【参考方案1】:

如果你可以接受 Spring,我会建议以下改进:

让 Spring 进行异常处理。 使用 JdbcTemplate 而不是自己创建准备好的语句。

独立于使用Spring,我会推荐以下:

不要将表名作为参数发送。这应该在初始化阶段完成。 在 id 参数上使用字符串,因为这样更通用。 考虑返回一个通用对象而不是集合,因为集合应该始终只包含一个对象。

使用 Spring 改进的 AbstractDao:

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;
        
    

更新:

当你知道 id 并且 id 对应于数据库中的单行时,你应该考虑返回一个泛型对象而不是一个集合。

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

这使您的服务代码更具可读性,因为您不需要从列表中检索用户,而只需:

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

【讨论】:

我正在尝试实施您的方法,除了决定在何处以及如何初始化 userDao 对象外,一切似乎都很好。在实施您的方法之前,我在 userService 类中使用了带有自动装配注释的 userDao。但是现在在您的示例中没有任何空构造函数,并且不能将 dao 用作自动装配对象。我认为可以有 dao 类的空构造函数,它使用自动装配的 jdbctemplate 调用其他构造函数。有用吗?【参考方案2】:

我的建议:

不要编写通用 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"));
  
));

【讨论】:

又一票投给了 Spring JDBC。它处理了很多样板连接的东西(以避免资源泄漏)。此外,它提供了一种将列映射到对象的更好方法。另一条评论,我不会硬编码主键列名。一旦你这样做了,就会有人来创建一个表,其中包含一个名为其他名称的列,或者使用多列主键。【参考方案3】:

没关系,换个方法

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; 

     

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循环中创建。

【讨论】:

如果抛出 SQLException,此代码将泄漏资源。您需要确保 ResultSet、Statement 和 Connection 已关闭。最简单的方法是使用 Spring JDBC 之类的框架,而不是原始 JDBC。 是因为这个吗? (WHERE 前缺少空格)更改下面的“SELECT * FROM”+ tablename+“WHERE id = ?”到 "SELECT * FROM "+ tablename+ " WHERE id = ?"【参考方案4】:

不要重新发明***,你已经可以找到这样做的好项目,例如谷歌上的generic-dao项目。

编辑: 可能回答得太快了,谷歌项目是基于 JPA 的,但您可以使用其中的一些概念。

【讨论】:

【参考方案5】:

您需要在“WHERE”子句之前添加一个空格 见下文:

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

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

【讨论】:

【参考方案6】:

如果我正确理解了问题陈述,那么您正在尝试在您的服务和通过 JDBC 接口公开的普通数据库之间实现一种隔离层。隔离层将充当 POJO 域对象到 SQL 数据集的数据映射器。这正是iBATIS library 的任务,我建议您考虑一下,而不是实现自制的 GenericDAO 类。

【讨论】:

【参考方案7】:

尽管这里的每个人都建议使用 Spring 及其 API,但它使用元数据并且是错误的代码组合。所以根本不要使用通用 DAO 或 Spring。

通用代码很繁重,确实会增加您的负担。

【讨论】:

听起来您对泛型代码的体验很糟糕。如果处理得当,元数据还不错——从代码中移入元数据的信息越多,剩余的代码就越好(如果处理得当)。在许多情况下,通用代码确实是唯一可行的方法(DRY 胜过大多数其他准则/气味,是大多数良好代码和编码实践的根源)。

以上是关于java中的通用DAO的主要内容,如果未能解决你的问题,请参考以下文章

Spring Web 开发中的通用 DAO

泛型DAO

通用 DAO 和嵌套属性支持

Java实现关系型数据库工具类JdbcUtils系列九:通用DAO

MVC 中的单个 DAO 还是多个 DAO?

如何仅使用 java 和 jdbc(无 ORM)为基本的 crud 操作实现通用 DAO?