preface
-
Mybatis database has its own data type, Java has its own data type, so Mybatis is how to put the type of database and Java data type corresponding to it?
-
This article will talk about the black box TypeHandler(type processor) in Mybatis, it is not too much to say that it is a black box, always silently dedicated, but unknown.
Environment configuration
- Everything in this article is based on
Mybatis3.5
andSpringBoot - 2.3.3. RELEASE
.
What is TypeHandler?
-
As the name implies, the type processor will convert the input parameter and result to the required type. Mybatis has many built-in type processors, which are enough to be used in practical development, as shown in the following figure:
-
The type handler interface is actually very simple. There are four methods in total. One method converts the Java type data of the input parameter to JDBC type, and three methods convert the return result to Java type. The source code is as follows:
public interface TypeHandler<T> {
// Set the parameter to convert the Java type to JDBC type
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
// Convert the result of the query to Java type
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
Copy the code
How do I customize and use TypeHandler?
- In practical application development, it is inevitable that some requirements need to be customized
TypeHandler
, such as a requirement: the age of the front end ismale
.female
, but database-defined fields areint
Type (1 male, 2 female
). At this point, you can customize an age type handler for conversion.
How to customize?
- There are two ways to customize, one is implementation
TypeHandler
This interface, the other one is inheritanceBaseTypeHandler
This is a convenient abstract class. - So let’s inherit directly
BaseTypeHandler
This abstract class defines an age type handler as follows:
@MappedJdbcTypes(JdbcType.INTEGER)
@MappedTypes(String.class)
public class GenderTypeHandler extends BaseTypeHandler {
// Sets the parameter, which converts the Java String type to the JDBC Integer type
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i, StringUtils.equals(parameter.toString(),"Male")?1:2);
}
// The following three parameters convert the results of the query
@Override
public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getInt(columnName)==1?"Male":"Female";
}
@Override
public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getInt(columnIndex)==1?"Male":"Female";
}
@Override
public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getInt(columnIndex)==1?"Male":"Female"; }}Copy the code
- There are two comments involved here, as follows:
@MappedTypes
: Specifies that it is associated withJava
Type list. If thejavaType
Property, the configuration on the annotation is ignored.@MappedJdbcTypes
: Specifies that it is associated withJDBC
Type list. If thejdbcType
Property, the configuration on the annotation is ignored.
How do I add it to Mybatis?
- Mybatis is easy to integrate with SpringBoot, but there are two ways to configure Mybatis, as described below.
- The first kind of: only in the configuration file
application.properties
Add a line of configuration as follows:
Handlers - - set up a package for your custom Typehandler to be scanned into Mybatispackage=cn.cb.demo.typehandler
Copy the code
- In fact, any framework with Springboot integration, as long as the configuration file can be configured, in the configuration class can be configured (unless there is a special custom, do not easily override automatic configuration). As follows:
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATOIN));
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
// Automatically convert underscores in the database to hump format
configuration.setMapUnderscoreToCamelCase(true);
configuration.setDefaultFetchSize(100);
configuration.setDefaultStatementTimeout(30);
sqlSessionFactoryBean.setConfiguration(configuration);
// Register TypeHandler with mybatis
GenderTypeHandler genderTypeHandler = new GenderTypeHandler();
TypeHandler[] typeHandlers=new TypeHandler[]{genderTypeHandler};
sqlSessionFactoryBean.setTypeHandlers(typeHandlers);
return sqlSessionFactoryBean.getObject();
}
Copy the code
- The idea behind the second approach is to override the auto-configuration class
MybatisAutoConfiguration
Method in.Note: Do not overwrite methods in auto-configuration classes unless you have specific customization.
How do I specify TypeHandler in an XML file?
- The above two steps are customized and injected into Mybatis respectively
XML
What about files? - Use is actually very simple, divided into two kinds, one is
update
, a kind ofThe query
, will be introduced one by one below. - update: Delete needless to say, what is said here is
update
andinsert
Two, just need to be in# {}
Property specified intypeHandler
This parameter is customizedFull name of the class
The code is as follows:
<insert id="insertUser">
insert into user_info(user_id,his_id,name,gender,password,create_time)
values(#{userId,jdbcType=VARCHAR},#{hisId,jdbcType=VARCHAR},#{name,jdbcType=VARCHAR},
#{gender,jdbcType=INTEGER,typeHandler=cn.cb.demo.typehandler.GenderTypeHandler},#{password,jdbcType=VARCHAR},now())
</insert>
Copy the code
- The query: Type processing will convert JDBC types to Java types during query, so it needs to be specified
typeHandler
That need to be inresultMap
Specified in thetypeHandler
This property has a value of zeroFull name of the class
That is as follows:
<resultMap id="userResultMap" type="cn.cb.demo.domain.UserInfo">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="his_id" property="hisId"/>
<! -- Specify typeHandler property as full class name -->
<result column="gender" property="gender" typeHandler="cn.cb.demo.typehandler.GenderTypeHandler"/>
<result column="name" property="name"/>
<result column="password" property="password"/>
</resultMap>
<select id="selectList" resultMap="userResultMap">
select * from user_info where status=1
and user_id in
<foreach collection="userIds" item="item" open="(" separator="," close=")" >
#{item}
</foreach>
</select>
Copy the code
How do YOU implement TypeHandler in source code?
- Since you know how to use
TypeHandler
In Mybatis, how does the type handler workJDBC
The type andJava
Type conversion, the following will be introduced in detail from the source point of view.
How to convert an input parameter?
- This must happen in the process of setting parameters, detailed code in
PreparedStatementHandler
In theparameterize()
Method, which is the method that sets parameters. The source code is as follows:
@Override
public void parameterize(Statement statement) throws SQLException {
// DefaultParameterHandler is actually called
parameterHandler.setParameters((PreparedStatement) statement);
}
Copy the code
- The actual implementation is
DefaultParameterHandler
In thesetParameters
The methods are as follows:
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// Get the parameter mapping
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
// Go through the parameter mapping and set one by one
if(parameterMappings ! =null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if(parameterMapping.getMode() ! = ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// Get the type handler, if none exists, use the default
TypeHandler typeHandler = parameterMapping.getTypeHandler();
//JdbcType
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// Call the method in the type handler to set the parameter to convert the Java type to JDBC type
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
Copy the code
- You can see this line of code from the source code above
typeHandler.setParameter(ps, i + 1, value, jdbcType);
Is to call the method in the type handler that sets the parameterJava
Type conversion toJDBC
Type.
How do the results translate?
- This process must occur in the execution of the query statement, previously also introduced Mybatis six swordsmen, one of the
ResultSetHandler
This component processes the results of the query, so it must be some method in this component. - in
PreparedStatementHandler
After executing the query, the call isResultSetHandler
In thehandleResultSets()
Method, the results are processed as follows:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
/ / SQL execution
ps.execute();
// Process the result
return resultSetHandler.handleResultSets(ps);
}
Copy the code
- The ultimate in
DefaultResultHandler
In thegetPropertyMappingValue()
Method is calledTypeHandler
In thegetResult()
The methods are as follows:
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
if(propertyMapping.getNestedQueryId() ! =null) {
return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
} else if(propertyMapping.getResultSet() ! =null) {
addPendingChildRelation(rs, metaResultObject, propertyMapping); // TODO is that OK?
return DEFERRED;
} else {
finalTypeHandler<? > typeHandler = propertyMapping.getTypeHandler();final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
// Execute the method in typeHandler to get the result and convert it to the corresponding Java type
returntypeHandler.getResult(rs, column); }}Copy the code
conclusion
- The above is only a brief introduction to how the type handler is implemented in Mybatis. If some concepts are not clear, please refer to the author’s previous article as follows:
- Mybatis source code analysis of six swordsmen
- Mybatis source code how to read, teach you a trick!!
- Mybatis how to execute Select statement, do you know?
conclusion
- In this article, TypeHandler is introduced in detail in Mybatis application, custom use and from the point of view of source code analysis of the execution process of the type processor, if you feel that the author wrote a good, harvest, you may wish to pay attention to a little, share a wave.