1.mybatis-config.xml template
<?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> </configuration>
2. properties
Use example
These properties can be set in the child elements of the properties element, or can be configured externally, introduced through resource, and dynamically replaced. For example:
// Introducing attributes through resource <properties resource="org/mybatis/example/config.properties"> // Set in child elements <property name="username" value="root"/> <property name="password" value="123456"/> </properties>
priority
If a property is configured in multiple places, Mybatis will load it in the following order:
- First read the properties specified in the properties element body.
- Then read the property file under the classpath according to the resource attribute in the properties element, or read the property file according to the path specified by the url attribute, and overwrite the previously read property with the same name.
- Finally, read the property passed as a method parameter and overwrite the previously read property with the same name.
We can see that the above order is overwritten every time, because the last order is in positive order. The attributes passed through the method parameters have the highest priority, followed by the configuration file specified in the resource/url attribute, and the attributes specified in the properties element have the lowest priority.
3. settings
Common configurations. See the official website for all configurations
Set name | describe | Effective value | Default value |
---|---|---|---|
cacheEnabled | Globally turn on or off any cache configured in all mapper profiles. | true | false | true |
lazyLoadingEnabled | Global switch for delayed loading. When on, all associated objects are loaded late. In a specific association, the switch state of the item can be overridden by setting the fetchType property. | true | false | false |
useColumnLabel | Use column labels instead of column names. The actual performance depends on the database driver. For details, please refer to the relevant documents of the database driver or observe through comparative test. | true | false | true |
defaultStatementTimeout | Set the timeout, which determines the number of seconds the database driver waits for a database response. | Any positive integer | Not set (null) |
mapUnderscoreToCamelCase | Whether to enable automatic hump naming mapping, that is, from the classic database column name A_COLUMN maps to the classic Java property name aColumn. | true | false | False |
Use example
<settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="autoMappingBehavior" value="PARTIAL"/> <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25"/> <setting name="defaultFetchSize" value="100"/> <setting name="safeRowBoundsEnabled" value="false"/> <setting name="mapUnderscoreToCamelCase" value="false"/> <setting name="localCacheScope" value="SESSION"/> <setting name="jdbcTypeForNull" value="OTHER"/> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> </settings>
4. Type aliases
Manual assignment
Type alias sets an abbreviated name for a Java type. It is only used for XML configuration and is intended to reduce redundant fully qualified class name writing. For example:
<typeAliases> // Set the alias of an entity class separately <typeAlias alias="Author" type="domain.blog.Author"/> <typeAlias alias="Blog" type="domain.blog.Blog"/> <typeAlias alias="Comment" type="domain.blog.Comment"/> </typeAliases>
You can specify a package name and set all aliases to lowercase class names
<typeAliases> <package name="com.jianan.entity"/> </typeAliases>
In the Java Bean of each package, the Alias defaults to the initial lowercase unqualified class name of the Bean, but we can modify it through the @ Alias annotation
@Alias("author") public class Author { ... }
Provided by default
alias | Type of mapping |
---|---|
_byte | byte |
_short | short |
_int | int |
_long | long |
_float | float |
_double | double |
_boolean | boolean |
_integer | int |
string | String |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
date | Date |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
5. Type handlers
When MyBatis sets the parameters in the PreparedStatement or takes a value from the result set, it will use the type processor to convert the obtained value into Java type in an appropriate way.
Provided by default
Some are listed below. For more information, please see the official website
Type processor | Java type | JDBC type |
---|---|---|
BooleanTypeHandler | java.lang.Boolean, boolean | Database compatible BOOLEAN |
ByteTypeHandler | java.lang.Byte, byte | Database compatible NUMERIC or BYTE |
ShortTypeHandler | java.lang.Short, short | Database compatible NUMERIC or SMALLINT |
IntegerTypeHandler | java.lang.Integer, int | Database compatible NUMERIC or INTEGER |
LongTypeHandler | java.lang.Long, long | Database compatible NUMERIC or BIGINT |
FloatTypeHandler | java.lang.Float, float | Database compatible NUMERIC or FLOAT |
DoubleTypeHandler | java.lang.Double, double | Database compatible NUMERIC or DOUBLE |
DateTypeHandler | java.util.Date | TIMESTAMP |
StringTypeHandler | java.lang.String | CHAR, VARCHAR |
... |
Custom processor
You can override an existing type processor or create your own type processor to handle unsupported or nonstandard types. The specific methods are as follows:
- Implement the org.apache.ibatis.type.TypeHandler interface, or inherit a convenient class org.apache.ibatis.type.BaseTypeHandler,
- Specifies that it can (optionally) map it to a JDBC type
- Register in Mybatis global configuration file
TypeHandler interface
public interface TypeHandler<T> { /** * It is used to define how to convert Java types to corresponding database types when mybatis sets parameters * @param preparedStatement Current PreparedStatement object * @param i The location of the current parameter * @param s Java object for the current parameter * @param jdbcType Database object for the current parameter * @throws SQLException */ void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException; /** * It is used to define how to convert the database type to the corresponding Java type when Mybatis obtains the dataset * @param resultSet Current dataset * @param s Converted Java object * @return * @throws SQLException */ T getResult(ResultSet rs, String columnName) throws SQLException; /** * It is used to define how to convert the database type to the corresponding Java type when Mybatis obtains field data through field location * @param resultSet Current result set * @param i The location of the current field * @return * @throws SQLException */ T getResult(ResultSet rs, int columnIndex) throws SQLException; /** * It is used for Mybatis to convert the data of database type to the corresponding Java type after calling the stored procedure * @param callableStatement CallableStatement after the current CallableStatement is executed * @param i The location of the current output parameter * @return * @throws SQLException */ T getResult(CallableStatement cs, int columnIndex) throws SQLException; }
BaseTypeHandler class (recommended)
We can see that the BaseTypeHandler class implements the TypeHandler interface and inherits TypeReference
- BaseTypeHandler is an abstract class that requires subclasses to implement the four abstract methods defined by it, and it itself implements the four methods of the typeHandler interface.
- The BaseTypeHandler class adds non null judgment, and subclasses are omitted. After the subclass rewrites the method, it only focuses on logic
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> { protected Configuration configuration; public void setConfiguration(Configuration c) { this.configuration = c; } @Override public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { if (parameter == null) { if (jdbcType == null) { // Both are null, setting exception throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters."); } try { // Make a null setting ps.setNull(i, jdbcType.TYPE_CODE); } catch (SQLException e) { throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " + "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " + "Cause: " + e, e); } } else { try { // Set parameters normally. This method is overridden and customized by subclasses setNonNullParameter(ps, i, parameter, jdbcType); } catch (Exception e) { throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " + "Try setting a different JdbcType for this parameter or a different configuration property. " + "Cause: " + e, e); } } } @Override public T getResult(ResultSet rs, String columnName) throws SQLException { T result; try { // Gets the result set. This method is overridden and customized by subclasses result = getNullableResult(rs, columnName); } catch (Exception e) { throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e); } if (rs.wasNull()) { return null; } else { return result; } } @Override public T getResult(ResultSet rs, int columnIndex) throws SQLException { T result; try { // Gets the result set. This method is overridden and customized by subclasses result = getNullableResult(rs, columnIndex); } catch (Exception e) { throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from result set. Cause: " + e, e); } if (rs.wasNull()) { return null; } else { return result; } } @Override public T getResult(CallableStatement cs, int columnIndex) throws SQLException { T result; try { // Gets the result set. This method is overridden and customized by subclasses result = getNullableResult(cs, columnIndex); } catch (Exception e) { throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from callable statement. Cause: " + e, e); } if (cs.wasNull()) { return null; } else { return result; } } // Abstract methods that require subclass inheritance implementation public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException; public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException; public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException; public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException; }
test case
We customize a type processor to realize the function of splicing the array into the database, and then converting it into an array when taking out the data
a> Custom TypeHandler
@MappedJdbcTypes(JdbcType.VARCHAR) @MappedTypes(Array.class) //Here, if you do not specify the JDBC type with annotations, you can specify it in the configuration file through the "JDBC type" attribute. Similarly, the javaType can also be specified through @ MappedTypes public class Example2TypeHandler extends BaseTypeHandler<String[]> { // Java -- > database splices arrays and stores them in the database @Override public void setNonNullParameter(PreparedStatement ps, int i, String[] parameter, JdbcType jdbcType) throws SQLException { StringBuffer result = new StringBuffer(); for (String value : parameter) { result.append(value).append(","); } ps.setString(i, result.toString()); } // Database -- > java intercepts data into an array and stores it in @Override public String[] getNullableResult(ResultSet rs, String columnName) throws SQLException { String columnValue = rs.getString(columnName); return this.getStringArray(columnValue); } @Override public String[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException { String columnValue = rs.getString(columnIndex); return this.getStringArray(columnValue); } @Override public String[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { String columnValue = cs.getString(columnIndex); return this.getStringArray(columnValue); } // Convert string to array private String[] getStringArray(String columnValue) { if (columnValue == null) { return null; } return columnValue.split(","); } }
b> Register TypeHandler with global profile
<typeHandlers> <typeHandler handler="com.jianan.config.Example2TypeHandler" /> </typeHandlers>
c> Specify mapping type
<resultMap id="users" type="com.jianan.entity.User"> <result column="id" property="id" /> <result column="name" property="names" typeHandler="com.jianan.config.Example2TypeHandler" /> </resultMap> <select id="listAll" resultMap="users"> SELECT id,name FROM `user` </select>
d> Test code
static { try { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); sqlSession = factory.openSession(); } catch (IOException e) { e.printStackTrace(); } } private static SqlSession sqlSession; @Test public void m1() { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> list = userMapper.listAll(); System.out.println(list); }
e> Results
[User{id=1, name='null', names=[Jialin 1, Jialin 2]}, User{id=2, name='null', names=[Canaan 1, Canaan 2]}]
6. Object factory
Every time mybatis creates a new instance of the result object, it uses an object factory instance to complete the instantiation. The default object factory only needs to instantiate the target class, either through the default parameterless construction method or through parameters to call the parameterless construction. If we want to override the default behavior of the object factory, we can customize it.
1. Class diagram
From the class diagram below, we can easily see that ObjectFactory is the interface that provides the basic method of the object factory, while DefaultObjectFactory is the default object factory. If we want to customize, we can inherit the DefaultObjectFactory class and override the method
2.ObjectFactory interface
public interface ObjectFactory { // Set properties to configure the object factory void setProperties(Properties properties); // Processing nonparametric construction method <T> T create(Class<T> type); // Processing parametric construction method <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs); // Determine whether it is a collection <T> boolean isCollection(Class<T> type); }
3.DefaultObjectFactory interface
public class DefaultObjectFactory implements ObjectFactory, Serializable { private static final long serialVersionUID = -8855120656740914948L; // The default processing is parameterless construction @Override public <T> T create(Class<T> type) { return create(type, null, null); } // The default process is parametric construction @SuppressWarnings("unchecked") @Override public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { // Type judgment if the type is an interface, convert it to a subclass because the interface cannot be created Class<?> classToCreate = resolveInterface(type); // we know types are assignable // create object return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs); } // set a property @Override public void setProperties(Properties properties) { // no props for default } <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { try { Constructor<T> constructor; // Parameter type or parameter judgment if (constructorArgTypes == null || constructorArgs == null) { // Get parameterless constructor constructor = type.getDeclaredConstructor(); if (!constructor.isAccessible()) { constructor.setAccessible(true); } // create object return constructor.newInstance(); } // If you execute here, you need to use a parametric construct constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()])); if (!constructor.isAccessible()) { constructor.setAccessible(true); } return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()])); } catch (Exception e) { // The following is to splice the exception information and throw an exception StringBuilder argTypes = new StringBuilder(); if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) { for (Class<?> argType : constructorArgTypes) { argTypes.append(argType.getSimpleName()); argTypes.append(","); } argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing , } StringBuilder argValues = new StringBuilder(); if (constructorArgs != null && !constructorArgs.isEmpty()) { for (Object argValue : constructorArgs) { argValues.append(String.valueOf(argValue)); argValues.append(","); } argValues.deleteCharAt(argValues.length() - 1); // remove trailing , } throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e); } } // Processing type when the created object is an interface, we type convert it because the interface cannot be created. We convert it into a subclass // At this time, we are thinking about isCollection(), which mainly provides collection judgment, but we rarely use it protected Class<?> resolveInterface(Class<?> type) { Class<?> classToCreate; if (type == List.class || type == Collection.class || type == Iterable.class) { classToCreate = ArrayList.class; } else if (type == Map.class) { classToCreate = HashMap.class; } else if (type == SortedSet.class) { // issue #510 Collections Support classToCreate = TreeSet.class; } else if (type == Set.class) { classToCreate = HashSet.class; } else { classToCreate = type; } return classToCreate; } // Is it a collection @Override public <T> boolean isCollection(Class<T> type) { return Collection.class.isAssignableFrom(type); } }
4. Example of official website
// ExampleObjectFactory.java public class ExampleObjectFactory extends DefaultObjectFactory { public Object create(Class type) { return super.create(type); } public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) { return super.create(type, constructorArgTypes, constructorArgs); } public void setProperties(Properties properties) { super.setProperties(properties); } public <T> boolean isCollection(Class<T> type) { return Collection.class.isAssignableFrom(type); }}
<!-- mybatis-config.xml --> <objectFactory type="org.mybatis.example.ExampleObjectFactory"> <!-- set a property --> <property name="someProperty" value="100"/> </objectFactory>
7. Environment configuration
MyBatis can be configured to adapt to a variety of environments. For example, development, testing and production environments need different configurations; Or you want to use the same SQL mapping in multiple production databases with the same Schema.
However, remember: Although multiple environments can be configured, only one environment can be selected for each SqlSessionFactory instance, and each database corresponds to one SqlSessionFactory instance.
Configuration example
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> <environment id="prod"> // ellipsis </environment> </environments>
matters needing attention
1.id configuration
- The default environment ID (for example, default = "development") must match one of the environment IDs.
- The environment ID defined by each environment element (for example: id = "development").
2. Transaction manager
There are two types of transaction managers in MyBatis, that is, type = "[JDBC managed]", which can also be customized
- JDBC – this configuration directly uses JDBC's commit and rollback facilities, which rely on connections obtained from data sources to manage transaction scopes.
- It never commits or rolls back a connection, but lets the container manage the entire life cycle of the transaction
Tip: if you are using Spring + MyBatis, there is no need to configure the transaction manager, because the Spring module will use its own manager to override the previous configuration.
3. Data source
The dataSource element uses the standard JDBC data source interface to configure the resources of the JDBC connection object. There are three built-in data source types, that is, type="[UNPOOLED|POOLED|JNDI],
UNPOOLED: do not use connection pool
- driver – this is the fully qualified name of a JDBC driven Java class (not a data source class that may be included in a JDBC driver).
- url – this is the JDBC URL address of the database.
- username – the user name to log in to the database.
- Password – password to log in to the database.
- defaultTransactionIsolationLevel – the default connection transaction isolation level.
- defaultNetworkTimeout – the default network timeout in milliseconds to wait for the database operation to complete. Check the API documentation for java.sql.Connection#setNetworkTimeout() for more information.
POOLED: use connection pool
In addition to the above mentioned attributes under UNPOOLED, there are more attributes to configure the data source of POOLED:
- poolMaximumActiveConnections – the number of active (in use) connections that can exist at any time. Default: 10
- poolMaximumIdleConnections – the number of idle connections that may exist at any time.
- poolMaximumCheckoutTime – the time that connections in the pool are checked out before being forcibly returned. The default value is 20000 milliseconds (i.e. 20 seconds)
- poolTimeToWait – this is an underlying setting. If it takes a long time to obtain a connection, the connection pool will print the status log and try to obtain a connection again (to avoid continuous failure in case of misconfiguration and no log printing). The default value is 20000 milliseconds (i.e. 20 seconds).
- poolMaximumLocalBadConnectionTolerance – this is a low-level setting for bad connection tolerance that applies to every thread trying to get a connection from the cache pool. If the thread gets a bad connection, the data source allows the thread to try to get a new connection again, but the number of retries should not exceed the sum of poolMaximumIdleConnections and poolMaximumLocalBadConnectionTolerance. Default value: 3 (added to 3.4.5)
- poolPingQuery – a probe query sent to the database to verify that the connection is working properly and ready to accept the request. The default is "NO PING QUERY SET", which will cause most database drivers to return appropriate error messages when errors occur.
- poolPingEnabled – whether to enable detection query. If enabled, you need to set the poolPingQuery property to an executable SQL statement (preferably a very fast SQL statement). The default value is false.
- poolPingConnectionsNotUsedFor – configure the frequency of poolPingQuery. It can be set as the database connection timeout to avoid unnecessary detection. The default value is 0 (that is, all connections are detected at every moment - of course, it is only applicable when poolPingEnabled is true).
JNDI - this data source implementation is designed to be used in containers such as EJB s or application servers,
8. Database vendor ID (databaseIdProvider)
Mybatis can execute different statements according to different database vendors
<databaseIdProvider type="DB_VENDOR"> <property name="SQL Server" value="sqlserver"/> <property name="DB2" value="db2"/> <property name="Oracle" value="oracle" /> </databaseIdProvider>
MyBatis loads statements with and without the databaseId attribute that match the current database. If the same statement with and without databaseId is found at the same time, the latter will be discarded.
<select id="selectTime" resultType="String" databaseId="mysql"> SELECT * FROM `user` </select>
9. mappers
<!-- Use resource references relative to Classpaths --> <mappers> <mapper resource="com.jianan.mapper.UserMapper" /> </mappers>
<!-- Register all the mapper interface implementations in the package as mappers --> <mappers> <package name="com.jianan.mapper"/> </mappers>
<!-- Use the mapper interface to implement the fully qualified class name of the class --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> <mapper class="org.mybatis.builder.BlogMapper"/> <mapper class="org.mybatis.builder.PostMapper"/> </mappers>
<!-- Use fully qualified resource locators( URL) --> <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <mapper url="file:///var/mappers/BlogMapper.xml"/> <mapper url="file:///var/mappers/PostMapper.xml"/> </mappers>