[mybatis series] understand mybatis field mapping hump naming from the perspective of source code

Time:2022-6-20

In the previous blog -[[jdbc] handle resultset and build Java objects]( https://my.oschina.net/kailun… As mentioned in, we need to analyze three things that mybatis does when converting results to required Java business objects, as follows:

  1. It solves the mapping from database column names to Java column names.
  2. The conversion from database type to Java type is solved.
  3. It has certain fault tolerance in the conversion process.

In fact, the core is:

  1. How the column names in the database correspond to the fields in the object.
  2. How to convert the type of the column in the database to the appropriate Java type will not cause conversion failure.

Today, let’s first look at how the column names in the database correspond to the fields in the object. The first is the daily Po (persistent object) citypo, which has five fields.

public class CityPO {
    Integer id;
    Long cityId;
    String cityName;
    String cityEnName;
    String cityPyName;

The column names in the database for this secondary query are as follows.

mysql> mysql> desc SU_City;
+--------------+-------------+------+-----+-------------------+-----------------------------+
| Field        | Type        | Null | Key | Default           | Extra                       |
+--------------+-------------+------+-----+-------------------+-----------------------------+
| id           | int(11)     | NO   | PRI | NULL              | auto_increment              |
| city_id      | int(11)     | NO   | UNI | NULL              |                             |
| city_name    | varchar(20) | NO   |     |                   |                             |
| city_en_name | varchar(20) | NO   |     |                   |                             |
| city_py_name | varchar(50) | NO   |     |                   |                             |
| create_time  | datetime    | NO   |     | CURRENT_TIMESTAMP |                             |
| updatetime   | datetime    | NO   | MUL | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+--------------+-------------+------+-----+-------------------+-----------------------------+
7 rows in set (0.01 sec)

We are named according to the hump type, and the column names in the database correspond to the field names of the objects. The following is the interface class and mapping file of mybatis.

public interface CityMapper {

    CityPO selectCity(int id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.CityMapper">
    <select id="selectCity" resultType="po.CityPO">
        select id,city_id,city_name,city_en_name from SU_City where id = #{id}
    </select>
</mapper>

In the above mapping file, the namespace specifies the fully qualified class name of the interface class, followed by the select statement, the ID is the name of the function in the interface class, and the resulttype represents the fully qualified name or alias of the class of the expected type returned from this statement. In this example, it is the class path of our business object citypo.

There are three main schemes

  1. Hump type naming switch, or not open, and the database column and field names are all consistent.
  2. Specify as when selecting.
  3. Resultmap is the most robust.

This article mainly takes a look at the first one, with examples and some source code for daily reading.

  1. Hump naming switch.
    Because the column names of citypo are completely named according to the hump type of database column names, mybatis provides a configuration item. After the open configuration item is enabled, the corresponding hump named fields can be found according to the database column names during matching.
<settings>
    <!--  Turn on the hump. After the hump is turned on, as long as the database field and object attribute name have the same letters, no matter how many underscores are added in the middle, it can be recognized -- >
    <setting name="mapUnderscoreToCamelCase" value="true" />
</settings>

From the perspective of source code, the mapping of resultset processed by mybat is completed in defaultresultsethandler by default.
When processing row data, it is mainly in the following areas:? Because we do not define an additional resultmap in the mapping file, we will directly enter the code of the else branch.

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      checkResultHandler();
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
}

Enter handlerowvaluesforsimpleresultmap. The main processing functions are as follows. Here, the generation and assignment of objects are completed.

Object rowValue = getRowValue(rsw, discriminatedResultMap); 

Here, an instance of the object is created first, and then the meta information of the object is obtained to prepare for reflection assignment.

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
    if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      final MetaObject metaObject = configuration.newMetaObject(rowValue);
      boolean foundValues = this.useConstructorMappings;
      if (shouldApplyAutomaticMappings(resultMap, false)) {
        foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
      }
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
      foundValues = lazyLoader.size() > 0 || foundValues;
      rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
    }
    return rowValue;
  }

After completing the whole process in applyautomaticmappings, let’s go in and explore.

[mybatis series] understand mybatis field mapping hump naming from the perspective of source code

The mapping relationship is created by the following function. The lower part of this function completes the assignment. The mapping part will be analyzed in detail next time.

List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix); 

In this method, the first half generates the column name of the database, and finds the corresponding field name in this function.

final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase()); 

Let’s go in and have a look. It passes in the generated database column name and the value of whether to name the mapping switch according to the hump type mentioned earlier.

[mybatis series] understand mybatis field mapping hump naming from the perspective of source codeIt turns out that it’s really simple. If you look down, you’ll remove all the underlining.

public String findProperty(String name, boolean useCamelCaseMapping) {
    if (useCamelCaseMapping) {
      name = name.replace("_", "");
    }
    return findProperty(name);
  }

I don’t think it’s case sensitive. Continue to look down and return the field names found here.

 private StringBuilder buildProperty(String name, StringBuilder builder) {
      ..........
      String propertyName = reflector.findPropertyName(name);
      if (propertyName != null) {
        builder.append(propertyName);
      }
    }
    return builder;
  }

Well, the truth comes out. It’s case insensitive.

public String findPropertyName(String name) {
    return caseInsensitivePropertyMap.get(name.toUpperCase(Locale.ENGLISH));
}

[mybatis series] understand mybatis field mapping hump naming from the perspective of source code

So if the field in your database is city_ id,city_ ID, capital I, then there may be a problem. However, if you think about it carefully, who will be thankless to do such a thing? It is OK to insist on a standard hump naming, but it doesn’t feel necessary.

[mybatis series] understand mybatis field mapping hump naming from the perspective of source code

After several crashes, I finally finished writing how to complete the mapping of database column and field names under the hump naming switch. The following blog posts will continue to look at the following two schemes and how object fields are assigned to SQL statements during DDL.


If you want to know more, please follow my wechat official account

[mybatis series] understand mybatis field mapping hump naming from the perspective of source code

Recommended Today

Drawing dynamic clock with canvas

Draw a dynamic clock as shown in the following figure <body><canvas width=”600px” height=”600px” style=”background-color: #ccc; display:block; margin: 0 auto”></canvas></body> <script> window.onload = function(){ //Get canvas object var canvas = document.querySelector(“canvas”); //Get context var context = canvas.getContext(“2d”); //Encapsulates a function used for clock dynamics function clock(){ //Draw disc context. beginPath();// start context.arc(300,300,200,0,2*Math.PI); context. fillStyle = “pink”;// […]