Mybatis3 detailed explanation of the global configuration file

1. Global configuration file

Not all of the Mybatis global files we saw earlier are listed, so in this chapter we will introduce it in detail. The global configuration file of Mybatis is not very complicated. All its elements and codes are as follows:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
 
<configuration> <!--配置-->
    <properties/> <!--属性-->
    <settings/> <!--全局配置参数-->
    <typeAliases/> <!--类型别名-->
    <typeHandlers/> <!--类型处理器-->
    <objectFactory/><!--对象工厂-->
    <plugins/><!--创建-->
    <environments default=""><!--环境配置-->
        <environment id=""><!--环境变量-->
            <transactionManager type=""/><!--事务管理器-->
            <dataSource type=""/><!--数据源-->
        </environment>
    </environments>
    <databaseIdProvider type=""/><!--数据库厂商标识-->
    <mappers/><!--映射器-->
</configuration>

注意:Mybatis的配置文件的顺序是严格按照从上至下的顺序声明,不颠倒顺序,如果颠倒了它们的顺序,那么Mybatis在启动阶段就会产生异常,导致程序无法运行

2. properties

The function of properties is to reference the configuration information in the java property file, such as: loading the configuration file of various properties connected to the database.

Mybatis provides three ways to use properties:

  • Property sub-element (not recommended): Add the sub-property property to the properties attribute to set some configured key-values.
  • properties file: It is to directly use properties to introduce external configuration files, which is equivalent to extracting sub-properties into an independent external file and importing them, such as db.properties.
  • Passing parameters through program code: setting information related to the configuration through code. For example, the user name and password in the database configuration file are generally ciphertext, but when connecting to the database, the configuration needs to be decrypted. In this case, the program code can only be used to pass parameters. The method is configured.

2.1. Property sub-element (not recommended)

Based on the example in the previous chapter, use the property sub-element to rewrite the database connection configuration information, as shown below:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
 
<configuration>
    <properties>
        <!--property子元素定义-->
        <property name="database.driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="database.url" value="jdbc:mysql://localhost:3306/user?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8"/>
        <property name="database.username" value="root"/>
        <property name="database.password" value="root"/>
    </properties>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <!--配置连接数据库的4个基本信息-->
                <property name="driver" value="${database.driver}"/>
                <property name="url" value="${database.url}"/>
                <property name="username" value="${database.username}"/>
                <property name="password" value="${database.password}"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

This configuration method has shortcomings. Although it can be referenced everywhere once defined in this way, if there are many configuration items, the configuration file will become very large, so using this method is obviously not a good choice. In order to solve this shortcoming , we can use the following configuration method, that is, using the properties file.

2.2. Properties file

The method of using properties files is relatively common in our development. The main reason is that this method is simple and convenient for future maintenance and modification. First, extract all the property attributes in the above configuration into a configuration file called databse.properties.

As shown in the following code:

#数据库连接配置
database.driver=com.mysql.cj.jdbc.Driver
database.url=jdbc:mysql://localhost:3306/user?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
database.username=root
database.password=root

Then use the resource attribute of the element in the Mybatis configuration file to introduce the properties file.

<properties resource="database.properties" />

This is equivalent to loading all the configurations in database.properties into the MyBatis configuration file, and then introducing the property parameters of the properties file in the manner of ${database.username}. However, this method of use also has its disadvantages. When the values ​​in the external configuration file need to be encrypted, such as the username and password for connecting to the database, they cannot be decrypted in the configuration file, so they can only be passed through program code, which is to The third type introduced is as follows.

2.3. Program code transfers parameters

In a real development environment, database usernames and passwords are kept secret from developers and other personnel. In order to keep data confidential, operation and maintenance personnel generally encrypt the database username and password, and configure the encrypted data into the properties file. Therefore, developers must use the decrypted username and password to connect to the database, and it is impossible to connect with encrypted data. At this time, you need to use this method to decrypt the configuration file. In fact, this method is generally used in conjunction with the second method to overwrite or rewrite special configurations. Taking the above database.properties as an example, the user name and password in the configuration are decrypted when the database configuration information is used. Here is an example of obtaining SqlSessionFactory in MyBatis. The code is as follows:

public static SqlSessionFactory getSqlSessionFactoryByXml() {
    
    
        synchronized (Lock) {
    
    
            if (null != sqlSessionFactory) {
    
    
                return sqlSessionFactory;
            }
            String resource = "mybatis-config.xml";
            InputStream inputStream;
            InputStream is = null;
            try {
    
    
                // 加载数据库配置文件
                is = Resources.getResourceAsStream("database.properties");
                Properties properties = new Properties();
                properties.load(is);
 
                // 获取加密信息
                String username= properties.getProperty("database.username");
                String password= properties.getProperty("database.password");
 
                // 解密用户名和密码,并重置属性
                properties.setProperty("database.username", CyperTool.decodeByBase64(username));
                properties.setProperty("database.password", CyperTool.decodeByBase64(password));
                // 读取mybatis配置文件
                inputStream = Resources.getResourceAsStream(resource);
                // 通过SqlSessionFactoryBuilder类的builder方法进行构建,并使用程序传递的方式覆盖原有属性
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, properties);
            } catch (IOException e) {
    
    
                e.printStackTrace();
                return null;
            }
            return sqlSessionFactory;
        }
    }

In order to ensure the accuracy of the data, we added a synchronized lock. First, use the Resources object to read the database.properties configuration file, then obtain its originally configured user and password, perform the decryption operation, and finally use the build method of SqlSessionFactoryBuilder to pass multiple property parameters to complete. This will overwrite the previously encrypted configuration so that you can connect to the database while also meeting the security requirements for database usernames and passwords.

3. settings attribute

Settings is the most complex configuration in MyBatis. It can profoundly affect the underlying operation of MyBatis. However, in most cases it can run using the default values, so in most cases there is no need for a lot of configuration. You only need to modify some common rules. . Commonly used rules include automatic mapping, camel case naming mapping, cascading rules, whether to enable caching, executor type, etc.

For all configurations, please refer to the MyBatis official documentation: https://mybatis.org/mybatis-3/zh/configuration.html#settings. You can see that there are a lot of configuration items in settings, but we don’t really use too many. We just need to figure out the commonly used ones. For example, CacheEnabled about caching, lazyLoadingEnabled and aggressiveLazyLoading about cascading, autoMappingBehavior and mapUnderscoreToCamelCase about automatic mapping, defaultExecutorType about executor type, etc. This article lists several important configuration items and their meanings, and selects a few common configurations for explanation:

<settings>
    <!--缓存配置的全局开关:如果这里设置成false,那么即便在映射器中配置开启也无济于事 -->
    <setting name="cacheEnabled" value="true" />
    <!--延时加载的全局开关 -->
    <setting name="lazyLoadingEnabled" value="false" />
    <!-- 是否允许单一语句返回多结果集 -->
    <setting name="multipleResultSetsEnabled" value="true" />
    <!-- 使用列标签代替列名,需要兼容驱动 -->
    <setting name="useColumnLabel" value="true" />
    <!-- 允许JDBC自动生成主键,需要驱动兼容。如果设置为true,则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍能正常工作 -->
    <setting name="useGeneratedKeys" value="false" />
    <!-- 指定MyBatis该如何自动映射列到字段或属性:NONE表示取消自动映射;PARTIAL表示只会自动映射,没有定义嵌套结果集和映射结果集;
    FULL会自动映射任意复杂的结果集,无论是否嵌套 -->
    <setting name="autoMappingBehavior" value="PARTIAL" />
    <!-- 指定发现自动映射目标未知列(或未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出警告日志FAILING: 映射失败 (抛出 SqlSessionException) -->
    <setting name="autoMappingUnknownColumnBehavior" value="WARNING" />
    <!-- 配置默认的执行器:SIMPLE是普通的执行器;REUSE会重用预处理语句;BATCH会重用语句并执行批量更新 -->
    <setting name="defaultExecutorType" value="SIMPLE" />
    <!--设置超时时间:它决定驱动等待数据库响应的秒数,任何正整数-->
    <setting name="defaultStatementTimeout" value="25"/>
    <!--设置数据库驱动程序默认返回的条数限制,此参数可以重新设置,任何正整数 -->
    <setting name="defaultFetchSize" value="100" />
    <!-- 允许在嵌套语句中使用分页(RowBounds) -->
    <setting name="safeRowBoundsEnabled" value="false" />
    <!-- 是否开启自动驼峰命名规则,即从a_example到aExample的映射 -->
    <setting name="mapUnderscoreToCamelCase" value="true" />
    <!-- 本地缓存机制,防止循环引用和加速重复嵌套循环 -->
    <setting name="localCacheScope" value="SESSION" />
    <!-- 当没有为参数提供特定JDBC类型时,为空值指定JDBC类型。某些驱动需要指定列的JDBC类型,多数情况直接用一般类型即可,如NULL/VARCHAR/OTHER -->
    <setting name="jdbcTypeForNull" value="OTHER" />
    <!-- 指定触发延迟加载的方法,如equals/clone/hashCode/toString -->
    <setting name="lazyLoadTriggerMethods" value="equals" />
</settings>

4. typeAlianses attribute

The typeAlianses attribute is an alias to make it easier to write input parameter types and output result types in the mapping file. Because the fully qualified name of the usual input and output mapping is very long, it is not very convenient during use, so in MyBatis Allows us to use an abbreviated method instead of a fully qualified name, which can improve our development efficiency.

Aliases are divided into system aliases and custom aliases. The system alias is the alias given to us by default by the system. For example, when we input a numerical parameter, we can directly write parameterType="int". This is because the system converts the Java type of Integer into The alias is int. We can check it through the official documentation of Mybatis: https://mybatis.org/mybatis-3/zh/configuration.html#typeAliases. The custom alias is a name defined by yourself, and how to use it will be introduced later.

4.1. System-defined aliases

Mybatis itself defines a large number of aliases for us, including basic data types, packaging classes, object types, collections, Maps, etc. The system-defined aliases are defined through the TypeAliasRegistry class, so we can use this object to obtain the aliases that have been defined in the system, or we can customize the aliases. First, we can use a piece of code to obtain which aliases are predefined in the system.

/**
 * 获取系统别名配置
 */
public static void getTypeAlias() {
    
    
    try {
    
    
        InputStream stream = getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
        SqlSession sqlSession = factory.openSession();
        //获取TypeAliasRegistry对象
        TypeAliasRegistry typeAliasRegistry = sqlSession.getConfiguration().getTypeAliasRegistry();
        Map<String, Class<?>> tarMap = typeAliasRegistry.getTypeAliases();
        int i =0;
        for (String key : tarMap.keySet()) {
    
    
            //这个++i统计数量
            System.out.println(++i+"*****"+tarMap.get(key).getSimpleName()+"*****"+key);
        }
        System.out.println("系统定义的别名个数为:"+i);
    } catch (IOException e) {
    
    
        e.printStackTrace();
    }
 
}
 
public static void main(String[] args) {
    
    
    getTypeAlias();
}

I won’t post the output, it’s a bit long, you can run it by yourself. Through the running results, it can be found that there are 72 system-customized aliases (the use of these 72 aliases is not case-sensitive). So we can use aliases instead of lengthy fully qualified names. For example, in the MyBatis mapping file, when we set the parameter type or return type of an SQL statement, if the type is a string, we can use string instead of java.lang .String. But there is a problem, how do I know which type of alias is? There are two ways to know without knowing:

The safe method: print out the system alias, or look for the official document;
look for the pattern: In fact, we can find a pattern from the above results, that is, if the class name starts with a capital, then as long as the upper case is changed to lower case, it will be the alias of the class; And if the class name is originally lowercase, just add an underscore in front of the lowercase.

4.2. Custom alias

In our daily development, there will be a large number of classes in the system, such as the User class, which need to be used repeatedly. However, the system does not give us aliases for these classes. Do we have to write long fully qualified names repeatedly? ? NO, Mybatis provides us with rules for user-defined aliases, which we can register through configuration files, package scans, or annotations. Here's how to use it:

①. Use the typeAliases attribute of the configuration file

<!--配置别名-->
<typeAliases>
    <!--对类单独进行别名设置  -->
    <typeAlias alias="user" type="com.thr.pojo.User"></typeAlias>
    <typeAlias alias="student" type="com.thr.pojo.Student"></typeAlias>
</typeAliases>

In this way, we have defined aliases for two classes, but this method has a disadvantage. If multiple classes need to configure aliases, it will be very troublesome, so this method is obviously not possible.

②. Automatic scanning through package

<!--配置别名-->
<typeAliases>
    <!-- 对包进行扫描,可以批量进行别名设置,设置规则是:获取类名称,将其第一个字母变为小写 -->
    <package name="com.thr.pojo1"/>
    <package name="com.thr.pojo2"/>
    <package name="com.thr.pojo3"/>
</typeAliases>

This method will give an alias to all classes under the scanned package. The naming rule for aliases is to change the first letter of the class name to lowercase as the alias. For example, com.thr.pojo.User becomes the alias user. . However, there is a disadvantage in using this method, that is, if a class with the same name appears in two different packages, an exception will occur during scanning (this usually does not happen). At this time, you can distinguish it by annotating @Alians("user1").

③. Through annotations

   这种方式比较简单,只要在对应包下的对应类上面使用注解@Alias("别名")即可,如下:
package com.thr.pojo;
import org.apache.ibatis.type.Alias;
 
@Alias("user")
public class User {
    
    
    省略......
}

This will avoid the problem of scan failure due to duplication.

5. typeHandlers attribute (understand)

typeHandlers are called type handlers. In JDBC, the parameters required for precompiled SQL need to be set in PreparedStatement. After executing SQL, the database data will be obtained according to the ResultSet object. The types in the database need to be converted to the types of fields in Java. These operations are implemented in MyBatis through typeHandler. In typeHandler, there are two types: javaType and jdbcType. javaType is used to define Java types, and jdbcType is used to define database types. Then the role of typeHandler is to undertake the conversion of javaType and jdbcType, as shown in the figure below.

Insert image description here

There are two types of typeHandlers in MyBatis: system-defined and custom. MyBatis will decide which typeHandler to use to handle these conversion rules based on javaType and jdbcType. The system-defined ones can meet most needs, but in some cases they are not enough, such as Our special conversion rules, enumeration types, then we need custom typeHandlers. The following describes the use of these two typeHandlers respectively.

5.1. System-defined typeHandler

Mybatis defines many useful typeHandlers internally. We can refer to the official documentation of Mybatis: https://mybatis.org/mybatis-3/zh/configuration.html#typeHandlers. You can also print it yourself through program code. The code is as follows:

/**
 * 获取类型处理器
 */
public static void getTypeHandlers() {
    
    
    //SqlSession代码省略......
    TypeHandlerRegistry typeHandlerRegistry = sqlSession.getConfiguration().getTypeHandlerRegistry();
    Collection<TypeHandler<?>> handlers =  typeHandlerRegistry.getTypeHandlers();
    System.out.println(handlers.size());
    int i = 0;
    for (TypeHandler<?> typeHandler : handlers) {
    
    
        System.out.println(++i+"*****"+typeHandler.getClass().getName());
    }
}

The execution results are not listed. Mybatis defines a total of 39 type processors. In most cases we do not need to explicitly declare JavaType and jdbcType, because Mybatis will automatically detect them.

In Mybatis, typeHandler needs to implement the interface org.apache.ibatis.type.TypeHandler, so let’s take a look at what this interface looks like. The source code is as follows:

public interface TypeHandler<T> {
    
    
    void setParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException;
    T getResult(ResultSet var1, String var2) throws SQLException;
    T getResult(ResultSet var1, int var2) throws SQLException;
    T getResult(CallableStatement var1, int var2) throws SQLException;
}

A brief introduction to the internally defined content:

T represents generics, specifically JavaType. For example, if we need String type parameters, then the implementation class can be written as implement TypeHandler.

The setParameter method is a specific method used when using typeHandler to set SQL parameters through the PreparedStatement object, where i is the subscript in SQL, parameter is the parameter, and jdbcType is the database type.

Among the three getResult methods, its function is to obtain data from the JDBC result set for conversion, either using column names or subscripts to obtain database data. The last method is a method dedicated to stored procedures.

Now that you have learned the TypeHandler interface, let's learn the BaseTypeHandler class that implements it. The source code is as follows (only a small amount is posted):

public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
    
    
      ......
    public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
    
    
      ......
    }
    public T getResult(ResultSet rs, String columnName) throws SQLException {
    
    
       ......
     }
     public T getResult(ResultSet rs, int columnIndex) throws SQLException {
    
    
       ......
     }
    public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
    
    
        ......
    }
    public abstract void setNonNullParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException;
    public abstract T getNullableResult(ResultSet var1, String var2) throws SQLException;
    public abstract T getNullableResult(ResultSet var1, int var2) throws SQLException;
    public abstract T getNullableResult(CallableStatement var1, int var2) throws SQLException;
 }

Let’s briefly analyze:

  • setParameter method, when parameter parameter and jdbcType are both empty, Mybatis will throw an exception. If the jdbcType can be found, it will continue to be empty; if the parameter is not empty, then it will use the setNonNullParameter method to set the parameter.
  • getResult method, the non-empty result set is obtained through the getNullableResult method. If it is judged to be empty, null is returned.
  • The getNullableResult method is used in stored procedures.
    The most used typeHandler in Mybatis is StringTypeHandler. It is used for string conversion, so let's learn about it. The source code is as follows:
public class StringTypeHandler extends BaseTypeHandler<String> {
    
    
    public StringTypeHandler() {
    
    
    }
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
    
    
        ps.setString(i, parameter);
    }
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
    
    
        return rs.getString(columnName);
    }
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    
    
        return rs.getString(columnIndex);
    }
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    
    
        return cs.getString(columnIndex);
    }
}

From the above code, we can see that it inherits the BaseTypeHandler class and implements the four abstract methods of BaseTypeHandler. The methods are as follows:

  • setNonNullParameter: This method is used to convert javaType into jdbcTpe.
  • getNullableResult: This method is used to convert the jdbcType of the data obtained from the result set based on the column name into javaType.
  • getNullableResult: This method is used to convert the jdbcType of the data obtained from the result set based on the column index into javaType.
  • getNullableResult: This method is used in stored procedures.

Here Mybatis exchanges JavaType and jdbcType, so how do they register? In Mybatis, the register method of the TypeHandlerRegistry class object is used to perform the rule.

public TypeHandlerRegistry(Configuration configuration) {
    
    
        ......
        this.register((Class)Boolean.class, (TypeHandler)(new BooleanTypeHandler()));
        this.register((Class)Boolean.TYPE, (TypeHandler)(new BooleanTypeHandler()));
        this.register((JdbcType)JdbcType.BOOLEAN, (TypeHandler)(new BooleanTypeHandler()));
        this.register((JdbcType)JdbcType.BIT, (TypeHandler)(new BooleanTypeHandler()));
        ......
    }

In this way, the typeHandler is registered in the form of code. But note that custom typeHandlers are generally not registered using code, but through configuration or scanning. Let's learn how to customize typeHandler below.

5.2. Custom typeHandler

We know that in most scenarios, Mybatis' typeHandler can handle it, but sometimes it is not enough, such as enumeration types. In this case, a custom typeHandler is needed to handle it. From the system-defined typeHandler, we can know that to implement typeHandler, you need to implement the interface typeHandler or implement baseTypeHandler.

Next, we create a MyTypeHandler by implementing the TypeHandler interface to complete the conversion between the String type in javaType and the type in jdbcType.

public class MyTypeHandler implements TypeHandler<String> {
    
    
    Logger log = Logger.getLogger(MyTypeHandler.class);
    @Override
    public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
    
    
        log.info("设置string参数:"+parameter);
        ps.setString(i,parameter);
    }
    @Override
    public String getResult(ResultSet rs, String columnName) throws SQLException {
    
    
        String result = rs.getString(columnName);
        log.info("读取string参数1:"+result);
        return result;
    }
    @Override
    public String getResult(ResultSet rs, int columnIndex) throws SQLException {
    
    
        String result = rs.getString(columnIndex);
        log.info("读取string参数2:"+result);
        return result;
    }
    @Override
    public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
    
    
        String result = cs.getString(columnIndex);
        log.info("读取string参数3:"+result);
        return result;
    }
}

The generic type we defined as String means that we need to convert the database type data into the String type, and then implement the methods of setting parameters and obtaining the result set. But the typeHandler has not been started at this time. You also need to configure it in the configuration file.

<typeHandlers>
    <typeHandler jdbcType="VARCHAR" javaType="string"
             handler="com.typeHandler.MyTypeHandler"/>
</typeHandlers>

The system will read it after the configuration is completed, so the registration is completed. When JavaType and jdbcType can correspond to MyTypeHandler, it will start MyTypeHandler. We have two ways to use custom typeHandler.

<!-- 模糊查询,根据username字段查询用户-->
<select id="selectUserByName" parameterType="string" resultType="user">
    select * from t_user where username like concat ('%',#{username,typeHandler=com.typeHandler.MyTypeHandler},'%');
</select>

or:

<select id="selectUserByName" parameterType="string" resultType="user">
    select * from t_user where username like concat ('%',#{username,javaType=string,jdbcType=VARCHAR},'%');
</select>

注意,要么指定了与自定义typeHandler一致的jdbcType和JavaType,要么直接使用typeHandler指定的具体实现类。在一些因为数据库返回为空导致无法判定采用哪个typeHandler来处理,而又没有注册对应的JavaType的typeHandler是,Mybatis无法找到使用哪个typeHandler来转换数据。

Insert image description here
Sometimes when there are many classes, we can also use package scanning.

<typeHandlers>
    <package name="com.typeHandler"/>
</typeHandlers>

But this will not be able to specify jdbcType and JavaType, but we can handle them through annotations. We can just modify the MyTypeHandler class.

@MappedTypes(String.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyTypeHandler implements TypeHandler<String> {
    
    
    ......
}

最后:在我们的日常开发中,一般都不需要定义,使用默认的就可以,除非是像枚举这种特殊类型就需要自己实现。

6. objectFacotry attribute (understand)

objectFacotry is represented as an object factory. We only need to understand the object factory, because when it is integrated with spring, it will be managed by spring.

When we use MyBatis to execute query statements, we usually have a return type. This is controlled by adding a resultType (or resultMap) attribute to the sql in the mapper file. Both resultType and resultMap can control the return type. As long as this configuration is defined, it can automatically return the results I want. So I was very confused about the implementation principle of this automatic process. I think most people should have the same experience as me when they first started. Confused and curious, so today I will share my research. The results of the query in JDBC will be saved in a result set. In fact, MyBatis also follows this principle, except that when MyBatis creates the result set, it will use its defined object factory DefaultObjectFactory to complete the corresponding work.

7. plugins attribute (understand)

The plug-in is the most powerful and flexible component in Mybatis, but it is also the most complex and difficult to use, and it is very dangerous because it will override the core methods and properties of the underlying object of Mybatis. Improper operation will have very serious consequences, even destroying the Mybatis framework, so we must not touch this plug-in property without understanding the underlying structure of Mybatis. If you want to study plug-ins, you must clearly understand the underlying structure and operating principles of Mybatis, otherwise it will be difficult to use it safely and efficiently. The regular functions we usually use in Mybatis fully satisfy our daily development, so we won’t introduce them here. Those who are interested can learn them by themselves.

8. environments attribute

The environments attribute represents the running environment. Its main function is to configure some information of the database. We can configure multiple databases, but only one can be selected. It is divided into two configurable elements: transaction manager (transactionManager) and data source (DataSource). In our daily development, these will be managed by Spring, and there is no need to write them in the global configuration. These will be explained later in Mybatis integration with Spring. Let's first take a look at the configuration code of environments environment configuration.

<configuration>
    <!-- 配置环境.-->
    <environments default="development">
        <!-- id属性必须和上面的default一致 -->
        <environment id="development">
            <!--配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象源 -->
            <dataSource type="POOLED">
                <!--配置连接数据库的4个基本信息-->
                <property name="driver" value="${database.driver}"/>
                <property name="url" value="${database.url}"/>
                <property name="username" value="${database.username}"/>
                <property name="password" value="${database.password}"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

Below we mainly introduce the two elements transactionManager and DataSource in detail.

8.1. transactionManager (transaction management)

In MyBatis, transactionManager provides two implementation classes, both of which need to implement the interface Transaction, so we can view the following Transaction source code:

public interface Transaction {
    
    
    Connection getConnection() throws SQLException;
 
    void commit() throws SQLException;
 
    void rollback() throws SQLException;
 
    void close() throws SQLException;
 
    Integer getTimeout() throws SQLException;
}

As can be seen from the above method, its main job is to commit, rollback, and close database transactions. MyBatis provides two implementation classes for the Transaction interface, namely JdbcTransaction and ManagedTransaction. As shown below:

Insert image description hereInsert image description here

And correspond to two factories, JdbcTransactionFactory and ManagedTransactionFactory respectively. These two factories implement the TransactionFactory interface. When we configure the transaction manager type through the type attribute of transactionManager in the configuration file, Mybatis will automatically obtain the instance from the corresponding factory. . We can configure the transaction manager in the following two ways:

<transactionManager type="JDBC"/>
<transactionManager type="MANAGED"/>

Let’s talk about the difference between the two:

JDBC: Use the JdbcTransaction object generated by the JdbcTransactionFactory factory to implement database submission, rollback and other operations in the JDBC manner.
MANAGED: Implemented using the ManagedTransaction object generated by the ManagedTransactionFactory factory. Its submission and rollback do not require any operation. Instead, the transaction is handed over to the container for processing. The connection will be closed by default. If you do not want to close it by default, just closeConnection in it. Just set the property to false.

<transactionManager type="MANAGED">
    <property name="closeConnection" value="false"/>
</transactionManager>

The most obvious difference discovered during the test is that if I use JDBC transaction processing, when I insert a piece of data into the database, after calling the insertion interface to execute SQL, I must execute sqlSession.commit(); Submit, otherwise although the insertion is successful, the data just inserted will still not be visible in the database; but using the MANAGED method is different, you only need to call the interface, no need to submit manually.

Of course, in addition to using the default one, we can also customize a transaction manager as needed, which requires the following three steps:

Step 1: Create a custom transaction factory MyTransactionFactory, which needs to implement the TransactionFactory interface. The code is as follows:

/**
 * 创建自定义事务工厂
 */
public class MyTransactionFactory implements TransactionFactory {
    
    
    @Override
    public void setProperties(Properties props) {
    
    
    }
 
    @Override
    public Transaction newTransaction(Connection connection) {
    
    
        //后面我们会创建这个类,它自定义的事务类
        return new MyTransaction(connection);
    }
 
    @Override
    public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean b) {
    
    
        return new MyTransaction(dataSource,level,b);
    }
}

All factory methods defined by TransactionFactory are implemented here. At this time, a custom transaction class is also needed, which we will create below.

Step 2: Create a custom transaction class MyTransaction to implement the Transaction interface. The code is as follows.

MyTransaction
Step 3: Configure a custom transaction manager

<transactionManager type="com.transaction.MyTransactionFactory"/>

Note: This place configures a custom factory class, not a transaction management class, because mybatis obtains specific instance objects based on the configured factory.

8.2. DataSource (data source)

In Mybatis, the database is provided through three factory classes: PooledDataSourceFactory, UnpooledDataSourceFactory and JndiDataSourceFactory. The first two generate PooledDataSource and UnpooledDataSource class objects respectively, and the third one will get the database connection object implemented by the external container based on JNDI information, but No matter what, they will eventually generate a database connection object that implements the DataSource interface.

Because there are three data sources, their configuration information is as follows:

<dataSource type="POOLED">
<dataSource type="UNPOOLED">
<dataSource type="JNDI">

The following introduces the significance of these three data sources:

①、UNPOOLED

UNPOOLED uses a non-database pool management method. Each request will create a new connection, so the performance is not very high. When using this data source, the following attributes can be configured for the UNPOOLED type data source:

  • driver: database driver name
  • url: database connection URL
  • username: username
  • password: password
  • defaultTransactionIsolationLevel: The default transaction isolation level. If you want to pass attributes to the driver, the prefix of the attribute is driver.

②、SIDES

POOLED uses the concept of connection pool to organize the database link object Connection. Multiple connections can be created during initialization and obtained directly from the connection pool when used. This avoids the initialization and authentication time required to repeatedly create connections, thereby improving efficiency. Therefore, This method is more suitable for applications with high performance requirements. In addition to the configuration properties in UNPOOLED, there are also the following configurations for the pool:

  • poolMaximumActiveConnections: the number of connections that will exist at any time, the default value is 10
  • poolMaxmumIdleConnections: The number of connections that can exist idle
  • poolMaxmumCheckoutTime: The waiting time to check for idle connections before being forced to return. That is, if there are 20 connections and only one is idle, the waiting time before the idle connection is found is configured with this attribute.
    poolTimeToWait: The time required to wait for a successful database connection. If this time is exceeded, try to reconnect.
    There are some other configurations that will not be detailed.

③、JNDI

JNDI Data Source JNDI is implemented for use in containers such as EJB or application servers. The container can configure the data source centrally or externally, and then place a reference to the JNDI context. This data source only needs to configure two properties:

  • initial_context: used to find context in InitialContext. Optional, if omitted, the data_source attribute will be found directly from the InitialContext;
  • data_source: Path that references the location context of the data source instance. When initial_context configuration is provided, data_source will be looked up in the context it returns, otherwise it will be looked up directly from InitialContext.

In addition to the above three data sources, Mybatis also provides third-party data sources, such as DBCP, but we need to customize the data source factory and configure it, which will not be studied for the time being.

9. databaseIdProvider attribute (understand)

The databaseIdProvider element is mainly to support databases from different vendors. This element is not commonly used. For example, some companies use MySQL for internal development, but customers require the use of Oracle, which is troublesome because Mybatis is not as portable as Hibernate, but Mybatis is not that stupid. In Mybatis, we can use the databaseIdProvider element to implement the database. Compatible with different manufacturers, that is, multiple databases can be configured.

The following uses two databases, Oracle and MySQL, to introduce them. The properties to be configured are as follows:

<!--数据库厂商标示 -->
<databaseIdProvider type="DB_VENDOR">
    <property name="Oracle" value="oracle"/>
    <property name="MySQL" value="mysql"/>
    <property name="DB2" value="d2"/>
</databaseIdProvider>

The type attribute of databaseIdProvider is required, and an error will be reported if not configured. The above attribute value uses an alias of the VendorDatabaseIdProvider class.

The property sub-element is to configure a database, where the name attribute is the database name, and value is our custom alias. Through the alias, we can identify which database operation is suitable for it in the SQL statement. If we don’t know the database name, we can get it through the following code: connection.getMetaData().getDatabaseProductName(), the code is as follows:

/**
 * 获取数据库名称
 */
public static void getDbInfo() {
    
    
    SqlSession sqlSession = null;
    Connection connection = null;
    try {
    
    
        InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
        sqlSession = sqlSessionFactory.openSession();
        connection = sqlSession.getConnection();
        String dbName = connection.getMetaData().getDatabaseProductName();
        String dbVersion = connection.getMetaData().getDatabaseProductVersion();
        System.out.println("数据库名称是:" + dbName + ";版本是:" + dbVersion);
    } catch (SQLException e) {
    
    
        e.printStackTrace();
    } catch (IOException e) {
    
    
        e.printStackTrace();
    }
}

My output is: database name is: MySQL; version is: 5.7.28-log

Then we can use the attribute databaseId to indicate the database type in our sql statement. The configuration is as follows:

<!-- 查询所有用户 -->
<select id="selectAllUser" resultType="com.thr.User" databaseId="oracle">
    select * from t_user
</select>

Note: In the above SQL, the databaseId I configured is oracle, but my actual database is mysql. As a result, a BindingException will definitely be reported in the end (but my code does not know why it reports another exception, which is very strange. Baidu searched for a long time to no avail). If we replace databaseId="oracle" with mysql, we can get the correct results. In addition to the above methods, we can also not configure the databaseId in SQL, so that mybatis will use the default configuration and can run successfully.

We know from the above practice: When using multi-database SQL, you need to configure the databaseIdProvider attribute. When the databaseId attribute is configured, the system will give priority to obtaining the SQL that is consistent with the database configuration. Otherwise, if the SQL without databaseId is configured, it can be used as the default value; if it still cannot be obtained, an exception will be thrown.

In addition to the system-defined logo, we can also customize a rule, which needs to implement the DatabaseIdProvider interface provided by MyBatis, as follows:

MyDatabaseIdProvider
then configures the following in databaseIdProvider:

<!--数据库厂商标示 -->
<databaseIdProvider type="com.databaseidprovider.MyDatabaseIdProvider" />

The property attribute does not need to be configured, everything else is the same.

10. mappers attribute

The mapper attribute is used to load the mapping file, that is, to load the SQL mapping file we configured. It can be loaded in four ways:

Use file path to import
Use URL to import
Use class registration to import Use
package name to import (recommended)

10.1. Import using file path

<mappers>
    <mapper resource="com/thr/mapper/UserMapper.xml" />
    <mapper resource="com/thr/mapper/StudentMapper.xml" />
    <mapper resource="com/thr/mapper/TeacherMapper.xml" />
</mappers>

This method is a relative path, relative to the project directory, so it must be separated by /.

10.2. Import using URL

<mappers>
    <mapper url="D:/mappers/UserMapper.xml" />
    <mapper url="D:/mappers/StudentMapper.xml" />
</mappers>

This method is an absolute path, which is to read the mapping file from our disk. This method is generally not used.

10.3. Import using class registration

<mappers>
    <mapper class="com.thr.mapper.UserMapper" />
    <mapper class="com.thr.mapper.StudentMapper" />
    <mapper class="com.thr.mapper.TeacherMapper" />
</mappers>

This method uses the fully qualified name of the Mapper interface, regardless of the path problem, and lets Mybatis find the mapping file through the fully qualified name. But the premise is that the name of the Mapper interface must be the same as the name of the mapping file, and must be under the same package name, otherwise it will not be found. For example: UserMapper.java (interface)-UserMapper.xml (mapping file). The Mapper mapping file corresponding to the Mapper interface will be introduced in detail later.

10.4. Import using package name (recommended)

<mappers>
    <package name="com.thr.mapper"/>
</mappers>

It is recommended to use this method, which means introducing all mapper interfaces under the package. Here, all interface files under the com.thr.mapper package are introduced, and then let Mybatis find the mapping file through the fully qualified name.

注意:这种方式的要求同样是Mapper接口和Mapper的映射文件的名称要相同,并且要放在相同的包名下,否则会导致找不到。

Guess you like

Origin blog.csdn.net/GoodburghCottage/article/details/133357166