Mybatis: the prototype of handwritten mybatis

theMine 2021-01-22 15:59:32
mybatis prototype handwritten mybatis


Mybatis Is currently the most commonly used development based on ORM Semi automatic persistence layer framework of ideas , Usually we just stay in the use stage , Yes mybatis How it works is not clear , Today, I found some materials and taught myself a lot , I wrote a mybatis The prototype of , Here is a record of the learning process
First , We'll build a new one to provide mybatis Frame function engineering IMybatis, The main task of this project is to complete mybatis Function development of the whole initialization and execution process .

 

The dependencies used in this project

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.my</groupId>
<artifactId>IMybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>6</source>
<target>6</target>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compile.encoding>UTF-8</maven.compile.encoding>
<java.version>1.8</java.version>
<maven.compile.source>1.8</maven.compile.source>
<maven.compile.target>1.8</maven.compile.target>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.26</version>
</dependency>
</dependencies>
</project>

 

We will package and publish the framework to the local warehouse after completing the first step above , Build a new test project IMybatis-test, Of this project pom The file will introduce IMybatis Engineering dependence , To complete the test

 

 

  The project depends on

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.my</groupId>
<artifactId>IMybatis-test</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.my</groupId>
<artifactId>IMybatis</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

 

mybatis To complete the connection to the database , Add, delete, change and search functions , Two configuration files are required ( No matter in the form of annotation mapper Interface sql), One is the connection information of the configured database , I am here datasourceConfig.xml,

<configuration>
<!-- Database configuration information -->
<dataSource>
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///test?serverTimezone=Asia/Shanghai"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</dataSource>
<mapper resource="UserMapper.xml"></mapper>
</configuration>

 

The other is to provide sql Of mapper file , Here is UserMapper.xml, Both of these files are in IMybatis-test Provide... In the project

<mapper namespace="com.my.dao.UserMapper">
<!-- sql The only representation of is given by namespace.id To form a statementId -->
<select id="findAll" resultType="com.my.pojo.User">
select * from user
</select>
<select id="findOne" parameterType="com.my.pojo.User" resultType="com.my.pojo.User">
select * from user where id = #{id}
</select>
<select id="findById" parameterType="java.lang.Long" resultType="com.my.pojo.User">
select * from user where id = #{id}
</select>
<delete id="delete" parameterType="com.my.pojo.User">
delete from user where id = #{id}
</delete>
<delete id="deleteById" parameterType="java.lang.Long">
delete from user where id = #{id}
</delete>
<update id="update" parameterType="com.my.pojo.User">
update user set name = #{name} where id = #{id}
</update>
<insert id="insert" parameterType="com.my.pojo.User">
insert into user(id, name) VALUES(#{id}, #{name})
</insert>
</mapper>

 

The next step is to complete IMybatis Function development .

One 、 newly build Resource Class complete pair datasourceConfig.xml Loading of files , Load it into memory as a stream

package com.my.io;
import java.io.InputStream;
/**
* @Description: Profile read
* @Author lzh
* @Date 2020/12/6 16:01
*/
public class Resource {
/**
* According to the path of delivery path To read the configuration file in this path datasourceConfig.xml, And read it back as a byte stream
* @param path
* @return InputStream
*/
public static InputStream getResourceAsStream(String path){
InputStream resourceAsStream = Resource.class.getClassLoader().getResourceAsStream(path);
return resourceAsStream;
}
}

Two 、 newly build SqlSessionFactoryBuilder class , To write build() Method , Build... Step by step SqlSessionFactory object

package com.my.sqlSession;
import com.my.config.XMLConfigBuilder;
import com.my.pojo.Configuration;
import java.io.InputStream;
/**
* @Description: Parse configuration file
* @Author lzh
* @Date 2020/12/6 16:23
*/
public class SqlSessionFactoryBuilder {
/**
* Parse the value of each tag in the configuration file according to the byte stream , And wrapped in Configuration in , establish DefaultSqlSessionFactory object
* @param in
* @return
* @throws Exception
*/
public SqlSessionFactory build(InputStream in) throws Exception {
// Create a XMLConfigBuilder object 
XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder();
// Parse the configuration file 
Configuration configuration = xmlConfigBuilder.parseConfig(in);
// establish DefaultSqlSessionFactory object 
DefaultSqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(configuration);
return defaultSqlSessionFactory;
}
}

3、 ... and 、 stay build As you can see in the method, first create a XMLConfigBuilder object , In this object, a parseConfig() Method to complete the parsing of the configuration file , And finish to Configuration Encapsulation of objects ,Configuration It is a very core object in our project , It stores the result of parsing the configuration file , Also in the real Mybatis The object is also in the framework , Of course, it's more powerful than mine .

package com.my.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.my.io.Resource;
import com.my.pojo.Configuration;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;
/**
* @Description:
* @Author lzh
* @Date 2020/12/6 16:26
*/
public class XMLConfigBuilder {
private Configuration configuration;
public XMLConfigBuilder() {
this.configuration = new Configuration();
}
/**
* analysis dataSourceConfig.xml
* @param in
* @return
* @throws Exception
*/
public Configuration parseConfig(InputStream in) throws Exception {
// utilize dom4j Technology to parse the configuration file 
Document document = new SAXReader().read(in);
Element rootElement = document.getRootElement();
// lookup dataSourceConfig.xml Medium property label 
List<Element> list = rootElement.selectNodes("//property");
Properties properties = new Properties();
for (Element element : list) {
// Remove each property The value in the tag is saved to Properties In the object 
String name = element.attributeValue("name");
String value = element.attributeValue("value");
properties.setProperty(name, value);
}
// from Properties Take each attribute from the database to build a connection pool , To provide management of database connections , Avoid waste of resources , Improve performance 
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(properties.getProperty("driverClass"));
druidDataSource.setUrl(properties.getProperty("url"));
druidDataSource.setUsername(properties.getProperty("username"));
druidDataSource.setPassword(properties.getProperty("password"));
// Put the connection pool object into Configuration In the object 
 configuration.setDataSource(druidDataSource);
// analysis dataSourceConfig.xml Medium mapper label ,mapper In the tag resource The property value stores UserMapper.xml File location of 
List<Element> mapperList = rootElement.selectNodes("//mapper");
for (Element element : mapperList) {
String mapperPath = element.attributeValue("resource");
InputStream resourceAsStream = Resource.getResourceAsStream(mapperPath);
// analysis UserMapper.xml file , Further encapsulation Configuration object 
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
xmlMapperBuilder.parse(resourceAsStream);
}
return configuration;
}
}

The red area above creates a XMLMapperBuilder object , This object provides a parse() Method , It's about doing the right thing UserMapper.xml File analysis , And finish to Configuration encapsulation

package com.my.config;
import com.my.config.eunm.SqlCommandType;
import com.my.pojo.Configuration;
import com.my.pojo.MappedStatement;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.List;
/**
* @Description:
* @Author lzh
* @Date 2020/12/6 17:03
*/
public class XMLMapperBuilder {
private Configuration configuration;
public XMLMapperBuilder(Configuration configuration) {
this.configuration = configuration;
}
/**
* analysis UserMapper.xml What's in the configuration file , Build each tag into a MappedStatement, And assign to Configuration in
* @param in
* @throws DocumentException
*/
public void parse(InputStream in) throws DocumentException {
Document document = new SAXReader().read(in);
Element rootElement = document.getRootElement();
String namespace = rootElement.attributeValue("namespace");
// analysis select label 
List<Element> selectList = rootElement.selectNodes("//select");
this.parseElement(selectList, namespace, SqlCommandType.SELECT);
// analysis insert label 
List<Element> insertList = rootElement.selectNodes("//insert");
this.parseElement(insertList, namespace, SqlCommandType.INSERT);
// analysis update label 
List<Element> updateList = rootElement.selectNodes("//update");
this.parseElement(updateList, namespace, SqlCommandType.UPDATE);
// analysis delete label 
List<Element> deleteList = rootElement.selectNodes("//delete");
this.parseElement(deleteList, namespace, SqlCommandType.DELETE);
}
/**
* analysis mapper.xml Add, delete, change and look up the label in the document
* @param elementList
* @param namespace
* @param sqlCommandType
*/
private void parseElement(List<Element> elementList, String namespace, SqlCommandType sqlCommandType) {
for (Element element : elementList) {
String id = element.attributeValue("id");
String resultType = element.attributeValue("resultType");
String parameterType = element.attributeValue("parameterType");
String sql = element.getTextTrim();
MappedStatement mappedStatement = new MappedStatement();
mappedStatement.setSqlCommandType(sqlCommandType);
mappedStatement.setId(id);
mappedStatement.setParameterType(parameterType);
mappedStatement.setResultType(resultType);
mappedStatement.setSql(sql);
configuration.getMappedStatementMap().put(namespace + "." + id, mappedStatement);
}
}
}

Used in this class SqlCommandType Is an enumeration class , That's the list UserMapper.xml A few of the main sql Add, delete, modify and query tag types , It is also the source of reference Mybatis Writing in the frame

package com.my.config.eunm;
public enum SqlCommandType {
INSERT,
UPDATE,
DELETE,
SELECT;
private SqlCommandType(){
}
}

One more MappedStatement object , In this object is every one of the encapsulation insert、update、delete、select The information in the tag ( Include... In each tag id、parameterType、resutType、sql Statements, etc. ), Each label is a MappedStatement object

package com.my.pojo;
import com.my.config.eunm.SqlCommandType;
/**
* @Description:
* @Author lzh
* @Date 2020/12/6 16:17
*/
public class MappedStatement {
private SqlCommandType sqlCommandType;
private String id;
private String resultType;
private String parameterType;
private String sql;
public SqlCommandType getSqlCommandType() {
return sqlCommandType;
}
public void setSqlCommandType(SqlCommandType sqlCommandType) {
this.sqlCommandType = sqlCommandType;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getResultType() {
return resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
public String getParameterType() {
return parameterType;
}
public void setParameterType(String parameterType) {
this.parameterType = parameterType;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
}

Package it MappedStatement After the object , Then put it in Configuration Object's mappedStatementMap Properties of the , This attribute is a Map aggregate ,key Namely UserMapper.xml In the document namespace Value + "." + Every label's id value ( For example, our com.my.dao.UserMapper.findAll), Because a Mapper The interface corresponds to a Mapper.xml file , And each Mapper.xml In the document namespace The value is Mapper The fully qualified class name of the interface , For each label id The value is Mapper The name of the corresponding method in the interface , So through this combination key And Mapper Interface , When we call Mapper Method in interface , You can go through Mapper The fully qualified class name of the interface and the method name of the call are in Configuration Medium Map Find the corresponding... In the collection MappedStatement object , That is to get what needs to be executed sql、 Parameter type 、 Return value type and so on .

package com.my.pojo;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/** The core object
* @Description:
* @Author lzh
* @Date 2020/12/6 16:18
*/
public class Configuration {
/**
* data source
*/
private DataSource dataSource;
/**
* key:statementId vlaue: Packaged MappedStatement
*/
private Map<String,MappedStatement> mappedStatementMap = new HashMap<String, MappedStatement>();
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Map<String, MappedStatement> getMappedStatementMap() {
return mappedStatementMap;
}
public void setMappedStatementMap(Map<String, MappedStatement> mappedStatementMap) {
this.mappedStatementMap = mappedStatementMap;
}
}

Here we are Configuration The object is encapsulated .

Four 、 And then we can do it in the second step SqlSessionFactoryBuilder Class build() Method , according to Configuration Object constructs DefaultSqlSessionFactory Factory object , The whole build DefaultSqlSessionFactory The process of building is the embodiment of a builder model ( Building a large object from multiple small objects )

package com.my.sqlSession;
public interface SqlSessionFactory {
SqlSession createSqlSession();
}

 

package com.my.sqlSession;
import com.my.pojo.Configuration;
/**
* @Description: SqlSession The object of our factory , Used in the production of SqlSession
* @Author lzh
* @Date 2020/12/6 17:17
*/
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
/**
* establish SqlSession conversation
* @return
*/
public SqlSession createSqlSession() {
return new DefaultSqlSession(configuration);
}
}

5、 ... and 、 utilize DefaultSqlSessionFactory Factory object Of createSqlSession() Method to get a SqlSession object , It's what we call a conversation object , This object is also a very important object

package com.my.sqlSession;
import java.util.List;
/**
* @Description: SqlSession
* @Author lzh
* @Date 2020/12/6 17:18
*/
public interface SqlSession {
<E> List<E> selectList(String statementId, Class<?> methodParameterType, Object... param) throws Exception;
<T> T selectOne(String statementId, Class<?> methodParameterType, Object... param) throws Exception;
<T> T getMapper(Class<?> mapperClass);
}
package com.my.sqlSession;
import com.my.config.eunm.SqlCommandType;
import com.my.pojo.Configuration;
import com.my.pojo.MappedStatement;
import java.lang.reflect.*;
import java.util.List;
/**
* @Description: SqlSession The realization of conversation
* @Author lzh
* @Date 2020/12/6 17:21
*/
public class DefaultSqlSession implements SqlSession, InvocationHandler {
private Configuration configuration;
public DefaultSqlSession(Configuration configuration) {
this.configuration = configuration;
}
/**
* Multiple queries
* @param statementId
* @param param
* @param <E>
* @return
* @throws Exception
*/
public <E> List<E> selectList(String statementId, Class<?> methodParameterType, Object... param) throws Exception {
SimpleExecutor simpleExecutor = new SimpleExecutor();
List<Object> query = simpleExecutor.query(configuration, configuration.getMappedStatementMap().get(statementId), methodParameterType, param);
return (List<E>) query;
}
/**
* Single query
* @param statementId
* @param param
* @param <T>
* @return
* @throws Exception
*/
public <T> T selectOne(String statementId, Class<?> methodParameterType, Object... param) throws Exception {
List<Object> objects = selectList(statementId, methodParameterType, param);
if (objects.size() == 1){
return (T) objects.get(0);
}else if (objects.size() <= 0){
return null;
}else {
throw new RuntimeException("Result more than one");
}
}
/**
* newly added
* @param statementId
* @param param
* @return
*/
public int insert(String statementId, Class<?> methodParameterType, Object... param) throws Exception {
return update(statementId, methodParameterType, param);
}
/**
* modify
* @param statementId
* @param param
* @return
*/
public int update(String statementId, Class<?> methodParameterType, Object... param) throws Exception {
SimpleExecutor simpleExecutor = new SimpleExecutor();
return simpleExecutor.update(configuration, configuration.getMappedStatementMap().get(statementId), methodParameterType, param);
}
/**
* Delete
* @param statementId
* @param param
* @return
*/
public int delete(String statementId, Class<?> methodParameterType, Object... param) throws Exception {
return update(statementId, methodParameterType, param);
}
/**
* Create proxy object
* @param mapperClass
* @param <T>
* @return
*/
@Override
public <T> T getMapper(Class<?> mapperClass) {
Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, this);
return (T) proxyInstance;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Class<?> methodParameterType = null;
if (null != method.getParameterTypes() && 0 < method.getParameterTypes().length){
methodParameterType = method.getParameterTypes()[0];
}
String className = method.getDeclaringClass().getName();
String statementId = className + "." + methodName;
MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
if (SqlCommandType.SELECT == sqlCommandType){
Type genericReturnType = method.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType){
return selectList(statementId, methodParameterType, args);
}
return selectOne(statementId, methodParameterType, args);
}else if (SqlCommandType.INSERT == sqlCommandType){
return insert(statementId, methodParameterType, args);
}else if (SqlCommandType.UPDATE == sqlCommandType){
return update(statementId, methodParameterType, args);
}else if (SqlCommandType.DELETE == sqlCommandType){
return delete(statementId, methodParameterType, args);
}else {
throw new RuntimeException("Unknown SqlCommandType For: " + sqlCommandType);
}
}
}

6、 ... and 、 stay SqlSession in , The method of adding, deleting, modifying and checking is provided , For operating the database , We can see another getMapper() Method , This method needs to pass in a Class Parameters , So what does this method do ? Our friends who have experience in development all know that they used it a long time ago spring+Mybatis Framework development time , For each Dao( That's what we're talking about here Mapper layer ) Layer interface will write an implementation class DaoImpl, In the implementation class through JDBC To complete the operation of the database , There are a lot of problems with this kind of coding , such as :

  1. Each time you execute a method, you get a Connection object , That is to create a database connection
  2. sql Statements and business code blend together , Increase code coupling , It's not easy to maintain
  3. Encapsulating the returned results is troublesome , Not smart enough

So for the first problem, we introduce a connection pool to manage database connections , Every time I get it from the pool , Reduced resource consumption , Improved efficiency , For the latter two problems , First Mybatis It's transferred DaoImpl Implementation class , secondly , adopt Java Reflection technology completes the assignment of parameters and the dynamic encapsulation of returned results ( This step will be reflected in the code later ). So take out DaoImpl Implementation class ,Dao What needs to be done in an interface always needs to be done by someone , Otherwise, the operation on the database cannot be completed , therefore Mybatis For each Dao Interface ( That's what we're talking about here Mapper Interface ) Generate a proxy object , Before we finish DaoImpl What to do . there getMapper() The method is to get the proxy object of the incoming parameter object , We're here to get UserMapper The proxy object of the interface , When we create a proxy object, we can see that getMapper() Methods Proxy.newProxyInstance(), You need to pass three parameters , The first parameter is a class loader , The second parameter is which object we need to generate a proxy object for , That is to say getMapper() Method parameters , The point is the third parameter , Need to pass in a InvocationHandler object , and InvocationHandler It's an interface , We have DefaultSqlSession Implemented this interface , So the third parameter is this, The class itself . Realized InvocationHandler Interfaces need to be rewritten invoke() Method , And we know how to call the proxy object , All the way to the right place invoke() In the method , So here we call UserMapper Method in interface , The same will be done here invoke Method , In this way invoke() In this way, we can complete our previous work in DaoImpl What needs to be done in the process .

7、 ... and 、 Let's take a look at invoke() What did zhong do , Let's first look at the three parameters , The first is a proxy object , The second is the method we call Method, The third is the parameter passed in when calling the method args, So we're based on Method Object to get the fully qualified class name of the method and the name of the method , So that we can combine a statemenId, And we passed in step five above createSqlSession() Method creation SqlSession Object time , It's what encapsulates us Configuration Object passed in , So here we can go through statementId stay Configuration Object's mappedStatementMap This Map We found our package in the collection MappedStatement object , adopt MappedStatement Object SqlCommandType We can determine which method we need to perform in the add delete modify query , So as to call the specific addition, deletion, modification and query methods of this class , When implementing specific methods , We're not here SqlSession Object to operate the database directly , It's about putting these crud The operation was given to a SimpleExecutor Executor to complete the real operation of the database .

package com.my.sqlSession;
import com.my.pojo.Configuration;
import com.my.pojo.MappedStatement;
import java.util.List;
public interface Executor {
<E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Class<?> methodParameterType, Object... param) throws Exception;
int update(Configuration configuration, MappedStatement mappedStatement, Class<?> methodParameterType, Object[] param) throws Exception;
}
package com.my.sqlSession;
import com.my.config.BoundSql;
import com.my.pojo.Configuration;
import com.my.pojo.MappedStatement;
import com.my.utils.GenericTokenParser;
import com.my.utils.ParameterMapping;
import com.my.utils.ParameterMappingTokenHandler;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @Description: Executor actuator
* @Author lzh
* @Date 2020/12/6 17:32
*/
public class SimpleExecutor implements Executor {
/**
* The real query method , Be responsible for the completion of JDBC The operation of
* @param configuration
* @param mappedStatement
* @param param
* @param <E>
* @return
* @throws Exception
*/
public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement,Class<?> methodParameterType, Object... param) throws Exception {
PreparedStatement preparedStatement = this.createPreparedStatement(configuration, mappedStatement, methodParameterType, param);
// perform sql, Return result set ResultSet
ResultSet resultSet = preparedStatement.executeQuery();
// Encapsulate the results , Map the corresponding return type 
String resultType = mappedStatement.getResultType();
Class<?> resultClass = getClassType(resultType);
List<Object> result = new ArrayList<Object>();
while (resultSet.next()){
Object o = resultClass.newInstance();
ResultSetMetaData metaData = resultSet.getMetaData();
for (int i = 1; i <= metaData.getColumnCount(); i++) {
String columnName = metaData.getColumnName(i);
Object object = resultSet.getObject(columnName);
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultClass);
Method writeMethod = propertyDescriptor.getWriteMethod();
writeMethod.invoke(o, object);
}
result.add(o);
}
return (List<E>) result;
}
@Override
public int update(Configuration configuration, MappedStatement mappedStatement, Class<?> methodParameterType, Object[] param) throws Exception {
PreparedStatement preparedStatement = this.createPreparedStatement(configuration, mappedStatement, methodParameterType, param);
preparedStatement.execute();
int row = preparedStatement.getUpdateCount();
return row;
}
/**
* obtain PreparedStatement object
* @param configuration
* @param mappedStatement
* @param param
* @return
* @throws Exception
*/
private PreparedStatement createPreparedStatement(Configuration configuration, MappedStatement mappedStatement, Class<?> methodParameterType, Object[] param) throws Exception {
// Get database connection 
Connection connection = configuration.getDataSource().getConnection();
// from MappedStatement Remove from sql, current sql Namely userMapper.xml We write in with #{} Of sql sentence 
String sql = mappedStatement.getSql();
// Handle sql sentence , It is concluded that sql In the sentence #{} Attribute value in , And will #{} Replace with ?, Package to BoundSql In the object 
BoundSql boundSql = getBoundSql(sql);
// obtain PreparedStatement object 
PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
// If there are parameters , Assign parameters 
String parameterType = mappedStatement.getParameterType();
if (null != parameterType){
Class<?> parameterClass = getClassType(parameterType);
List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
for (int i = 0; i < parameterMappingList.size(); i++) {
if (isObject(methodParameterType)){
preparedStatement.setObject(i + 1, param[0]);
}else {
ParameterMapping parameterMapping = parameterMappingList.get(i);
// The content It's us sql in #{id} Medium id
String content = parameterMapping.getContent();
// Use reflection in parameterClass Remove from content The value of this property , And finish sql Assignment 
Field declaredField = parameterClass.getDeclaredField(content);
declaredField.setAccessible(true);
Object o = declaredField.get(param[0]);
preparedStatement.setObject(i + 1, o);
}
}
}
return preparedStatement;
}
/**
* According to the parameter type or return value type, get the Class
* @param parameterType
* @return
* @throws ClassNotFoundException
*/
private Class<?> getClassType(String parameterType) throws ClassNotFoundException {
Class<?> aClass = Class.forName(parameterType);
return aClass;
}
/**
* analysis sql, Encapsulated into BoundSql
* @param sql
* @return
*/
private BoundSql getBoundSql(String sql) {
ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
// Resolved sql
String parseSql = genericTokenParser.parse(sql);
// Resolved id and name
List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
BoundSql boundSql = new BoundSql(parseSql, parameterMappings);
return boundSql;
}
private Boolean isObject(Class<?> methodParameterType){
if (null == methodParameterType){
return false;
}
if (Integer.class.getName().equals(methodParameterType.getName())
|| Long.class.getName().equals(methodParameterType.getName())
|| String.class.getName().equals(methodParameterType.getName())
|| Double.class.getName().equals(methodParameterType.getName())
|| Float.class.getName().equals(methodParameterType.getName())
|| Byte.class.getName().equals(methodParameterType.getName())
|| Short.class.getName().equals(methodParameterType.getName())
|| Character.class.getName().equals(methodParameterType.getName())
|| Boolean.class.getName().equals(methodParameterType.getName())
|| Date.class.getName().equals(methodParameterType.getName())){
return true;
}
return false;
}
}

8、 ... and 、 In this executor, the operation on the database is actually completed , Get one from the connection pool Connection Connect , from MappedStatement Get the sql, Here, pay attention to sql Or from the UserMapper.xml The result of the analysis in sql(select * from user where id = #{id}), It needs to be treated with ? Replace #{}, And record the parameters in the braces , because JDBC The placeholder for the parameter in is ?, So here getBoundSql() The way is to do these things , Finally, it's packaged into a BoundSql object .

package com.my.config;
import com.my.utils.ParameterMapping;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: sql
* @Author lzh
* @Date 2020/12/6 17:42
*/
public class BoundSql {
private String sqlText;
private List<ParameterMapping> parameterMappingList = new ArrayList<ParameterMapping>();
public BoundSql(String sqlText, List<ParameterMapping> parameterMappingList) {
this.sqlText = sqlText;
this.parameterMappingList = parameterMappingList;
}
public String getSqlText() {
return sqlText;
}
public void setSqlText(String sqlText) {
this.sqlText = sqlText;
}
public List<ParameterMapping> getParameterMappingList() {
return parameterMappingList;
}
public void setParameterMappingList(List<ParameterMapping> parameterMappingList) {
this.parameterMappingList = parameterMappingList;
}
}

 

I also post several tool classes here , This is also from Mybatis From the source code , That's right sql Parsing , There's no need to pay too much attention here .

package com.my.utils;
/**
* @author lzh
*/
public interface TokenHandler {
String handleToken(String content);
}

 

package com.my.utils;
import java.util.ArrayList;
import java.util.List;
public class ParameterMappingTokenHandler implements TokenHandler {
private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
// context Is the parameter name #{id} #{username}
public String handleToken(String content) {
parameterMappings.add(buildParameterMapping(content));
return "?";
}
private ParameterMapping buildParameterMapping(String content) {
ParameterMapping parameterMapping = new ParameterMapping(content);
return parameterMapping;
}
public List<ParameterMapping> getParameterMappings() {
return parameterMappings;
}
public void setParameterMappings(List<ParameterMapping> parameterMappings) {
this.parameterMappings = parameterMappings;
}
}
package com.my.utils;
/**
* @author Clinton Begin
*/
public class GenericTokenParser {
private final String openToken; // Start marker 
private final String closeToken; // End mark 
private final TokenHandler handler; // Tag processor 
public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
this.openToken = openToken;
this.closeToken = closeToken;
this.handler = handler;
}
/**
* analysis ${} and #{}
* @param text
* @return
* This method mainly realizes the configuration file 、 Script and other fragments of the analysis of the placeholder 、 Handling work , And return the final required data .
* among , Parsing is done by this method , Processing is done by the processor handler Of handleToken() Method to implement
*/
public String parse(String text) {
// Verification parameter problem , If it is null, It returns an empty string .
if (text == null || text.isEmpty()) {
return "";
}
// Let's continue to verify that the start tag is included , If not , The default is not a placeholder , Just go back as it is , Otherwise, continue to execute .
int start = text.indexOf(openToken, 0);
if (start == -1) {
return text;
}
// hold text Convert to character array src, And define the default offset offset=0、 Store variables that ultimately need to return a string builder,
// text Variable name corresponding to the place holder in the variable expression. Judge start Is it greater than -1( namely text Whether there is openToken), If it exists, execute the following code 
char[] src = text.toCharArray();
int offset = 0;
final StringBuilder builder = new StringBuilder();
StringBuilder expression = null;
while (start > -1) {
// Determine if there is an escape character before the start tag , Not as openToken To deal with , Otherwise, continue to deal with 
if (start > 0 && src[start - 1] == '\\') {
builder.append(src, offset, start - offset - 1).append(openToken);
offset = start + openToken.length();
} else {
// Reset expression Variable , Avoid null pointer or old data interference .
if (expression == null) {
expression = new StringBuilder();
} else {
expression.setLength(0);
}
builder.append(src, offset, start - offset);
offset = start + openToken.length();
int end = text.indexOf(closeToken, offset);
while (end > -1) {//// When there is an end tag 
if (end > offset && src[end - 1] == '\\') {// If the end tag is preceded by an escape character
// this close token is escaped. remove the backslash and continue.
expression.append(src, offset, end - offset - 1).append(closeToken);
offset = end + closeToken.length();
end = text.indexOf(closeToken, offset);
} else {// There is no escape character , That is, it needs to be treated as a parameter 
expression.append(src, offset, end - offset);
offset = end + closeToken.length();
break;
}
}
if (end == -1) {
// close token was not found.
builder.append(src, start, src.length - start);
offset = src.length;
} else {
// First of all, according to the parameter of key( namely expression) Processing parameters , return ? As placeholder 
 builder.append(handler.handleToken(expression.toString()));
offset = end + closeToken.length();
}
}
start = text.indexOf(openToken, offset);
}
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
return builder.toString();
}
}
package com.my.utils;
public class ParameterMapping {
private String content;
public ParameterMapping(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}

The analysis is finished sql And then there's the creation of PreparedStatement object , And pass MappedStatement The type of parameter recorded in the object , utilize java The reflection technique is used to assign values , And then execute sql, Last pass MappedStatement Object to encapsulate the result , It's also used java Reflection , In this way, the dynamic assignment of parameters and the dynamic encapsulation of results are realized . This is the whole Mybatis The implementation process of , It's done here IMybatis Framework writing , Let's test it .

Nine 、 take IMybatis Package to local warehouse , stay IMybatis-test Introduce dependency in , Write a user Pojo class 、UserMapper Interface and a test class ,UserMapper.xml It has been provided above

package com.my.pojo;
/**
* @Description:
* @Author lzh
* @Date 2020/12/6 15:57
*/
public class User {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
package com.my.dao;
import com.my.pojo.User;
import java.util.List;
public interface UserMapper {
/**
* Query all
* @return
*/
List<User> findAll() ;
/**
* Check the list
* @param user
* @return
*/
User findOne(User user);
/**
* according to id Check the list
* @param id
* @return
*/
User findById(Long id);
/**
* according to id Delete user
* @param id
* @return
*/
int deleteById(Long id);
/**
* Delete user
* @param user
* @return
*/
int delete(User user);
/**
* New users
* @param user
* @return
*/
int insert(User user);
/**
* Modify the user
* @param user
* @return
*/
int update(User user);
}
package com.my.test;
import com.my.dao.UserMapper;
import com.my.io.Resource;
import com.my.pojo.User;
import com.my.sqlSession.SqlSession;
import com.my.sqlSession.SqlSessionFactory;
import com.my.sqlSession.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
/**
* @Description:
* @Author lzh
* @Date 2020/12/6 16:05
*/
public class IMybatisTest {
private SqlSession sqlSession;
@Before
public void before() throws Exception {
InputStream resourceAsStream = Resource.getResourceAsStream("dataSourceConfig.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream);
sqlSession = sqlSessionFactory.createSqlSession();
}
@Test
public void test1() {
User user = new User();
user.setId(2L);
user.setName(" Wang Wu ");
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.findOne(user);
System.out.println(user1);
}
@Test
public void test2() {
User user = new User();
user.setId(1L);
user.setName(" Wang Wu ");
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.findAll();
for (User user1 : all) {
System.out.println(user1);
}
}
@Test
public void test3() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1= mapper.findById(2L);
System.out.println(user1);
}
@Test
public void test4() {
User user = new User();
user.setId(3L);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int row = mapper.delete(user);
System.out.println(row);
}
@Test
public void test5() {
User user = new User();
user.setId(3L);
user.setName(" Wang Wu ");
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int row = mapper.update(user);
System.out.println(row);
}
@Test
public void test6() {
User user = new User();
user.setId(3L);
user.setName(" Zhang San ");
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int row = mapper.insert(user);
System.out.println(row);
}
}

I won't post all the test results here , Just post one to see the effect , You can see the normal output of the console , Explain that we wrote it ourselves IMybatis That's all right. , Can successfully execute .

 

summary : We can see that in the end it's still through JDBC Completed database operations . So here we can know Mybatis It's still called in the end JDBC To manipulate the database , It's just doing JDBC I did this series of parsing configuration files before , Encapsulating objects and so on ,Mybatis That's right JDBC Packaging .

 

版权声明
本文为[theMine]所创,转载请带上原文链接,感谢
https://javamana.com/2021/01/20210122155723237q.html

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云