0
点赞
收藏
分享

微信扫一扫

设计模式 中介者模式

快乐码农Alan007 2022-04-01 阅读 67

文章目录

中介者模式

中介者模式使用的场景: 把复杂功能的重复调用, 中间添加一层包装, 对外提供简单, 易扩展的服务能力. 例如中间件组提供的统一的接口与服务等.

中介者模式实战

模拟jdbc连接数据库的操作. 使用中介者模式,手写一个简易版的mybatis.

不使用中介者模式写法

创建模块tutorials-19.0-0 创建JDBCUtil类
该类是纯手写jdbc的过程.

public class JDBCUtil {

    private static Logger logger = LoggerFactory.getLogger(JDBCUtil.class);

    public static final String URL = "jdbc:mysql://127.0.0.1:3306/itstack_demo_ddd";
    public static final String USER = "root";
    public static final String PASSWORD = "123456";

    public static void main(String[] args) throws Exception {
        // 1. 加载数据库驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 获取连接.
        Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
        // 3.操作数据库
        Statement stmt = conn.createStatement();
        ResultSet resultSet = stmt.executeQuery("select id, name, age from user ");
        // 如果有数据, rs.next()返回true
        while (resultSet.next()) {
            logger.info("测试结果 姓名: {}, 年龄: {}", resultSet.getString("name"), resultSet.getInt("age"));
        }
    }
}

使用中介者模式写法

使用中介者模式, 手写一个简易的mybatis查询框架.
uml图如下.

实现对配置文件的加载, xml的解析, 获取数据库的session, 数据库操作以及返回结果等.

定义SqlSession接口

public interface SqlSession {

    <T> T selectOne(String statement);

    <T> T selectOne(String statement, Object parameter);

    <T> List<T> selectList(String statement);

    <T> List<T> selectList(String statement, Object parameter);

    void close();
}

SqlSession具体实现类 DefaultSqlSession

public class DefaultSqlSession implements SqlSession{

    // sql 连接
    private Connection connection;
    private Map<String, XNode> mapperElement;

    public DefaultSqlSession(Connection connection, Map<String, XNode> mapperElement) {
        this.connection = connection;
        this.mapperElement = mapperElement;
    }

    /**
     *  无参数查询返回一个结果
     * @param statement
     * @param <T>
     * @return
     */
    @Override
    public <T> T selectOne(String statement) {
        try {
            XNode xNode = mapperElement.get(statement);
            PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());
            ResultSet resultSet = preparedStatement.executeQuery();
            List<T> resultList = resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));
            return resultList.get(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 有参数查询, 返回一个结果
     * @param statement
     * @param parameter
     * @param <T>
     * @return
     */
    @Override
    public <T> T selectOne(String statement, Object parameter) {
        XNode xNode = mapperElement.get(statement);
        Map<Integer, String> parameterMap = xNode.getParameter();
        try {
            PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());
            buildParameter(preparedStatement, parameter, parameterMap);
            ResultSet resultSet = preparedStatement.executeQuery();
            List<T> objects = resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));
            return objects.get(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     *  解析查询参数
     * @param preparedStatement
     * @param parameter
     * @param parameterMap
     * @throws Exception
     */
    private void buildParameter(PreparedStatement preparedStatement,
                                Object parameter,
                                Map<Integer, String> parameterMap) throws Exception {
        int size = parameterMap.size();

        // 判断参数类型
        if (parameter instanceof Long) {
            //  the first parameter is 1, the second is 2 因此这里要从1开始.
            for (int i = 1; i <= size; i++) {
                preparedStatement.setLong(i, Long.parseLong(parameter.toString()));
            }
            return;
        }

        if (parameter instanceof Integer) {
            for (int i = 1; i <=size; i++) {
                preparedStatement.setInt(i, Integer.parseInt(parameter.toString()));
            }
            return;
        }

        if (parameter instanceof String) {
            for (int i = 1; i <= size; i++) {
                preparedStatement.setString(i, parameter.toString());
            }
            return;
        }

        // key 参数名  value: 字段的值.
        HashMap<String, Object> fieldMap = new HashMap<>();
        // 对象参数
        Field[] declaredFields = parameter.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            String name = field.getName();
            Object obj = field.get(parameter);
            fieldMap.put(name, obj);
        }

        for (int i = 1; i <=size; i++) {
            String parameterDefine = parameterMap.get(i);
            Object obj = fieldMap.get(parameterDefine);

            if (obj instanceof Short) {
                preparedStatement.setShort(i, Short.parseShort(obj.toString()));
                continue;
            }

            if (obj instanceof Integer) {
                preparedStatement.setInt(i, Integer.parseInt(obj.toString()));
                continue;
            }
            if (obj instanceof Long) {
                preparedStatement.setLong(i, Long.parseLong(obj.toString()));
                continue;
            }
            if (obj instanceof String) {
                preparedStatement.setString(i, obj.toString());
                continue;
            }
            if (obj instanceof Date) {
                // 如果是日期类型的, 转为sql的日期
                preparedStatement.setDate(i, (java.sql.Date)obj);
            }
        }
    }

    /**
     *  查询list集合 无参数
     * @param statement
     * @param <T>
     * @return
     */
    @Override
    public <T> List<T> selectList(String statement) {
        XNode xNode = mapperElement.get(statement);
        try {
            PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());
            ResultSet resultSet = preparedStatement.executeQuery();
            return resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public <T> List<T> selectList(String statement, Object parameter) {
        XNode xNode = mapperElement.get(statement);
        Map<Integer, String> parameterMap = xNode.getParameter();
        try {
            PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());
            buildParameter(preparedStatement, parameter, parameterMap);
            ResultSet resultSet = preparedStatement.executeQuery();
            return resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 释放连接
     */
    @Override
    public void close() {
        if (connection == null) {
            return;
        }
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     *  ResultSet 转化为对象集合
     * @param resultSet
     * @param clazz
     * @param <T>
     * @return
     */
    private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz) {
        List<T> list = new ArrayList<>();
        try {
            ResultSetMetaData metaData = resultSet.getMetaData();
            // 获取有多少列 (多少字段的数据)
            int columnCount = metaData.getColumnCount();
            // 遍历每一行数据的值
            while (resultSet.next()) {
                T obj = (T) clazz.newInstance();
                // 遍历每一个字段
                for (int i = 1; i <= columnCount; i++) {
                    Object value = resultSet.getObject(i);
                    String columnName = metaData.getColumnName(i);

                    // 通过反射 set方法给对象设置值 例如id  setId
                    String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);
                    Method method;
                    if (value instanceof Timestamp) {
                        method = clazz.getMethod(setMethod, Date.class);
                    } else {
                        method = clazz.getMethod(setMethod, value.getClass());
                    }
                    method.invoke(obj, value);
                }
                list.add(obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }
}

对查询的方法进行了实现, 包含sql的解析 ,结果的解析等.

定义SqlSessionFactory接口. 用于定义开启sqlSession

public interface SqlSessionFactory {

    //开启一个SqlSession,有数据库操作的时候都会获取每一次执行的SqlSession
    SqlSession openSession();
}

SqlSessionFactory具体实现类
开启SqlSession时会进行返回一个DefaultSqlSession

public class DefaultSqlSessionFactory implements SqlSessionFactory{
    //配置文件类
    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSession() {
        // 开启SqlSession时会进行返回一个DefaultSqlSession
        return new DefaultSqlSession(configuration.connection, configuration.mapperElement);
    }
}

Configuration 类如下

public class Configuration {

    protected Connection connection;
    protected Map<String, String> dataSource;
    protected Map<String, XNode> mapperElement;

    public void setConnection(Connection connection) {
        this.connection = connection;
    }

    public void setDataSource(Map<String, String> dataSource) {
        this.dataSource = dataSource;
    }

    public void setMapperElement(Map<String, XNode> mapperElement) {
        this.mapperElement = mapperElement;
    }
}

Resources类

public class Resources {

    public static Reader getResourceAsReader(String resource) throws Exception {
        return new InputStreamReader(getResourceAsStream(resource));
    }

    private static InputStream getResourceAsStream(String resource) throws Exception {
        ClassLoader[] classLoaders = getClassLoaders();
        for (ClassLoader classLoader : classLoaders) {
            InputStream inputStream = classLoader.getResourceAsStream(resource);
            if (inputStream != null) {
                return inputStream;
            }
        }
        throw new IOException("could not find resource " + resource);
    }

    private static ClassLoader[] getClassLoaders() {
        return new ClassLoader[]{
                ClassLoader.getSystemClassLoader(),
                Thread.currentThread().getContextClassLoader()
        };
    }
}

SqlSessionFactoryBuilder

public class SqlSessionFactoryBuilder {

    // (构建实例化元素)
    public DefaultSqlSessionFactory build(Reader reader) {
        // 解析xml的sql 创建解析xml文件的类
        SAXReader saxReader = new SAXReader();
        // 这是为了保证在不联网的时候一样可以解析xml,否则会需要从互联网获取dtd文件。
        saxReader.setEntityResolver(new XMLMapperEntityResolver());
        try {
            Document document = saxReader.read(new InputSource(reader));
            Configuration configuration = parseConfiguration(document.getRootElement());
            // 初始化SqlSession工厂类DefaultSqlSessionFactory
            return new DefaultSqlSessionFactory(configuration);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return null;
    }

    //(解析配置)   对xml中的元素进行获取
    private Configuration parseConfiguration(Element root) {
        Configuration configuration = new Configuration();
        // 获取数据库连接信息
        configuration.setDataSource(dataSource(root.selectNodes("//dataSource")));
        //获取连接
        configuration.setConnection(connection(configuration.dataSource));
        //获取数据库操作 mapper.xml的sql语句信息
        configuration.setMapperElement(mapperElement(root.selectNodes("mappers")));
        return configuration;
    }

    //  (解析sql语句)
    private Map<String, XNode> mapperElement(List<Element> list) {
        Map<String, XNode> map = new HashMap<>();
        Element element = list.get(0);
        List<Element> content = element.content();
        for (Element e : content) {
            String resource = e.attributeValue("resource");
            try {
                Reader reader = Resources.getResourceAsReader(resource);
                SAXReader saxReader = new SAXReader();
                Document document = saxReader.read(new InputSource(reader));
                Element root = document.getRootElement();
                // 获取命名空间
                String namespace = root.attributeValue("namespace");
                // 获取select语句
                List selectNodes = root.selectNodes("select");
                // 遍历所有的查询语句, 封装到map中
                for (Object obj : selectNodes) {
                    Element node = (Element) obj;
                    String id = node.attributeValue("id");
                    String parameterType = node.attributeValue("parameterType");
                    String resultType = node.attributeValue("resultType");

                    String sql = node.getText();

                    // 匹配  ?
                    Map<Integer, String> parameter = new HashMap<>();
                    Pattern pattern = Pattern.compile("(#\\{(.*?)})");
                    Matcher matcher = pattern.matcher(sql);

                    for (int i = 1; matcher.find(); i++) {
                        String g1 = matcher.group(1);
                        String g2 = matcher.group(2);
                        parameter.put(i, g2);
                        sql = sql.replace(g1, "?");
                    }

                    XNode xNode = new XNode();
                    xNode.setNamespace(namespace);
                    xNode.setId(id);
                    xNode.setParameterType(parameterType);
                    xNode.setResultType(resultType);
                    xNode.setSql(sql);
                    xNode.setParameter(parameter);
                    // 一个名称空间下, 所有的sql的集合映射
                    map.put(namespace + "." + id, xNode);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return map;
    }

    /**
     * 获取连接
     * @param dataSource
     * @return
     */
    private Connection connection(Map<String, String> dataSource) {
        try {
            // 此处没有写死是加载MySQL还是oracle等数据库
            Class.forName(dataSource.get("driver"));
            return DriverManager.getConnection(dataSource.get("url"), dataSource.get("username"), dataSource.get("password"));
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    private Map<String, String> dataSource(List<Element> list) {
        HashMap<String, String> dataSource = new HashMap<>();
        Element element = list.get(0);
        List<Element> content = element.content();

        for (Element e : content) {
            String name = e.attributeValue("name");
            String value = e.attributeValue("value");
            dataSource.put(name, value);
        }
        return dataSource;
    }
}

实现sql语句解析, 解析数据库配置等.

测试验证

创建表

CREATE TABLE school ( id bigint NOT NULL AUTO_INCREMENT, name varchar(64), address varchar(256), createTime datetime, updateTime datetime, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into school (id, name, address, createTime, updateTime) values (1, '北京大学', '北京市海淀区颐和园路5号', '2019-10-18 13:35:57', '2019-10-18 13:35:57');
insert into school (id, name, address, createTime, updateTime) values (2, '南开大学', '中国天津市南开区卫津路94号', '2019-10-18 13:35:57', '2019-10-18 13:35:57');
insert into school (id, name, address, createTime, updateTime) values (3, '同济大学', '上海市彰武路1号同济大厦A楼7楼7区', '2019-10-18 13:35:57', '2019-10-18 13:35:57');
CREATE TABLE user ( id bigint(11) NOT NULL AUTO_INCREMENT, name varchar(32), age int(4), address varchar(128), entryTime datetime, remark varchar(64), createTime datetime, updateTime datetime, status int(4) DEFAULT '0', dateTime varchar(64), PRIMARY KEY (id), INDEX idx_name (name) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into user (id, name, age, address, entryTime, remark, createTime, updateTime, status, dateTime) values (1, '水水', 18, '吉林省榆树市黑林镇尹家村5组', '2019-12-22 00:00:00', '无', '2019-12-22 00:00:00', '2019-12-22 00:00:00', 0, '20200309');
insert into user (id, name, age, address, entryTime, remark, createTime, updateTime, status, dateTime) values (2, '豆豆', 18, '辽宁省大连市清河湾司马道407路', '2019-12-22 00:00:00', '无', '2019-12-22 00:00:00', '2019-12-22 00:00:00', 1, null);
insert into user (id, name, age, address, entryTime, remark, createTime, updateTime, status, dateTime) values (3, '花花', 19, '辽宁省大连市清河湾司马道407路', '2019-12-22 00:00:00', '无', '2019-12-22 00:00:00', '2019-12-22 00:00:00', 0, '20200310');

创建用户类

@Data
public class User {

    private Long id;
    private String name;
    private Integer age;
    private Date createTime;
    private Date updateTime;
}

创建学校类

@Data
public class School {

    private Long id;
    private String name;
    private String address;
    private Date createTime;
    private Date updateTime;
}

创建 mybatis-config-datasource.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://192.168.....?useUnicode=true"/>
                <property name="username" value="root"/>
                <property name="password" value="........"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="mapper/User_Mapper.xml"/>
        <mapper resource="mapper/School_Mapper.xml"/>
    </mappers>

</configuration>

创建ISchoolDao

public interface ISchoolDao {

    School querySchoolInfoById(Long treeId);

}

创建IUserDao

public interface IUserDao {

    User queryUserInfoById(Long id);

    List<User> queryUserList(User user);
}

创建School_Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.thc.design.dao.ISchoolDao">

    <select id="querySchoolInfoById" resultType="com.thc.design.po.School">
        SELECT id, name, address, createTime, updateTime
        FROM school
        where id = #{id}
    </select>

</mapper>

创建User_Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.thc.design.dao.IUserDao">

    <select id="queryUserInfoById" parameterType="java.lang.Long" resultType="com.thc.design.po.User">
        SELECT id, name, age, createTime, updateTime
        FROM user
        where id = #{id}
    </select>

    <select id="queryUserList" parameterType="com.thc.design.po.User" resultType="com.thc.design.po.User">
        SELECT id, name, age, createTime, updateTime
        FROM user
        where age = #{age}
    </select>
</mapper>

ApiTest 测试类

  private Logger logger = LoggerFactory.getLogger(ApiTest.class);

    // 单个结果查询测试
    @Test
    public void test_queryUserInfoById() {
        String resource = "mybatis-config-datasource.xml";
        Reader reader;

        try {
            reader = Resources.getResourceAsReader(resource);
            SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);

            SqlSession session = sqlMapper.openSession();
            try {
                User user = session.selectOne("com.thc.design.dao.IUserDao.queryUserInfoById", 1L);
                logger.info("测试结果: {}", JSON.toJSONString(user));
            } finally {
                session.close();
                reader.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

集合查询

 // 集合结果查询测试
    @Test
    public void test_queryUserList() {
        String resource = "mybatis-config-datasource.xml";
        Reader reader;

        try {
            reader = Resources.getResourceAsReader(resource);
            SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);

            SqlSession session = sqlMapper.openSession();
            try {
                User req = new User();
                req.setAge(18);
                List<User> userList = session.selectList("com.thc.design.dao.IUserDao.queryUserList", req);
                logger.info("测试结果: {}", JSON.toJSONString(userList));
            } finally {
                session.close();
                reader.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
举报

相关推荐

0 条评论