Introduction to MyBatis

Introduction to MyBatis

Building SqlSessionFactory from XML

Each MyBatis application is based on an instance of SSF (SqlSessionFactory), which can be obtained through SSF Builder. SSF Builder can build an SSF through XML configuration file or configuration class.

It is very convenient to build SSF through XML files. It is recommended to use classpath Resources for configuration. You can also build instances through InputStream, such as file path or a file:// URL. MyBatis contains a tool class Resources, which contains several methods to help load Resources from classpath and elsewhere.

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
	new SqlSessionFactoryBuilder().build(inputStream);

The configuration file includes the core settings of the MyBatis system, including obtaining the DataSource of the database Connection instance, and determining the transaction scope and control mode.

<?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>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <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>
  </environments>
  <mappers>
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>

The above contents include the most important parts of the configuration file. The XML header is used to validate the XML document, the enviornment element includes the environment configuration for transaction management and connection pool, and the mapper element contains the list of mappers (XML file and annotated Java class containing SQL code and mapping definition)

SqlSessionFactory is not built from XML

You can also build configurations directly by creating configuration classes.

DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory =
  new JdbcTransactionFactory();
Environment environment =
  new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory =
  new SqlSessionFactoryBuilder().build(configuration);

In this case, a mapper class is being added. The mapper class is a Java class that can discard XML and contain SQL mapping annotations. However, due to the limitations of Java annotations and the complexity of some MyBatis mappings, advanced mappings still need XML mappings (such as nested Join mappings). Therefore, MyBatis will automatically find and load an XML file of the same level (if it exists, for example, BlogMapper.xml will be loaded based on BlogMapper.class).

Get a SqlSession from SqlSessionFactory

With SqlSessionFactory, you can get an instance of SqlSession. SqlSession contains all methods for executing database commands. You can execute mapped SQL statements through the SqlSession instance, for example:

try (SqlSession session = sqlSessionFactory.openSession()) {
  Blog blog = session.selectOne(
    "org.mybatis.example.BlogMapper.selectBlog", 101);
}

Another clearer way is to use the interface to correctly describe the parameters and return values of a given attribute sentence to execute cleaner and safer type code.

try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  Blog blog = mapper.selectBlog(101);
}

SQL statement mapping analysis

You may wonder how SqlSession and Mapper classes are executed correctly. Next, let's take an example to see:

In the above example code, statements can be defined through XML or annotation methods. Let's first look at the XML method.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
  </select>
</mapper>

Although this XML looks complex, it's actually very cheap. You define as many mapping statements as possible in a single mapper XML file.

In the mapper element, the name of the mapping statement "selectBlog" is defined. In the namespace "org.mybatis.example.BlogMapper", you can call it by specifying the fully qualified name "org.mybatis.example.BlogMapper.selectBlog".

Blog blog = session.selectOne(
  "org.mybatis.example.BlogMapper.selectBlog", 101);

This is similar to the call method in a Java class, which can be directly mapped to a mapping class with the namespace. The name, parameter and return type of the method match with the mapping selection statement. In this way, you can simply call the method for the mapping interface.

BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);

Tips for namespace

Namespaces are now necessary, and not just to isolate statements with longer, fully qualified names

Namespace can implement interface binding, use namespace and put it in the appropriate Java package namespace to improve the availability of MyBatis.

Name resolution

In order to reduce the amount of input, MyBatis uses the following name resolution rules for all named configuration elements, including statements, result graphs, caches, etc.

  • Full names (such as "com.mypackage.MyMapper.selectAllThings") will be retrieved and used directly
  • Short names (such as "selectAllThing") can be used to refer to any explicit entry. If there are the same short names in different namespaces, an error will be reported, and the full name must be used.

Another way not to use XML Mapping is to use the annotation function of Java. For example, the above XML can be replaced with:

package org.mybatis.example;
public interface BlogMapper {
  @Select("SELECT * FROM blog WHERE id = #{id}")
  Blog selectBlog(int id);
}

For more complex statements, XML is recommended.

Scope and lifecycle

SqlSessionFactoryBuilder

This class can be instantiated, used, and discarded. Once SqlSessionFactory is created, it is no longer needed. Therefore, the best scope of the SqlSessionFactoryBuilder instance is the method scope (that is, local method variables). You can reuse SqlSessionFactory builder to create multiple SqlSessionFactory instances, but it's best not to keep it all the time to ensure that all XML parsing resources can be released to more important things.

SqlSessionFactory

Once SqlSessionFactory is created, it should always exist during the operation of the application. There is no reason to discard it or recreate another instance. The best practice of using SqlSessionFactory is not to create it repeatedly during application operation. Rebuilding SqlSessionFactory multiple times is regarded as a code "bad habit". Therefore, the best scope of SqlSessionFactory is the application scope. There are many ways to do this. The simplest is to use singleton mode or static singleton mode.

SqlSession

Each thread should have its own SqlSession instance. The instance of SqlSession is not thread safe, so it cannot be shared, so its best scope is the request or method scope. Never put the reference of SqlSession instance in the static field of a class, or even the instance variable of a class. You must never put a reference to a SqlSession instance in any type of managed scope, such as HttpSession in the Servlet framework. If you are currently using a Web framework, consider putting SqlSession in a scope similar to HTTP requests. In other words, each time you receive an HTTP request, you can open a SqlSession and close it after returning a response. This close operation is very important. In order to ensure that the close operation can be performed every time, you should put this close operation in the finally block. The following example is a standard mode to ensure that SqlSession is closed:

try (SqlSession session = sqlSessionFactory.openSession()) {
  // Your application logic code
}

Following this usage pattern in all code can ensure that all database resources can be shut down correctly.

Mapper instance

Mappers are interfaces that bind mapping statements. The instance of the mapper interface is obtained from SqlSession. Although technically, the maximum scope of any mapper instance is the same as the SqlSession requesting them. However, the method scope is the most appropriate scope for the mapper instance. That is, mapper instances should be obtained in the methods that call them and discarded after use. The mapper instance does not need to be explicitly closed. Although there is no problem keeping mapper instances in the entire request scope, you will soon find that managing too many resources like SqlSession in this scope will keep you busy. Therefore, it is best to put the mapper within the method scope. Like the following example:

try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  // Your application logic code
}

Keywords: Java Mybatis Spring Spring Boot

Added by mpiaser on Wed, 05 Jan 2022 10:52:39 +0200