暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Spring 使用DAO

编程牧马人 2021-06-21
158

点击上方 编程牧马人选择 设为星标

优质项目,及时送达

-----

使用DAO[1]

在传统的多层应用程序中,通常是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<Textends JdbcDaoSupport {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @PostConstruct
    public void init(){
        super.setJdbcTemplate(jdbcTemplate);
    }

    private String table;// table 相当于 users 表
    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);
    }


    public RowMapper<T> getRowMapper(){
        return this.rowMapper;
    }

    private Class<T> getParameterizedType(){
        Type type = getClass().getGenericSuperclass();
        System.out.println(type);
        if(!(type instanceof ParameterizedType)){
            throw new IllegalArgumentException("Class " + getClass().getName() + " does not have parameteriezd type.");
        }
        ParameterizedType pt = (ParameterizedType) type;
        Type[] types = pt.getActualTypeArguments();
        if(types.length != 1){
            throw new IllegalArgumentException(
                    "Class " + getClass().getName() + " has more than 1 parameterized types.");

        }
        Type r = types[0];
        if(!(r instanceof Class<?>)){
            throw new IllegalArgumentException(
                    "Class " + getClass().getName() + " does not have parameterized type of class.");

        }

        return (Class<T>) r;
    }
}

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

@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
层操作数据库也是完全没有问题的。

小结

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

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

参考资料

[1]

使用DAO: https://www.liaoxuefeng.com/wiki/1252599548343744/1282383605137441


文章转载自编程牧马人,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论