One of SSM mybatis: getting started

Posted by mightymax on Tue, 18 Jan 2022 17:40:53 +0100

The following is based on MyBatis official website Document extension

1. What is MyBatis?

MyBatis is an excellent persistence layer framework, which supports custom SQL, stored procedures and advanced mapping. MyBatis eliminates almost all JDBC code and the work of setting parameters and obtaining result sets. MyBatis can configure and map primitive types, interfaces and Java POJO s (Plain Old Java Objects) to records in the database through simple XML or annotations.

2. Introduction

  • pom.xml file import dependency

  <!--mybatis-->
  <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.7</version>
  </dependency>

  <!--Connect to database-->
  <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.26</version>
  </dependency>
  • Build SqlSessionFactory from XML and get SqlSession from SqlSessionFactory

Each MyBatis based application takes an instance of SqlSessionFactory as the core. An instance of SqlSessionFactory can be obtained through SqlSessionFactoryBuilder. SqlSessionFactory builder can build a SqlSessionFactory instance from an XML Configuration file or a pre configured Configuration instance.

Building an instance of SqlSessionFactory from an XML file is very simple. It is recommended to use the resource file under the classpath for configuration. However, you can also use any input stream instance, such as an input stream constructed with a file path string or a file:// URL. MyBatis contains a tool class called Resources, which contains some practical methods to make it easier to load resource files from the classpath or other locations.

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

mybatis tool class: MybatisUtils

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

//sqlSessionFactory  sqlSession
public class MybatisUtils {

    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(){
        return sqlSessionFactory.openSession();
    }
}

The XML configuration file contains the core settings for the MyBatis system, including the data source to obtain the database connection instance and the transaction manager to determine the transaction scope and control mode. The details of the XML configuration file will be discussed later. Here is 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="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL= true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="55555"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="com/wang/dao/UserMapper.xml"/>
    </mappers>
</configuration>

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

  • Build SqlSessionFactory without XML

If you prefer to create configurations directly from Java code rather than XML files, or want to create your own configuration builder, MyBatis also provides complete configuration classes and provides all 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. Mapper classes are Java classes that contain SQL mapping annotations to avoid dependency on XML files. However, due to some limitations of Java annotations and the complexity of some MyBatis mappings, XML configuration is still required to use most advanced mappings (such as nested union mapping). In view of this, if there is an XML configuration file with the same name, MyBatis will automatically find and load it (in this example, BlogMapper.xml will be loaded based on the class path and the class name of BlogMapper.class).

  • Execute mapped SQL statements

SqlSession provides all the methods required to execute SQL commands in the database. You can directly execute the mapped SQL statements through the SqlSession instance. For example:

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

Admittedly, this method works normally and is familiar to users using the old version of MyBatis. But now there is a more concise way - use an interface that matches the parameters and return values of the specified statement (such as BlogMapper.class). Now your code is not only clearer and more type safe, but also you don't have to worry about the possible wrong string literal and forced type conversion.

For example:

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

Now let's explore what this code does.

  • Explore mapped SQL statements

    Now you may want to know what specific operations SqlSession and Mapper perform, but SQL statement mapping is a very wide topic and may occupy most of the document. But to give you a general idea, here are a few examples.

    In the example mentioned above, a statement can be defined either by XML or by annotation. Let's first look at the way XML defines statements. In fact, all the features provided by MyBatis can be implemented using XML based mapping language, which has made MyBatis popular in the past few years. If you have used the old version of MyBatis, you should be familiar with this concept. However, compared with the previous version, the new version has improved many XML configurations, which we will mention later. Here is an example based on XML Mapping statement, which should meet the call of 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. In an XML Mapping file, you can define countless mapping statements, which makes the XML header and document type declaration insignificant. The rest of the document is straightforward and easy to understand. It defines a mapping statement named "selectBlog" in the namespace "org.mybatis.example.BlogMapper", so that you can call the mapping statement with the fully qualified name "org.mybatis.example.BlogMapper.selectBlog", as in the above example:

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

    You may notice that this approach is similar to calling Java objects with fully qualified names. In this way, the name can be directly mapped to the mapper class with the same name in the namespace, and the mapped select statement can be matched to the method with the corresponding name, parameter and return type. Therefore, you can easily call methods on the corresponding mapper interface as above, as follows:

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

    The second method has many advantages. Firstly, it does not depend on string literal value, which will be safer; Secondly, if your IDE has code completion function, code completion can help you quickly select mapped SQL statements.

  • MyBatis process analysis

    First, we loaded and obtained mybatis - config XML resource, loaded into Resources and returned an inputStream


After that, we call the build method of SqlSessionFactory. Here we go in and look at the source code.



Here, the xml file stream is parsed and parser. xml is called The parse () method returns a Configuration



Then pass the returned Configuration into DefaultSqlSessionFactory to instantiate SqlSessionFactory

Finally, the created SqlSessionFactory creates a SqlSession object

The overall flow chart of Mybatis is as follows

A little supplement to namespace

In previous versions of MyBatis, * * Namespaces) * * did not play a significant role and was optional. But now, as Namespaces become more important, you have to specify Namespaces.

Namespace has two functions. One is to use a longer fully qualified name to isolate different statements. At the same time, it also implements the interface binding you see above. Even if you don't think you can use 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 appropriate Java package namespace, your code will become cleaner and easier for you to use MyBatis.

**Name resolution: * * in order to reduce the amount of input, MyBatis uses the following name resolution rules for all configuration elements with names (including statements, result mapping, cache, etc.).

  • Fully qualified names (such as "com.mypackage.MyMapper.selectAllThings") will be used directly for finding and using.
  • Short names (such as "selectAllThings") can also be used as a separate reference if they are globally unique. If it is not unique and there are two or more identical names (such as "com.foo.selectAllThings" and "com.bar.selectAllThings"), the error of "short name is not unique" will occur when using it. In this case, the fully qualified name must be used.

For mapper classes like BlogMapper, there is another way to complete statement mapping. Their mapped statements can be configured using Java annotations instead of XML. For example, the above XML example can be replaced with the following configuration:

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

Using annotations to map simple statements will make the code more concise, but for slightly more complex statements, Java annotations will not only fail, but also make your already complex SQL statements more chaotic. Therefore, if you need to do some very complex operations, it is best to use XML to map statements.

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 of mapping statement definition. In other words, never stick to one way. You can easily migrate and switch between annotation based and XML based statement mapping.

  • Scope and lifecycle

It is important to understand the different scopes and lifecycle categories we discussed earlier, because incorrect use can lead to very serious concurrency problems.

Object lifecycle and dependency injection framework

The dependency injection framework can create thread safe, transaction based sqlsessions and mappers and inject them directly into your bean s, so you can directly ignore their life cycle. If you are interested in how to use MyBatis through the dependency injection framework, you can study the MyBatis spring or MyBatis Guice subprojects.

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
}
  • Entry example

    • Build database

      CREATE DATABASE `mybatis`;
      
      USE `mybatis`;
      
      CREATE TABLE `user`(
          `id` INT(20) NOT NULL PRIMARY KEY,
          `name` VARCHAR(30) DEFAULT NULL,
          `pwd` VARCHAR(30) DEFAULT NULL
      )ENGINE=INNODB DEFAULT CHARSET=utf8;
      
      INSERT INTO `user`(`id`,`name`,`pwd`)VALUES
      (1,'Zhang San','123456'),
      (2,'Li Si','123456'),
      (3,'Wang Wu','123456')
      
    • New project

      • Create a normal maven project
      • Delete src directory
      • Import maven dependencies
       <dependencies>
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>8.0.26</version>
              </dependency>
      
              <dependency>
                  <groupId>org.mybatis</groupId>
                  <artifactId>mybatis</artifactId> 
                  <version>3.5.2</version>
              </dependency>
      
              <dependency>
                  <groupId>junit</groupId>
                  <artifactId>junit</artifactId>
                  <version>4.11</version>
              </dependency>
       </dependencies>
      
    • Configure resources resource filtering

      <build>
              <resources>
                  <resource>
                      <directory>src/main/resources</directory>
                      <includes>
                          <include>**/*.properties</include>
                          <include>**/*.xml</include>
                      </includes>
                      <filtering>true</filtering>
                  </resource>
      
                  <resource>
                      <directory>src/main/java</directory>
                      <includes>
                          <include>**/*.properties</include>
                          <include>**/*.xml</include>
                      </includes>
                      <filtering>true</filtering>
                  </resource>
              </resources>
          </build>
      
    • Write the core configuration file of mybatis

      <?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="com.mysql.cj.jdbc.Driver"/>
                      <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL= true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                      <property name="username" value="root"/>
                      <property name="password" value="66666"/>
                  </dataSource>
              </environment>
          </environments>
      
          <mappers>
              <mapper resource="com/wang/dao/UserMapper.xml"/>
          </mappers>
      </configuration>
      
    • Write mybatis tool class

      import org.apache.ibatis.io.Resources;
      import org.apache.ibatis.session.SqlSession;
      import org.apache.ibatis.session.SqlSessionFactory;
      import org.apache.ibatis.session.SqlSessionFactoryBuilder;
      
      import java.io.IOException;
      import java.io.InputStream;
      
      public class MybatisUtils {
      
          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(){
              return sqlSessionFactory.openSession();
          }
      }
      
    • Entity class

      public class User {
          private int id;
          private String name;
          private String pwd;
      
          public User() {
          }
      
         public User(int id, String name, String pwd) {
            this.id = id;
           this.name = name;
            this.pwd = pwd;
         }
      
          public int getId() {
              return id;
          }
      
          public void setId(int id) {
              this.id = id;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public String getPwd() {
              return pwd;
          }
      
          public void setPwd(String pwd) {
              this.pwd = pwd;
          }
      
          @Override
          public String toString() {
              return "User{" +
                      "id=" + id +
                      ", name='" + name + '\'' +
                      ", pwd=" + pwd + '\'' +
                      '}';
          }
      }
      
    • Mapper interface

      public interface UserDao {
      
          List<User> getUserList();
      }
      
    • The interface implementation class is transformed from the original UserDaoImpl to a Mapper configuration file

      <?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="com.wang.dao.UserDao">
          <select id="getUserList" resultType="com.wang.pojo.User">
              select * from mybatis.user
          </select>
        </mapper>
      
    • junit test

      import com.wang.pojo.User;
      import com.wang.utils.MybatisUtils;
      import org.apache.ibatis.session.SqlSession;
      import org.junit.Test;
      
      import java.util.List;
      
      public class UserDaoTest {
      
          @Test
          public void test(){
      
              SqlSession sqlSession = MybatisUtils.getSqlSession();
              
              UserDao userDao= sqlSession.getMapper(UserDao.class);
              List<User> userList = userDao.getUserList();
      
              for (User user : userList) {
                  System.out.println(user);
              }
      
              sqlSession.close();
      }
      

Topics: Java JavaEE Spring