Mybatis source code analysis preparation

Mybatis source code analysis preparation

tips:mybatis

1. Build Mybatis source environment

1. Environmental requirements:

JDK1.8, Maven, Eclipse or IntelliJ IDEA

2. Get the source code

  • Mybatis source code:

https://github.com/mybatis/mybatis-3

  • Mybatis and Spring integrated source code:

https://github.com/mybatis/spring

  • Mybatis parent class source code:

https://github.com/mybatis/parent

3. Import the source code into the IDE

  • File => New => Project… => Empty Project => Next => 命名 => Finish

  • File => New => Module from Existing Sources… => Select three source codes and one test code respectively, and import them separately

4. Introduction to HSQLDB

  • The memory mode of HSQLDB embedded in Mybatis is used as a database for unit testing, so there is no need to install databases such as Mysql.
  • The user name and password of the memory mode can be specified at will, but if you need to persist it locally, you need to specify the user name and password.
  • Unit tests don't care, they can be specified at will. The default user name is sa and the password is blank.
  • ScriptRunner is used to execute (XXX.sql) scripts, and SqlRunner is used to execute sql statements (addition, deletion, modification and query statements).

5. Source code analysis entry:

public class Example01 {
    private Connection conn = null;
    @Before
    public void initData() {
        try {
            //Example02 example02 = new Example02();//調用方法的時候回提示,引入依賴,但是調用common裡面的文件的時候卻不用。
            // 加载HSQLDB驱动
            Class.forName("org.hsqldb.jdbcDriver");
            // 获取Connection对象,用户名和密码可以随便指定
            conn = DriverManager.getConnection("jdbc:hsqldb:mem:mybatis",
                    "satestlalla", "");
            // 使用Mybatis的ScriptRunner工具类执行数据库脚本
            ScriptRunner scriptRunner = new ScriptRunner(conn);
            scriptRunner.setLogWriter(null);
            //文件都放在mybatis-common module的resource下面
            scriptRunner.runScript(Resources.getResourceAsReader("create-table.sql"));
            scriptRunner.runScript(Resources.getResourceAsReader("init-data.sql"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testHsqldbQuery() {
        // SqlRunner是Mybatis封装的操作数据库的工具类
        SqlRunner sqlRunner = new SqlRunner(conn);
        try {
            //调用SqlRunner类的selectAll()方法查询数据
            List<Map<String, Object>> results = sqlRunner.selectAll("select * from user");
            results.forEach(System.out::println);//jdk8之后新特性
            sqlRunner.closeConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Second, the JDBC specification

1. jdbc specification document

  • JDBC4.2 specification document download address: https://download.oracle.com/otndocs/jcp/jdbc-4_2-mrel2-spec/index.html

2. Steps to operate the database

  • Establish a data connection
  • Execute SQL statement
  • Retrieve SQL execution results
  • Close connection

3. Establish a connection to the database

Three ways:

  • DriverManager——provided by JDBC1.0

    Need to show loading driver class:

    Class.forName("org.hsqldb.jdbcDriver");

  • DataSource——provided by JDBC2.0 (recommended, more flexible configuration)

    • DataSource: the most basic class
    • DataSourceFactory: factory class
    • ConnectionPoolDataSource: supports caching and reuse of Connection objects
    • XADataSource: supports distributed transactions

The source code is as follows:

//第一种方式
// 加载驱动 到JDBC4之后不需要直接声明驱动,可以在META-INF.services目录下面创建一个跟驱动同名的文件即可,参考chapter02.SPIExample
/*原理如下
ServiceLoader<Driver> drivers = ServiceLoader.load(java.sql.Driver.class);
        for (Driver driver : drivers ) {
            System.out.println(driver.getClass().getName());
        }
*/
Class.forName("org.hsqldb.jdbcDriver");
// 获取Connection对象
Connection connection = DriverManager.getConnection("jdbc:hsqldb:mem:mybatis",
                    "sa", "");

//第二种方式
// 创建DataSource实例
DataSource dataSource = new UnpooledDataSource("org.hsqldb.jdbcDriver","jdbc:hsqldb:mem:mybatis", "sa", "");
// 获取Connection对象
Connection connection = dataSource.getConnection();

//第三种方式
// 创建DataSource实例
DataSourceFactory dsf = new UnpooledDataSourceFactory();
Properties properties = new Properties();
InputStream configStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("database.properties");
properties.load(configStream);
dsf.setProperties(properties);
DataSource dataSource = dsf.getDataSource();
// 获取Connection对象
Connection connection = dataSource.getConnection();

//插入操作
Statement statement = connection.createStatement();
statement.addBatch("insert into  " +
                    "user(create_time, name, password, phone, nick_name) " +
                    "values('2010-10-24 10:20:30', 'User1', 'test', '18700001111', 'User1');");
            statement.addBatch("insert into " +
                    "user (create_time, name, password, phone, nick_name) " +
                    "values('2010-10-24 10:20:30', 'User2', 'test', '18700002222', 'User2');");
            statement.executeBatch();

//PreparedStatement可配置参数方式操作数据库
PreparedStatement stmt = connection.prepareStatement("insert into  " +
                    "user(create_time, name, password, phone, nick_name) " +
                    "values(?,?,?,?,?);");
            stmt.setString(1,"2010-10-24 10:20:30");
            stmt.setString(2,"User1");
            stmt.setString(3,"test");
            stmt.setString(4,"18700001111");
            stmt.setString(5,"User1");
            ParameterMetaData pmd = stmt.getParameterMetaData();
            for(int i = 1; i <= pmd.getParameterCount(); i++) {
                String typeName = pmd.getParameterTypeName(i);
                String className = pmd.getParameterClassName(i);
                System.out.println("第" + i + "个参数," + "typeName:" + typeName + ", className:" + className);
            }
            stmt.execute();

//自增长主键按,可以获取数据库里面自增长的当前值
Statement stmt = conn.createStatement();
            String sql = "insert into user(create_time, name, password, phone, nick_name) " +
                    "values('2010-10-24 10:20:30','User1','test','18700001111','User1');";
            ResultSet genKeys = stmt.getGeneratedKeys();
            stmt.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
            genKeys = stmt.getGeneratedKeys();
            if(genKeys.next()) {
                System.out.println("自增长主键:" + genKeys.getInt(1));
            }

//数据库配置信息
DatabaseMetaData dmd = conn.getMetaData();
            System.out.println("数据库URL:" + dmd.getURL());
            System.out.println("数据库用户名:" + dmd.getUserName());
            System.out.println("数据库产品名:" + dmd.getDatabaseProductName());
            System.out.println("数据库产品版本:" + dmd.getDatabaseProductVersion());
            System.out.println("驱动主版本:" + dmd.getDriverMajorVersion());
            System.out.println("驱动副版本:" + dmd.getDriverMinorVersion());
            System.out.println("数据库供应商用于schema的首选术语:" + dmd.getSchemaTerm());
            System.out.println("数据库供应商用于catalog的首选术语:" + dmd.getCatalogTerm());
            System.out.println("数据库供应商用于procedure的首选术语:" + dmd.getProcedureTerm());
            System.out.println("null值是否高排序:" + dmd.nullsAreSortedHigh());
            System.out.println("null值是否低排序:" + dmd.nullsAreSortedLow());
            System.out.println("数据库是否将表存储在本地文件中:" + dmd.usesLocalFiles());
            System.out.println("数据库是否为每个表使用一个文件:" + dmd.usesLocalFilePerTable());
            System.out.println("数据库SQL关键字:" + dmd.getSQLKeywords());

//保存节点savepoint,可以回滚使用。
String sql1 = "insert into user(create_time, name, password, phone, nick_name) " +
                    "values('2010-10-24 10:20:30','User1','test','18700001111','User1')";
            String sql2 = "insert into user(create_time, name, password, phone, nick_name) " +
                    "values('2010-10-24 10:20:30','User2','test','18700001111','User2')";
            conn.setAutoCommit(false);
            Statement stmt = conn.createStatement();
            stmt.executeUpdate(sql1);
            // 创建保存点
            Savepoint savepoint = conn.setSavepoint("SP1");
            stmt.executeUpdate(sql2);
            // 回滚到保存点
            conn.rollback(savepoint);
            conn.commit();

//后续打印统一代码
			ResultSet rs  = conn.createStatement().executeQuery("select * from user ");
			//上面是下面的简单缩写
			//Statement statement = connection.createStatement();
            //ResultSet resultSet = statement.executeQuery("select * from user");
            // 遍历ResultSet
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columCount = metaData.getColumnCount();
            while (resultSet.next()) {
                for (int i = 1; i <= columCount; i++) {
                    String columName = metaData.getColumnName(i);
                    String columVal = resultSet.getString(columName);
                    System.out.println(columName + ":" + columVal);
                }
                System.out.println("--------------------------------------");
            }
            // 关闭连接
            IOUtils.closeQuietly(statement);
            IOUtils.closeQuietly(connection);

Three, Mybatis commonly used tools

1. Use the SQL class to generate statements:

Traditionally, string concatenation requires attention to details such as spaces, which is very troublesome. This problem can be solved by SQL encapsulation.

Two ways of implementation:

  • new SQL().SELECT().SELECT().FROM()....

  • new SQL(){{SELECT();SELECT();FROM();....}}

The specific code is as follows:

String insertSql = new SQL().
            INSERT_INTO("PERSON").
            VALUES("ID, FIRST_NAME", "#{id}, #{firstName}").
            VALUES("LAST_NAME", "#{lastName}").toString();
/*结果是:
INSERT INTO PERSON
 (ID, FIRST_NAME, LAST_NAME)
VALUES (#{id}, #{firstName}, #{lastName})
*/
String sql_area = new SQL()
            {
                {
                    SELECT("P.ID, P.USERNAME, P.PASSWORD");
                    SELECT("P.FIRST_NAME, P.LAST_NAME");
                    FROM("PERSON P");
                    WHERE("P.ID = #{id}");
                    WHERE("P.FIRST_NAME = ${first_name}");
                }
            }.toString();
/*结果是:
SELECT P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME
FROM PERSON P
WHERE (P.ID = #{id} AND P.FIRST_NAME = ${first_name})
*/

2. Use ScriptRunner to execute scripts

Source logic:

  • Call the setAuoCommit () method to set whether the transaction is automatically committed based on the value of the autoCommit attribute .
  • Determine the value of the sendFullScript attribute . If it is true, call the executeFullScript () method to read the file content at one time , and then call the execute () method of the Statement object to execute all SQL statements at once.
  • If the value of the sendFullScript property is false, the executeLineByLine () method is called to read the SQ L line by line, and the SQL statement is executed line by line as the end of a SQL statement.

3. Use SqlRunner to execute scripts

  • Summary of class methods:

  • closeConnection: Close the Connection connection.

  • selectOne, selectAll: Find a record (return one), find all records (return multiple).

  • insert, update, delete: insert, update, delete operations

  • run: execute a sql statement, preferably DDL

  • Source logic:

  • After calling the PrepareStatement () method of the Connection object to obtain the PreparedStatement object, call the setParameters () method to assign values ​​to the parameter placeholders in SQL , and to determine the type of each parameter .

  • Call the executeQuery () method of PreparedStatement to execute the query operation

  • Call the getResult () method to convert the ResultSet object to a List object , where each Map object of List corresponds to a record.

Fourth, involving design patterns

  1. Builder模式:例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder;

  2. Factory mode: for example, SqlSessionFactory, ObjectFactory, MapperProxyFactory;

  3. Singleton mode: such as ErrorContext and LogFactory;

  4. Proxy mode: the core of Mybatis implementation, such as MapperProxy, ConnectionLogger, jdk dynamic proxy; and the executor.loader package uses cglib or javassist to achieve the effect of lazy loading;

  5. Combination mode: for example, SqlNode and various subclasses ChooseSqlNode, etc .;

  6. Template method pattern: such as BaseExecutor and SimpleExecutor, as well as BaseTypeHandler and all subclasses such as IntegerTypeHandler;

  7. Adapter mode: For example, the Mybatis interface of Log and its adaptation to various log frameworks such as jdbc and log4j;

  8. Decorator mode: For example, the implementation of various decorators in the cache.decorators sub-package in the Cache package;

  9. Iterator mode: For example, iterator mode PropertyTokenizer;

Five, source code logic

XML parsing = "Get Configuration =" Contains DateSource data source, statementId and MappedStatement corresponding Map variables = "MappedStatement contains sqlSource, parameters and return value types and other XML parsing information =" The sqlSouce interface implements a variety of classes, including the SqlNode interface class ( XML parses the original SQL. Only when it is executed, will it know the specific SQL, because there are judgments and specific attribute values) and get the BoundSql method (the result of the original SQL after analysis is only encapsulated in a class) => Sqlnode implementation The class is a list. The elements in the list are a tree structure that stores the text and label loops under the sql label. At the same time, the SqlSouce implementation class has a BoundSql attribute value. Sqlsouce is obtained after parsing the obtained XML, and boundSql is parsed after the XML parsing result obtained during execution. = "Execute sql according to the analysis result
Insert picture description here

Published 10 original articles · Likes2 · Visits 1924

Guess you like

Origin blog.csdn.net/yuhao22/article/details/105619630