Mybatis(2.1)手写Mybatis核心流程2.0版本

手写mybatis2.0版本(面向过程)

配置和代码分离,配置是XML文件,代码是使用面向过程去编写

手写mybatis的2.0版本(配置和代码分离,配置是XML文件,代码是使用面向过程去编写)

核心流程

	a)加载XML配置文件(XML如何配置,我们直接是参考的mybatis的配置方式)
		* 【解析全局配置文件】(配置的是全应用范围内一次性的配置信息)
			** 数据源的配置信息(driver、url、username、password)----最终解析出DataSource对象,并封装到Configuration对象中
			
			** 映射文件的信息
			
	* 【解析映射文件】SqlSource和SqlNode,都是为了去存储数据,并且对存储的数据对外提供了一些操作。
			** 解析select标签(statement标签)---封装【MappedStatement】对象,将id、parameterType、ResultType、statementType
			** 解析select标签中的SQL脚本信息(script)-----封装成【SqlSource对象】---->【SqlNode】
				*** DynamicSqlSource和RawSqlSource区别:
					**** DynamicSqlSource的SqlNode集合信息【解析】工作是发生在【每一次调用getBoundSql方法的时候】
					**** RawSqlSource(#{}也就是替换为?占位符)的SqlNode集合信息【解析】工作是发生在【第一次构造RawSqlSource的时候】,因为被RawSqlSource封装的节点信息,【只需要被解析一次】
					
	
	b)JDBC执行代码(如果需要配置文件的信息,则需要从【Configuration】对象中获取)
		* 【获取连接】
			** 使用数据源对象去优化连接的创建(DBCP,需要添加第三方的依赖),并且将该信息配置到XML文件中
			** 需要通过Configuration对象,去获取解析出来的【DataSource】对象信息
		
		* 【获取SQL】
			** 拼接SQL语句:将所有的【SqlNode】(【SqlSource】(里边存储了)--->SqlNode集合(树状结构)中保存的信息,都【拼接】到StringBuffer对象中。在拼接的过程中,会将【${}】解决掉。
				*** SqlNode中包含SqlNode,SqlNode中包含SqlNode集合
			
			** 解析SQL语句:将完成的SQL语句中的【#{}】处理掉,在解析之后,会形成两部分数据【最终的SQL语句、#{}中的属性名称】,将信息封装到StaticSqlSource中
				Sqlsource-->就是SQL源(里边存储的就是SQL信息)-->SQL信息就是以SQLNode这种形态呈现出来的
			** 通过SqlSource获取BoundSql(jdbc可执行的SQl语句,参数信息集合)
			** 通过【BoundSql】,获取里面存储的SQL语句
				*** 【BoundSql】将最终的【SQL语句(可执行的)】和【该SQL语句中解析出来的参数信息】,绑定到一起,方便后边一起使用。
					select * from user where id  =#{id} and name = #{name}
					
					BoundSql(
						select * from user where id  = ? and name = ?
						List<ParameterMapping>    
							id和name这两个【参数名称】
							除了参数名称,还需要封装【参数类型】
					)
		* 【创建Statement】
			** 通过【MappedStatement】对象获取statementType
		* 【设置参数】
			** 通过【MappedStatement】对象获取入参类型(简单类型、引用类型)
			** 如果是引用类型,则需要SQL解析过程中,产生的参数信息(ParameterMapping集合信息),我要根据这个参数信息,去入参对象获取指定属性值
			** 调用statment.setString(1,"zhangsan")
		* 【执行Statement】
		* 【处理结果】
			** 通过【MappedStatement】对象输出映射类型
			** 通过反射给输出映射类型对应的对象,去设置属性值(通过resultSet结果集中的每一列中来)

目的是使用XML来表达mybatis的全局配置信息,和业务相关的SQL映射信息 (映射文件) 其次,优化数据连接的创建(使用连接池)

全局配置文件

<configuration>
	<!-- mybatis 数据源环境配置 -->
	<environments default="dev">
		<environment id="dev">
			<!-- 配置数据源信息 -->
			<dataSource type="DBCP">
				<property name="driver" value="com.mysql.jdbc.Driver"></property>
				<property name="url"
					value="jdbc:mysql://XXX"></property>
				<property name="username" value="XXX"></property>
				<property name="password" value="XXX"></property>
			</dataSource>
		</environment>
	</environments>

	<!-- 映射文件加载 -->
	<mappers> 
		<!-- resource指定映射文件的类路径 -->
		<mapper resource="mapper/UserMapper.xml"></mapper>
		<!-- <mapper resource="mapper/UserMapper.xml"></mapper> -->
		<!-- <mapper resource="mapper/UserMapper.xml"></mapper> -->
		<!-- <mapper resource="mapper/UserMapper.xml"></mapper> -->
		<!-- <mapper resource="mapper/UserMapper.xml"></mapper> -->
	</mappers>
</configuration>

映射文件

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

		<!-- and sex = #{sex} 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>

类图
在这里插入图片描述
在这里插入图片描述

核心流程代码

public class TestMybatis2 {

	private Configuration configuration = new Configuration();

	private String namespace;

	//${}和动态标签都是一种字符串拼接的操作,这种操作认为是一种动态的SQL信息
	private boolean isDynamic;

	/**
	 * 加载全局配置文件,然后将数据封装到Configuration对象中
	 * 
	 * @return
	 */
	public void loadConfiguration(String location) {
		// 读取指定路径的配置文件
		InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(location);
		// 根据InputStream,创建Document对象,使用的是sax解析
		Document document = DocumentUtils.readDocument(inputStream);
		// 按照XML标签中的语义去进行解析
		parseConfiguration(document.getRootElement());
	}

	//解析全局配置文件
	private void parseConfiguration(Element rootElement) {
		Element environments = rootElement.element("environments");
		parseEnv(environments);

		Element mappers = rootElement.element("mappers");
		parseMappers(mappers);
	}

	//解析mappers标签 遍历每个mapper标签
	private void parseMappers(Element mappers) {
		List<Element> mapperElements = mappers.elements("mapper");
		for (Element mapperElement : mapperElements) {
			parseMapper(mapperElement);
		}
	}

	//解析mapper标签 然后通过流 读到映射文件 
	private void parseMapper(Element mapperElement) {
		String resource = mapperElement.attributeValue("resource");

		InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(resource);
		Document document = DocumentUtils.readDocument(inputStream);
		parseXmlMapper(document.getRootElement());
	}

	//解析映射文件 遍历解析select标签
	private void parseXmlMapper(Element rootElement) {
		// 为了方便管理statement,需要使用statement唯一标识
		namespace = rootElement.attributeValue("namespace");

		List<Element> selectElements = rootElement.elements("select");
		for (Element selectElement : selectElements) {
			parseStatementElement(selectElement);
		}

	}

	//解析每一个select标签  将SQL信息封装到sqlsource里
	private void parseStatementElement(Element selectElement) {
		String statementId = selectElement.attributeValue("id");

		if (statementId == null || selectElement.equals("")) {
			return;
		}
		// 一个CURD标签对应一个MappedStatement对象
		// 一个MappedStatement对象由一个statementId来标识,所以保证唯一性
		// statementId = namespace + "." + CRUD标签的id属性
		statementId = namespace + "." + statementId;

		String parameterType = selectElement.attributeValue("parameterType");
		Class<?> parameterClass = resolveType(parameterType);

		String resultType = selectElement.attributeValue("resultType");
		Class<?> resultClass = resolveType(resultType);

		String statementType = selectElement.attributeValue("statementType");
		statementType = statementType == null || statementType == "" ? "prepared" : statementType;

		// 解析SQL信息
		SqlSource sqlSource = createSqlSource(selectElement);

		// TODO 建议使用构建者模式去优化
		MappedStatement mappedStatement = new MappedStatement(statementId, parameterClass, resultClass, statementType,
				sqlSource);
		configuration.addMappedStatement(statementId, mappedStatement);

	}

	//创建sqlsource 对象 用来封装  封装了不同SQL信息的SQLnode  
	private SqlSource createSqlSource(Element selectElement) {
		// 去解析select等CURD标签中的SQL脚本信息
		SqlSource sqlSource = parseScriptNode(selectElement);
		return sqlSource;
	}

	//解析crud标签中的SQL脚本信息 对不同类型的SQL信息 分别封装到不同实现sqlsource(接口)对象中
	private SqlSource parseScriptNode(Element selectElement) {
		// 1.解析出来所有的SqlNode信息
		MixedSqlNode rootSqlNode = parseDynamicTags(selectElement);
		// isDynamic是parseDynamicTags过程中,得到的值
		// 如果包含${}或者包含动态标签,则isDynamic为true
		SqlSource sqlSource;
		// 2.将SqlNode信息封装到SqlSource中,并且要根据是否动态节点去选择不同的SqlSource
		// 如果isDynamic为true,则说明SqlNode集合信息里面包含${}SqlNode节点信息或者动态标签的节点信息
		if (isDynamic) {
			sqlSource = new DynamicSqlSource(rootSqlNode);
		} else {
			sqlSource = new RawSqlSource(rootSqlNode);
		}

		return sqlSource;
	}

	//解析(select标签) sql信息大集合(包括select子标签 和文本信息),将未分类的sqlnode从select标签中区分出来 以便对应的封装到不同的sqlsource中
	private MixedSqlNode parseDynamicTags(Element selectElement) {
		List<SqlNode> sqlNodes = new ArrayList<>();
		int nodeCount = selectElement.nodeCount();
		// 获取select标签的子标签
		for (int i = 0; i < nodeCount; i++) {
			Node node = selectElement.node(i);
			// 区分是文本标签还是元素标签
			if (node instanceof Text) {
				String sqlText = node.getText().trim();// trim 此字符串移除了前导和尾部空白的副本
				if (sqlText == null || "".equals(sqlText)) {
					continue;
				}
				TextSqlNode textSqlNode = new TextSqlNode(sqlText);
				if (textSqlNode.isDynamic()) {//判断是否含有${}
					// 设置是否动态为true
					isDynamic = true;
					sqlNodes.add(textSqlNode);
				} else {
					sqlNodes.add(new StaticTextSqlNode(sqlText));
				}
			} else if (node instanceof Element) {
				// 获取动态标签的标签名称
				String nodeName = node.getName();
				// TODO 设计模式优化
				if ("if".equals(nodeName)) {
					// 封装成IfSqlNode(test信息、子标签信息)
					Element element = (Element) node;
					// if标签的test属性信息
					String test = element.attributeValue("test");
					// if标签的子标签信息
					MixedSqlNode rootSqlNode = parseDynamicTags(element);

					// IfSqlNode就是封装if标签信息的
					IfSqlNode ifSqlNode = new IfSqlNode(test, rootSqlNode);
					sqlNodes.add(ifSqlNode);
				}
				isDynamic = true;
			}
		}

		return new MixedSqlNode(sqlNodes);
	}

	private Class<?> resolveType(String parameterType) {
		try {
			Class<?> clazz = Class.forName(parameterType);
			return clazz;
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}

	private void parseEnv(Element environments) {
		String def = environments.attributeValue("default");
		List<Element> elements = environments.elements("environment");
		for (Element envElement : elements) {
			String id = envElement.attributeValue("id");
			if (id.equals(def)) {
				parseEnvironment(envElement);
			}
		}

	}

	private void parseEnvironment(Element envElement) {
		Element dataSource = envElement.element("dataSource");
		parseDataSource(dataSource);

	}

	private void parseDataSource(Element dataSourceElement) {
		String type = dataSourceElement.attributeValue("type");
		if ("DBCP".equals(type)) {
			BasicDataSource dataSource = new BasicDataSource();

			Properties properties = new Properties();

			List<Element> propertyElements = dataSourceElement.elements();
			for (Element prop : propertyElements) {
				String name = prop.attributeValue("name");
				String value = prop.attributeValue("value");
				properties.put(name, value);
			}

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

			configuration.setDataSource(dataSource);
		}

	}
	public List<Object> queryByJDBC(Configuration configuration, String statementId, Object param) {
		Connection connection = null;
		PreparedStatement preparedStatement = null;
		ResultSet rs = null;

		try {

			// 1、获取Connection
			// 此处使用DataSource去优化connection的获取
			// DataSource是通过XML配置来产生的
			// XML信息是通过Configuration对象保存
			connection = getConnection(configuration);

			// 2、获取可以JDBC执行的SQL语句
			// 【SQL信息】是配置在映射文件中的,是通过【select等statement标签】来配置的
			// 不同脚本的【SQL信息】,是封装到不同类型的【SqlNode】对象中
			// 而【SqlNode】信息是保存到【SqlSource】中的
			// 【SqlSource】的信息是封装到【MappedStatement】对象中
			// 【MappedStatement】对象被保存到【Configuration】对象中的
			MappedStatement mappedStatement = configuration.getMappedStatementById(statementId);

			if (mappedStatement == null) {
				return null;
			}
			// 获取SqlSource
			SqlSource sqlSource = mappedStatement.getSqlSource();
			// 执行SqlSource去获取Sql信息
			BoundSql boundSql = sqlSource.getBoundSql(param);
			// 获取JDBC可以直接执行的SQL语句
			String sql = boundSql.getSql();

			// 需要获取statament的类型,这个类型是保存到MappedStatement对象中的
			String statementType = mappedStatement.getStatementType();

			if ("prepared".equals(statementType)) {
				// 3、获取statement
				preparedStatement = connection.prepareStatement(sql);//创建一个 PreparedStatement 对象来将参数化的 SQL 语句发送到数据库
				// 4、设置参数
				handleParameter(preparedStatement, mappedStatement, boundSql, param);

				// 5、向数据库发出 sql 执行查询,查询出结果集
				rs = preparedStatement.executeQuery();//在此 PreparedStatement 对象中执行 SQL 查询,并返回该查询生成的 ResultSet 对象。

				// 6、处理结果集
				return handleResultSet(rs, mappedStatement);

			} else if ("simple".equals(statementType)) {
				// 创建简单的Statement对象....
			} // ....

			return null;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 释放资源
			if (rs != null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if (preparedStatement != null) {
				try {
					preparedStatement.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if (connection != null) {
				try {
					connection.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block e.printStackTrace();
				}
			}
		}

		return null;
	}

	private List<Object> handleResultSet(ResultSet rs, MappedStatement mappedStatement) throws Exception {
		List<Object> results = new ArrayList<Object>();
		Class<?> resultTypeClass = mappedStatement.getResultTypeClass();
		while (rs.next()) {
			// 遍历一次是一行,也对应一个对象,利用反射new一个对象
			Object result = resultTypeClass.newInstance();//创建此 Class 对象所表示的类的一个新实例

			// 要获取每一列的值,然后封装到结果对象中对应的属性名称上
			//ResultSetMetaData 可用于获取关于 ResultSet 对象中列的类型和属性信息的对象
			ResultSetMetaData metaData = rs.getMetaData();//获取此 ResultSet 对象的列的编号、类型和属性
			int columnCount = metaData.getColumnCount();//返回此 ResultSet 对象中的列数。 
			for (int i = 0; i < columnCount; i++) {
				// 获取每一列的值   Object 的形式获取此 ResultSet 对象的当前行中指定列的值。 
				Object value = rs.getObject(i + 1);

				// 列的名称
				String columnName = metaData.getColumnName(i + 1);
				// 列名和属性名称要严格一致
				Field field = resultTypeClass.getDeclaredField(columnName);//返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段
				field.setAccessible(true);
				// 给映射的对象赋值
				field.set(result, value);
			}
			results.add(result);
		}

		return results;
	}
	
		//参数处理
	private void handleParameter(PreparedStatement preparedStatement, MappedStatement mappedStatement,
			BoundSql boundSql, Object param) throws Exception {
		// 从mappedStatement获取入参的类型
		Class<?> parameterTypeClass = mappedStatement.getParameterTypeClass();
		// 如果入参是8种基本类型和String类型
		if (SimpleTypeRegistry.isSimpleType(parameterTypeClass)) {
			preparedStatement.setObject(1, param);
		} else if (parameterTypeClass == Map.class) {
			// 如果入参是Map类型
			// ....
		} else {
			// 如果入参是POJO类型(比如User类型)
			List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
			for (int i = 0; i < parameterMappings.size(); i++) {
				ParameterMapping parameterMapping = parameterMappings.get(i);
				// 封装的#{}里面的属性名称
				String name = parameterMapping.getName();
				// 利用反射去入参对象根据属性名称获取指定的属性值
				Field field = parameterTypeClass.getDeclaredField(name);//返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段
				field.setAccessible(true);//值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查
				Object value = field.get(param);
				//参数:obj - 从中提取所表示字段的值的对象  返回:对象 obj 中的所表示字段的值;在返回之前,基值包装在一个适当的对象中
				// TODO 可以使用ParameterMapping里面的type对Object类型的value进行类型处理
				// 设置statement占位符中的值
				preparedStatement.setObject(i + 1, value);
			}
		}

	}

	private Connection getConnection(Configuration configuration) throws Exception {
		DataSource dataSource = configuration.getDataSource();
		Connection connection = dataSource.getConnection();//尝试建立与此 DataSource 对象所表示的数据源的连接。 
		return connection;
	}
	@Test
	public void test() throws Exception {
		// 第一步:加载XML配置文件,包括全局配置文件和映射文件
		loadConfiguration("mybatis-config.xml");

		String statementId = "test" + "." + "findUserById";
		User user = new User();
		user.setId(1);
		// 第二步:执行JDBC代码,并返回已经映射的结果
		List<Object> list = queryByJDBC(configuration, statementId, user);
		for (Object object : list) {
			User u = (User) object;
			System.out.println(u);
		}
	}
}

Configuration类和MappedStatement代码

Configuration类

  • 封装XML配置文件中的信息 封装mybatis-config.xml和*mapper.xml中存储的数据
public class Configuration {

	// 封装MappedStatement对象集合
	private Map<String, MappedStatement> mappedStatements = new HashMap<String, MappedStatement>();
	
	// 封装数据源对象
	private DataSource dataSource;

	public MappedStatement getMappedStatementById(String statementId) {
		return this.mappedStatements.get(statementId);
	}

	public void addMappedStatement(String statementId, MappedStatement mappedStatement) {
		this.mappedStatements.put(statementId, mappedStatement);
	}

	public DataSource getDataSource() {
		return dataSource;
	}

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

MappedStatement类

  • 用来封装映射文件中的CRUD标签脚本内容,比如select标签
public class MappedStatement {

	private String statementId;
	private SqlSource sqlSource;

	private String statementType;

	private Class<?> parameterTypeClass;
	private Class<?> resultTypeClass;

	public MappedStatement(String statementId, Class<?> parameterTypeClass, Class<?> resultTypeClass,
			String statementType, SqlSource sqlSource) {
		this.statementId = statementId;
		this.parameterTypeClass = parameterTypeClass;
		this.resultTypeClass = resultTypeClass;
		this.statementType = statementType;
		this.sqlSource = sqlSource;
	}

	public SqlSource getSqlSource() {
		return sqlSource;
	}

	public void setSqlSource(SqlSource sqlSource) {
		this.sqlSource = sqlSource;
	}

	public String getStatementType() {
		return statementType;
	}

	public void setStatementType(String statementType) {
		this.statementType = statementType;
	}

	public String getStatementId() {
		return statementId;
	}

	public void setStatementId(String statementId) {
		this.statementId = statementId;
	}

	public Class<?> getParameterTypeClass() {
		return parameterTypeClass;
	}

	public void setParameterTypeClass(Class<?> parameterTypeClass) {
		this.parameterTypeClass = parameterTypeClass;
	}

	public Class<?> getResultTypeClass() {
		return resultTypeClass;
	}

	public void setResultTypeClass(Class<?> resultTypeClass) {
		this.resultTypeClass = resultTypeClass;
	}
}

SqlNode接口及其实现类代码

SQLNode接口

  • 1、封装SQL节点的信息
  • 2、提供对封装的SQL节点的解析功能
public interface SqlNode {
	/**
	 * 解析功能
	 * 最终解析出来的SQL语句,封装到DynamicContext中的StringBuffer对象中
	 * 解析的时候,可能要用到入参对象
	 * 
	 * 此时解析出来的SQL语句,只是根据动态标签的逻辑,完成了字符串的拼接,它还没有被解析
	 * @param context
	 */
	void apply(DynamicContext context);
}

DynamicContext 类

  • 动态上下文,就是封装的入参信息,解析过程中的SQL信息
public class DynamicContext {

	private StringBuffer sb = new StringBuffer();

	private HashMap<String, Object> bindings = new HashMap<>();
	
	public DynamicContext(Object param) {
		bindings.put("_parameter", param);
	}

	public String getSql() {
		return sb.toString();
	}

	public void appendSql(String sqlText) {
		this.sb.append(sqlText);
		this.sb.append(" ");
	}

	public HashMap<String, Object> getBindings() {
		return bindings;
	}

	public void addBinding(String name, Object param) {
		this.bindings.put(name, param);
	}
}

MixedSqlNode 类(实现SqlNode接口)

  • 封装的是带有${}的文本字符串
  • 封装SqlNode集合
public class MixedSqlNode implements SqlNode {

	// 封装SqlNode集合信息
	private List<SqlNode> sqlNodes;

	public MixedSqlNode(List<SqlNode> sqlNodes) {
		this.sqlNodes = sqlNodes;
	}

	/**
	 * 对外提供对封装数据的操作
	 */
	@Override
	public void apply(DynamicContext context) {
		for (SqlNode sqlNode : sqlNodes) {
			sqlNode.apply(context);
		}
	}
}

StaticTextSqlNode 类(实现SqlNode接口)

  • 封装的是带有#{}的文本字符串
public class StaticTextSqlNode implements SqlNode {

	private String sqlText;

	public StaticTextSqlNode(String sqlText) {
		this.sqlText = sqlText;
	}

	@Override
	public void apply(DynamicContext context) {
		context.appendSql(sqlText);
	}
}

TextSqlNode 类(实现SqlNode接口)

  • 封装的是带有${}的文本字符串
public class TextSqlNode implements SqlNode {

	private String sqlText;

	public TextSqlNode(String sqlText) {
		this.sqlText = sqlText;
	}

	// ${}的处理,就是在这个时候
	@Override
	public void apply(DynamicContext context) {

		BindingTokenParser handler = new BindingTokenParser(context);
		// 将#{}解析为?并保存参数信息
		GenericTokenParser tokenParser = new GenericTokenParser("${", "}", handler);
		// 获取真正可以执行的SQL语句
		String sql = tokenParser.parse(sqlText);
		context.appendSql(sql);
	}

	/**
	 * 对外提供保存数据的处理功能
	 * 
	 * @return
	 */
	public boolean isDynamic() {
		if (sqlText.indexOf("${") > -1) {
			return true;
		}

		return false;
	}
	private static class BindingTokenParser implements TokenHandler {
		private DynamicContext context;

		public BindingTokenParser(DynamicContext context) {
			this.context = context;
		}
		/**
		 * expression:比如说${username},那么expression就是username username也就是Ognl表达式
		 */
		@Override
		public String handleToken(String expression) {
			Object paramObject = context.getBindings().get("_parameter");
			if (paramObject == null) {
				// context.getBindings().put("value", null);
				return "";
			} else if (SimpleTypeRegistry.isSimpleType(paramObject.getClass())) {
				// context.getBindings().put("value", paramObject);
				return String.valueOf(paramObject);
			}
			// 使用Ognl api去获取相应的值
			Object value = OgnlUtils.getValue(expression, paramObject);
			String srtValue = value == null ? "" : String.valueOf(value);
			return srtValue;
		}
	}
}

IfSqlNode 类(实现SqlNode接口)

  • 封装if标签的SQL信息
public class IfSqlNode implements SqlNode {

	private String test;

	private SqlNode rootSqlNode;

	public IfSqlNode(String test, SqlNode rootSqlNode) {
		super();
		this.test = test;
		this.rootSqlNode = rootSqlNode;
	}

	@Override
	public void apply(DynamicContext context) {
		boolean evaluateBoolean = OgnlUtils.evaluateBoolean(test, context.getBindings().get("_parameter"));
		if (evaluateBoolean) {
			rootSqlNode.apply(context);
		}
	}
}

SqlSource接口及其实现类

SqlSource 接口

  • 1、实现类要封装未解析的SQL信息
  • 2、要提供对未解析的SQL信息,进行解析的功能
public interface SqlSource {

	// String username = "王五";
	// statement的使用方式,也就是动态拼接SQL语句
	// "select * from user where username = "+username

	// "select * from user where username = ? AND age = ?"
	// preparedStatement.setString(1,"王五") //【王五】得去入参对象的username属性中获取
	// preparedStatement.setString(2,"男") //【男】得去入参对象的age属性中获取
	BoundSql getBoundSql(Object param);
}

BoundSql 类

  • 封装解析之后的SQL信息,以及解析占位符?时产生的参数信息
public class BoundSql {

	// 解析之后的SQL语句
	private String sql;

	// 解析过程中产生的SQL参数信息
	private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();

	public BoundSql(String sql, List<ParameterMapping> parameterMappings) {
		this.sql = sql;
		this.parameterMappings = parameterMappings;
	}

	public String getSql() {
		return sql;
	}

	public void setSql(String sql) {
		this.sql = sql;
	}

	public List<ParameterMapping> getParameterMappings() {
		return parameterMappings;
	}
	public void addParameterMappings(ParameterMapping parameterMapping) {
		this.parameterMappings.add(parameterMapping);
	}
}

ParameterMapping 类

  • 从#{}中解析出来的参数信息,包括参数名称和类型
public class ParameterMapping {

	private String name;
	private Class<?> type;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Class<?> getType() {
		return type;
	}
	public void setType(Class<?> type) {
		this.type = type;
	}
	public ParameterMapping(String name) {
		super();
		this.name = name;
	}
}

DynamicSqlSource 类(实现SqlSource)

  • 封装带有${}或者动态SQL标签的SQL信息
public class DynamicSqlSource implements SqlSource {

	private SqlNode rootSqlNode;

	public DynamicSqlSource(SqlNode mixedSqlNode) {
		this.rootSqlNode = mixedSqlNode;
	}

	@Override
	public BoundSql getBoundSql(Object param) {

		// #{}处理的时候,不需要入参对象的支持
		DynamicContext context = new DynamicContext(param);
		// 处理SqlNode,先去处理动态标签和${},拼接成一条SQL文本,该SQL文本还包含#{}
		rootSqlNode.apply(context);

		// 处理#{}
		ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler();
		// 将#{}解析为?并保存参数信息
		GenericTokenParser tokenParser = new GenericTokenParser("#{", "}", handler);
		// 获取真正可以执行的SQL语句
		String sql = tokenParser.parse(context.getSql());

		return new BoundSql(sql, handler.getParameterMappings());
	}
}

RawSqlSource 类 (实现SqlSource接口)

  • 封装最多只带有#{}的SQL信息
  • 也就是#{}需要被处理一次就可以,就可以使用占位符来长期使用。 而这一点和 很 不 一 样 。 {}很不一样。 {}每一次被调用时,就需要去解析一次SQL语句
public class RawSqlSource implements SqlSource {

	private StaticSqlSource staticSqlSource;

	public RawSqlSource(SqlNode mixedSqlNode) {
		// #{}处理的时候,不需要入参对象的支持
		DynamicContext context = new DynamicContext(null);
		// 处理SqlNode
		mixedSqlNode.apply(context);

		ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler();
		// 将#{}解析为?并保存参数信息
		GenericTokenParser tokenParser = new GenericTokenParser("#{", "}", handler);
		// 获取真正可以执行的SQL语句
		String sql = tokenParser.parse(context.getSql());

		// 该SqlSource就是封装已经解析完成的Sql语句
		staticSqlSource = new StaticSqlSource(sql, handler.getParameterMappings());

	}

	@Override
	public BoundSql getBoundSql(Object param) {
		return staticSqlSource.getBoundSql(param);
	}

}

StaticSqlSource 类(实现SqlSource接口)

  • 存储被解析之后的SQL信息和参数信息
public class StaticSqlSource implements SqlSource {

	// 解析之后的SQL语句
	private String sql;

	// 解析过程中产生的SQL参数信息
	private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();

	
	public StaticSqlSource(String sql, List<ParameterMapping> parameterMappings) {
		this.sql = sql;
		this.parameterMappings = parameterMappings;
	}

	@Override
	public BoundSql getBoundSql(Object param) {
		 
		return new BoundSql(sql, parameterMappings);
	}
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值