Get started with MyBatis


May 16, 2021 15:00 MyBatis



Installation

To use MyBatis, simply place the mybatis-x.x.x.jar file in the classpath.

If you are using Maven to build a project, you need to place the following dependency code in the pom.xml file:

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>

Build SqlSesionFactory from XML

Each MyBatis-based app is centered on an instance of SqlSesionFactory. E xamples of SqlSesionFactory are available through SqlSesionFactoryBuilder. SqlSesionFactory Builder, on the other way, can build sqlSessionFactory instances from an XML profile or a pre-configured Configuration instance.

Building an instance of SqlSesionFactory from an XML file is simple and is recommended for configuration using the resource file under the class path. H owever, you can also use any input stream instance, such as an input stream constructed file:// URL an input URL. MyBatis contains a tool class called Resources that contains practical methods to make it easier to load resource files from class paths or other locations.

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

The XML profile contains the core settings for the MyBatis system, including data Source for getting the DB connection instance and transaction managers that determine the scope and control of transactions. We'll look at the details of the XML profile later, and here's a simple example:

<?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>

Of course, there are many options that can be configured in an XML file, and the example above lists only the most critical parts. N ote the declaration of the XML header, which is used to verify the correctness of the XML document. T he environment element body contains the configuration of the transaction management and connection pool. The mappers element contains a set of mappers that contain SQL code and mapping definition information in their XML mapping files.

Build SqlSesionFactory without XML

If you prefer to create a configuration directly from Java code rather than an XML file, or if you want to create your own configuration builder, MyBatis also provides a complete configuration class that provides all the configuration items equivalent to XML files.

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);

Note that in this example, configuration adds a mapper class. T he mapptor class is a Java class that contains SQL mapping annotations to avoid relying on XML files. H owever, due to some limitations of Java annotations and the complexity of some MyBatis maps, XML configurations are still required to use most advanced maps, such as nested federation maps. W ith this in mind, if there is an XML profile with the same name, MyBatis automatically finds and loads it (in this BlogMapper.class on BlogMapper.xml The details will be discussed later.

Get SqlSession from SqlSessionFactory

Now that we have SqlSesionFactory, as the name implies, we can get an example of SqlSession from it. S qlSession provides all the methods you need to execute SQL commands in a database. Y ou can execute mapped SQL statements directly from the SqlSession instance. For example:

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

True, this works well and is familiar to users of older versions of MyBatis. But now that you have a cleaner way of using interfaces that match the parameters and return values of a specified statement, such as BlogMapper.class your code is now not only clearer and more type-safe, but you don't have to worry about string literal values that might go wrong, as well as casting type conversions.

For example:

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

Now let's explore what this code does.

Explore the mapped SQL statement

Now you might be wondering exactly what SqlSession and Mapper did, but SQL statement mapping is a fairly broad topic that may take up most of the document. But in order for you to understand the general, here are a few examples.

In the example mentioned above, a statement can be defined between XML and annotation. L et's start by looking at how XML defines statements, and in fact all the features that MyBatis provides can be implemented using XML-based mapping languages, which has made MyBatis popular over the past few years. I f you've used an older version of MyBatis, you should be familiar with the concept. H owever, the new version improves the configuration of many XML compared to previous versions, and we'll mention these improvements later. Here's an example based on an XML mapping statement that should satisfy the call to SqlSession in the previous example.

<?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>

For this simple example, we seem to have written a lot of configurations, but not much. I n an XML mapping file, countless mapping statements can be defined, which makes the XML header and document type declaration section insignificant. T he rest of the document is straight and easy to understand. It defines a mapping statement called "selectBlog" in the namespace "org.mybatis.example.blogMapper" so that you can call the mapping statement with the full qualified name "org.mybatis.example.BlogMapper.selectBlog", as in the example above:

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

You may notice that this approach is similar to the way Java objects are called with a fully qualified name. T his allows the name to map directly to the mapster class with the same name in the namespace and matches the mapped select statement to the corresponding name, parameter, and method that returns the type. So you can call the method on the corresponding maper interface, as you did above, without effort:

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

The second approach has many advantages, first, it's a little more secure if it doesn't depend on string literal values, and second, if your IDE has code complements, then code complements can help you quickly select a mapped SQL statement.

Tip A little supplement to the namespace

In previous versions of MyBatis, namespaces were not very important and optional. But now, as namespaces become more important, you have to specify namespaces.

Namespaces work in two ways, one is to isolate different statements with longer, fully qualified names, and also to implement the interface bindings you see above. E ven if you don't feel like you're using interface binding for the time being, you should follow the rules here in case you change your mind one day. In the long run, as long as you put the namespace in the right Java package namespace, your code will be cleaner and easier to use MyBatis.

Name resolution: To reduce the amount of input, MyBatis uses the following naming resolution rules for all configuration elements with names, including statements, result maps, caches, and so on.

  • Fully qualified names (e.g. "com.mypackage.MyMapper.selectAllThings") will be used directly for search and use.
  • Short names, such as selectAllThings, can also be used as a separate reference if they are globally unique. If you are not unique and have two or more identical names (e.g. "com.foo.selectAllThings" and "com.bar.selectAllThings"), then a "short name is not unique" error occurs when used, in which case a fully qualified name must be used.

For a mapper class like BlogMapper, there is another way to complete statement mapping. T he statements they map can be configured without XML, but with Java annotations. For example, the XML example above can be replaced with a configuration like this:

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

Mapping simple statements with annotations makes your code look cleaner, but for slightly more complex statements, Java annotations are not only overwhelming, they can also make your already complex SQL statements more confusing. Therefore, if you need to do something very complex, it's a good idea to map statements with XML.

It is entirely up to you and your team to choose how to configure the mapping and whether you think you should unify the form defined by the mapping statement. In other words, never stick to one way, you can easily port and switch between annotation- and XML-based statement mapping.

Scope and lifecycle

Understanding the different scopes and lifecycle categories we discussed earlier is critical because mis-use can lead to very serious complications.

Tip The object lifecycle and dependency injection framework

Dependency injection frameworks create thread-safe, transaction-based SqlSession and maples and inject them directly into your beans, so you can ignore their lifecycles directly. If you're interested in using MyBatis through the Dependency Injection Framework, you can look at two sub-projects, MyBatis-Spring or MyBatis-Guice.

SqlSessionFactoryBuilder

This class can be instantiated, used, and discarded, and once SqlSesionFactory is created, it is no longer needed. S o the best scope for sqlSessionFactoryBuilder instances is the method scope (that is, local method variables). You can reuse SqlSesionFactoryBuilder to create multiple SqlSesionFactory instances, but it's best not to keep it all the time to ensure that all XML resolution resources can be freed up to more important things.

SqlSessionFactory

Once created, SqlSesionFactory should have existed during the run of the app, for no reason to discard it or re-create another instance. T he best practice for using SqlSesionFactory is not to create multiple times while the app is running, and rebuilding SqlSesionFactory multiple times is considered a code "bad habit." S o the best scope for SqlSesionFactory is the application scope. There are many ways to do this, the simplest of which is to use a single-case mode or a static single-case pattern.

SqlSession

Each thread should have its own SqlSession instance. A n instance of SqlSession is not thread-safe and therefore cannot be shared, so its best scope is the request or method scope. Y ou can never put a reference to a SqlSession instance in a static domain of a class, not even an instance variable of a class. Y ou should never place references to SqlSession instances in any type of managed scope, such as HttpSession in the Servlet framework. I f you're using a Web framework right now, consider putting SqlSession in a scope similar to http requests. I n other words, each time you receive an HTTP request, you can open a SqlSession and close it when you return a response. T his shutdown is important, and to ensure that you can do it every time, you should put it in the final block. The following example is a standard pattern that ensures that SqlSession is turned off:

try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的应用逻辑代码
}

This usage pattern is followed in all code to ensure that all database resources are shut down correctly.

The mapor instance

The mapping machine is the interface for some binding mapping statements. A n instance of the mapping interface is obtained from SqlSession. T echnically, though, the maximum scope of any mapter instance is the same as sqlSession requesting them. H owever, method scope is the most appropriate scope for mapter instances. T hat is, maper instances should be acquired in the methods that call them and discarded when they are used. T he mapptor instance does not need to be explicitly closed. A lthough it's no problem to keep mapter instances throughout the request scope, you'll soon find that managing too many resources like SqlSession on that scope can keep you busy. T herefore, it is a good idea to place the mapter within the scope of the method. Like the following example:

try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class); // 你的应用逻辑代码 }