mybatis - detailed explanation of custom interceptor object

Transferred from: https://www.jianshu.com/p/8440d1d66608

mybatis custom interceptor (I) basic usage
mybatis custom interceptor (II) object details

If mybatis wants to implement a custom Interceptor, it needs to implement the Interceptor interface. The object will first execute the plugin(Object target) method to determine whether to intercept according to the @ Intercepts annotation on the class. If interception is required, the intercept (invocation) method is called.

1. Preparation

sql to be intercepted:

  <select id="selectByNameAndGroup" parameterType="java.lang.String" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from sys_quartz_job_config
    where job_Name = #{jobName,jdbcType=VARCHAR} AND
    job_Group =#{jobGroup,jdbcType=VARCHAR}
  </select>

  

Mapper objects to be intercepted:

 

 
SysQuartzJobConfig selectByNameAndGroup(@Param("jobName") String jobName,@Param("jobGroup") String jobGroup);

  

invocation object:

 

 

 

You can see the args parameter in invocation, which is the args parameter in @ Intercepts.

 

 
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
                RowBounds.class, ResultHandler.class})})

  

2. mappedStatement object

1. How to get mappedStatement object for invocation object:

A mappedStatement object corresponds to a select/update/insert/delete node in Mapper configuration file. It mainly describes an sql statement.


//Get parameter 1: MappedStatement object
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];

  

 

 

2. MappedStatement object details:

 

 
public final class MappedStatement {
  //The absolute path of the Mapper.xml
  private String resource;
  //All configurations of mybatis
  private Configuration configuration;
  //ID of sql (command space + key)
  private String id;
  //The number of result lines returned by the driver in each batch is equal to this setting
  private Integer fetchSize;
  //SQL timeout
  private Integer timeout;
  //Type of Statement, state / prepare / callable
  private StatementType statementType;
  //Result set type, FORWARD_ONLY/SCROLL_SENSITIVE/SCROLL_INSENSITIVE 
  private ResultSetType resultSetType;
  //Represents the parsed SQL
  private SqlSource sqlSource;
  //cache
  private Cache cache;
  //obsolete 
  private ParameterMap parameterMap;
  //Corresponding ResultMap
  private List<ResultMap> resultMaps;
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  //SQL type, INSERT/SELECT/DELETE
  private SqlCommandType sqlCommandType;
  //Related to the SELECTKEY tag
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;
  //Database ID, which is used to distinguish different environments
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  //Multiple result sets
  private String[] resultSets;

  MappedStatement() {
    // constructor disabled
  }
  ...
  }

The field that really represents SQL is the SqlSource object.
The SqlSource interface is very simple, with only one getBoundsql method.

public interface SqlSource {
  BoundSql getBoundSql(Object parameterObject);
}

  

sqlSource has many implementations. We need to focus on StaticSqlSource, RawSqlSource and DynamicSqlSource. Through the above implementation, you can convert the unique sql format of mybatis into sql that can be directly executed by PrepareStatement.

mappedStatement -- BoundSql object:

 BoundSql boundSql = mappedStatement.getBoundSql(parameter);

  

The above method is mainly to parse dynamic tags and obtain fully executable sql. Parse the #{} character and replace it with?, Finally, they are wrapped into domain expressions for PrepareStatement to call.

  • The parsed sql is saved in the sql object;
  • The request parameters are saved in the parameterObject object;
  • #The {} key attribute and the corresponding parameter mapping, such as javaType and JDBC type, are saved in the parameterMapping attribute of BoundSql. Used for the assignment of the last field expression object PrepareStatement.

 

 

BoundSql - parsed sql object:

The sql object in the BoundSql object is the fully executable sql after dynamic label parsing.

select
     
    ID, JOB_NAME, JOB_GROUP, ENABLE, CLASS_NAME, CRON, CONCURRENT, CREATE_TIME, MODIFY_TIME
   
    from sys_quartz_job_config
    where job_Name = ? AND
    job_Group =?

  

BoundSql - ParameterObject object:

This object is a parameter for sql execution. That is, the parameters we passed in. Because the @ param annotation is used, there are two ways to obtain the value.

//Get parameterObject object
Object parameter = null;
if (invocation.getArgs().length > 1) {
     parameter = invocation.getArgs()[1];
}

  

 

 

Note: ParameterObject is an Object object. When uploading different parameters, the Object type is different.

  • If a parameter object is uploaded: it is the type of the parameter;
  • When uploading multiple parameter objects: it is a ParamMap object. Because we use the @ param annotation, we can use the annotation key or param1 to get the variable.
  • If the uploaded object is a Criteria object (as shown in the following figure): it is also the type of the object, but the method of obtaining the object is different.

 

 

Note: if the real passed in parameter is declared in the TypeHandlerRegistry object, the real passed in parameter is used as the real variable name.

In other words, we write delete(Integer id) in the Mapper interface, and the variable #{var} defined in Mapper.xml can be named arbitrarily and can be handled correctly by mybatis.

BoundSql - ParameterMapping object:

When a variable is referenced in the form of #{var}, the variable will be replaced with a placeholder "?" when parsing the statement in Mapper.xml file, and the corresponding variable information will be recorded through parametermapping class. When the corresponding statement is actually executed, the real parameters are passed back. Set parameters for ParameterStatement according to parametermapping information.


 

  ParameterMapping object.png for normal objects

Note that the property parameter is VaR in #{var}.

BoundSql - additionalParameters object

If the Criteria object is used as the sql, then there is a value in additionalParameters. We can use:

//Get request parameters
//Get key in #{var}
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
      Object obj = boundSql.getAdditionalParameter(propertyName);
}

  

 

 

BoundSql - MetaObject object object

MetaObject class is equivalent to a tool class, which is often used by Mybatis in sql parameter setting and result set mapping.

 //Original object
  private Object originalObject;
  //A wrapper around the original object
  private ObjectWrapper objectWrapper;
  
  //These two properties are basically not used because no valid implementation class of ObjectWrapperFactory can be found in Mybatis
  private ObjectFactory objectFactory;
  private ObjectWrapperFactory objectWrapperFactory;

  

 

 

//Setting parameters through MetaObject
//Get key
 String propertyName = parameterMapping.getProperty();
 if (metaObject.hasGetter(propertyName)) {
     Object obj = metaObject.getValue(propertyName);
}

MappedStatement -- Configuration object

mybatis will read all Configuration files at startup and load them into memory. The Configuration object is the class that carries the entire Configuration.

 

Configuration configuration = mappedStatement.getConfiguration();

  

public class Configuration {
  /**
   * MyBatis It can be configured to adapt to a variety of environments. This mechanism helps to apply SQL mapping to a variety of databases,
   * For example, set different development, test and online configurations. In each configuration, you can configure the transaction manager and data source object
   */
  protected Environment environment;
 
  //Allow paging (rowboundaries) in nested statements. Set to false if allowed.
  protected boolean safeRowBoundsEnabled = false; 
  //Allows paging (ResultHandler) in nested statements. Set to false if allowed
  protected boolean safeResultHandlerEnabled = true;
  //Whether to enable automatic hump naming rule (camel case) mapping, that is, from the classic database column name a_ A similar mapping from column to the classic Java property name aColumn.
  protected boolean mapUnderscoreToCamelCase = false; 
  //When on, any method call will load all the properties of the object. Otherwise, each attribute will be loaded on demand (see lazyloadtrigger methods)
  protected boolean aggressiveLazyLoading = true;
  //Whether to allow a single statement to return multiple result sets (compatible driver is required)
  protected boolean multipleResultSetsEnabled = true; 
  //JDBC is allowed to support automatic generation of primary keys, which requires driver compatibility. If it is set to true, this setting forces the use of automatic generation of primary keys. Although some drivers are not compatible, they can still work normally (such as Derby).
  protected boolean useGeneratedKeys = false;
  //Use column labels instead of column names. Different drivers will have different performance in this regard. For details, please refer to the relevant driver documents or observe the results of the used drivers by testing these two different modes.
  protected boolean useColumnLabel = true;
  //Configure global cache switch
  protected boolean cacheEnabled = true;
  /*Specifies whether to call the setter (put) method of the mapping object when the value in the result set is null, which is useful for Map.keySet() dependency or null value initialization.
    Note that basic types (int, boolean, etc.) cannot be set to null.*/
  protected boolean callSettersOnNulls = false;
  //Specifies the prefix that MyBatis adds to the log name.
  protected String logPrefix;
  //Specify the specific implementation of the log used by MyBatis. If it is not specified, it will be found automatically
  protected Class <? extends Log> logImpl;
  /*MyBatis Local Cache is used to prevent circular references and accelerate repeated nested queries. 
    The default value is SESSION, in which case all queries executed in a SESSION are cached. 
    If the value is set to state, the local session is only used for STATEMENT execution, and different calls to the same SqlSession will not share data.*/
  protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  /*Specify a JDBC type for a null value when no specific JDBC type is provided for the parameter. 
    Some drivers need to specify the JDBC type of the column. In most cases, they can directly use the general type, such as NULL, VARCHAR or OTHER.*/
  protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  //Specifies which object's method triggers a deferred load.
  protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
  //Set the timeout, which determines the number of seconds the driver waits for a response from the database.

  

Configuration - TypeHandlerRegistry object

The type processor registers the object. When the TypeHandlerRegistry object is built, the type is registered.

The type processor TypeHandlerRegistry is simply a processor used to handle the type conversion between javaType and JDBC type. Mybatis performs matching processing for many Java types and database types.


//Get type processor
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();

  

Format of processor type:

 

 
public TypeHandlerRegistry() {
    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());
}

 

Posted by phpbeginner0120 on Tue, 09 Nov 2021 12:22:57 -0800