Writing in the front
If you have read my blog before, you should know that I wrote MyBatis blog in October last year
However, I only learned Java for only three months (Java was also the first programming language I learned), during which I also learned other knowledge (such as MySQL).
So I’m not sure how much quality THERE is in what I wrote before, and I don’t want to confuse people
I was going to go back and read what I had written, but when I opened it, I was struck by the colorful Markdown syntax. Probably because I had just learned how to use the markdown syntax at the time, some of it was overused, resulting in poor readability (at least I couldn’t bear to read it down), or the smelly and long prenatal education type…
So with this article, maybe I’m a bit of a pedantic person and it’s hard for me to write something so bad
I plan to learn MyBatis again, and the output of blog will naturally follow. If I finish the learning process of MyBatis, I will change the previous six articles.
What is MyBatis
MyBatis is an excellent persistence layer framework that supports custom SQL, stored procedures, and advanced mapping
MyBatis eliminates almost all of the JDBC code and the work of setting parameters and fetching result sets
MyBatis can configure and map primitive types, interfaces, and Java POJOs (Plain Old Java Objects) to records in the database via simple XML or annotations
Since you read this blog is to learn MyBatis, so you should understand these concepts, I do not need to repeat.
In the early years of development, it was common for developers to use JDBC to interact with databases, but those of you who have written native JDBC code know that this approach is extremely inefficient and limited.
Here I’ll show you some of the native JDBC code for the Dao layer of the JavaWeb project I wrote earlier
@Override
public User getLoginUser(Connection connection, String userCode) {
// Raise some object scopes
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
User user = null;
// If the connection object passed in is not empty when the method is called, continue down
if(connection! =null) {// Fixed SQL statement to query all user data with userCode
String sql = "select * from smbms_user where userCode = ?";
// Encapsulate the parameters needed to execute the query SQL, convenient to directly call the written tool class to complete the query operation
Object params[] = {userCode};
try {
// Call the query method in the utility class, passing in the parameters it needs
resultSet = BaseDao.execute(connection, resultSet, preparedStatement, sql, params);
// Iterate over and assign values to the user data queried by userCode
while (resultSet.next()){
user = new User();
user.setId(resultSet.getInt("id"));
user.setUserCode(resultSet.getString("userCode"));
user.setUserName(resultSet.getString("userName"));
user.setUserPassword(resultSet.getString("userPassword"));
user.setGender(resultSet.getInt("gender"));
user.setBirthday(resultSet.getDate("birthday"));
user.setPhone(resultSet.getString("phone"));
user.setAddress(resultSet.getString("address"));
user.setUserRole(resultSet.getInt("userRole"));
user.setCreatedBy(resultSet.getInt("createdBy"));
user.setCreationDate(resultSet.getDate("creationDate"));
user.setModifyBy(resultSet.getInt("modifyBy"));
user.setModifyDate(resultSet.getDate("modifyDate"));
user.setAge(new Date().getYear()-resultSet.getDate("birthday").getYear());
}
// Close the resource
BaseDao.closeResource(null,preparedStatement,resultSet);
} catch(SQLException throwables) { throwables.printStackTrace(); }}// Returns the queried user
return user;
}
Copy the code
Writing code in this way can be very painful, or use a written tool class to simplify some JDBC code…
So in order to simplify the tedious JDBC code, MyBatis came into being
Simplify the code with MyBatis
Before we touch SpringBoot, we need to set up the environment with MyBatis. This is probably “simplified, but not completely simplified”.
Import dependence
We’ll use Maven to import some of the necessary dependencies (we’ll also need to create the database for testing and write the entity classes)
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
</dependency>
</dependencies>
Copy the code
Configuration files, data sources
Next we create mybatis-config. XML and db.properties under the Resources directory
The former is required and the latter you may or may not write
<! -- Mybatis -- config. XML, mybatis -->
<! DOCTYPEconfiguration
PUBLIC "- / / mybatis.org//DTD Config / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<! -- Import the data source configuration
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<! If you do not have the data source configured in db. Properties, then you need to enter your database connection information in value, if there is a direct reference.
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.xxxx.mapper.xxxxxx"/>
</mappers>
</configuration>
Copy the code
# db. Properties = db
db.driver=com.mysql.cj.jdbc.Driver
ServerTimezone =GMT%2B8 if MySQL 8.0 is used, add a line of time zone serverTimezone=GMT%2B8
db.url=JDBC: mysql: / / localhost: port/mysql database to connect? useSSL=true&useUnicode=true&characterEncoding=UTF-8
db.username=Write your own
db.password=Write your own
Copy the code
Write utility classes
In order to easily use MyBatis each time, we can write a tool class in advance just like simplifying JDBC to directly fetch the object instance we need to call when using MyBatis
Each MyBatis application is built around an instance of the SqlSessionFactory.
An instance of SqlSessionFactory can be obtained from SqlSessionFactoryBuilder.
SqlSessionFactoryBuilder, on the other hand, can build an SqlSessionFactory instance from an XML Configuration file or a pre-configured Configuration instance.
Building an instance of SqlSessionFactory from an XML file is straightforward, and it is recommended to use a resource file under the classpath for configuration.
But you can also use an arbitrary instance of an InputStream, such as an InputStream constructed from a file path string or a file:// URL.
MyBatis includes a utility class called Resources, which contains utility methods that make it easier to load resource files from the classpath or elsewhere.
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch(IOException e) { e.printStackTrace(); }}public static SqlSession getSqlSession(a){
returnsqlSessionFactory.openSession(); }}Copy the code
Write interface, mapper file
With that done, we can start writing our first MyBatis program, which is different from the Dao layer where we wrote interfaces and implemented classes to operate on the database
We just need to write the interface (the interface is written in mapper package, this is for specification purposes if you want to change mapper to DAO), as for implementation classes and tedious SQL, to hell with it.
public interface UserMapper {
// Query the specified user by id
public User selectUserById(int id);
}
Copy the code
Of course, only write interface is not able to achieve operation database, we need to interface in the package (mapper/ DAO) should be the corresponding XML file
<! DOCTYPEmapper
PUBLIC "- / / mybatis.org//DTD Config / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<! -- Bind your interface -->
<mapper namespace="com.molu.mapper.UserMapper">
</mapper>
Copy the code
This is the key part of using MyBatis, we need to master the use of several XML tags, using these tags to manipulate the database.
Tags are a lot easier to learn and you need to pay attention to the use of some attributes and SQL writing, if you are in the habit of blogging these things can be quickly picked up even if you forget
<! DOCTYPEmapper
PUBLIC "- / / mybatis.org//DTD Config / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.molu.mapper.UserMapper">
<select id="selectUserById" parameterType="int" resultType="com.molu.pojo.User">
select * from `mybatis`.user where id = #{id};
</select>
</mapper>
Copy the code
Compared with the above code, we only added a pair of < SELECT > tags, this tag is also easy to understand, MyBatis operating database tags and database DML language is the same, if you write basic SQL ability no problem, then these things you are sure to understand.
The first id we write is the interface method called by the database operation, and the remaining two properties refer to the parameter type and return value type
The writing of these two attributes has a minor detail, namely that the fully qualified name must be written if the parameter type return value type is a reference type (for example: java.lang.string).
If it’s a basic type, you can actually omit int sometimes (but it’s better for the sake of specification)
Then we bind the mapper file in the core configuration file (mybatis-config.xml), in the tag at the bottom of the class with XXXX
<mappers>
<! If you are writing along this blog, your Mapper file must have the same name as the interface as follows:
<mapper class="com.molu.mapper.UserMapper"/>
</mappers>
Copy the code
test
After binding, we can use junit to test. Although we need to prepare a lot of things, most of them are once and for all. After using MyBatis, we will only modify the interface, Mapper file and core configuration file. It’s a lot better than JDBC.
@Test
public void test(a){
// Obtain the instance of the sqlSession object from the tool class
SqlSession sqlSession = MybatisUtil.getSqlSession();
// Get mapper from sqlSession object
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// Call the interface method
User user = mapper.selectUserById(2);
System.out.println(user.toString());
// Release resources
sqlSession.close();
User{id=2, name=' qiu', gender=' female ', age=20, password=' molu13QIU ', address=' Jianggrao '}
}
Copy the code
To here the first MyBatis program even if written well, no JDBC that nightmare resultSet through, the success of the ID to get stored in the database user…
It’s a little bit simpler, but it’s not all the way to SpringBoot where we need to configure a little bit less stuff, so let’s go ahead and build on the basics.
If you get an error saying you can’t find mapper. XML, try putting a few lines of code like this in your pop.xml. This is Maven’s static resource filtering exception.
After you add these lines of code, refresh your Maven project and your mapper. XML file in the Mapper package won’t be filtered out
There is another way to solve this problem, which is to create the mapper.xml file in the Resources directory, but that is not standard, we will discuss this problem in the extension.
<! Maven static resource filtering -->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
Copy the code
Learn about XML configuration files
In the previous post, we didn’t go into too much detail about the core configuration file because it takes a long time to unfold, so I’ll separate it out and use it as the subject of this blog post.
structure
Let’s start by looking at the structure of the core configuration file
<! DOCTYPEconfiguration
PUBLIC "- / / mybatis.org//DTD Config / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<! -- Import the data source configuration
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.molu.mapper.UserMapper"/>
</mappers>
</configuration>
Copy the code
The header of the MyBatis configuration file above the first < Configuration > TAB is fixed and generally not modified
The secondary tag of the < Configuration > tag body represents the configuration items of MyBatis one by one
Paradoxically, we put the new
According to most people’s coding habits, new additions should be written at the bottom, but if you try to place the
The given error message indicates that the contents of the element type configuration must match
In human language, the order of configuration items must be specified, otherwise an error is reported. The order given is in parentheses
Therefore, the
With that in mind, this is the first problem most newcomers encounter when writing configuration files
properties
The
Most of the time, we import the configuration file in the Resources directory directly as a single tag, but there is another way to use this: to write the
<properties resource="db.properties">
<property name="db.oldDriver" value="com.mysql.jdbc.Driver"/>
</properties>
Copy the code
An attribute written in the body of the tag is the same as an attribute imported in the configuration file, and can be referenced globally in the configuration file
<dataSource type="POOLED">
<property name="driver" value="${db.oldDriver}"/>
</dataSource>
Copy the code
So the question is, what if the properties in our configuration file have the same name as the properties in the
Obviously, there is a priority problem. The test shows that the same name attribute in the configuration file has a higher priority than the attribute configured in the tag body, but it is not the highest priority. The highest priority is the attributes passed as method parameters.
The following is the property reading process
- First read in
<properties>
Attributes specified in the tag body. - Then according to the
<properties>
The resource property in the tag reads the properties file in the classpath, or the path specified by the URL property, and overwrites previously read properties of the same name. - The property passed as a method parameter is finally read, overwriting previously read properties of the same name.
In other words, the value of an attribute with a higher priority must be correct. If there is an error in the value of an attribute, the attribute in the next priority will not be read.
After MyBatis 3.4.2 we can also specify a default value for placeholders
<property name="driver" value="${db.driver:com.mysql.jdbc.Driver}"/>
Copy the code
If the db.properties property is not configured, the default value in the placeholder is used. If there is a configuration of the same name, it is overridden. In other words, it has the lowest priority.
This feature is disabled by default, and you need to configure it in advance, either in the < Properties > TAB body or in the configuration file
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/><! -- Enable default feature -->
Copy the code
The key value of the default value is delimited by: by default. If there is a conflict and the: cannot be delimited, we can configure another property to define the delimiter in its value value
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="/"/> <! -- Change the default value to / -->
Copy the code
As shown above, with the default key value separator set to /, we can no longer use: to separate kV, only /
<property name="driver" value="${db.driver/com.mysql.jdbc.Driver}"/>
Copy the code
settings
These are very important tuning Settings in MyBatis, and they change the runtime behavior of MyBatis. The following table describes the meanings and default values of the Settings.
Set the name | describe | Valid values | The default value |
---|---|---|---|
cacheEnabled | Globally turn on or off any caches that have been configured in all mapper profiles. | true | false | true |
lazyLoadingEnabled | Global switch of lazy loading. When enabled, all associated objects are lazily loaded. The fetchType attribute can be set to override the on/off state of an item in a particular association. | true | false | false |
aggressiveLazyLoading | When turned on, calls to either method load all lazy-loaded properties of the object. Otherwise, each lazy-loaded attribute is loaded on demand (see lazyLoadTriggerMethods). | true | false | False (in 3.4.1 and Default in previous versions To true) |
multipleResultSetsEnabled | Whether a single statement is allowed to return multiple result sets (requires database driver support). | true | false | true |
useColumnLabel | Use column labels instead of column names. Actual performance depends on database drivers, which can be observed by referring to the database driver documentation or by comparing tests. | true | false | true |
useGeneratedKeys | Allows JDBC support for automatic primary key generation, requiring database driver support. If set to true, automatic primary key generation is enforced. Although some database drivers do not support this feature, it still works (Derby, for example). | true | false | False |
autoMappingBehavior | Specifies how MyBatis should automatically map columns to fields or attributes. NONE indicates that automatic mapping is disabled. PARTIAL only automatically maps fields that do not define nested result mappings. FULL automatically maps any complex result set (whether nested or not). | NONE, PARTIAL, FULL | PARTIAL |
autoMappingUnknownColumnBehavior | Specifies the behavior of discovering unknown columns (or unknown attribute types) for the auto-mapping target. NONE: Does nothingWARNING : Outputs warning logs ('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' Log level must be set toWARN )FAILING : Mapping failed (thrownSqlSessionException ) |
NONE, WARNING, FAILING | NONE |
defaultExecutorType | Configure the default actuator. SIMPLE is a plain actuator; The REUSE executor reuses preparedStatements. The BATCH executor not only reuses statements but also performs BATCH updates. | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | Sets the timeout period, which determines the number of seconds the database driver waits for a database response. | Arbitrarily positive integer | Is not set (null) |
defaultFetchSize | Set a recommended value for the driver’s result set fetchSize. This parameter can only be overridden in query Settings. | Arbitrarily positive integer | Is not set (null) |
defaultResultSetType | Specifies the default scrolling policy for the statement. (Added in 3.5.2) | FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT (equivalent to not set) | Is not set (null) |
safeRowBoundsEnabled | Whether paging (RowBounds) is allowed in nested statements. Set to false if allowed. | true | false | False |
safeResultHandlerEnabled | Whether result handlers (ResultHandler) are allowed in nested statements Set to false if allowed. | true | false | True |
mapUnderscoreToCamelCase | Whether to enable automatic camel name mapping, that is, from the classic database column name A_COLUMN to the classic Java property name aColumn. | true | false | False |
localCacheScope | MyBatis uses Local caching to prevent circular references and speed up repeated nested queries. The default value is SESSION, which caches all queries executed in a SESSION. If the value is set to STATEMENT, the local cache will only be used to execute statements. Different queries of the same SqlSession will not be cached. | SESSION | STATEMENT | SESSION |
jdbcTypeForNull | The default JDBC type for a null value when no specific JDBC type is specified for the parameter. Some database drivers need to specify the JDBC type of a column, and in most cases just use the generic type, such as NULL, VARCHAR, or OTHER. | JdbcType constant. Common values: NULL, VARCHAR, or OTHER. | OTHER |
lazyLoadTriggerMethods | Specifies which methods of the object trigger a lazy load. | A comma-separated list of methods. | equals,clone hashCode,toString |
defaultScriptingLanguage | Specifies the default scripting language for dynamic SQL generation. | A type alias or fully qualified class name. | org.apache.ibatis .scripting.xmltags .XMLLanguageDriver |
defaultEnumTypeHandler | Specifies the default TypeHandler used by Enum. (Added in 3.4.5) | A type alias or fully qualified class name. | org.apache.ibatis.type .EnumTypeHandler |
callSettersOnNulls | Specifies whether setter (put for map objects) methods are called when the result set value is null. This is useful when initialization depends on map.keyset () or null values. Note that primitive types (int, Boolean, etc.) cannot be set to NULL. | true | false | false |
returnInstanceForEmptyRow | MyBatis returns NULL by default when all columns of the return row are empty. When this setting is enabled, MyBatis returns an empty instance. Note that it also applies to nested result sets (such as collections or associations). (Added in 3.4.2) | true | false | false |
logPrefix | Specifies the prefix MyBatis adds to the log name. | Any string | Is not set |
logImpl | Specify the specific implementation of logging used by MyBatis. If not specified, it will be automatically found. | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | Is not set |
proxyFactory | Specifies the proxy tool Mybatis uses to create lazy-loadable objects. | CGLIB | JAVASSIST | JAVASSIST (MyBatis 3.3 +) |
vfsImpl | Specify the implementation of the VFS | The class fully qualified name of the implementation of the custom VFS, separated by commas. | Is not set |
useActualParamName | Allows names in method signatures to be used as statement parameter names. To use this feature, your project must be compiled in Java 8 with the -parameters option. (Added in 3.4.1) | true | false | true |
configurationFactory | Specify a class that provides an instance of Configuration. The returned Configuration instance is used to load the lazy load property value of the deserialized object. The class must contain a method signed static Configuration getConfiguration(). (Added in 3.2.3) | A type alias or fully qualified class name. | Is not set |
shrinkWhitespacesInSql | Remove redundant space characters from SQL. Note that this also affects literal strings in SQL. (Added in 3.5.5) | true | false | false |
defaultSqlProviderType | Specifies an SQL provider class that holds provider method (Since 3.5.6). This class applies to the type(or value) attribute on sql provider annotation(e.g. @SelectProvider), when these attribute was omitted. | A type alias or fully qualified class name | Not set |
Too many things, not a demo and description, interested in their own search article, limited ability to skip.
typeAliases
Alias configuration, if the return value type and parameter type are reference types, then we have to use fully qualified names in the corresponding properties. This doesn’t add a lot of code, but repeating fully qualified names can be a little boring and easy to write wrong.
So MyBatis provides the alias setting, we can specify a reference type in the core configuration file and alias it.
Then in other XML files (MyBatis related, such as mapper.xml) we can use aliases instead of fully qualified names, this operation is feasible; Here’s a demo
<! -- Configure an alias for the User object -->
<typeAliases>
<typeAlias type="com.molu.pojo.User" alias="user"/>
</typeAliases>
Copy the code
<! We can use the alias User for the return value type.
<select id="selectUserById" parameterType="int" resultType="user">
select * from `mybatis`.user where id = #{id};
</select>
Copy the code
In addition to aliasing a single object, we can alias all objects in a package. This approach is often used more often than not
<! -- Configure aliases for objects under poJO package -->
<typeAliases>
<package name="com.molu.pojo"/>
</typeAliases>
Copy the code
After aliases are specified for all objects in a package, these objects are aliases with their own lowercase letters. For example, the alias of com.molu.pojo.User is User
If we use this method and then want to configure other aliases for the specified objects in the package, we can do so without using lowercase letters
Note, however, that the
tag must be written on top of the
<typeAliases>
<typeAlias type="com.molu.pojo.User" alias="_user"/>
<package name="com.molu.pojo"/>
</typeAliases>
Copy the code
So the question is, who gets the higher priority if we do this?
We don’t have to worry about priorities, they’re both valid so we can use _user, we can use user, because there’s something second; One exception to this rule is when annotations are used
// Use annotations to alias the User object
@Alias("_user_")
public class User {}Copy the code
There’s no doubt it’s easier to use annotations if there’s less code, but annotations are kind of like a word for word, and once they’re configured your other alias configurations for that object will be invalidated.
This means that neither _user nor user can be used. You can only use _user_, which is important
Some Java types have default aliases, such as int, whose alias is _int. These aliases are specified and cannot be customized. Also, these aliases are case insensitive, so it doesn’t matter if you write them like this
parameterType="_InT"
Copy the code
Below are all types that have default aliases
The alias | Type of mapping |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
typeHandlers
When MyBatis sets a parameter in a PreparedStatement or fetches a value from a result set, it uses a type handler to convert the obtained value to a Java type in an appropriate manner.
The following table describes some of the default type handlers.
As of 3.4.5, MyBatis supports JSR-310 (Date and Time API) by default.
Type processor | Java type | The JDBC type |
---|---|---|
BooleanTypeHandler | java.lang.Boolean, boolean | BOOLEAN for database compatibility |
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 | NUMER~IC or INTEGER compatible with the database |
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 |
BigDecimalTypeHandler | java.math.BigDecimal | Database-compatible NUMERIC or DECIMAL |
StringTypeHandler | java.lang.String | CHAR, VARCHAR |
ClobReaderTypeHandler | java.io.Reader | – |
ClobTypeHandler | java.lang.String | CLOB, LONGVARCHAR |
NStringTypeHandler | java.lang.String | NVARCHAR, NCHAR |
NClobTypeHandler | java.lang.String | NCLOB |
BlobInputStreamTypeHandler | java.io.InputStream | – |
ByteArrayTypeHandler | byte[] | Database compatible byte stream type |
BlobTypeHandler | byte[] | BLOB, LONGVARBINARY |
DateTypeHandler | java.util.Date | TIMESTAMP |
DateOnlyTypeHandler | java.util.Date | DATE |
TimeOnlyTypeHandler | java.util.Date | TIME |
SqlTimestampTypeHandler | java.sql.Timestamp | TIMESTAMP |
SqlDateTypeHandler | java.sql.Date | DATE |
SqlTimeTypeHandler | java.sql.Time | TIME |
ObjectTypeHandler | Any | OTHER or unspecified type |
EnumTypeHandler | Enumeration Type | VARCHAR or any compatible string type used to store the name of an enumeration (rather than an index-ordered value) |
EnumOrdinalTypeHandler | Enumeration Type | Any compatible NUMERIC or DOUBLE type that is used to store the ordinal value of the enumeration (rather than the name). |
SqlxmlTypeHandler | java.lang.String | SQLXML |
InstantTypeHandler | java.time.Instant | TIMESTAMP |
LocalDateTimeTypeHandler | java.time.LocalDateTime | TIMESTAMP |
LocalDateTypeHandler | java.time.LocalDate | DATE |
LocalTimeTypeHandler | java.time.LocalTime | TIME |
OffsetDateTimeTypeHandler | java.time.OffsetDateTime | TIMESTAMP |
OffsetTimeTypeHandler | java.time.OffsetTime | TIME |
ZonedDateTimeTypeHandler | java.time.ZonedDateTime | TIMESTAMP |
YearTypeHandler | java.time.Year | INTEGER |
MonthTypeHandler | java.time.Month | INTEGER |
YearMonthTypeHandler | java.time.YearMonth | VARCHAR or LONGVARCHAR |
JapaneseDateTypeHandler | java.time.chrono.JapaneseDate | DATE |
You can override existing type handlers or create your own type handlers to handle unsupported or non-standard types.
Specific measures are as follows: Implement org. Apache. Ibatis. Type. TypeHandler interface, or classes inherit a very convenient org. Apache. Ibatis. The BaseTypeHandler, and (optionally) to map it to a JDBC type. Such as:
// ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getString(columnIndex);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getString(columnIndex);
}
}
<!-- mybatis-config.xml -->
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
Copy the code
Using the above type handler overrides existing type handlers that handle Java String attributes as well as VARCHAR arguments and results. Note that MyBatis does not detect database metadata to determine which type to use, so you must specify that the field is of VARCHAR type in the parameter and result mapping so that it can be bound to the correct type processor. This is because MyBatis does not know the data type until the statement is executed.
MyBatis knows what Java types are being processed by the type handler’s generics, but this behavior can be changed in two ways:
- Add a javaType attribute to the configuration element of the typeHandler (typeHandler element) (for example:
javaType="String"
); - Add one to the class of the type handler
@MappedTypes
The annotation specifies the list of Java types associated with it. If it is also specified in the javaType property, the configuration on the annotation is ignored.
You can specify the associated JDBC type in two ways:
- Add a jdbcType attribute to the configuration element of the type handler (for example:
jdbcType="VARCHAR"
); - Add one to the class of the type handler
@MappedJdbcTypes
The annotation specifies the list of JDBC types associated with it. If it is also specified in the jdbcType attribute, the configuration on the annotation is ignored.
When deciding which type of processor to use in a ResultMap, the Java type is known (obtained from the result type), but the JDBC type is unknown. So Mybatis uses a combination of javaType=[Java type], jdbcType= NULL to select a type handler. This means that using the @mappedjdbcTypes annotation limits the scope of type handlers and ensures that type handlers will not take effect in ResultMap unless explicitly set. To implicitly use type handlers in ResultMap, set @mappedjDBcTypes annotations to includeNullJdbcType=true. However, starting with Mybatis 3.4.0, if a Java type has only one registered type handler, that type handler will be the default handler for using Java types in ResultMap, even without setting includeNullJdbcType=true.
Finally, you can ask MyBatis to help you find the type handler:
<! -- mybatis-config.xml -->
<typeHandlers>
<package name="org.mybatis.example"/>
</typeHandlers>
Copy the code
Note that the TYPE of JDBC can only be specified by annotation when using automatic discovery.
You can create generic type handlers that can handle multiple classes. To use a generic type handler, we need to add a constructor that takes the class of that class so that MyBatis will pass in a concrete class when constructing an instance of the type handler.
//GenericTypeHandler.java
public class GenericTypeHandler<E extends MyObject> extends BaseTypeHandler<E> {
private Class<E> type;
public GenericTypeHandler(Class<E> type) {
if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
this.type = type; }...Copy the code
The above quotes from the official document, this thing I use less memory wrote a longtext processor, write out the thing may not nutrition directly quoted the official document, forgive me.
objectFactory
MyBatis uses the object factory instance to do this each time it creates a new instance of the resulting object, which is normally used when creating a new instance
It either goes to the no-parameter construct or calls its corresponding parameter construct according to the corresponding mapping and does nothing else
So if we want this object factory to do some assignment or something when we create a new instance of an object, we need to use this tag
MyBatis uses the DefaultObjectFactory object and overwrites the methods in it.
The subclass of this override method is registered with the tag to perform some additional operations when instantiating the object.
It’s not hard. Here’s a demonstration:
// Inherit the default factory object
public class ExampleObjectFactory extends DefaultObjectFactory {
// Override its no-argument constructor instantiation method
@Override
public <T> T create(Class<T> type) {
if (type.equals(User.class)){
User user = (User) super.create(type);
user.setName("moluu");
user.setAge(18);
user.setGender("Male");
user.setPassword("12345678");
user.setAddress("Nanjing, Jiangsu");
System.out.println(user.toString());
return (T) user;
}
return super.create(type); }}Copy the code
Inherits the instance and overwrites the create() call it makes when it creates a new instance; Method, we register it in the configuration file
<! Write the objectFactory tag in the configuration file and register the factory object we wrote.
<objectFactory type="com.molu.utils.ExampleObjectFactory"/>
Copy the code
Added the method of adding a user to an interface
public int addUser(User user);
Copy the code
Write a corresponding < INSERT > tag for this interface method in mapper.xml
<insert id="addUser" parameterType="user">
insert into `user` (`name`,age,gender,password,address)
values (#{name},#{age},#{gender},#{password},#{address});
</insert>
Copy the code
test
@Test
public void objectTest(a){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
ExampleObjectFactory e = new ExampleObjectFactory();
User user = e.create(User.class);
mapper.addUser(user);
sqlSession.commit();
sqlSession.close();
}
Copy the code
You will notice that some attributes of the User instance created using this factory object have initial values
Of course, this can be done in most cases by adding some assignment code to a no-parameter construct, but it’s just one more way, depending on how you apply it, right
We can also override setProperties(); Method, which is not really useful and needs to be used with
tag body).
<objectFactory type="com.molu.utils.ExampleObjectFactory">
<property name="age" value="18"/>
</objectFactory>
Copy the code
The properties we write in this tag are passed to setProperties(); In the method
@Override
public void setProperties(Properties properties) {
Iterator<Object> iterator = properties.keySet().iterator();
while (iterator.hasNext()){
String value = String.valueOf(iterator.next());
System.out.println(properties.getProperty(value));
// When MyBatis is initialized, it will
}
super.setProperties(properties);
}
Copy the code
This method will be called once when MyBatis reads the configuration file initialization and then outputs the property values we set in the
Plugins
MyBatis allows you to intercept calls at some point during the execution of a mapping statement. By default, MyBatis allows you to intercept method calls using plug-ins:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
The details of the methods in these classes can be discovered by looking at the signature of each method, or by looking directly at the source code in the MyBatis distribution. If you want to do more than monitor method calls, you’d better know a lot about the behavior of the method you’re overwriting. Attempts to modify or override the behavior of existing methods may break the core module of MyBatis. These are lower-level classes and methods, so be careful when using plug-ins.
MyBatis provides a powerful mechanism, using the plug-in is very simple, just implement the Interceptor interface, and specify the method signature you want to intercept.
// ExamplePlugin.java
@Intercepts({@Signature( type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
private Properties properties = new Properties();
public Object intercept(Invocation invocation) throws Throwable {
// implement pre processing if need
Object returnObject = invocation.proceed();
// implement post processing if need
return returnObject;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
<!-- mybatis-config.xml -->
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
Copy the code
The above plug-in will intercept all “Update” method calls in the Executor instance, which is the internal object responsible for executing the underlying mapping statement.
Prompt to override configuration classes
In addition to using plug-ins to modify the core behavior of MyBatis, you can accomplish this by overwriting the configuration classes completely. Just inherited configuration after class to cover one of the methods, and then pass it to the SqlSessionFactoryBuilder is. The build (myConfig) method. Again, this may greatly affect the behavior of MyBatis, please use caution.
Same as typeHandlers, I haven’t used them very much and I’m going to put them in the official documentation so I’m going to skip them if I don’t understand them sorry…
environments
MyBatis supports multiple environment configurations, allowing us to quickly switch between production and development environments. This mechanism helps to apply SQL mapping to multiple databases, and there are many reasons to do this in real life.
<! -- Specify the environment to use in default -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver/com.mysql.jdbc.Driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
<! -- You can configure multiple environments -->
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<! Driver: com.mysql.jdbc.driver: com.mysql.jdbc.driver
<property name="driver" value="${db.oldDriver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
Copy the code
MyBatis will use the same environment configured below if the environment id has the same name
Although only one default environment can be specified in default, we can construct multiple instances of sqlSessionFactory to build with different environments
If we click on the source of the sqlSessionFactoryBuilder object, we’ll find its build(); Method has multiple overloads
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null.null);
}
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return build(inputStream, null, properties); }...Copy the code
The environment parameter of type String can be passed the environment ID we wrote in
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// Pass the environment's ID attribute value as the second argument to the build overload method
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream,"test");
SqlSession sqlSession = sqlSessionFactory.openSession();
Copy the code
MyBatis will use the specified environment to construct an instance of the sqlSessionFactory object when initialized by calling the overloaded method and passing in the specified environment ID.
When you start the test class, the console will print a message indicating that the driver has been deprecated, indicating that the environment has been successfully switched to “test”.
transactionManager
As you can see, only two child element tags can be written inside the body of the
tag. Let’s look at
If your core configuration files are copied from the official website or from me, the transaction manager defaults to the JDBC transaction manager, or you can switch to MANAGED
These two transaction managers are the only two transaction managers in MyBatis. The differences between them are as follows:
-
JDBC – This configuration directly uses JDBC’s commit and rollback facilities, which rely on connections obtained from data sources to manage transaction scopes.
-
MANAGED – This configuration does little. It never commits or rolls back a connection, but lets the container manage the entire life cycle of the transaction (such as the context of the JEE application server). It closes the connection by default.
However, some containers do not want the connection to be closed, so you need to set the closeConnection property to false to prevent the default closing behavior. As follows:
The most intuitive difference between the two is:
SqlSession.com MIT () if you are using a JDBC transaction manager, you must call sqlSession.com MIT () when performing operations such as adding, deleting, or modifying data in the database. Methods;
Otherwise, even if no error is reported, your operations do not change the database, and MANAGED does not commit transactions manually.
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
Copy the code
If you are using Spring + MyBatis, there is no need to configure the transaction manager because the Spring module overrides the previous configuration using its own manager.
Neither of these transaction manager types requires any properties to be set. They are really type aliases; in other words, you can replace them with the fully qualified name or type alias of the class implemented by the TransactionFactory interface.
public interface TransactionFactory {
default void setProperties(Properties props) { As of 3.5.2, this method is the default method
/ / empty implementation
}
Transaction newTransaction(Connection conn);
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
Copy the code
In addition to using the two transaction managers provided by MyBatis, we can also customize the required transaction manager configuration
First of all, we need a custom transaction manager factory class, it must inherit TransactionFactory (org. Apache. Ibatis. Transaction. TransactionFactory) interface, and rewrite the method.
public class MyTransactionFactory implements TransactionFactory {
@Override
public void setProperties(Properties props) {
TransactionFactory.super.setProperties(props);
}
@Override
public Transaction newTransaction(Connection conn) {
return null;
}
@Override
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
return null; }}Copy the code
We also need to write a Transaction implementation class that implements the Transaction interface, as shown below.
public class MyTransaction extends JdbcTransaction implements Transaction {
public MyTransaction(DataSource ds, TransactionIsolationLevel desiredLevel,
boolean desiredAutoCommit) {
super(ds, desiredLevel, desiredAutoCommit);
}
public MyTransaction(Connection connection) {
super(connection);
}
public Connection getConnection(a) throws SQLException {
return super.getConnection();
}
public void commit(a) throws SQLException {
super.commit();
}
public void rollback(a) throws SQLException {
super.rollback();
}
public void close(a) throws SQLException {
super.close();
}
public Integer getTimeout(a) throws SQLException {
return super.getTimeout(); }}Copy the code
This allows you to customize transaction rules to meet specific needs.
<transactionManager type="com.molu.utils.MyTransactionFactory"/>
Copy the code
This is where the custom factory class is configured, not the transaction management class, because MyBatis gets the concrete instance object based on the configured factory
We can also alias it so that we don’t have to write a long fully qualified name.
dataSource
Let’s look at another tag
<dataSource type="POOLED">
<property name="driver" value="${db.oldDriver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
Copy the code
The
tag uses the standard JDBC dataSource interface to configure the resources of the JDBC connection object.
There are three built-in data source types in MyBatis: UNPOOLED, POOLED, and JNDI.
POOLED is a common connection pool. UNPOOLED, of course, means the opposite: no connection pool
Some people might wonder, isn’t connection pooling used to improve performance? What if you don’t use connection pooling? Isn’t it a waste of resources to open and close a connection every time
There are specific effects of not using connection pooling as well, because not every type of database relies heavily on connection pooling (possibly on the performance of the database itself); That’s where UNPOOLED comes in, and it’s one more option and it’s actually rarely used.
The properties it can configure are
driver
– This is the fully qualified Java class name of the JDBC driver (not the data source class that might be included in the JDBC driver).url
– This is the JDBC URL of the database.username
– User name for logging in to the database.password
– Password for logging in to the database.defaultTransactionIsolationLevel
– Default connection transaction isolation level.defaultNetworkTimeout
– Default network timeout duration (in milliseconds) to wait for database operations to complete. To viewjava.sql.Connection#setNetworkTimeout()
API documentation for more information.
The most commonly used is POOLED, which is a data source that uses connection pooling
Connection pooling is a very popular and practical concept. Generally speaking, we need to connect to the database every time we perform read and write operations, and frequent connection to close the database is a high performance overhead behavior. With connection pooling, we can put some idle connections into the pool instead of closing them. These idle connections can be used by other threads, thus avoiding frequent connection closing of the database.
For example, your bedroom is on the third floor and the water dispenser is on the first floor. If you want to drink water, you have to go up and down the stairs to exercise. You can actually move the water cooler to your bedroom and put it back when you don’t need it.
POOLED can also be configured with UNPOOLED attributes. POOLED can also be configured with other attributes such as:
-
PoolMaximumActiveConnections – any time there are number of connections, the default value is 10
-
PoolMaxmumIdleConnections – can free the number of connections
-
PoolMaxmumCheckoutTime – Wait time to check for idle connections before being forced back. That is, if there are 20 connections and only one is idle, the wait time before the idle connection is found is configured with this property.
-
PoolTimeToWait – The amount of time it takes to wait for a database connection to succeed and then try to reconnect.
JNDI is a data source that can be used in a container such as an EJB or an application server. The container can configure the data source centrally or externally. A reference to the JNDI context is then placed.
This data source requires only two properties to be configured:
Initial_context: Used to find the context in the InitialContext. Optional, if omitted, the datA_source attribute will be found directly from the InitialContext;
Data_source: Path to reference the location context of the data source instance. When the Initial_Context configuration is provided, the datA_source looks in the context it returns, otherwise it looks directly from the InitialContext.
In addition to the above three data sources, Mybatis also provides third-party data sources, such as DBCP and C3P0, which are not expanded here.
databaseIdProvider
The
tag is mainly used to support different vendors’ databases. For example, sometimes we use PG(Postgresql) databases for in-house development, but customers require MySql.
In MyBatis, we can use the
tag to implement database compatibility with different vendors.
MyBatis can execute different statements based on different database vendors. This multi-vendor support is based on the databaseId attribute in the mapping statement.
MyBatis loads statements with the databaseId attribute matching the current database and all statements without the databaseId attribute.
If the same statement is found with and without the databaseId, the latter is discarded.
To support multi-vendor features, simply add databaseIdProvider to the mybatis-config.xml file as follows:
<databaseIdProvider type="DB_VENDOR" />
Copy the code
The corresponding DB_VENDOR implementation of databaseIdProvider sets databaseId to the string returned by DatabaseMetaData#getDatabaseProductName(). Since these strings are usually quite long, and different versions of the same product return different values, you might want to make them shorter by setting property aliases:
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>
Copy the code
When a property alias is provided, the DB_VENDOR implementation of the databaseIdProvider sets the databaseId to the value of the database product name that matches the first name in the property, or to “null” if there is no matching property. In this example, if getDatabaseProductName() returns “Oracle (DataDirect)”, databaseId will be set to “Oracle”.
You can implement the interface org. Apache. Ibatis. Mapping. DatabaseIdProvider and in mybatis – config. Registered in the XML to build your own DatabaseIdProvider:
public interface DatabaseIdProvider {
default void setProperties(Properties p) { As of 3.5.2, this method is the default method
/ / empty implementation
}
String getDatabaseId(DataSource dataSource) throws SQLException;
}
Copy the code
mappers
mapper
Let’s start with
One is through the class attribute, the other is through the resource attribute (the most used), and the last is through the URL attribute
Using the class attribute requires our mapper.xml file to have the same package name as the interface, which is the way we started
<mapper class="com.molu.mapper.UserMapper"/>
Copy the code
By binding the interface directly to the class attribute, we can find our mapper.xml file (provided it has the same package name and maven static resource filtering is ok).
The resource attribute uses resource references relative to the classpath to find our mapper.xml file
By default, our resources directory is sibling to the Java directory, based on the relative path of the resources directory to the mapper. XML file
So if we want to write this relative path we can start at the next level of the Java directory, so I have com packages here, and we can write our own package directory depending on the environment
<mapper resource="com/molu/mapper/UserMapper.xml"/>
<! -->
<mapper resource="./com/molu/mapper/UserMapper.xml"/>
Copy the code
If we’re just testing, regardless of the specification, we can also put the mapper. XML file in the Resources directory, so we can map to it just by writing the file name
<mapper resource="UserMapper.xml"/>
<mapper resource="./UserMapper.xml"/>
Copy the code
Mapper. XML is probably the least used way to map mapper. XML files using URL attributes. To use modified attributes we need to know the fully qualified name of the mapper. XML file
<! -- You need to prefix the absolute path with "file:" -->
<mapper url="file:D:\IDEAproject\mybatis_study\mybaits_firstDemo\src\main\java\com\molu\mapper\UserMapper.xml"/>
Copy the code
package
The
However, we still need to follow certain specifications so that our interface and mapper.xml file have the same package name; Otherwise you won’t find it.
<package name="com.molu.mapper"/>
Copy the code
This concludes the entire XML configuration file, which is nearly 12,000 words long; I hope you can give me a thumbs-up
Write a little too much, if there is any mistake please correct