前言
你是怎么面对功能迭代的?
很多程序员在刚开始做编程或者新加入一家公司时,都没有多少机会可以做一个新项目,大部分时候都是在老项目上不断的迭代更新。在这个过程你可能要学习N个前人留下的各式各样的风格迥异的代码片段,在这些纵横交错的流程中,找到一席之地,把自己的ifelse加进去。
但说回来,其实不能逐步清理一片屎山,让代码在你的手上逐步清晰、整洁、干净,很多时候也是作为码农自身经验的不足,不懂得系统重构、不了解设计原则、不熟悉业务背景、不清楚产品走向等等原因造成的。所以最好的办法是提升自身的能力,每接到一次需求都有一些技术上的改变,既然它是屎山,那就当做打怪升级了,修一点、改一块、补一片,总会在你手上越来越易于维护和扩展的。
设计
上一章节我们使用了 MapperRegistry 对包路径进行扫描注册映射器,并在 DefaultSqlSession 中进行使用。那么在我们可以把这些命名空间、SQL描述、映射信息统一维护到每一个 DAO 对应的 Mapper XML 的文件以后,其实 XML 就是我们的源头了。通过对 XML 文件的解析和处理就可以完成 Mapper 映射器的注册和 SQL 管理。
XML 解析和注册类实现关系
1:SqlSessionFactoryBuilder 作为整个 Mybatis 的入口,提供建造者工厂,包装 XML 解析处理,并返回对应 SqlSessionFactory 处理类。
2:通过解析把 XML 信息注册到 Configuration 配置类中,再通过传递 Configuration 配置类到各个逻辑处理类里,包括 DefaultSqlSession 中,这样就可以在获取映射器和执行SQL的时候,从配置类中拿到对应的内容了。
构建SqlSessionFactory建造者工厂
package com.lm.mybatis.session;
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) {
XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(reader);
return build(xmlConfigBuilder.parse());
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
package com.lm.mybatis.builder.xml;
import com.lm.mybatis.builder.BaseBuilder;
import org.dom4j.Element;
public class XMLConfigBuilder extends BaseBuilder {
private Element root;
public XMLConfigBuilder(Reader reader) {
// 1. 调用父类初始化Configuration
super(new Configuration());
// 2. dom4j 处理 xml
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(new InputSource(reader));
root = document.getRootElement();
} catch (DocumentException e) {
e.printStackTrace();
}
}
/**
* 解析配置;类型别名、插件、对象工厂、对象包装工厂、设置、环境、类型转换、映射器
*
* @return Configuration
*/
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");
// SELECT
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);
}
// 注册Mapper映射器
configuration.addMapper(Resources.classForName(namespace));
}
}
}
package com.lm.mybatis.io;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
public class Resources {
public static Reader getResourceAsReader(String resource) throws IOException {
return new InputStreamReader(getResourceAsStream(resource));
}
private static InputStream getResourceAsStream(String resource) throws IOException {
ClassLoader[] classLoaders = getClassLoaders();
for (ClassLoader classLoader : classLoaders) {
InputStream inputStream = classLoader.getResourceAsStream(resource);
if (null != inputStream) {
return inputStream;
}
}
throw new IOException("Could not find resource " + resource);
}
private static ClassLoader[] getClassLoaders() {
return new ClassLoader[]{
ClassLoader.getSystemClassLoader(),
Thread.currentThread().getContextClassLoader()};
}
public static Class<?> classForName(String className) throws ClassNotFoundException {
return Class.forName(className);
}
}
package com.lm.mybatis.mapping;
import com.lm.mybatis.session.Configuration;
import java.util.Map;
public class MappedStatement {
private Configuration configuration;
private String id;
private SqlCommandType sqlCommandType;
private String parameterType;
private String resultType;
private String sql;
private Map<Integer, String> parameter;
MappedStatement() {
// constructor disabled
}
/**
* 建造者
*/
public static class Builder {
private MappedStatement mappedStatement = new MappedStatement();
public Builder(Configuration configuration, String id, SqlCommandType sqlCommandType, String parameterType, String resultType, String sql, Map<Integer, String> parameter) {
mappedStatement.configuration = configuration;
mappedStatement.id = id;
mappedStatement.sqlCommandType = sqlCommandType;
mappedStatement.parameterType = parameterType;
mappedStatement.resultType = resultType;
mappedStatement.sql = sql;
mappedStatement.parameter = parameter;
}
public MappedStatement build() {
assert mappedStatement.configuration != null;
assert mappedStatement.id != null;
return mappedStatement;
}
}
public Configuration getConfiguration() {
return configuration;
}
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public SqlCommandType getSqlCommandType() {
return sqlCommandType;
}
public void setSqlCommandType(SqlCommandType sqlCommandType) {
this.sqlCommandType = sqlCommandType;
}
public String getParameterType() {
return parameterType;
}
public void setParameterType(String parameterType) {
this.parameterType = parameterType;
}
public String getResultType() {
return resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
public Map<Integer, String> getParameter() {
return parameter;
}
public void setParameter(Map<Integer, String> parameter) {
this.parameter = parameter;
}
}
package com.lm.mybatis.mapping;
public enum SqlCommandType {
/**
* 未知
*/
UNKNOWN,
/**
* 插入
*/
INSERT,
/**
* 更新
*/
UPDATE,
/**
* 删除
*/
DELETE,
/**
* 查找
*/
SELECT;
}
<?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.lm.test.dao.IUserDao">
<select id="queryUserInfoById" parameterType="java.lang.Long" resultType="com.lm.test.pojo.User">
SELECT id, userId, userHead, createTime
FROM user
where id = #{id}
</select>
</mapper>
<?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/User_Mapper.xml"/>
</mappers>
</configuration>
package com.lm.test;
import com.lm.mybatis.binding.MapperProxyFactory;
import com.lm.mybatis.binding.MapperRegistry;
import com.lm.mybatis.io.Resources;
import com.lm.mybatis.session.SqlSession;
import com.lm.mybatis.session.SqlSessionFactory;
import com.lm.mybatis.session.SqlSessionFactoryBuilder;
import com.lm.mybatis.session.defaults.DefaultSqlSessionFactory;
import com.lm.test.dao.IUserDao;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.Map;
public class TestApi {
private Logger logger = LoggerFactory.getLogger(TestApi.class);
@Test
public void test_SqlSessionFactory() throws IOException {
// 1. 从SqlSessionFactory中获取SqlSession
Reader reader = Resources.getResourceAsReader("mybatis-config-datasource.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
// 2. 获取映射器对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
// 3. 测试验证
String res = userDao.queryUserInfoById("10001");
logger.info("测试结果:{}", res);
}
}
package com.lm.test.pojo;
import java.util.Date;
public class User {
private Long id;
private String userId; // 用户ID
private String userHead; // 头像
private Date createTime; // 创建时间
private Date updateTime; // 更新时间
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserHead() {
return userHead;
}
public void setUserHead(String userHead) {
this.userHead = userHead;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
好了到这里就结束了手写mybatis之Mapper XML的解析和注册使用的学习,大家一定要跟着动手操作起来。需要的源码的 可si我获取;