共计 2867 个字符,预计需要花费 8 分钟才能阅读完成。
在传统的多层应用程序中,通常是 Web 层调用业务层,业务层调用数据访问层。业务层负责处理各种业务逻辑,而数据访问层只负责对数据进行增删改查。因此,实现数据访问层就是用 JdbcTemplate
实现对数据库的操作。
编写数据访问层的时候,可以使用 DAO 模式。DAO 即 Data Access Object 的缩写,它没有什么神秘之处,实现起来基本如下:
public class UserDao {@Autowired
JdbcTemplate jdbcTemplate;
User getById(long id) {...}
List<User> getUsers(int page) {...}
User createUser(User user) {...}
User updateUser(User user) {...}
void deleteUser(User user) {...}
}
Spring 提供了一个 JdbcDaoSupport
类,用于简化 DAO 的实现。这个 JdbcDaoSupport
没什么复杂的,核心代码就是持有一个JdbcTemplate
:
public abstract class JdbcDaoSupport extends DaoSupport {private JdbcTemplate jdbcTemplate;
public final void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;
initTemplateConfig();}
public final JdbcTemplate getJdbcTemplate() {return this.jdbcTemplate;
}
...
}
它的意图是子类直接从 JdbcDaoSupport
继承后,可以随时调用 getJdbcTemplate()
获得 JdbcTemplate
的实例。那么问题来了:因为 JdbcDaoSupport
的jdbcTemplate
字段没有标记@Autowired
,所以,子类想要注入JdbcTemplate
,还得自己想个办法:
@Component
@Transactional
public class UserDao extends JdbcDaoSupport {@Autowired
JdbcTemplate jdbcTemplate;
@PostConstruct
public void init() {super.setJdbcTemplate(jdbcTemplate);
}
}
有的童鞋可能看出来了:既然 UserDao
都已经注入了 JdbcTemplate
,那再把它放到父类里,通过getJdbcTemplate()
访问岂不是多此一举?
如果使用传统的 XML 配置,并不需要编写@Autowired JdbcTemplate jdbcTemplate
,但是考虑到现在基本上是使用注解的方式,我们可以编写一个AbstractDao
,专门负责注入JdbcTemplate
:
public abstract class AbstractDao extends JdbcDaoSupport {@Autowired
private JdbcTemplate jdbcTemplate;
@PostConstruct
public void init() {super.setJdbcTemplate(jdbcTemplate);
}
}
这样,子类的代码就非常干净,可以直接调用getJdbcTemplate()
:
@Component
@Transactional
public class UserDao extends AbstractDao {public User getById(long id) {return getJdbcTemplate().queryForObject("SELECT * FROM users WHERE id = ?",
new BeanPropertyRowMapper<>(User.class),
id
);
}
...
}
倘若肯再多写一点样板代码,就可以把 AbstractDao
改成泛型,并实现 getById()
,getAll()
,deleteById()
这样的通用方法:
public abstract class AbstractDao<T> extends JdbcDaoSupport {private String table;
private Class<T> entityClass;
private RowMapper<T> rowMapper;
public AbstractDao() {// 获取当前类型的泛型类型:
this.entityClass = getParameterizedType();
this.table = this.entityClass.getSimpleName().toLowerCase() + "s";
this.rowMapper = new BeanPropertyRowMapper<>(entityClass);
}
public T getById(long id) {return getJdbcTemplate().queryForObject("SELECT * FROM" + table + "WHERE id = ?", this.rowMapper, id);
}
public List<T> getAll(int pageIndex) {int limit = 100;
int offset = limit * (pageIndex - 1);
return getJdbcTemplate().query("SELECT * FROM" + table + "LIMIT ? OFFSET ?",
new Object[] { limit, offset},
this.rowMapper);
}
public void deleteById(long id) {getJdbcTemplate().update("DELETE FROM" + table + "WHERE id = ?", id);
}
...
}
这样,每个子类就自动获得了这些通用方法:
@Component
@Transactional
public class UserDao extends AbstractDao<User> {// 已经有了:
// User getById(long)
// List<User> getAll(int)
// void deleteById(long)
}
@Component
@Transactional
public class BookDao extends AbstractDao<Book> {// 已经有了:
// Book getById(long)
// List<Book> getAll(int)
// void deleteById(long)
}
可见,DAO 模式就是一个简单的数据访问模式,是否使用 DAO,根据实际情况决定,因为很多时候,直接在 Service 层操作数据库也是完全没有问题的。
练习
使用 DAO 模式访问数据库。
下载练习
小结
Spring 提供了 JdbcDaoSupport
来便于我们实现 DAO 模式;
可以基于泛型实现更通用、更简洁的 DAO 模式。