[MyBatis interview questions (20 questions)]

MyBatis interview questions (20 questions)

image-20230820160603581

Base

1. What is MyBatis?

Let’s blow it first:

  • Mybatis is a semi-ORM (Object Relational Mapping) framework that encapsulates JDBC internally . When developing, you only need to focus on the SQL statement itself, and do not need to spend energy on complex processes such as loading drivers, creating connections, and creating statements. Programmers directly write original SQL, which can strictly control SQL execution performance and provide high flexibility .
  • MyBatis can use XML or annotations to configure and map native information, mapping POJOs into records in the database, avoiding almost all JDBC code and manual setting of parameters and obtaining result sets.

Let’s talk about the disadvantages:

  • The workload of writing SQL statements is relatively large, especially when there are many fields and related tables, which requires developers to have certain skills in writing SQL statements.
  • SQL statements depend on the database, resulting in poor database portability and the database cannot be replaced at will.

What is ORM?

image-20230820160803554

ORM (Object Relational Mapping) is a technology that solves the mapping relationship between relational database data and simple Java objects (POJO).

Simply put, ORM automatically persists objects in the program into a relational database by using metadata that describes the mapping between the object and the database .

Why is Mybatis said to be a semi-automatic ORM mapping tool? What is the difference between it and fully automatic?

  • Hibernate is a fully automatic ORM mapping tool. When you use Hibernate to query related objects or related collection objects, you can directly obtain them based on the object relationship model, so it is fully automatic.
  • When Mybatis queries related objects or related collection objects, it needs to be manually written SQL to complete, so it is called a semi-automatic ORM mapping tool.

What are the shortcomings of JDBC programming and how does MyBatis solve them?

image-20230820160939128

  1. Frequent creation and release of data connections causes a waste of system resources and affects system performance. Configure the data connection pool in mybatis-config.xml and use the connection pool to uniformly manage database connections.
  2. Writing sql statements in the code makes the code difficult to maintain . Configure the sql statements in the XXXXmapper.xml file and separate them from the java code.
  3. It is troublesome to pass parameters to the SQL statement , because the where condition of the SQL statement is not necessarily certain, it may be more or less, and the placeholder needs to correspond to the parameter. Mybatis automatically maps java objects to sql statements.
  4. It is troublesome to parse the result set . SQL changes lead to changes in the parsing code, and it needs to be traversed before parsing. It would be more convenient to parse the database records into pojo objects. Mybatis automatically maps sql execution results to java objects.

2.What is the difference between Hibernate and MyBatis?

Same point:

  • They are all encapsulations of jdbc and frameworks applied to the persistence layer.

difference:

1) Mapping relationship

  • MyBatis is a semi-automatic mapping framework that configures the correspondence between Java objects and SQL statement execution results. It is simple to configure multi-table relationships.
  • Hibernate is a full table mapping framework that configures the correspondence between Java objects and database tables, and configures complex tables with multi-table relationships.

2) SQL optimization and portability

  • Hibernate encapsulates SQL statements and provides features such as logging, caching, and cascading (cascading is more powerful than MyBatis). In addition, it also provides HQL (Hibernate Query Language) to operate the database. It has good database-independent support, but it will consume more performance. If the project needs to support multiple databases, the amount of code development will be small, but SQL statement optimization will be difficult .
  • MyBatis requires manual writing of SQL and supports dynamic SQL, processing lists, dynamically generating table names, and supporting stored procedures. The development workload is relatively large. Directly using SQL statements to operate the database does not support database independence, but it is easy to optimize SQL statements .

3) MyBatis and Hibernate have different applicable scenarios

  • Hibernate is a standard ORM framework. It requires less SQL writing but is not flexible enough. It is suitable for small and medium-sized software projects with relatively stable requirements, such as office automation systems.
  • MyBatis is a semi-ORM framework that requires writing more SQL, but it is more flexible and suitable for projects with frequent changes in demand and rapid iteration, such as e-commerce websites.

3.MyBatis usage process? life cycle?

The basic process of using MyBatis can be roughly divided into the following steps:

image-20230820161432104

1) Create SqlSessionFactory

SqlSessionFactory can be created from configuration or direct coding

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

2) Create SqlSession through SqlSessionFactory

​ SqlSession (session) can be understood as the bridge between the program and the database

SqlSession session = sqlSessionFactory.openSession();

3) Perform database operations through sqlsession

​Mapped SQL statements can be executed directly through the SqlSession instance:

Blog blog = (Blog)session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);

A more common way is to obtain the Mapper first and then execute the SQL statement:

BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);

4) Call session.commit() to commit the transaction

​ If it is an update or delete statement, we also need to submit the transaction.

5) Call session.close() to close the session

​ Finally, be sure to remember to close the session.

MyBatis life cycle?

Several MyBatis components are mentioned above. Generally speaking, the MyBatis life cycle is the life cycle of these components.

SqlSessionFactoryBuilder

  • Once the SqlSessionFactory is created, it is no longer needed. Therefore, the life cycle of the SqlSessionFactoryBuilder instance only exists inside the method.

SqlSessionFactory

  • SqlSessionFactory is used to create SqlSession, which is equivalent to a database connection pool. Every time SqlSessionFactory is created, database resources will be used. Multiple creation and destruction are a waste of resources. So SqlSessionFactory has an application-level life cycle and should be a singleton.

SqlSession

  • SqlSession is equivalent to Connection in JDBC. SqlSession instances are not thread-safe and therefore cannot be shared, so its optimal life cycle is one request or one method.

Mapper

  • Mapper is an interface that binds mapping statements. The instance of the mapper interface is obtained from SqlSession. Its life cycle is within the sqlsession transaction method and is generally controlled at the method level.

image-20230820162114969

Of course, everything can be integrated with Spring. MyBatis is usually integrated with Spring. Spring can help us create thread-safe, transaction-based SqlSession and mappers and inject them directly into our beans. We don’t need to care about them. The creation process and life cycle are another story.

4. How to pass multiple parameters in mapper?

image-20230820162226243

Method 1: Sequential parameter passing method

public User selectUser(String name, int deptId);

<select id="selectUser" resultMap="UserResultMap">
    select * from user
    where user_name = #{0} and dept_id = #{1}
</select>
  • #{}The numbers inside represent the order of the parameters passed in.
  • This method is not recommended because the SQL layer expression is not intuitive and is prone to errors once the order is adjusted.

Method 2: @Param annotation parameter passing method

public User selectUser(@Param("userName") String name, int @Param("deptId") deptId);

<select id="selectUser" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>
  • #{}The name inside corresponds to the name modified in the annotation @Param brackets.
  • This method is relatively intuitive when there are not many parameters (recommended).

Method 3: Map parameter passing method

public User selectUser(Map<String, Object> params);

<select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>
  • #{}The names inside correspond to the key names in the Map .
  • This method is suitable for passing multiple parameters , and the parameters are volatile and can be passed flexibly.

Method 4: Java Bean parameter passing method

public User selectUser(User user);

<select id="selectUser" parameterType="com.jourwon.pojo.User" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>
  • #{}The names inside correspond to the member attributes in the User class.
  • This method is intuitive and requires building an entity class. It is not easy to extend and requires adding attributes. However, the code is highly readable and the business logic is easy to process. (Recommended Use).

5. What should I do if the entity class attribute name is different from the field name in the table?

Type 1: By defining the alias of the field name in the query SQL statement , make the alias of the field name consistent with the attribute name of the entity class.

<select id="getOrder" parameterType="int" resultType="com.jourwon.pojo.Order">
    select order_id id, order_no orderno, order_price price form orders 
    where order_id=#{id};
</select>

Type 2: Use <result> in resultMap to map the one-to-one correspondence between field names and entity class attribute names.

<select id="getOrder" parameterType="int" resultMap="orderResultMap">
	select * from orders where order_id=#{id}
</select>

<resultMap type="com.jourwon.pojo.Order" id="orderResultMap">
    <!–用id属性来映射主键字段–>
    <id property="id" column="order_id">

    <!–用result属性来映射非主键字段,property为实体类属性名,column为数据库表中的属性–>
    <result property ="orderno" column ="order_no"/>
    <result property="price" column="order_price" />
</resultMap>

6.Can Mybatis map the Enum enumeration class?

  • Of course, Mybatis can map enumeration classes. Not only can it map enumeration classes, Mybatis can map any object to a column of the table. The mapping method is to customize a TypeHandler and implement the TypeHandler setParameter()and getResult()interface methods.
  • TypeHandler has two functions:
    • One is to complete the conversion from javaType to jdbcType;
    • The second is to complete the conversion from jdbcType to javaType.

It is embodied in two methods: setParameter()and getResult(), which respectively represent setting the sql question mark placeholder parameters and obtaining the column query results.

7. What is the difference between #{} and ${}?

image-20230820163559653

  • #{} is a placeholder, pre-compiled; s{} is a splicer, string replacement, not pre-compiled.
  • When Mybatis processes #{}, the #{} incoming parameter is passed in as a string. Will it replace #{} in SQL with? No., call the set method of PreparedStatement to assign value.
  • #{} can effectively prevent SQL injection and improve system security; ${} cannot prevent SQL injection.
  • The variable substitution of #{} is in the DBMS; the variable substitution of s{} is outside the DBMS.

DBMS:Database Management System

8. How to write a fuzzy query like statement?

image-20230820163856729

  1. '%${question}%'May cause SQL injection, not recommended
  2. "%"#{question}"%"Note: Because when #{.…} is parsed into a sql statement, single quotes '' will be automatically added outside the variable, so % needs to use double quotes "" here, and single
    quotes cannot be used, otherwise no results will be found.
  3. CONCAT('%', #{question},'%')Use the CONCAT() function, (recommended)
  4. Use bind tag (not recommended)
<select id="listUserLikeUsername" resultType="com.jourwon.pojo.User">
	<bind name="pattern" value="'%' + username + '%'" />
	select id,sex,age,username,password from person where username LIKE #{pattern}
</select>

9. Can Mybatis perform one-to-one and one-to-many related queries?

Of course, it not only supports one-to-one and one-to-many related queries, but also supports many-to-many and many-to-one related queries.

image-20230820164506343

  • One-on-one <association>

For example, orders and payments have a one-to-one relationship. The realization of this relationship:

Entity class:

public class Order {
    
    
    private Integer orderId;
    private String orderDesc;
    /**
    * 支付对象
    */
    private Pay pay;
    //……
}

Result mapping:

<!-- 订单resultMap -->
<resultMap id="peopleResultMap" type="cn.fighter3.entity.Order">
    <id property="orderId" column="order_id" />
    <result property="orderDesc" column="order_desc"/>
    
    <!--一对一结果映射-->
    <association property="pay" javaType="cn.fighter3.entity.Pay">
        <id column="payId" property="pay_id"/>
        <result column="account" property="account"/>
    </association>
</resultMap>

The query is an ordinary correlation query:

<select id="getTeacher" resultMap="getTeacherMap" parameterType="int">
    select * from order o
    left join pay p on o.order_id=p.order_id
    where o.order_id=#{orderId}
</select>
  • One-to-many<collection>

For example, product categories and products have a one-to-many relationship.

Entity class:

public class Category {
    
    
    private int categoryId;
    private String categoryName;
    /**
    * 商品列表
    **/
    List<Product> products;
    //……
}

Result mapping:

<resultMap type="Category" id="categoryBean">
    <id column="categoryId" property="category_id" />
    <result column="categoryName" property="category_name" />
    
    <!-- 一对多的关系 -->
    <!-- property: 指的是集合属性的值, ofType:指的是集合中元素的类型 -->
    <collection property="products" ofType="Product">
        <id column="product_id" property="productId" />
        <result column="productName" property="productName" />
        <result column="price" property="price" />
    </collection>
</resultMap>

The query is just an ordinary relational query:

<!-- 关联查询分类和产品表 -->
<select id="listCategory" resultMap="categoryBean">
	select c.*, p.* from category_ c left join product_ p on c.id = p.cid
</select>

So how to implement many-to-one and many-to-many? Still use <association> and <collection>. Due to space limitations, we will not go into details here.

10.Does Mybatis support lazy loading? principle?

  • Mybatis supports lazy loading of association objects and collection objects . Association refers to one-to-one, and collection refers to one-to-many query. In the Mybatis configuration file, you can configure whether to enable lazy loading lazyLoadingEnabled=true|false .
  • Its principle is to use CGLIB to create a proxy object of the target object. When the target method is called, the interceptor method is entered . For example, if a.getB().getName()the interceptor invoke()method finds a.getB()a null value, then the pre-saved query associated with the B object will be sent separately. sql, query B , and then call it a.setB(b), so the object b attribute of a has a value, and then complete a.getB().getName()the method call. This is the basic principle of lazy loading.
  • Of course, not only Mybatis, but almost all including Hibernate, the principle of supporting lazy loading is the same.

11. How to get the generated primary key?

  • Add: keyProperty="ID" to the new tag
<insert id="insert" useGeneratedKeys="true" keyProperty="userId" >
    insert into user(
    user_name, user_password, create_time)
    values(#{userName}, #{userPassword} , #{createTime, jdbcType= TIMESTAMP})
</insert>
  • At this time, you can complete the backfilling of the primary key
mapper.insert(user);
user.getId;

12.Does MyBatis support dynamic SQL?

There are some tags in MyBatis that support dynamic SQL. Their principle is to use OGNL to calculate the value of the expression from the SQL parameter object and dynamically splice SQL based on the value of the expression to complete the function of dynamic SQL.

OGNL: The full name is Object-Graph Navigation Language, which is an object graph navigation language. It is a powerful open source expression language.

image-20230820170227229

  • if

Form a where clause based on conditions

<select id="findActiveBlogWithTitleLike" resultType="Blog">
    SELECT * FROM BLOG
    WHERE state = ‘ACTIVE’
    <if test="title != null">
    	AND title like #{title}
    </if>
</select>
  • choose(when, otherwise)

This is somewhat similar to the switch statement in Java

<select id="findActiveBlogLike" resultType="Blog">
    SELECT * FROM BLOG WHERE state = ‘ACTIVE’
    <choose>
        <when test="title != null">
        	AND title like #{title}
        </when>
        <when test="author != null and author.name != null">
        	AND author_name like #{author.name}
        </when>
        <otherwise>
        	AND featured = 1
        </otherwise>
    </choose>
</select>
  • trim(where, set)
  • <where> can be used when all query conditions are dynamic
<select id="findActiveBlogLike" resultType="Blog">
    SELECT * FROM BLOG
    <where>
        <if test="state != null">
        	state = #{state}
        </if>
        <if test="title != null">
        	AND title like #{title}
        </if>
        <if test="author != null and author.name != null">
        	AND author_name like #{author.name}
        </if>
    </where>
</select>
  • <set> can be used during dynamic updates
<update id="updateAuthorIfNecessary">
    update Author
    <set>
        <if test="username != null">username=#{username},</if>
        <if test="password != null">password=#{password},</if>
        <if test="email != null">email=#{email},</if>
        <if test="bio != null">bio=#{bio}</if>
    </set>
    where id=#{id}
</update>
  • foreach

You will know when you see the name. This is used for looping and traversing the collection.

<select id="selectPostIn" resultType="domain.blog.Post">
    SELECT * FROM POST P
    <where>
        <foreach item="item" index="index" collection="list"
            open="ID in (" separator="," close=")" nullable="true" >
            #{item}
        </foreach>
    </where>
</select>

13.How does MyBatis perform batch operations?

image-20230820183750988

First method: use foreach tag

foreach is mainly used to build in conditions, it can iterate a collection in a SQL statement.

The attributes of the foreach tag mainly include item, index, collection, open, separator, and close.

  • item represents the alias of each element in the collection when iterating, and it is a random variable name;
  • index specifies a name to indicate the position reached by each iteration during the iteration process, which is not commonly used;
  • open indicates what the statement starts with, commonly used "(";
  • separator indicates what symbol is used as the separator between each iteration, commonly used ",";
  • close indicates what to end with, commonly used ")".

The most critical and most error-prone thing when using foreach is the collection attribute . This attribute must be specified, but in different situations, the value of this attribute is different. There are three main situations:

  1. If a single parameter is passed in and the parameter type is a List, the collection attribute value is list.
  2. If a single parameter is passed in and the parameter type is an array array, the attribute value of the collection is array.
  3. If there are multiple parameters passed in, we need to encapsulate them into a Map. Of course, a single parameter can also be encapsulated into a map. In fact, if you pass in parameters, it will also be encapsulated in MyBatis. It becomes a Map, and the key of the map is the parameter name, so at this time, the collection attribute value is the key of the passed in List or array object in its own encapsulated map.

Take a look at two ways to save in batches:

<!-- MySQL下批量保存,可以foreach遍历 mysql支持values(),(),()语法 --> //推荐使用
<insert id="addEmpsBatch">
    INSERT INTO emp(ename,gender,email,did)
    VALUES
    <foreach collection="emps" item="emp" separator=",">
    	(#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})
    </foreach>
</insert>
<!-- 这种方式需要数据库连接属性allowMutiQueries=true的⽀支持
如jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true -->
<insert id="addEmpsBatch">
    <foreach collection="emps" item="emp" separator=";">
        INSERT INTO emp(ename,gender,email,did)
        VALUES(#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})
    </foreach>
</insert>

Second method: use ExecutorType.BATCH

  • There are three built-in ExecutorTypes in Mybatis. The default is simple. In this mode, it creates a new preprocessing statement for the execution of each statement and submits a single SQL statement;
  • The batch mode reuses preprocessed statements and executes all update statements in batches. Obviously, batch performance will be better;
  • But the batch mode also has its own problems. For example, during the Insert operation, there is no way to obtain the auto-incremented ID before the transaction is submitted. In some cases, it does not meet the needs of the business.

The specific usage is as follows:

//批量保存方法测试
@Test
public void testBatch() throws IOException{
    
    
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    //可以执行批量操作的sqlSession
    SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
    //批量保存执行前时间
    long start = System.currentTimeMillis();
    try {
    
    
    	EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
    	for (int i = 0; i < 1000; i++) {
    
    
            mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0, 5),"b", "1"));
    	}
        openSession.commit();
        long end = System.currentTimeMillis();
        
        //批量保存执行后的时间
        System.out.println("执行时长" + (end - start));        
        //批量 预编译sql一次==》设置参数==》10000次==》执行1次 677
        //非批量 (预编译=设置参数=执行 )==》10000次 1121
    } finally {
    
    
    	openSession.close();
    }
}
  • mapper and mapper.xml are as follows:
public interface EmployeeMapper {
    
    
    //批量保存员工
    Long addEmp(Employee employee);
}
<mapper namespace="com.jourwon.mapper.EmployeeMapper">
    <!--批量量保存员⼯工 -->
    <insert id="addEmp">
        insert into employee(lastName,email,gender)
        values(#{lastName},#{email},#{gender})
    </insert>
</mapper>

14. Talk about Mybatis’s first-level and second-level cache?

  1. First-level cache: HashMap local cache based on PerpetualCache. Its storage scope is SqlSession . The caches between each SqlSession are isolated from each other . When the Session is flushed or closed, all Cache in the SqlSession will be emptied. MyBatis opens the first-level cache by default. .

image-20230820185111588

  1. The second-level cache has the same mechanism as the first-level cache. By default, it also uses PerpetualCache and HashMap storage. The difference is that its storage scope is Mapper (Namespace) , which can be shared between multiple SqlSession , and the storage source can be customized, such as Ehcache. The second-level cache is not enabled by default. To enable the second-level cache, you need to implement the Serializable serialization interface (which can be used to save the state of the object) to use the second-level cache attribute class, which can be configured in its mapping file.

image-20230820185247411

principle

15. Can you tell me how MyBatis works?

We already have a rough idea of ​​the workflow of MyBatis. According to the working principle, it can be divided into two major steps: 生成会话工厂, 会话运行.

image-20230820203332214

MyBatis is a mature framework, and the space is limited. Let’s focus on the big and let go of the small, and take a look at its main workflow.

Build session factory

Constructing a session factory can also be divided into two steps:

image-20230820203453775

Get configuration:

The step of obtaining the configuration has gone through several transformations, and finally a configuration class Configuration instance is generated. This configuration class instance is very important. Its main functions include:

  • Read configuration files, including basic configuration files and mapping files
  • Initialize basic configuration, such as MyBatis alias, and other important class objects, such as plug-ins, mappers, ObjectFactory, etc.
  • Provide a singleton as an important parameter for session factory construction
  • Its build process will also initialize some environment variables, such as data sources
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    
    
    SqlSessionFactory var5;
    //省略异常处理
        //xml配置构建器
        XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
        //通过转化的Configuration构建SqlSessionFactory
        var5 = this.build(parser.parse());
}
  • BuildSqlSessionFactory

SqlSessionFactory is just an interface, and what is built is actually an instance of its implementation class. Generally, we use its implementation class DefaultSqlSessionFactory.

public SqlSessionFactory build(Configuration config) {
    
    
	return new DefaultSqlSessionFactory(config);
}

session run

Session running is the most complex part of MyBatis. Its operation cannot be separated from the cooperation of four major components:

image-20230820203959562

To summarize the session run as a whole:

image-20230820204719649

Executor

  • Executor plays a vital role. SqlSession is just a facade, equivalent to customer service. The real work is Executor, like an unknown engineer. It provides corresponding query and update methods, as well as transaction methods .
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//通过Configuration创建executor
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);

StatementHandler (database session handler)

  • StatementHandler, as the name suggests, handles database sessions . Let's take SimpleExecutor as an example and take a look at its query method. First, it creates a StatementHandler instance, and then uses this handler to execute the query.
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, 
                           ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    
    
    Statement stmt = null;
    
    List var9;
    try {
    
    
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        stmt = this.prepareStatement(handler, ms.getStatementLog());
        var9 = handler.query(stmt, resultHandler);
    } finally {
    
    
    	this.closeStatement(stmt);
    }
    return var9;
}
  • Let’s take a look at its query method using the most commonly used PreparedStatementHandler. In fact, the above prepareStatement has already pre-compiled the parameters. At this point, the sql is executed directly and the ResultHandler is used to process the returned results.
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    
    
    PreparedStatement ps = (PreparedStatement)statement;
    ps.execute();
    return this.resultSetHandler.handleResultSets(ps);
}

ParameterHandler (parameter handler)

  • SQL is precompiled in PreparedStatementHandler
public void parameterize(Statement statement) throws SQLException {
    
    
	this.parameterHandler.setParameters((PreparedStatement)statement);
}
  • ParameterHandler is used here, and the function of setParameters is to set the parameters of the precompiled SQL statement.
  • The typeHandler type processor is also used to process types.
public interface ParameterHandler {
    
    
    Object getParameterObject();
    void setParameters(PreparedStatement var1) throws SQLException;
}

ResultSetHandler (result handler)

  • We have also seen before that the final result must be processed through ResultSetHandler. The handleResultSets method is used to wrap the result set.
  • Mybatis provides us with a DefaultResultSetHandler, which is usually used to process results.
public interface ResultSetHandler {
    
    
    <E> List<E> handleResultSets(Statement var1) throws SQLException;
    <E> Cursor<E> handleCursorResultSets(Statement var1) throws SQLException;
    void handleOutputParameters(CallableStatement var1) throws SQLException;
}

It will use typeHandle to process the type, then assemble the object using the rules provided by ObjectFactory and return it to the caller.

Finally, we connect the entire workflow together and briefly summarize:

image-20230820204928378

  1. Read the MyBatis configuration file —mybatis-config.xml, load the mapping file—the mapping file is the SQL mapping file, and the SQL statements for operating the database are configured in the file. Finally, a configuration object is generated .
  2. Construct a session factory : Construct the session factory SqlSessionFactory through MyBatis environment and other configuration information.
  3. Create session object : The SqlSession object is created by the session factory, which contains all methods for executing SQL statements .
  4. Executor executor : The bottom layer of MyBatis defines an Executor interface to operate the database. It will dynamically generate SQL statements that need to be executed based on the parameters passed by SqlSession , and is also responsible for the maintenance of the query cache.
  5. StatementHandler: Database session handler, which connects the processing of parameter mapping and the processing of running result mapping in series .
  6. Parameter processing: Process the types of input parameters and precompile them .
  7. Result processing: Process the type of the returned result, and return the corresponding object according to the object mapping rules .

16.What is the functional architecture of MyBatis?

image-20230820205408347

We generally divide the functional architecture of Mybatis into three layers:

  • API interface layer : Interface API provided for external use. Developers use these local APIs to manipulate the database . Once the interface layer receives the call request, it will call the data processing layer to complete specific data processing.
  • Data processing layer : Responsible for specific SQL search, SQL parsing, SQL execution and execution result mapping processing , etc. Its main purpose is to complete a database operation according to the calling request.
  • Basic support layer : Responsible for the most basic functional support, including connection management, transaction management, configuration loading and cache processing . These are common things, and they are extracted as the most basic components. Provide the most basic support for the upper data processing layer.

17.Why does the Mapper interface not require an implementation class?

Four-word answer: dynamic proxy. Let’s take a look at the process of obtaining Mapper:

image-20230820205821566

  • Get Mapper

We all know that the defined Mapper interface has no implementation class, and Mapper mapping is actually implemented through dynamic proxy .

BlogMapper mapper = session.getMapper(BlogMapper.class);

After going around and around to take a look, I found that the process of obtaining Mapper requires first obtaining MapperProxyFactory—Mapper agent factory.

  • MapperProxyFactory

The role of MapperProxyFactory is to generate MapperProxy (Mapper proxy object).

Here you can see the binding of the dynamic proxy to the interface. Its function is to generate a dynamic proxy object (placeholder), and the proxy method is placed in MapperProxy.

  • MapperProxy

In MapperProxy, a MapperMethod object is usually generated, which is initialized through the cachedMapperMethod method, and then executes the excute method.

  • MapperMethod

The excute method in MapperMethod will actually execute sql. The command mode is used here. In fact, in a circle, it ultimately runs the object's SQL through the instance of SqlSession.

18.What Executors are available in Mybatis?

image-20230820210336429

Mybatis has three basic Executor executors, SimpleExecutor, ReuseExecutor, and BatchExecutor .

  • SimpleExecutor : Every time an update or select is executed, a Statement object is opened and the Statement object is closed immediately after use.
  • ReuseExecutor : Execute update or select, use sql as the key to find the Statement object, use it if it exists, and create it if it does not exist. After use, the Statement object is not closed, but placed in Map<String, Statement> for next use. In short, it is to reuse Statement objects .
  • BatchExecutor : execute update (no select, JDBC batch processing does not support select), add all sql to the batch (addBatch()), wait for unified execution (executeBatch()), it caches multiple Statement objects, each After the Statement objects are completed with addBatch(), they wait for executeBatch() batch processing to be executed one by one. Same as JDBC batch processing.

Scope of scope: These features of Executor are strictly limited within the scope of the SqlSession life cycle .

How to specify which Executor to use in Mybatis?

  • In the Mybatis configuration file, you can specify the default ExecutorType executor type in settings, or you can manually pass ExecutorType type parameters to the DefaultSqlSessionFactory method of creating SqlSession, such as sqlsession openSession(ExecutorType execType).
  • Configure the default executor. SIMPLE is an ordinary executor; the REUSE executor will reuse prepared statements; the BATCH executor will reuse statements and perform batch updates.

plug-in

19. Talk about the operating principle of Mybatis plug-in and how to write a plug-in?

How does the plug-in work?

The operation of Mybatis session requires the cooperation of four major objects: ParameterHandler, ResultSetHandler, StatementHandler, and Executor. The principle of the plug-in is to insert some of our own code when scheduling these four objects.

image-20230820210956715

Mybatis uses JDK's dynamic proxy to generate proxy objects for target objects. It provides a tool class pluginthat implements InvocationHandlerthe interface.

image-20230820211042127

Using Pluginthe generated proxy object, the proxy object will enter the invoke method when calling the method. In the invoke method, if there is a signed interception method, the plug-in's intercept method will be called here by us, and then the result will be returned. If the signature method does not exist, the method we want to execute will be called directly through reflection.

How to write a plugin?

When we write the MyBatis plug-in ourselves, we only need to implement the interceptor interface Interceptor(org.apache.ibatis.plugin Interceptor)and process the interception objects and methods in the implementation class.

  • Implement the Interceptor interface of Mybatis and rewrite intercept()the method

Here we only print before and after the target object executes the target method;

public class MyInterceptor implements Interceptor {
    
    
    Properties props=null;
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
    
    
        System.out.println("before……");
        //如果当前代理的是一个非代理对象,那么就会调用真实拦截对象的方法
        // 如果不是它就会调用下个插件代理对象的invoke方法
        Object obj=invocation.proceed();
        System.out.println("after……");
        return obj;
    }
}
  • Then write annotations for the plug-in to determine the objects to be intercepted and the methods to be intercepted.
@Intercepts({
    
    @Signature(
        type = Executor.class, //确定要拦截的对象
        method = "update", //确定要拦截的⽅方法
        args = {
    
    MappedStatement.class, Object.class} //拦截⽅方法的参数
)})
public class MyInterceptor implements Interceptor {
    
    
    Properties props=null;
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
    
    
        System.out.println("before……");
        //如果当前代理的是一个非代理对象,那么就会调用真实拦截对象的方法
        // 如果不是它就会调用下个插件代理对象的invoke方法
        Object obj=invocation.proceed();
        System.out.println("after……");
        return obj;
    }
}
  • Finally, configure the plug-in in the MyBatis configuration file
<plugins>
    <plugin interceptor="xxx.MyPlugin">
    <property name="dbType",value="mysql"/>
    </plugin>
</plugins>

20.How does MyBatis perform paging? What is the principle of paging plug-in?

How does MyBatis paginate?

MyBatis uses RowBounds objects for paging, which is memory paging performed for the ResultSet result set, rather than physical paging . You can directly write parameters with physical paging in SQL to complete the physical paging function, or you can use a paging plug-in to complete physical paging.

What is the principle of paging plug-in?

  • The basic principle of the paging plug-in is to use the plug-in interface provided by Mybatis to implement custom plug-ins and intercept the query method of the Executor.
  • When executing the query, intercept the SQL to be executed, then rewrite the SQL, and add the corresponding physical paging statements and physical paging parameters according to the dialect dialect.
  • For example: select * from student, after intercepting the sql, rewrite it as:select t.* from (select * from student) t limit 0, 10

Source: The counterattack of noodle scum: Twenty-two pictures, eight thousand words, twenty questions, completely defeat MyBatis!

Guess you like

Origin blog.csdn.net/weixin_45483322/article/details/132396191