白话设计模式之模板方法模式

2022-07-26 16:59:27 浏览数 (1)

模板方法介绍

一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行,形象一点来说,就是已经将步骤定义好了,直接运行就行,换到现实中来,我们有些同学从出生到世界这个摇篮里的时候,爸爸妈妈已经将他的人生规划好了,要上什么学校,上什么课外班,每天的营养搭配,假期去柏林玩还是爱尔兰耍,将来要找什么条件的女朋友,要从事什么工作,都已经安排得明明白白的。

jdbc链接

学过java的同学都知道jdbc,我们刚学Java基础的时候,jdbc是必学的,以前刚学的时候,无论执行一个查询,还是一个更新操作,我想大多同学都会将整个连接过程放在每一个逻辑里面,每个添加,每个查询都写一遍,类似如下:

代码语言:javascript复制
public void get() throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/system","root","root");
        PreparedStatement preparedStatement = connection.prepareStatement("select * from user");
        ResultSet resultSet = preparedStatement.executeQuery();
        while (resultSet.next()){
            System.out.println(resultSet.getString("user_id"));
        }
        connection.close();
        preparedStatement.close();
        resultSet.close();
    }

不难看出,上面的jdbc连接和关闭过程基本是固定的,所以可以抽象为一个模板,这样的话,针对于数据库的CRUD操作就不必写过多的冗余代码,下面使用模板方法模式开始改造。

模板方法模式改造jdbc

1.配置数据源

代码语言:javascript复制
public class MyDataSource {
    protected DataSource dataSource;
    {
        com.alibaba.druid.pool.DruidDataSource dataSource = new com.alibaba.druid.pool.DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3307/system");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        this.dataSource = dataSource;
    }
}

2.对于查询,我们编写一个映射结果集的接口RowMapper<T>

代码语言:javascript复制
public interface RowMapper<T> {
    T mapRow(ResultSet resultSet) throws SQLException;
}

3.编写JdbcTemplate,JdbcTemplate继承了MyDataSource,这样就可以使用数据源了,我们将获取链接,预处理,结果集,关闭连接等单独提取出来,编写了查询和修改方法executeQuery,executeUpdate。

代码语言:javascript复制
package template.jdbc;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public abstract class JdbcTemplate extends MyDataSource{

    private Connection connection;
    private PreparedStatement preparedStatement;
    private ResultSet resultSet;

    //查询
    protected <T> List<T> executeQuery(String sql , RowMapper<T> rowMapper) throws SQLException {
        if (sql == null || sql.equals("")) throw new SQLException("sql is null");
        preparedStatement = preparedStatement(sql);
        resultSet = preparedStatement.executeQuery();
        List<T> list = resultSet(resultSet, rowMapper);
        close();
        return list;
    }
    
    //更新
    protected int executeUpdate(String sql) throws SQLException {
        if (sql == null || sql.equals("")) throw new SQLException("sql is null");
        preparedStatement = preparedStatement(sql);
        int i = preparedStatement.executeUpdate();
        close();
        return i;
    }

    //获取链接
    private Connection getConnection() throws SQLException {
        return this.dataSource.getConnection();
    }

    //预执行sql
    private PreparedStatement preparedStatement(String sql) throws SQLException {
        this.connection = getConnection();
        return connection.prepareStatement(sql);
    }

    //结果集
    private <T> List<T> resultSet(ResultSet resultSet , RowMapper<T> rowMapper) throws SQLException {
        List<T> list = new ArrayList<>();
        while (resultSet.next()){
            list.add(rowMapper.mapRow(resultSet));
        }
        return list;
    }

    //关闭
    private void close() throws SQLException {
        if (this.connection != null) this.connection.close();
        if (this.preparedStatement != null) this.preparedStatement.close();
        if (this.resultSet != null) this.resultSet.close();
    }
}

4.编写具体实现类,UserMapper 继承JdbcTemplate

代码语言:javascript复制
public class UserMapper extends JdbcTemplate{
    //获取人员列表
    public List<User> list(String sql) throws SQLException {
        return executeQuery(sql, new RowMapper<User>() {
            @Override
            public User mapRow(ResultSet resultSet) throws SQLException {
                return User.builder().userId(resultSet.getString("user_id"))
                        .uuid(resultSet.getString("uuid"))
                        .id(resultSet.getLong("id")).build();
            }
        });
    }
}

5.测试查询接口

代码语言:javascript复制
public class Client {
    public static void main(String[] args) throws SQLException {
        String sql = "SELECT * FROM sys_user";
        List<User> list = new UserMapper().list(sql);
        System.out.println(list);
    }
}

结果
[
        User(id=1000, uuid=9df9de08354f456c97fae0cdb3df214f, userId=super_admin), 
        User(id=1002, uuid=26220d63bfd345dabb2f114287965313, userId=mikey), 
        User(id=1003, uuid=b8c1e673060c437ba0925f119538bdb6, userId=fasf)
]

6.测试更新接口,直接使用JdbcTemplate的executeUpdate方法

代码语言:javascript复制
public class Client {
    public static void main(String[] args) throws SQLException {
        String updateSQL = "UPDATE sys_user SET user_id = "super_admin" WHERE user_id = "admin"";
        int update = new UserMapper().executeUpdate(updateSQL);
    }
}

上面我们查询的是用户,如果要查询角色,直接编写一个RoleMapper ,然后进行相应的CRUD操作

代码语言:javascript复制
public class RoleMapper extends JdbcTemplate{
    public List<Role> list(String sql) throws SQLException {
       return executeQuery(sql, new RowMapper<Role>() {
            @Override
            public Role mapRow(ResultSet resultSet) throws SQLException {
                return Role.builder().roleName(resultSet.getString("role_name"))
                        .uuid(resultSet.getString("uuid"))
                        .id(resultSet.getLong("id")).build();
            }
        });
    }
}

模板方法模式应用场景

存在固定步骤的操作,可以提出公共的部分。

模板方法模式的优点

1.对于有固定步骤的代码,将其封装,有扩展的部分允许自己实现

2.行为由父类控制,子类实现。

模板方法模式的缺点

子类会变得很多,不过去这其实算不上缺点,对于数据库操作,数据表肯定会对应很多实体类,使用ORM框架,自然每一个实体会对应一个Mapper,这是无法避免的。

今天的分享就到这里,感谢你的观看,我们下期见。

0 人点赞