使用DAO

在传统的多层应用程序中,通常是Web层调用业务层,业务层调用数据访问层。业务层负责处理各种业务逻辑,而数据访问层只负责对数据进行增删改查。因此,实现数据访问层就是用JdbcTemplate实现对数据库的操作。

编写数据访问层的时候,可以使用DAO模式。DAO即Data Access Object的缩写,它没有什么神秘之处,实现起来基本如下:

  1. public class UserDao {
  2. @Autowired
  3. JdbcTemplate jdbcTemplate;
  4. User getById(long id) {
  5. ...
  6. }
  7. List<User> getUsers(int page) {
  8. ...
  9. }
  10. User createUser(User user) {
  11. ...
  12. }
  13. User updateUser(User user) {
  14. ...
  15. }
  16. void deleteUser(User user) {
  17. ...
  18. }
  19. }

Spring提供了一个JdbcDaoSupport类,用于简化DAO的实现。这个JdbcDaoSupport没什么复杂的,核心代码就是持有一个JdbcTemplate

  1. public abstract class JdbcDaoSupport extends DaoSupport {
  2. private JdbcTemplate jdbcTemplate;
  3. public final void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
  4. this.jdbcTemplate = jdbcTemplate;
  5. initTemplateConfig();
  6. }
  7. public final JdbcTemplate getJdbcTemplate() {
  8. return this.jdbcTemplate;
  9. }
  10. ...
  11. }

它的意图是子类直接从JdbcDaoSupport继承后,可以随时调用getJdbcTemplate()获得JdbcTemplate的实例。那么问题来了:因为JdbcDaoSupportjdbcTemplate字段没有标记@Autowired,所以,子类想要注入JdbcTemplate,还得自己想个办法:

  1. @Component
  2. @Transactional
  3. public class UserDao extends JdbcDaoSupport {
  4. @Autowired
  5. JdbcTemplate jdbcTemplate;
  6. @PostConstruct
  7. public void init() {
  8. super.setJdbcTemplate(jdbcTemplate);
  9. }
  10. }

有的童鞋可能看出来了:既然UserDao都已经注入了JdbcTemplate,那再把它放到父类里,通过getJdbcTemplate()访问岂不是多此一举?

如果使用传统的XML配置,并不需要编写@Autowired JdbcTemplate jdbcTemplate,但是考虑到现在基本上是使用注解的方式,我们可以编写一个AbstractDao,专门负责注入JdbcTemplate

  1. public abstract class AbstractDao extends JdbcDaoSupport {
  2. @Autowired
  3. private JdbcTemplate jdbcTemplate;
  4. @PostConstruct
  5. public void init() {
  6. super.setJdbcTemplate(jdbcTemplate);
  7. }
  8. }

这样,子类的代码就非常干净,可以直接调用getJdbcTemplate()

  1. @Component
  2. @Transactional
  3. public class UserDao extends AbstractDao {
  4. public User getById(long id) {
  5. return getJdbcTemplate().queryForObject(
  6. "SELECT * FROM users WHERE id = ?",
  7. new BeanPropertyRowMapper<>(User.class),
  8. id
  9. );
  10. }
  11. ...
  12. }

倘若肯再多写一点样板代码,就可以把AbstractDao改成泛型,并实现getById()getAll()deleteById()这样的通用方法:

  1. public abstract class AbstractDao<T> extends JdbcDaoSupport {
  2. private String table;
  3. private Class<T> entityClass;
  4. private RowMapper<T> rowMapper;
  5. public AbstractDao() {
  6. // 获取当前类型的泛型类型:
  7. this.entityClass = getParameterizedType();
  8. this.table = this.entityClass.getSimpleName().toLowerCase() + "s";
  9. this.rowMapper = new BeanPropertyRowMapper<>(entityClass);
  10. }
  11. public T getById(long id) {
  12. return getJdbcTemplate().queryForObject("SELECT * FROM " + table + " WHERE id = ?", this.rowMapper, id);
  13. }
  14. public List<T> getAll(int pageIndex) {
  15. int limit = 100;
  16. int offset = limit * (pageIndex - 1);
  17. return getJdbcTemplate().query("SELECT * FROM " + table + " LIMIT ? OFFSET ?",
  18. new Object[] { limit, offset },
  19. this.rowMapper);
  20. }
  21. public void deleteById(long id) {
  22. getJdbcTemplate().update("DELETE FROM " + table + " WHERE id = ?", id);
  23. }
  24. ...
  25. }

这样,每个子类就自动获得了这些通用方法:

  1. @Component
  2. @Transactional
  3. public class UserDao extends AbstractDao<User> {
  4. // 已经有了:
  5. // User getById(long)
  6. // List<User> getAll(int)
  7. // void deleteById(long)
  8. }
  9. @Component
  10. @Transactional
  11. public class BookDao extends AbstractDao<Book> {
  12. // 已经有了:
  13. // Book getById(long)
  14. // List<Book> getAll(int)
  15. // void deleteById(long)
  16. }

可见,DAO模式就是一个简单的数据访问模式,是否使用DAO,根据实际情况决定,因为很多时候,直接在Service层操作数据库也是完全没有问题的。

练习

使用DAO - 图1下载练习:使用DAO模式 (推荐使用IDE练习插件快速下载)

小结

Spring提供了JdbcDaoSupport来便于我们实现DAO模式;

可以基于泛型实现更通用、更简洁的DAO模式。

读后有收获可以支付宝请作者喝咖啡,读后有疑问请加微信群讨论:

使用DAO - 图2 使用DAO - 图3