【手写MyBatis】(03)- 项目搭建及配置文件加载

手写MyBatis框架

需求定义

使用手写框架,根据用户ID查询用户信息

项目搭建

框架代码结构图

在这里插入图片描述

设计配置文件

全局配置文件

<configuration>
    <!-- MyBatis 数据源环境配置 -->
    <environments default="dev">
        <environment id="dev">
            <!-- 配置数据源信息 -->
            <dataSource type="DBCP">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://192.168.152.100:3306/ssm" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments>
    <!-- 映射文件加载 -->
    <mappers>
        <!-- resource指定映射文件的类路径 -->
        <mapper resource="mapper/UserMapper.xml" />
    </mappers>
</configuration>

mapper.xml文件

<mapper namespace="test">
	<!-- select标签,封装了SQL语句信息、入参类型、结果映射类型 -->
	<select id="findUserById"
			parameterType="com.osmond.mybatis.po.User"
			resultType="com.osmond.mybatis.po.User"
			statementType="prepared">

		SELECT * FROM user WHERE id = #{id} AND username like '%${username}'
		<if test="username != null and username !='' ">
			AND username like '%${username}'
			<if test="username != null and username !=''">
				AND 1=1
			</if>
		</if>
	</select>
</mapper>

Code:解析全局配置文件

定义Configuration类与主解析类

当前我们只需要从全局配置文件中解析出标签下的数据库连接配置,因此先在Configuration.java中定义DataSource对象。

public class Configuration {

	private DataSource dataSource;

	public DataSource getDataSource() {
		return dataSource;
	}

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
}

将全局配置文件的解析放在一个单独的类中进行处理:XMLConfigParser.java,该类最后是将整个全局配置文件的内容解析成Configuration,因此我们在类中定义Configuration。

我们假定在全局配置文件解析前,会通过一系列手段转换成Element对象(使用Dom4j可以使xml文件转换成Element).所以parse的方法入参为Element。
在这里插入图片描述

public class XMLConfigParser {

    private Configuration configuration;

    public XMLConfigParser() {
        configuration = new Configuration();
    }

    /**
     *
     * @param rootElement
     *            <configuration>
     * @return
     */
    public Configuration parse(Element rootElement) {
        parseEnvironments(rootElement.element("environments"));
        parseMappers(rootElement.element("mappers"));
        return configuration;
    }
}

解析标签

标签中需要解析:

  • default属性
  • N多个标签(只解析id与environments标签的default一致的那个)

在这里插入图片描述

public class XMLConfigParser {
    
    ...
    
    /**
     * 解析environments标签
     * @param element
     *            <environments>
     */
    private void parseEnvironments(Element element) {
        String defaultEnvId = element.attributeValue("default");
        if (defaultEnvId == null || "".equals(defaultEnvId)) {
            return;
        }
        List<Element> elements = element.elements("environment");
        for (Element envElement : elements) {
            String envId = envElement.attributeValue("id");
            // 判断defaultEnvId和envId是否一致,一致再继续解析
            if (defaultEnvId.equals(envId)) {
                parseEnvironment(envElement);
            }
        }
    }
    
}

解析标签

标签内部只包含一个标签,标签下包含多个标签。这些标签解析后得到一个Properties对象,我们使用Properties对象来组装DataSource对象,并放入Configuration对象中。

在这里插入图片描述

public class XMLConfigParser {
    
    ...
    
    /**
     * 解析environment标签
     * @param envElement
     *            <environment>
     */
    private void parseEnvironment(Element envElement) {
        Element dataSourceEnv = envElement.element("dataSource");

        String type = dataSourceEnv.attributeValue("type");
        type = type == null || type.equals("") ? "DBCP" : type;
        if ("DBCP".equals(type)) {
            Properties properties = parseProperty(dataSourceEnv);

            BasicDataSource dataSource = new BasicDataSource();
            dataSource.setDriverClassName(properties.getProperty("driver"));
            dataSource.setUrl(properties.getProperty("url"));
            dataSource.setUsername(properties.getProperty("username"));
            dataSource.setPassword(properties.getProperty("password"));

            // 将解析出来的DataSource对象,封装到Configuration对象中
            configuration.setDataSource(dataSource);
        }

    }
    
    /**
     * 解析dataSource标签中的多个property
     * @param dataSourceEnv
     * @return
     */
    private Properties parseProperty(Element dataSourceEnv) {
        Properties properties = new Properties();
        List<Element> elements = dataSourceEnv.elements("property");
        for (Element element : elements) {
            String name = element.attributeValue("name");
            String value = element.attributeValue("value");

            properties.put(name, value);
        }
        return properties;
    }
    
}

至此,Configuration对象中的DataSource属性解析完毕了。

解析标签

标签中包含多个标签,其resource属性指向了一个Mapper.xml的路径信息,因此我们读取到每个resource就可以通过一定方式获取到Mapper.xml的内容,进而转化成Document去解析。

在parseMapper方法中,我们通过定义Resources工具类获得了标签的resource属性指向的xml文件的IO流对象,随后使用DocumentUtils将IO流对象转换为了Document。

在这里插入图片描述

public class XMLConfigParser {

    ...

    /**
     * 解析mappers子标签,最终该标签会去解析每个映射文件
     *
     * @param element
     */
    private void parseMappers(Element element) {
        List<Element> elements = element.elements("mapper");

        for (Element mapperElement : elements) {
            parseMapper(mapperElement);
        }
    }

    /**
     * 解析多个mapper标签
     * @param mapperElement
     */
    private void parseMapper(Element mapperElement) {
        // 获取映射文件的路径
        String resource = mapperElement.attributeValue("resource");
        // 获取指定路径的IO流
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 获取映射文件对应的Document对象
        Document document = DocumentUtils.readDocument(inputStream);
        // 按照mapper标签语义去解析Document
        ...
    }
}

资源文件转换工具类

/**
 * 资源文件转换
 * 单独提出一个工具类意义在于可以更多元化提供类似的需求
 */
public class Resources {

	/**
	 * 转换成InputStream
	 * @param resource
	 * @return
	 */
	public static InputStream getResourceAsStream(String resource) {
		return Resources.class.getClassLoader().getResourceAsStream(resource);
	}

	/**
	 * 转换成Reader
	 * @param resource
	 * @return
	 */
	public static Reader getResourceAsReader(String resource) {
	    // 仅做演示,未实现
		return null;
	}
}

IO流转Document工具类

/**
 * Document转换工具类,基于Dom4J
 */
public class DocumentUtils {

	public static Document readDocument(InputStream inputStream) {
		try {
			SAXReader saxReader = new SAXReader();
			Document document = saxReader.read(inputStream);
			return document;
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		return null;
	}

}

Test:全局配置文件读取

添加依赖

创建新项目,引入手写框架依赖及JUnit依赖

 <dependencies>
    <!-- 单元测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!-- 手写MyBatis框架 -->
    <dependency>
        <groupId>com.osmond</groupId>
        <artifactId>03-mybatis-freamwork-handwritten</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

编写测试类

测试类中同样使用手写框架中的Resources工具类及DocumentUtils工具类来获取全局配置文件的Document对象。调用XMLConfigParser的parse方法进行解析:

public class UserDaoTest {

    @Test
    public void testInitConfiguration() throws Exception {
        // 指定全局配置文件路径
        String resource = "SqlMapConfig.xml";
        // 获取指定路径的IO流
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 获取Document对象
        Document document = DocumentUtils.readDocument(inputStream);
        // 解析Document获取Configuration对象
        XMLConfigParser configParser = new XMLConfigParser();
        // <configuration>
        Configuration configuration = configParser.parse(document.getRootElement());
        System.out.println(configuration);
    }
}

debug方式运行,断点打到最后一个println方法上,查看Configuration对象内容如下:

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值