手撸mybatis03: xml配置的解析与注册

在使用mybatis时,都是直接将sql写在xml中,由mybatis去解析后,映射执行,再通过代理模式实现IDao.

SqlSessionFactoryBuilder

package com.linnine.mybatis.session;

/**
 * SqlSessionFactory 建造者工厂
 * 整个mybatis的入口类
 */
public class SqlSessionFactoryBuilder {

    public SqlSessionFactory builder(Reader reader){
        //解析xml文件
        XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(reader);
        //将解析内容转换成配置信息类
        return builder(xmlConfigBuilder.parse());
    }

    public SqlSessionFactory builder(Configuration config){
        return new DefaultSqlSessionFactory(config);
    }
}

XMLConfigBuilder

先引入一下这个依赖

<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.3</version>
</dependency>

xml解析类

package com.linnine.mybatis.builder;

/**
 * xml 解析类
 */
public class XMLConfigBuilder extends BaseBuilder {

    private Element root;

    public XMLConfigBuilder(Reader reader) {
        super(new Configuration());
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(new InputSource(reader));
            root =document.getRootElement();
        } catch (DocumentException e) {
            e.printStackTrace();
        }

    }

    public Configuration parse() {
        //解析映射器
        try {
            mapperElement(root.element("mappers"));
        } catch (Exception e) {
            throw new RuntimeException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
        return configuration;
    }

    private void mapperElement(Element mappers) throws Exception {
        List<Element> mapperList = mappers.elements("mapper");
        for (Element e : mapperList) {
            String resource = e.attributeValue("resource");
            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");
            List<Element> selectNodes = root.elements("select");
            for (Element node : selectNodes) {
                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, "?");
                }
                String msId=namespace+"."+id;
                String nodeName = node.getName();
                SqlCommandType sqlCommandType=SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
                //解析处理
                MappedStatement mappedStatement = new MappedStatement.Builder(configuration,msId,sqlCommandType,parameterType,resultType,sql,parameter).build();
                //添加解析后的sql
                configuration.addMappedStatement(mappedStatement);
            }
            configuration.addMapper(Resources.classForName(namespace));
        }
    }
}

BaseBuilder

管理configuration

package com.linnine.mybatis.builder;

public abstract class BaseBuilder {

    protected final Configuration configuration;

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

    public Configuration getConfiguration() {
        return configuration;
    }
}

Configuration

配置管理类, 主要是管理mapper的注册,以及statement 也就是sql的相关信息.

package com.linnine.mybatis.session;

public class Configuration {

    protected MapperRegistry mapperRegistry = new MapperRegistry(this);

    protected final Map<String, MappedStatement>  mappedStatements = new HashMap();

    public <T> void addMapper(Class<T> type){
        mapperRegistry.addMapper(type);
    }

    public <T> T getMapper(Class<T> type,SqlSession sqlSession){
        return mapperRegistry.getMapper(type,sqlSession);
    }


    public void addMappedStatement(MappedStatement ms) {
        mappedStatements.put(ms.getId(), ms);
    }

    public MappedStatement getMappedStatement(String id) {
        return mappedStatements.get(id);
    }

}

MapperRegistry

对映射代理工厂进行管理

package com.linnine.mybatis.binding;

/**
 * 再对映射代理工厂进行管理
 */
public class MapperRegistry {

    // 注册 代理映射工厂
    private final Map<Class<?>,MapperProxyFactory<?>> knownMappers = new HashMap<>();

    private Configuration configuration;

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


    /**
     * 获取代理类
     * @param type
     * @param sqlSession
     * @param <T>
     * @return
     */
    public <T> T getMapper(Class<T> type, SqlSession sqlSession){
        // 获取代理工厂
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        if (mapperProxyFactory == null){
            throw new RuntimeException("Type " + type + " is not known to the MapperRegistry.");
        }
        try {
            //通过代理工厂生产代理类
            return mapperProxyFactory.newInstance(sqlSession);
        }catch (Exception e){
            throw new RuntimeException("Error getting mapper instance. Cause: " + e, e);
        }
    }

    public <T> void addMapper(Class<T> type){
        //只注册接口
        if (type.isInterface()){
            //如果已存在不添加
            if (hasMapper(type)){
                throw new RuntimeException("Type " + type + " is already known to the MapperRegistry.");
            }
            //根据类型创建一个代理工厂后注册
            knownMappers.put(type,new MapperProxyFactory<>(type));
        }
    }

    /**
     * 通过包名扫描添加
     * @param packageName
     */
    public void addMappers(String packageName){
        Set<Class<?>> mapperSet = ClassScanner.scanPackage(packageName);
        for (Class<?> mapperClass : mapperSet) {
            addMapper(mapperClass);
        }
    }

    public <T> boolean hasMapper(Class<T> type) {
        return knownMappers.containsKey(type);
    }
}

mybatis里好多循环依赖套来套去的 特别是configuration 吐了.

DefaultSqlSession

改造一下sql会话
package com.linnine.mybatis.session;

public class DefaultSqlSession implements  SqlSession{

    private final Configuration configuration;

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


    /**
     * 在这个类里统一代理
     * @param statement
     * @param parameter
     * @param <T>
     * @return
     */
    @Override
    public <T> T selectOne(String statement, Object parameter) {
        MappedStatement mappedStatement = configuration.getMappedStatement(statement);
        return (T)("你被代理了!"+"方法:"+statement+",入参:"+parameter+"\n待执行sql: "+ mappedStatement.getSql());
    }

    /**
     * 从注册中心拿
     * @param type
     * @param <T>
     * @return
     */
    @Override
    public <T> T getMapper(Class<T> type) {
        //返回代理类,并返回代理方法的处理结果
        return configuration.getMapper(type,this);
    }
}

DefaultSqlSessionFactory

改造一下工厂

package com.linnine.mybatis.session;

public class DefaultSqlSessionFactory implements SqlSessionFactory{

    private Configuration config;

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

    @Override
    public SqlSession openSqlSession() {
        return new DefaultSqlSession(config);
    }
}

SqlCommandType

sql操作类型

package com.linnine.mybatis.binding.mapping;

public enum SqlCommandType {
    /**
     * 未知
     */
    UNKNOWN,
    /**
     * 插入
     */
    INSERT,
    /**
     * 更新
     */
    UPDATE,
    /**
     * 删除
     */
    DELETE,
    /**
     * 查找
     */
    SELECT;

}

mybatis-config-datasource.xml

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>

    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>

</configuration>

UserMapper.xml

<mapper namespace="com.linnine.mybatis.dao.IUserDao">

    <select id="queryUserInfoById" parameterType="java.lang.String" resultType="java.lang.String">
        SELECT id, userId, userHead, createTime
        FROM user
        where id = #{id}
    </select>

</mapper>

IUserDao

package com.linnine.mybatis.dao;

public interface IUserDao {
String queryUserInfoById(String uId);
}

开测

@Test
public void testMapperProxyFactory() throws IOException {
    Reader reader = Resources.getResourceAsReader("mybatis-config-datasource.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().builder(reader);
    SqlSession sqlSession = sqlSessionFactory.openSqlSession();
    IUserDao mapper = sqlSession.getMapper(IUserDao.class);
    String user = mapper.queryUserInfoById("10001");
    System.out.println(user);
}

在这里插入图片描述

这里模拟了方法代理,和sql解析映射,日志打印

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值