Simple implementation of custom persistence layer framework, handwriting MyBatis to achieve basic functions

Simple implementation of custom persistence layer framework, handwriting MyBatis to achieve basic functions

For the source code, please click -> Handwriting Persistence Framework Source Code

1. Analyze JDBC operation problems

public static void main(String[] args) {
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    ResultSet resultSet = null;
	try {
		// 加载数据库驱动
		// 通过驱动管理类获取数据库链接
		connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?
						characterEncoding=utf-8", "root", "root");
		// 定义sql语句?表示占位符
		String sql = "select * from user where username = ?";
		// 获取预处理statement
		preparedStatement = connection.prepareStatement(sql);
		// 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
		preparedStatement.setString(1, "tom");
		// 向数据库发出sql执行查询,查询出结果集
		resultSet = preparedStatement.executeQuery();
		// 遍历查询结果集
		while ( {
			int id = resultSet.getInt("id");
			String username = resultSet.getString("username");
			// 封装User
	} catch (Exception e) {
	} finally {
		// 释放资源
		if (resultSet != null) {
			try {
			} catch (SQLException e) {
		if (preparedStatement != null) {
			try {
			} catch (SQLException e) {
		if (connection != null) {
			try {
			} catch (SQLException e) {

Summary of JDBC problems:

The problems of original jdbc development are as follows:

  1. Frequent database connection creation and release cause waste of system resources, thereby affecting system performance.
  2. Sql statements are hard-coded in the code, which makes the code difficult to maintain. In actual applications, SQL may change a lot. SQL changes require changing the java code.
  3. Using preparedStatement to pass parameters to the occupied position symbol is hard-coded, because the where condition of the SQL statement is not necessarily, it may be more or less, and the code must be modified to modify the sql, and the system is not easy to maintain.
  4. There is hard coding (query column name) for the result set analysis. SQL changes lead to changes in the analysis code, and the system is not easy to maintain. It is more convenient if the database records can be encapsulated into pojo object analysis.

2. Problem solving ideas

  1. Use database connection pool to initialize connection resources
  2. Extract the sql statement into the xml configuration file
  3. Use underlying technologies such as reflection and introspection to automatically map entities and tables to attributes and fields

3. Custom frame design

Use end:

Provide core configuration files:

sqlMapConfig.xml: store data source information, import mapper.xml

Mapper.xml: sql statement configuration file information

Frame end:

1. Read the configuration file

After the reading is completed, it exists in the form of a stream. We cannot store the read configuration information in the form of a stream in the memory. It is not easy to operate. You can create javaBean to store

Configuration: Store basic information of the database, Map<Unique ID, Mapper> Unique ID: namespace + "." + id

MappedStatement: sql statement, statement type, input parameter java type, output parameter java type

2. Parse the configuration file

Create the sqlSessionFactoryBuilder class:

Method: sqlSessionFactory build():

First: Use dom4j to parse the configuration file and encapsulate the parsed content into Configuration and MappedStatement

Second: Create the implementation class DefaultSqlSession of SqlSessionFactory

3. Create SqlSessionFactory:

Method: openSession(): Obtain an instance object of the implementation class of the sqlSession interface

4. Create sqlSession interface and implementation class: mainly encapsulate the crud method

Method: selectList(String statementId,Object param): query all

selectOne(String statementId,Object param): query a single

Specific implementation: Encapsulate JDBC to complete query operations on database tables

Design patterns involved:

Builder design mode, factory mode, agency mode

4. Implementation of custom framework

Create a configuration profile in the consumer project

Create sqlMapConfig.xml

    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    <property name="jdbcUrl" value="jdbc:mysql:///zdy_mybatis"></property>
    <property name="user" value="root"></property>
    <property name="password" value="root"></property>
    <! --引入sql配置信息-->
    <mapper resource="mapper.xml"></mapper>


<mapper namespace="User">
    <select id="selectOne" paramterType="com.tao.pojo.User" resultType="com.tao.pojo.User">
    	select * from user where id = #{id} and username =#{username}
    <select id="selectList" resultType="com.tao.pojo.User">
    	select * from user

User entity

public class User {
    private Integer id;
    private String username;
    public Integer getId() {
    return id;
    public void setId(Integer id) { = id;
    public String getUsername() {
    return username;
    public void setUsername(String username) {
    this.username = username;
    public String toString() {
    return "User{" +
    "id=" + id +
    ", username='" + username + '\'' + '}';

Create another Maven subproject and import the dependent coordinates that need to be used




public class Configuration {
    private DataSource dataSource;
    //map集合: key:statementId value:MappedStatement
    private Map<String,MappedStatement> mappedStatementMap = new HashMap<String,
    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;


public class MappedStatement {
    private Integer id;
    private String sql;
    private Class<?> paramterType;
    private Class<?> resultType;
    public Integer getId() {
    	return id;
    public void setId(Integer id) { = id;
    public String getSql() {
    	return sql;
    public void setSql(String sql) {
    	this.sql = sql;
    public Class<?> getParamterType() {
    	return paramterType;
    public void setParamterType(Class<?> paramterType) {
    	this.paramterType = paramterType;
    public Class<?> getResultType() {
    	return resultType;
    public void setResultType(Class<?> resultType) {
    	this.resultType = resultType;


public class Resources {
	public static InputStream getResourceAsSteam(String path){ 
    	InputStream resourceAsStream = Resources.class.getClassLoader.getResourceAsStream(path);
		return resourceAsStream;


public class SqlSessionFactoryBuilder {
	private Configuration configuration;
	public SqlSessionFactoryBuilder() {
		this.configuration = new Configuration();
	public SqlSessionFactory build(InputStream inputStream) throws 
        	DocumentException, PropertyVetoException, ClassNotFoundException {
		//1.解析配置文件,封装Configuration XMLConfigerBuilder
		xmlConfigerBuilder = new XMLConfigerBuilder(configuration);
		Configuration configuration =
		//2.创建 sqlSessionFactory
		SqlSessionFactory sqlSessionFactory = new
		return sqlSessionFactory;


public class XMLConfigerBuilder {
    private Configuration configuration;
    public XMLConfigerBuilder(Configuration configuration) {
    	this.configuration = new Configuration();
    public Configuration parseConfiguration(InputStream inputStream) throws
        DocumentException, PropertyVetoException, ClassNotFoundException {
        Document document = new SAXReader().read(inputStream); //<configuation>
        Element rootElement = document.getRootElement();
        List<Element> propertyElements = rootElement.selectNodes("//property");
        Properties properties = new Properties();
        for (Element propertyElement : propertyElements) {
            String name = propertyElement.attributeValue("name");
            String value = propertyElement.attributeValue("value");
        ComboPooledDataSource comboPooledDataSource = new
        //填充 configuration
        //mapper 部分
        List<Element> mapperElements = rootElement.selectNodes("//mapper");
        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
        for (Element mapperElement : mapperElements) {
            String mapperPath = mapperElement.attributeValue("resource");
            InputStream resourceAsSteam = Resources.getResourceAsSteam(mapperPath);
		return configuration;


public class XMLMapperBuilder {

    private Configuration configuration;

    public XMLMapperBuilder(Configuration configuration) {
        this.configuration = configuration;

    public void parse(InputStream inputStream) throws DocumentException {

        Document document = new SAXReader().read(inputStream);
        Element rootElement = document.getRootElement();

        String namespace = rootElement.attributeValue("namespace");

        List<Element> list = rootElement.selectNodes("//select");
        for (Element element : list) {
            String id = element.attributeValue("id");
            String resultType = element.attributeValue("resultType");
            String paramterType = element.attributeValue("paramterType");
            String sqlText = element.getTextTrim();
            MappedStatement mappedStatement = new MappedStatement();
            String key = namespace + "." + id;


sqlSessionFactory interface and D efaultSqlSessionFactory implementation class

public interface SqlSessionFactory {
	public SqlSession openSession();

public class DefaultSqlSessionFactory implements SqlSessionFactory {
	private Configuration configuration;
    public DefaultSqlSessionFactory(Configuration configuration) {
    	this.configuration = configuration;
    public SqlSession openSession(){
    	return new DefaultSqlSession(configuration);

sqlSession interface and DefaultSqlSession implementation class

public interface SqlSession {
	public <E> List<E> selectList(String statementId, Object... param) throws Exception;
    public <T> T selectOne(String statementId,Object... params) throws Exception;
    public void close() throws SQLException;

public class DefaultSqlSession implements SqlSession {
    private Configuration configuration;
    public DefaultSqlSession(Configuration configuration) {
		this.configuration = configuration;
	private Executor simpleExcutor = new SimpleExecutor();
	public <E > List < E > selectList(String statementId, Object...param) throws Exception {
		MappedStatement mappedStatement =
		List<E> query = simpleExcutor.query(configuration, mappedStatement, param);
		return query;
	//selectOne 中调用 selectList
	public <T > T selectOne(String statementId, Object...params) throws Exception {
		List<Object> objects = selectList(statementId, params);
		if (objects.size() == 1) {
			return (T) objects.get(0);
		} else {
			throw new RuntimeException("返回结果过多");
	public void close () throws SQLException {


public interface Executor {
	<E> List<E> query(Configuration configuration, MappedStatement
			mappedStatement,Object[] param) throws Exception;
	void close() throws SQLException;


public class SimpleExecutor implements Executor {
	private Connection connection = null;
	public <E> List<E> query(Configuration configuration, MappedStatement
			mappedStatement, Object[] param) throws SQLException, NoSuchFieldException,
			IllegalAccessException, InstantiationException, IntrospectionException,
			InvocationTargetException {
		connection = configuration.getDataSource().getConnection();
        // select * from user where id = #{id} and username = #{username} String
        sql = mappedStatement.getSql();
        BoundSql boundsql = getBoundSql(sql);
        // select * from where id = ? and username = ?
        String finalSql = boundsql.getSqlText();
        Class<?> paramterType = mappedStatement.getParamterType();
        PreparedStatement preparedStatement = connection.prepareStatement(finalSql);
        List<ParameterMapping> parameterMappingList = boundsql.getParameterMappingList();
        for (int i = 0; i < parameterMappingList.size(); i++) {
            ParameterMapping parameterMapping = parameterMappingList.get(i);
            String name = parameterMapping.getName();
            Field declaredField = paramterType.getDeclaredField(name);
            Object o = declaredField.get(param[0]);
            preparedStatement.setObject(i + 1, o);
        ResultSet resultSet = preparedStatement.executeQuery();
        Class<?> resultType = mappedStatement.getResultType();
        ArrayList<E> results = new ArrayList<E>();
        while ( {
            ResultSetMetaData metaData = resultSet.getMetaData();
            (E) resultType.newInstance();
            int columnCount = metaData.getColumnCount();
			for (int i = 1; i <= columnCount; i++) {
                String columnName = metaData.getColumnName(i);
                Object value = resultSet.getObject(columnName);
                PropertyDescriptor propertyDescriptor = new
                PropertyDescriptor(columnName, resultType);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(o, value);
		return results;
    public void close() throws SQLException {
    private BoundSql getBoundSql(String sql) {
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        //GenericTokenParser :通用的标记解析器,完成了代码片段中的占位符的解析,然后再根 据给定的标记处理器(TokenHandler)来进行表达式的处理
        //三个参数:分别为openToken (开始标记)、closeToken (结束标记)、handler (标记处理器)
        GenericTokenParser genericTokenParser = new GenericTokenParser("# {", "}", parameterMappingTokenHandler);
        String parse = genericTokenParser.parse(sql);
        List<ParameterMapping> parameterMappings =
        BoundSql boundSql = new BoundSql(parse, parameterMappings);
        return boundSql;


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;

1.5, custom framework optimization

Through the above-mentioned custom framework, we have solved some of the problems caused by JDBC operating database: for example, frequent creation and release of database connections, hard coding, manual packaging and return of result sets, etc., but now we have just completed the custom framework code, but also There are some problems:

  • There are duplicate codes in the implementation class of dao, and the process template of the entire operation is duplicated (create sqlsession, call sqlsession method, close sqlsession)
  • There is hard-coded in the implementation class of dao. When calling the sqlsession method, the id of the parameter statement is hard-coded

Solution: Use the proxy mode to create the proxy object of the interface

Add method in sqlSession

public interface SqlSession {
	public <T> T getMappper(Class<?> mapperClass);

Implementation class

public <T> T getMappper(Class<?> mapperClass) {
	T o = (T) Proxy.newProxyInstance(mapperClass.getClassLoader(), new
				Class[] {mapperClass}, new InvocationHandler() {
        public Object invoke(Object proxy, Method method, Object[] args)
        				throws Throwable {
            // selectOne
            String methodName = method.getName();
            // className:namespace
            String className = method.getDeclaringClass().getName();
            String key = className+"."+methodName;
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(key);
            Type genericReturnType = method.getGenericReturnType();
            ArrayList arrayList = new ArrayList<> ();
            if(genericReturnType instanceof ParameterizedType){
            	return selectList(key,args);
            return selectOne(key,args);
    return o;

Only the query function was implemented this time, and the addition, deletion, and modification functions of the framework were added later. See the source link below for the detailed process

Please click -> Handwritten persistence layer framework source code