SqlSessionFactoryBuilderTest
@Test
public void readerTest(a) throws IOException {
// Get a Reader class (character stream)
try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/mytest/mybatis-config.xml")) {
// Environment: data source ID in mybtis-config. XML
// properties: configuration file information. Generally, it is database configuration information. You can do this here or you can import the Properties configuration file in myBatis -config. XML with the
tag
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader,"development", Resources.getResourceAsProperties("org/apache/ibatis/mytest/db.properties"));
}
SqlSession sqlSession = sqlSessionFactory.openSession();
List<StudentDto> rs = sqlSession.selectList("org.apache.ibatis.mytest.StudentMapper.getStudentsByAlias".null);
System.out.println(rs.toString());
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
rs = mapper.getStudentsByAlias();
System.out.println(rs.toString());
}
Copy the code
SqlSessionFactoryBuilder
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
/ / 1
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
/ / 2
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.}}}Copy the code
1. Create an XML configuration builder that includes an Xpath parser
Parser.parse () returns a Configuration object
Next, look at parser.parse()
XMLConfigBuilder
public Configuration parse(a) {
/ / 1
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
/ / 2
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
Copy the code
1. If it is parsed, throw an exception and ensure that it is parsed only once
2. Parse the myBatis -config. XML configuration file from the root node /configuration, parse the package into a Configuration object, and return the configuration object
Next look at parseConfiguration(Parser.evalNode (“/configuration”));
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
/ / 1
propertiesElement(root.evalNode("properties"));
/ / 2
Properties settings = settingsAsProperties(root.evalNode("settings"));
/ / 3
loadCustomVfs(settings);
/ / 4
loadCustomLogImpl(settings);
/ / 5
typeAliasesElement(root.evalNode("typeAliases"));
/ / 6
pluginElement(root.evalNode("plugins"));
/ / 7
objectFactoryElement(root.evalNode("objectFactory"));
/ / 8
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
/ / 9
reflectorFactoryElement(root.evalNode("reflectorFactory"));
/ / 10
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
/ / 11
environmentsElement(root.evalNode("environments"));
/ / 12
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
/ / 13
typeHandlerElement(root.evalNode("typeHandlers"));
/ / 14
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: "+ e, e); }}Copy the code
1. The Properties node is used to read the Properties configuration file
/** * Parse the properties tag **@param context
*
* @throws Exception
*/
private void propertiesElement(XNode context) throws Exception {
if(context ! =null) {
/ / 1
Properties defaults = context.getChildrenAsProperties();
/ / 2
String resource = context.getStringAttribute("resource");
/ / 3
String url = context.getStringAttribute("url");
if(resource ! =null&& url ! =null) {
/ / 4
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
if(resource ! =null) {
/ / 5
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if(url ! =null) {
/ / 6
defaults.putAll(Resources.getUrlAsProperties(url));
}
/ / 7
Properties vars = configuration.getVariables();
if(vars ! =null) {
// If variables are not empty, fill in the Properties object
defaults.putAll(vars);
}
/ / 8
parser.setVariables(defaults);
/ / 9
configuration.setVariables(defaults);
// This is where the priority is: Subtag -> resource/ URL -> configuration.variables The end result is to shove Properties into the XPathParser and Configuration}}Copy the code
1. Check whether the properties tag has a property subtag. Read the name and value attributes and put them into the Properties object. 2. Resource attribute under the Properties tag 5. Resource, the configuration of the resource path is parsed into the Properties (in this case, the property subtag in the XML is parsed first, so there is a Properties file that overwrites the configuration in the XML. 6. Url, then go to the network request, and then read the configuration to properties 7. First get the existing variables(also of the Properties type) in the Configuration (the SqlSessionFactoryBuilder is priority. Build introduced the properties (properties) > XML > property child tags defined properties) 8. The Properties configuration information is then stuffed into the XPathParser 9. Shove the Properties Configuration information into the Configuration variables
1.
2.
2. Parse all Settings subtags in the settingss TAB
Configuration items | role | Configuration options | The default value |
---|---|---|---|
cacheEnabled | This configuration affects the global switch for configuring caching in all mappers | true/false | true |
lazyLoadingEnabled | Global switch for lazy loading. When enabled, all associated objects are lazily loaded. The on-off state of this item can be overridden in a particular association by setting the fetchType property | true/false | false |
aggressiveLazyLoading | When enabled, a call to any lazy property causes the object with the lazy loaded property to be fully loaded; Instead, each property will be loaded on demand | true/false | True before version 3.4.1 (not included), false after |
multipleResultSetsEnabled | Allows a single statement to return multiple result sets (requires compatible driver) | true/false | true |
useColumnLabel | Use column labels instead of column names. Different drivers will have different performance, specific can refer to the relevant driver documentation or by testing the two different modes to observe the results of the driver | true/false | true |
useGeneratedKeys | Allow JDBC support to automatically generate primary keys, requiring driver compatibility. If set to true, this setting enforces the use of automatically generated primary keys, which will work even though some drivers are not compatible (such as Derby) | true/false | false |
autoMappingBehavior | Specifies how MyBatis should automatically map columns to fields or attributes. NONE indicates that the automatic mapping is cancelled. PARTIAL indicates that only automatic mapping is performed. Nested and mapped result sets are not defined. FULL automatically maps arbitrarily complex result sets (nested or not) | NONE, PARTIAL, FULL | PARTIAL |
autoMappingUnkno wnColumnBehavior | Specifies the behavior when an unknown column (or unknown attribute type) is in an automatic mapping. The default is no processing. Logs are displayed only when the log level reaches the WARN level or below. If the processing fails, an SqlSessionException is thrown | NONE, WARNING, and FAILING | NONE |
defaultExecutorType | Configure the default actuator. SIMPLE is an ordinary actuator; REUSE reuses prepared statements. The BATCH executor reuses statements and performs BATCH updates | SIMPLE, REUSE, or BATCH | SIMPLE |
defaultStatementTimeout | Sets the timeout, which determines the number of seconds the driver waits for the database response | Any positive integer | Not Set (null) |
defaultFetchSize | Sets the default limit on the number of items returned by the database driver. This parameter can be reset | Any positive integer | Not Set (null) |
safeRowBoundsEnabled | Allows the use of RowBounds in nested statements. If yes, set false | true/false | false |
safeResultHandlerEnabled | Allows paging (ResultHandler) in nested statements. If yes, set false | true/false | true |
mapUnderscoreToCamelCase | Whether to enable automatic hump naming rule mapping, that is, from A_COLUMN to classic database column nameJavaA similar mapping for the property name aColumn | true/false | false |
localCacheScope | MyBatis uses the Local Cache mechanism to prevent circular references and accelerate unduplicative nested queries. The default value is SESSION, in which case all queries executed in a SESSION are cached. If STATEMENT is set, the local session is used only for STATEMENT execution. Different calls to the same SqlScssion do not share data | SESSION | STATEMENT |
jdbcTypeForNull | Specifies the 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, just use the generic type, such as NULL, VARCHAR, or OTHER | NULL, VARCHAR, or OTHER | OTHER |
lazyLoadTriggerMethods | Specifies which object’s method triggers a lazy load | – | Equals, Clone, hashCode, toString |
defaultScriptingLanguage | Specifies the default language for dynamic SQL generation | – | org.apache.ibatis .script.ing.xmltags .XMLDynamicLanguageDriver |
callSettersOnNulls | Specifies whether the setter (put for map objects) method is called when the result set value is NULL, which is useful for map.kcyset () dependencies or null value initialization. Note that basic types (int, Boolean, etc.) cannot be set to null | true/false | false |
logPrefix | Specifies the prefix to add MyBatis to the log name | Any string | Not set |
loglmpl | Specifies the implementation of the log used by MyBatis. If not specified, it will be automatically looked up | SLF4J/LOG4J/LOG4J2/JDK_LOGGING/COMMONS_LOGGING /ST DOUT_LOGGING/NO_LOGGING | Not set |
proxyFactory | Specifies the proxy tool MyBatis uses to create objects with deferred seeding | CGLIB | JAVASSIST |
vfsImpl | Specifies the implementation class of the VFS | Provide the fully qualified names of the VFS classes, separated by commas if multiple exist | Not set |
useActualParamName | Allows a parameter to be referenced by the actual name declared in the method parameter. To use this feature, the project must be compiled with a selection of Java 8 parameters. (Available from version 3.4.1) | true/false | true |
You’re not really going to use that much configuration in the actual configuration just a little bit more common configuration
<! -- Settings are important adjustments in MyBatis that change the runtime behavior of MyBatis. -->
<settings>
<! -- Global switch for caching configured in all mappers affected by this configuration. Default value: true -->
<setting name="cacheEnabled" value="true"/>
<! -- Global switch for lazy loading. When enabled, all associated objects are lazily loaded. The on-off state of this item can be overridden by setting the fetchType property in a particular association. Default value: false -->
<setting name="lazyLoadingEnabled" value="true"/>
<! -- Whether to allow a single statement to return multiple result sets (requires driver compatibility). Default value: true -->
<setting name="multipleResultSetsEnabled" value="true"/>
<! Use column labels instead of column names. Different drivers in this respect will have different performance, specific can refer to the relevant driver documents or by testing the two different modes to observe the results of the driver. Default value: true -->
<setting name="useColumnLabel" value="true"/>
<! Allow JDBC support for automatic primary key generation, required driver compatibility. If set to true, this setting enforces the use of automatically generated primary keys, which works even though some drivers are not compatible (such as Derby). Default value: false -->
<setting name="useGeneratedKeys" value="false"/>
<! -- specifies how MyBatis should automatically map columns to fields or attributes. NONE indicates that the automatic mapping is cancelled. PARTIAL only automatically maps result sets that have no nested result set mappings defined. FULL automatically maps arbitrarily complex result sets (whether nested or not). -->
<! -- default value PARTIAL -->
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<! -- Configure the default actuator. SIMPLE is the ordinary actuator; The REUSE executor reuses prepared statements. The BATCH executor reuses statements and performs BATCH updates. The default SIMPLE -- -- >
<setting name="defaultExecutorType" value="SIMPLE"/>
<! -- Sets the timeout, which determines the number of seconds the driver waits for the database response. -->
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<! -- Allowed to use the RowBounds default value False in nested statements -->
<setting name="safeRowBoundsEnabled" value="false"/>
<! -- Whether to enable Camel case mapping, which is similar to the mapping from the classic database column name A_COLUMN to the classic Java attribute name aColumn. The default false -- -- >
<setting name="mapUnderscoreToCamelCase" value="false"/>
<! -- MyBatis uses the Local Cache mechanism 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 STATEMENT is set, the local session is used only for STATEMENT execution and data will not be shared between different calls to the same SqlSession. -->
<setting name="localCacheScope" value="SESSION"/>
<! Specifies a null JDBC type when no specific JDBC type is provided for the parameter. Some drivers need to specify the JDBC type of the column. In most cases, just use the generic type, such as NULL, VARCHAR, or OTHER. -->
<setting name="jdbcTypeForNull" value="OTHER"/>
<! -- Specifies which object's method triggers a lazy load. -->
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
Copy the code
3. Load VFS configuration information in the Settings node
VFS is a virtual file system. Mainly through the program can easily read the file resources in the local file system, FTP file system and other systems. The VFS configuration is provided in Mybatis, which allows you to load custom virtual file system applications.
4. Load logImpl configuration information in the Settings node [Log configuration]
Configuration Indicates the log configuration
private void loadCustomLogImpl(Properties props) {
Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
configuration.setLogImpl(logImpl);
}
Copy the code
5. TypeAliases Alias configuration
In mapper. XML, you can use aliases directly by setting aliases. For example:
<typeAliases>
<typeAlias alias="student" type="org.apache.ibatis.mytest.StudentDto"/>
</typeAliases>
Copy the code
<select id="getStudentsByAlias" resultType="student" useCache="false" >
select id,name,age from student
</select>
Copy the code
Query operations can be performed directly
6. Configure plugins
Mybatis performs plug-in configuration. Read the plug-in information and register it with the interceptor chain
private void pluginElement(XNode parent) throws Exception {
if(parent ! =null) {
The Executor ParameterHandler ResultSetHandler StatementHandler method can be intercepted by the interceptor
// Executor: where SQL statements are executed
// ParameterHandler: indicates parameter processing
// ResultSetHandler: result set processing
for (XNode child : parent.getChildren()) {
// Get the interceptor property of the
tag
// If you need to customize a plug-in, you should inherit from the Interceptor interface
String interceptor = child.getStringAttribute("interceptor");
// Get the interceptor property information and convert it into a Properties object
Properties properties = child.getChildrenAsProperties();
// Create an interceptor instance. This can be configured using aliases
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
// Set the interceptor instance properties
interceptorInstance.setProperties(properties);
// Add an interceptor instance to the interceptor chainconfiguration.addInterceptor(interceptorInstance); }}}Copy the code
Implement a pagination plug-in
package org.apache.ibatis.mytest;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.testcontainers.shaded.com.google.common.base.Joiner;
import java.util.Properties;
/ * * *@author yangqiang
* @dateThe 2021-07-26 20:57 * /
@Slf4j
@Intercepts({ @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) })
public class PageInterceptor implements Interceptor {
// Corresponding to the args sequence number above
private static final Integer MAPPED_STATEMENT_INDEX = 0 ;
private static final Integer PARAMTER_INDEX = 1;
private static final Integer ROWBOUNDS_INDEX = 2;
private static final Integer RESULT_HANDLER_INDEX = 3;
@Override
public Object intercept(Invocation invocation) throws Throwable {
// Get the parameters from the Invocation
Object[] queryArgs = invocation.getArgs();
MappedStatement ms = (MappedStatement)queryArgs[MAPPED_STATEMENT_INDEX];
Object parameter = queryArgs[PARAMTER_INDEX];
Page page = PageUtil.getPaingParam();
if(page ! =null) {Select * from ** where limit **
BoundSql boundSql = ms.getBoundSql(parameter);
String pagingSql = getPagingSql(boundSql.getSql(), page.getOffset(), page.getLimit());
BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), pagingSql, boundSql.getParameterMappings(), boundSql.getParameterObject());
queryArgs[MAPPED_STATEMENT_INDEX] = newMappedStatement(ms,newBoundSql);
/ / reset RowBounds
queryArgs[ROWBOUNDS_INDEX] = new RowBounds(RowBounds.NO_ROW_OFFSET, RowBounds.NO_ROW_LIMIT);
}
log.info("Custom pagination plug-in execution");
Object result = invocation.proceed();
PageUtil.removePagingParam();
return result;
}
public String getPagingSql(String sql, int offset, int limit) {
StringBuilder result = new StringBuilder(sql.length() + 100);
result.append(sql).append(" limit ");
if (offset > 0) {
result.append(offset).append(",").append(limit);
}else{
result.append(limit);
}
return result.toString();
}
/** * Rebuild MappedStatement * to overlay the original BoundSql with the modified BoundSql *@paramMs original MappedStatement *@paramNewBoundSql newBoundSql *@returnNew MappedStatement * /
private MappedStatement newMappedStatement(MappedStatement ms, BoundSql newBoundSql) {
MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(),
new BoundSqlSource(newBoundSql), ms.getSqlCommandType());
builder.keyColumn(delimitedArrayToString(ms.getKeyColumns()));
builder.keyGenerator(ms.getKeyGenerator());
builder.keyProperty(delimitedArrayToString(ms.getKeyProperties()));
builder.lang(ms.getLang());
builder.resource(ms.getResource());
builder.parameterMap(ms.getParameterMap());
builder.resultMaps(ms.getResultMaps());
builder.resultOrdered(ms.isResultOrdered());
builder.resultSets(delimitedArrayToString(ms.getResultSets()));
builder.resultSetType(ms.getResultSetType());
builder.timeout(ms.getTimeout());
builder.statementType(ms.getStatementType());
builder.useCache(ms.isUseCache());
builder.cache(ms.getCache());
builder.databaseId(ms.getDatabaseId());
builder.fetchSize(ms.getFetchSize());
builder.flushCacheRequired(ms.isFlushCacheRequired());
return builder.build();
}
public String delimitedArrayToString(String[] array) {
if (array == null || array.length == 0) {
return "";
}
Joiner joiner = Joiner.on(",");
return joiner.join(array);
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target,this);
}
@Override
public void setProperties(Properties properties) {}}Copy the code
package org.apache.ibatis.mytest;
/ * * *@author yangqiang
* @dateThe 2021-07-26 20:59 * /
public class PageUtil {
private static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();
public static void setPagingParam(int offset, int limit) {
Page page = new Page(offset, limit);
LOCAL_PAGE.set(page);
}
public static void removePagingParam(a) {
LOCAL_PAGE.remove();
}
public static Page getPaingParam(a) {
returnLOCAL_PAGE.get(); }}Copy the code
package org.apache.ibatis.mytest;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
/ * * *@author yangqiang
* @dateAt about the 2021-07-26 * /
public class BoundSqlSource implements SqlSource {
public BoundSql boundSql;
public BoundSqlSource(BoundSql boundSql) {
this.boundSql = boundSql;
}
@Override
public BoundSql getBoundSql(Object parameterObject) {
returnboundSql; }}Copy the code
package org.apache.ibatis.mytest;
import lombok.AllArgsConstructor;
import lombok.Data;
/ * * *@author yangqiang
* @dateThe 2021-07-26 20:59 * /
@Data
@AllArgsConstructor
public class Page {
/** * start query ** /
public Integer offset;
/** * query how many data ** /
public Integer limit;
}
Copy the code
test
@Test
public void readerTest(a) throws IOException {
PageUtil.setPagingParam(1.5);
// Get a Reader class (character stream)
try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/mytest/mybatis-config.xml")) {
// Environment: data source ID in mybtis-config. XML
// properties: configuration file information. Generally, it is database configuration information. You can do this here or you can import the Properties configuration file in myBatis -config. XML with the
tag
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader,"development", Resources.getResourceAsProperties("org/apache/ibatis/mytest/db.properties"));
}
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<StudentDto> rs = mapper.getStudentsByAlias();
System.out.println(rs.toString());
}
Copy the code
The results of
[StudentDto(id=2, name=mybatis, age=10), StudentDto(id=3, name=mybatis, age=10), StudentDto(id=4, name=mybatis, age=10), StudentDto(id=5, name=mybatis, age=10), StudentDto(id=6, name=mybatis, age=10)]
Copy the code
You can see that the paging feature has succeeded.
7. Object factory, can be customized, has a default implementation
Every time MyBatis creates a new instance of the result object, it does so using an ObjectFactory instance. All the default object factory needs to do is instantiate the target class, either through the default constructor or through the parameter constructor if the parameter map exists. If you want to override the default behavior of an object factory, you can do so by creating your own object factory.
This step reads the custom objectFactory from the objectFactory tag and sets it to the configuration objectFactory
Customize an object factory. Make an output when the result object is created
The XML configuration has aliases configured in advance
<typeAliases>
<package name="org.apache.ibatis.mytest"/>
</typeAliases>
Copy the code
<objectFactory type="MyObjectFactory"></objectFactory>
Copy the code
package org.apache.ibatis.mytest;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import java.util.*;
/ * * *@author yangqiang
* @dateCame 2021-07-27 * /
public class MyObjectFactory extends DefaultObjectFactory {
private static final long serialVersionUID = -8855120656740914948L;
@Override
public <T> T create(Class<T> type) {
// The next create method is called
return super.create(type);
}
@Override
public <T> T create(Class
type, List
> constructorArgTypes, List
>
{
System.out.println("create bean "+ type.getSimpleName());
return super.create(type, constructorArgTypes, constructorArgs);
}
/** Determine the set type parameter */
@Override
public <T> boolean isCollection(Class<T> type) {
boolean retBool = false;
System.out.println(type.getSimpleName()+" is collection?"+(retBool=super.isCollection(type)));
return retBool;
}
@Override
public void setProperties(Properties properties) {
super.setProperties(properties); }}Copy the code
test
@Test
public void readerTest(a) throws IOException {
PageUtil.setPagingParam(1.5);
// Get a Reader class (character stream)
try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/mytest/mybatis-config.xml")) {
// Environment: data source ID in mybtis-config. XML
// properties: configuration file information. Generally, it is database configuration information. You can do this here or you can import the Properties configuration file in myBatis -config. XML with the
tag
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader,"development", Resources.getResourceAsProperties("org/apache/ibatis/mytest/db.properties"));
}
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<StudentDto> rs = mapper.getStudentsByAlias();
System.out.println(rs.toString());
}
Copy the code
The results of
List is collection?truecreate bean List create bean StudentDto create bean StudentDto create bean StudentDto create bean StudentDto create bean StudentDto [StudentDto(id=2, name=mybatis, age=10), StudentDto(id=3, name=mybatis, age=10), StudentDto(id=4, name=mybatis, age=10), StudentDto(id=5, name=mybatis, age=10), StudentDto(id=6, name=mybatis, age=10)]
Copy the code
The StudentDto object is created 5 times because the length of the collection is 5. You can also extend other features on your own.
8. Object packaging factory
We’ll look at that later
9. Reflection factory
For more flexibility, Mybatis supports user-defined reflection factories, but in general, they are not used much. To implement ReflectorFactory, just implement the ReflectorFactory interface as ReflectorFactory interface. The default reflection factory is DefaultReflectorFactory. In general, use the default reflection factory.
10. Take the properties resolved by Settings and assign values to the Configuration object
Parse the configuration under the Setting TAB into the Configuration TAB
private void settingsElement(Properties props) {
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior"."PARTIAL")));
configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior"."NONE")));
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType"."SIMPLE")));
configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope"."SESSION")));
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull"."OTHER")));
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
configuration.setLogPrefix(props.getProperty("logPrefix"));
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false));
configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType")));
}
Copy the code
11. Environments Configure reading
Loads the specified data source or the default data source information and the transaction factory
private void environmentsElement(XNode context) throws Exception {
if(context ! =null) {
// The default environment is default if not specified
if (environment == null) {
environment = context.getStringAttribute("default");
}
// Iterate over each env environment
for (XNode child : context.getChildren()) {
/ / to get id
String id = child.getStringAttribute("id");
// If the id matches the above environment, load the configuration of the environment
if (isSpecifiedEnvironment(id)) {
MyBatis has two types of transaction factory, JDBC and MANAGED
// JDBC: transactions that use JDBC
// MANAGED: transactions delegate to the container
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// Data source configuration
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
// Building Environment objects requires transactions and data sources
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
// Insert the Environment object into the Configuration
// 1, configuration --> Environment --> DataSource -->...
// 2. The definition hierarchy of configuration files and entity classes is the same.
// Todo 1/2 === // todo 1/2 === // todo 1/2 === // todo 1/2 === // todo 1/2 === // todo 1/2 === // todo 1/2 === // todo 1/2 ===
// Our Configuration information is parsed and stored in the Configuration variable.configuration.setEnvironment(environmentBuilder.build()); }}}}Copy the code
12. Configure the databaseIdProvider database
private void databaseIdProviderElement(XNode context) throws Exception {
DatabaseIdProvider databaseIdProvider = null;
if(context ! =null) {
String type = context.getStringAttribute("type");
// awful patch to keep backward compatibility
if ("VENDOR".equals(type)) {
type = "DB_VENDOR";
}
Properties properties = context.getChildrenAsProperties();
databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
databaseIdProvider.setProperties(properties);
}
Environment environment = configuration.getEnvironment();
if(environment ! =null&& databaseIdProvider ! =null) { String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource()); configuration.setDatabaseId(databaseId); }}Copy the code
MyBatis can execute different statements according to different database vendors. This multi-vendor support is based on the databaseId attribute in the mapped statement.
MyBatis loads all statements without the databaseId property and all statements with the databaseId property matching the current database. If the same statement is found with and without the databaseId, the latter is discarded. To support multi-vendor features, simply add databaseIdProvider to mybatis-config. XML as follows:
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
</databaseIdProvider>
Copy the code
<select id="getStudentsByAlias" resultType="StudentDto" useCache="false" databaseId="mysql">
select id,name,age from student
</select>
<select id="getStudentsByAlias" resultType="StudentDto" useCache="false">
select id,name,age from student limit 1
</select>
Copy the code
Idea is red for SQL with the same tag ID but can be executed normally. Does this mean that SQL tag ids can be repeated? See the parsing of sqlMapper later
In this case, Mybatis encapsulates the basic handlers, so we can replace them in
private void typeHandlerElement(XNode parent) {
if(parent ! =null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// Similar to aliases, there are package scan registrations or single class registrations
String typeHandlerPackage = child.getStringAttribute("name");
typeHandlerRegistry.register(typeHandlerPackage);
} else {
String javaTypeName = child.getStringAttribute("javaType");
String jdbcTypeName = child.getStringAttribute("jdbcType");
String handlerTypeName = child.getStringAttribute("handler"); Class<? > javaTypeClass = resolveClass(javaTypeName); JdbcType jdbcType = resolveJdbcType(jdbcTypeName); Class<? > typeHandlerClass = resolveClass(handlerTypeName);if(javaTypeClass ! =null) {
if (jdbcType == null) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else{ typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); }}else {
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
Copy the code
Next, define your own TypeHandler
Implementation stores Java collection types as characters to the mysql database. Such as [1, 2, 3, 4] – > ‘1, 2, 3, 4’
1. Customize a TypeHandler
package org.apache.ibatis.mytest;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import java.util.*;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.stream.Collectors;
/ * * *@author yangqiang
* @dateThe 2021-07-29 14:27 * /
public class MyTypeHandler implements TypeHandler<List<Integer>> {
@Override
public void setParameter(PreparedStatement ps, int i, List<Integer> parameter, JdbcType jdbcType) throws SQLException {
StringBuffer sb = new StringBuffer();
for (Integer s : parameter) {
sb.append(s).append(",");
}
ps.setString(i, sb.substring(0,sb.length()-1).toString());
}
@Override
public List<Integer> getResult(ResultSet rs, String columnName) throws SQLException {
String string = rs.getString(columnName);
if(string! =null) {return Arrays.stream(string.split(",")).map(Integer::valueOf).collect(Collectors.toList());
}
return null;
}
@Override
public List<Integer> getResult(ResultSet rs, int columnIndex) throws SQLException {
String string = rs.getString(columnIndex);
if(string! =null) {return Arrays.stream(string.split(",")).map(Integer::valueOf).collect(Collectors.toList());
}
return null;
}
@Override
public List<Integer> getResult(CallableStatement cs, int columnIndex) throws SQLException {
String string = cs.getString(columnIndex);
if(string! =null) {return Arrays.stream(string.split(",")).map(Integer::valueOf).collect(Collectors.toList());
}
return null; }}Copy the code
2. Declare the conversion in mybatis-config. XML
<typeAliases>
<package name="org.apache.ibatis.mytest"/>
</typeAliases>
<typeHandlers>
<typeHandler handler="MyTypeHandler" javaType="java.util.List" jdbcType="VARCHAR"/>
</typeHandlers>
Copy the code
3. Define the interfaces to be added and queried
<! DOCTYPEmapper PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN" "./mybatis-3-mapper.dtd">
<mapper namespace="org.apache.ibatis.mytest.StudentMapper">
<resultMap id="studentDto" type="StudentDto">
<result column="id" jdbcType="INTEGER" property="id"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
<result column="age" jdbcType="INTEGER" property="age"/>
<result column="hobby" jdbcType="VARCHAR" property="hobby" />
</resultMap>
<select id="getStudentsByAlias" resultType="studentDto" useCache="false" databaseId="mysql">
select id,name,age,hobby from student
</select>
<select id="getStudentsByAlias" resultType="studentDto" useCache="false">
select id,name,age,hobby from student limit 1
</select>
<insert id="insertStudent" parameterType="StudentDto" databaseId="mysql">
insert into student (id,name,age,hobby) values(
#{id},
#{name},
#{age},
#{hobby}
)
</insert>
</mapper>
Copy the code
Here I’m compiling the source code. It is found that adding TypeHandler properties to XML fields can also be translated! We’ll see this later when we parse the mapper. The conclusions are as follows: 1. For user-defined type conversion configuration, for example, if only TypeHandler conversion is configured. This conversion is performed for all entity classes of type List Integer. And the database will be converted to String storage. When queried, the character is converted to List 2. For types that have multiple TypeHandlers configured for the same JdbcType. Specify the corresponding field jdbcType or TypeHandler that must be displayed in order to find the corresponding TypeHandler in the 14 analysis section
4. Test
@Test
public void readerTest(a) throws IOException {
PageUtil.setPagingParam(1.5);
// Get a Reader class (character stream)
try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/mytest/mybatis-config.xml")) {
// Environment: data source ID in mybtis-config. XML
// properties: configuration file information. Generally, it is database configuration information. You can do this here or you can import the Properties configuration file in myBatis -config. XML with the
tag
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader,"development", Resources.getResourceAsProperties("org/apache/ibatis/mytest/db.properties"));
}
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<StudentDto> rs = mapper.getStudentsByAlias();
System.out.println(rs.toString());
int count = mapper.insertStudent(new StudentDto(11."smallwhite".24, Arrays.asList(1.2.3.4.5)));
sqlSession.commit();
System.out.println(count);
}
Copy the code
Results of 5.
[StudentDto(id=2, name=mybatis, age=10, hobby=[1.2.3.4]), StudentDto(id=3, name=mybatis, age=10, hobby=[1.2.3.4]), StudentDto(id=4, name=mybatis, age=10, hobby=[1.2.3.4]), StudentDto(id=5, name=mybatis, age=10, hobby=[1.2.3.4]), StudentDto(id=6, name=mybatis, age=10, hobby=[1.2.3.4]), StudentDto(id=7, name=mybatis, age=10, hobby=[1.2.3.4]), StudentDto(id=8, name=mybatis, age=10, hobby=[1.2.3.4]), StudentDto(id=9, name=mybatis, age=10, hobby=[1.2.3.4]), StudentDto(id=10, name=mybatis, age=10, hobby=[1.2.3.4]), StudentDto(id=11, name=yangqiang, age=24, hobby=[1.2.3.4.5]]Copy the code
14. The point is that mapper maps are parsed here, all SQL and so on will be parsed here
There are several ways to use Mappers tags
<package name=”org.apache.ibatis. Mytest “/>
This approach requires that the XML and mapper files are in the same directory. Otherwise, the XML file will not be loaded. The first step in parsing is to find all the interface classes under the package. Parse the interface class. Parse SQL tag information such as @select. Then look for XML files in the same directory for parsing. If there is no XML file, the parsing is complete. It is also not possible to add a mapper tag inside the mappers tag to parse the XML separately. Because mapper files are parsed and stored in a hashMap. An error will be reported if it is parsed again.
2. According to the XML file mapping < mapper resource = “org/apache/ibatis/parsing/StudentMapper XML” / >
This does not require XML and mapper to be in the same folder.
Obtain the sqlSessionFactory factory
public SqlSessionFactory build(Configuration config) {
// Return the default SqlSessionFactory object, passing in configuration information
// Obtain the sqlSessionFactory factory and obtain the sqlSession from the factory
return new DefaultSqlSessionFactory(config);
}
Copy the code