MyBatis Quick Start and Key Details

1. Definition

MyBatis is an excellent persistence layer framework that supports custom SQL, stored procedures and advanced mapping. MyBatis avoids almost all JDBC code and manually setting parameters and getting result sets. MyBatis can use simple XML or annotations to configure and map native information, and map interfaces and Java POJOs (Plain Old Java Objects, ordinary Java objects) to records in the database.

Official website

http://www.mybatis.org/

2. Use MyBatis

  1. Programmatically
    i.e. use mybatis without integration with other frameworks.
    Getting started example reference:
    http://www.mybatis.org/mybatis-3/zh/getting-started.html
    My own example: https://github.com/ljmomo/learn-mybatis mybatis-demo module

  2. Integrated managed integration into spring
    integration Spring http://www.mybatis.org/spring/zh/

    The use in work is generally integrated managed integration into spring and the use of
    MyBatis Generator to generate and generate Bean and Mapper. How to use it in IDEA can refer to my other article: Using MyBatis Generator in IDEA

3. Scope and life cycle

class name SCOPE
SqlSessionFactoryBuilder method
SqlSessionFactory application
SqlSession request/method (can be thought of as thread level)
Mapper method

Detailed instructions:
http://www.mybatis.org/mybatis-3/en/getting-started.html

4. mybatis config file

  1. typeAliases Type aliases are used to set a short name for a Java type. It is only relevant to XML configuration, and exists only to reduce the redundancy of fully qualified class names. E.g:
<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

2.typeHandlers
Whether MyBatis sets a parameter in the prepared statement (PreparedStatement) or takes a value from the result set, it will use the type handler to convert the obtained value into a Java type in an appropriate way.
You can override type handlers or create your own to handle unsupported or non-standard types. The specific approach is: implement the org.apache.ibatis.type.TypeHandler interface, or inherit a very convenient class org.apache.ibatis.type.BaseTypeHandler, and then optionally map it to a JDBC type. for example:

@MappedJdbcTypes(JdbcType.VARCHAR)
public class JunliTypeHandler extends BaseTypeHandler<String> {
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
        preparedStatement.setString(i, s + "LIJUN");
    }

    @Override
    public String getNullableResult(ResultSet resultSet, String s) throws SQLException {
        return resultSet.getString(s)+"LIJUN";
    }

    @Override
    public String getNullableResult(ResultSet resultSet, int i) throws SQLException {
        return resultSet.getString(i);
    }

    @Override
    public String getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        return callableStatement.getString(i);
    }
}

<!-- mybatis-config.xml -->
   <typeHandlers>
        <typeHandler handler="com.junli.mybatis.demo.mybatis.JunliTypeHandler"/>
    </typeHandlers>

@MappedJdbcTypes(JdbcType.VARCHAR) Using this type handler will override the existing type handlers that handle Java's String type properties and VARCHAR parameters and results. preparedStatement.setString(i, s + "LIJUN"); means adding LIJUN to all String types but sometimes we just want to add LIJUN to specific fields. It can be configured as follows (mybatis-config.xml is not needed):

//插入
 insert into test (id, nums, name
    )
    values (#{id,jdbcType=INTEGER}, #{nums,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR,typeHandler=com.junli.mybatis.demo.mybatis.JunliTypeHandler}
    )



 //返回   
<resultMap id="BaseResultMap" type="com.junli.mybatis.beans.Test">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="nums" jdbcType="INTEGER" property="nums" />
    <result column="name" jdbcType="VARCHAR" property="name" typeHandler="com.junli.mybatis.demo.mybatis.JunliTypeHandler"/>
  </resultMap>    

3. Plugins (plugins)
MyBatis allows you to intercept calls at a certain point in the execution of mapped statements. By default, MyBatis allows plugins to intercept method calls including:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)

The following is a custom plug-in to print out the sql statement of the query:

@Intercepts({@Signature(type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class JunliPlugin  implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        BoundSql boundSql = mappedStatement.getBoundSql(invocation.getArgs()[1]);
        System.out.println(String.format("plugin output sql = %s , param=%s", boundSql.getSql(),boundSql.getParameterObject()));
        return invocation.proceed();
    }
    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o,this);
    }
    @Override
    public void setProperties(Properties properties) {

    }

Configure the plugin:

  <plugins>
      <plugin interceptor="com.junli.mybatis.demo.mybatis.JunliPlugin"/>
  </plugins>

4. Mappers
Now that the behavior of MyBatis has been configured by the above elements, we will now define the SQL mapping statement. But first we need to tell MyBatis where to find these statements. Java doesn't provide a good way to find this automatically, so the best way is to tell MyBatis where to look for the mapping file. You can use classpath-relative resource references, or fully qualified resource locators (including file:/// URLs), or class and package names, etc. E.g:

 <mappers>
    <mapper resource="xml/TestMapper.xml"/>
    <mapper resource="xml/PostsMapper.xml"/>
</mappers>

Detailed mybatis config reference official website: http://www.mybatis.org/mybatis-3/zh/configuration.html

5. Interpretation of Mapper XML file

The real power of MyBatis is its mapping statement, and that's where its magic lies. Because of how powerful it is, the mapper's XML file is relatively simple. If you compare it to JDBC code with the same functionality, you'll immediately notice that you've saved nearly 95% of the code. MyBatis is built for SQL and does it better than the normal way.
The SQL map file has a few top-level elements (in the order they should be defined):

  • cache – cache configuration for the given namespace.
  • cache-ref - A reference to other namespace cache configurations.
  • resultMap
  • – is the most complex and powerful element that describes how to load objects from the database result set.
  • sql – A block of reusable statements that can be referenced by other statements.
  • insert – map insert statement
  • update – map update statement
  • delete – map delete statement
  • select – map query statement

Mainly talk about resultMap
The resultMap element has many sub-elements and a structure worth discussing.

resultMap

  • constructor - used to inject the result into the constructor when the class is instantiated
    idArg - ID parameter; marking the result as an ID can help improve overall performance
    arg - a common result that will be injected into the constructor
  • id – an ID result; marking the result as an ID can help improve overall performance
  • result – the normal result injected into the field or JavaBean property
  • association – an association of a complex type; many results will be wrapped into this type
  • Nested result maps – associations can be specified as a resultMap element, or reference a
  • collection – a complex type of collection nested result map – the collection can be specified as a resultMap element, or reference a
  • discriminator - use the result value to decide which resultMap to use.

    resultMap related query.
    There are two types of related queries:

  • Associated Nested Queries

example

 <resultMap id="BaseResultMap" type="com.junli.mybatis.beans.Blog">
        <result column="bid" jdbcType="INTEGER" property="bid"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <result column="author_id" jdbcType="INTEGER" property="authorId"/>
        <association property="author" column="author_id" javaType="com.junli.mybatis.beans.Author" select="selectAuthor" />
    </resultMap>

    <resultMap id="BaseResultMap_Author" type="com.junli.mybatis.beans.Author">
        <result column="aid" jdbcType="INTEGER" property="aid"/>
        <result column="author_name" jdbcType="VARCHAR" property="authorName"/>
    </resultMap>


 <select id="selectAuthor" resultType="com.junli.mybatis.beans.Author" resultMap="BaseResultMap_Author">
        SELECT * FROM AUTHOR WHERE aid = #{author_id}
    </select>

    <select id="selectById" resultMap="BaseResultMap">
        SELECT  * FROM  blog WHERE bid = #{id} ;
    </select>

We have two query statements: one to load the blog and one to load the author, and the blog's result map describes that the "BaseResultMap_Author" statement should be used to load its author property.
All other properties will be loaded automatically, assuming their column and property names match.
This way is simple, but will not perform well for large data collections and lists. The problem is what we know as the "N+1 query problem". In a nutshell, the N+1 query problem can be caused by:
1. You execute a single SQL statement to get a list of results (that is, "+1").
2. For each record returned, you execute a query to load the details for each (that is, "N").
This problem can cause hundreds of SQL statements to be executed. This is usually not desired.
It is a benefit that MyBatis can lazy load such queries, so you can spread the cost of running these statements concurrently. However, if you load a list and then quickly iterate to access the nested data, you'll be calling all lazy loading and this behavior can be bad. So there is another way.

  • Associative nested results
    Re the above example
   <resultMap id="blogResult" type="com.junli.mybatis.beans.Blog">
        <result column="bid" jdbcType="INTEGER" property="bid"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <result column="author_id" jdbcType="INTEGER" property="authorId"/>
        <association property="author" column="author_id" javaType="com.junli.mybatis.beans.Author" resultMap="BaseResultMap_Author2"/>
    </resultMap>

    <resultMap id="BaseResultMap_Author2" type="com.junli.mybatis.beans.Author">
        <result column="aid" jdbcType="INTEGER" property="aid"/>
        <result column="author_name" jdbcType="VARCHAR" property="authorName"/>
    </resultMap>

    <select id="selectBlogById" resultMap="blogResult">
      SELECT
            B.bid,
            B.`name`,
            B.author_id,
            A.aid,
            A.author_name
        FROM
            Blog B
        LEFT OUTER JOIN Author A ON B.author_id = A.aid
        WHERE
            B.bid = #{id}
    </select>

Nested Queries for Collections
Let 's continue the above example, a blog has only one author. But blogs have a lot of articles. In the blog class, this can be represented by writing something like this:

private List<Posts> posts;

Example:

  <resultMap id="blogResultAndPosts" type="com.junli.mybatis.beans.Blog">
        <collection property="posts" javaType="ArrayList" column="bid"
                    ofType="com.junli.mybatis.beans.Posts" select="selectPostsForBlog"/>
    </resultMap>

    <resultMap id="PostsForBlogResult" type="com.junli.mybatis.beans.Posts">
        <result column="pid" jdbcType="INTEGER" property="pid" />
        <result column="post_name" jdbcType="VARCHAR" property="postName" />
        <result column="blog_id" jdbcType="INTEGER" property="blogId" />
    </resultMap>

    <select id="selectBlogAndPosts" resultMap="blogResultAndPosts">
        SELECT * FROM BLOG WHERE BID = #{id}
    </select>

    <select id="selectPostsForBlog" resultMap="PostsForBlogResult">
        SELECT * FROM POSTS WHERE BLOG_ID = #{bid}
    </select>

Nested result implementation for collections:

<resultMap id="blogResultAndPostsResultQuery" type="com.junli.mybatis.beans.Blog">
        <result column="bid" jdbcType="INTEGER" property="bid"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <result column="author_id" jdbcType="INTEGER" property="authorId"/>
        <collection property="posts" javaType="ArrayList" column="bid" ofType="com.junli.mybatis.beans.Posts">
            <result column="pid" jdbcType="INTEGER" property="pid" />
            <result column="post_name" jdbcType="VARCHAR" property="postName" />
            <result column="blog_id" jdbcType="INTEGER" property="blogId" />
        </collection>
    </resultMap>

    <select id="selectBlogAndPostsResultQuery" resultMap="blogResultAndPostsResultQuery">
        SELECT
            B.bid,
            B.`name`,
            B.author_id,
            p.pid,
            p.post_name,
            p.blog_id
        FROM
            Blog B
        LEFT  JOIN posts p ON p.blog_id = b.bid
        WHERE
            B.bid = #{id}
    </select>

Cache:
Mybatis has a first-level cache and a second-level cache. By default, the first-level cache is turned on and cannot be turned off. The first-level cache refers to the SqlSession-level cache. When the same SQL statement is queried in the same SqlSession, the second and subsequent queries will not be queried from the database, but will be obtained directly from the cache. The first-level cache can cache up to 1024 SQL. The second level cache refers to the cache that can cross SqlSession.
The SQL query in Mybatis is performed through the org.apache.ibatis.executor.Executor interface. Generally speaking, it has two types of implementations, one is BaseExecutor and the other is CachingExecutor. The former is used when the second-level cache is not enabled, while the latter is the decorator mode used when the second-level cache is enabled. When the second-level cache is not hit, the bottom layer is still implemented by BaseExecutor.

  • Level 1 cache
    Level 1 cache is enabled by default and is implemented in the query() method of BaseExecutor. The bottom layer uses PerpetualCache by default, and PerpetualCache uses HashMap to store data. The first-level cache will be cleared when adding, deleting, and modifying operations.
  • Second-level cache
    Second -level cache is enabled by default. If you want to cancel, you can specify cacheEnabled as false through the sub-element under the element in the Mybatis configuration file.
  <settings>

      <setting name="cacheEnabled" value="false" />

   </settings>

If we want to use the second-level cache, we need to define which cache the query statement needs to use to cache the data in the corresponding Mapper.xml file. This can be defined in two ways, one is defined by the cache element, and the other is defined by the cache-ref element. However, it should be noted that for the same Mapper, it can only use one Cache, and the priority of using and definition is higher when both are used at the same time. The Cache used by Mapper is bound to the namespace corresponding to our Mapper, and a namespace can only have at most one Cache bound to it.

  • Custom cache
    As mentioned earlier, Mybatis' Cache will use PerpetualCache to store data by default. If we don't want to implement it according to its logic, or we want to use other caching frameworks, such as Ehcache, Redis, etc., at this time we can use our own Cache implementation, Mybatis leaves us the corresponding interface, allowing us to customize. To implement a custom Cache, we must define our own class to implement the Cache interface provided by Mybatis and implement the corresponding interface method.
**
 * 自定义缓存
 */
public class JunliCache implements Cache {
    private ReadWriteLock lock = new ReentrantReadWriteLock();
    private ConcurrentHashMap<Object, Object> cache = new ConcurrentHashMap<Object, Object>();
    private String id;

    public JunliCache() {
        System.out.println("初始化-1!");
    }

    /**
     * 必须有该构造函数
     */
    public JunliCache(String id) {
        System.out.println("初始化-2!");
        this.id = id;
    }

    /**
     * 获取缓存编号
     */
    @Override
    public String getId() {
        System.out.println("得到ID:" + id);
        return id;
    }


    /***
     * 获取缓存对象的大小
     * @return int
     */
    @Override
    public int getSize() {
        System.out.println("获取缓存大小!");
        return 0;
    }

    /**
     * 保存key值缓存对象
     *
     * @param key   key
     * @param value value
     */
    @Override
    public void putObject(Object key, Object value) {
        System.out.println("往缓存中添加元素:key=" + key + ",value=" + value);
        cache.put(key, value);
    }


    /**
     * 通过KEY
     *
     * @param key key
     * @return Object
     */
    @Override
    public Object getObject(Object key) {
        System.out.println("通过kEY获取值:" + key);
        System.out.println("OVER");
        System.out.println("=======================================================");
        System.out.println("值为:" + cache.get(key));
        System.out.println("=====================OVER==============================");
        return cache.get(key);
    }


    /**
     * 通过key删除缓存对象
     *
     * @param key key
     * @return
     */
    @Override
    public Object removeObject(Object key) {
        System.out.println("移除缓存对象:" + key);
        return null;
    }


    /**
     * 清空缓存
     */
    @Override
    public void clear() {
        System.out.println("清除缓存!");
        cache.clear();
    }


    /**
     * 获取缓存的读写锁
     *
     * @return ReadWriteLock
     */
    @Override
    public ReadWriteLock getReadWriteLock() {
        System.out.println("获取锁对象!!!");
        return lock;
    }
}

More Mapper XML file configuration reference http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html

5. Dynamic SQL

One of the powerful features of MyBatis is its dynamic SQL. If you have experience with JDBC or other similar frameworks, you can appreciate the pain of concatenating SQL statements based on different conditions. For example, when concatenating, make sure not to forget to add the necessary spaces, and also pay attention to removing the comma from the last column name of the list. Take advantage of the dynamic SQL feature to get rid of this pain completely. Dynamic SQL elements are similar to JSTL or similar XML-based text processors. MyBatis uses powerful OGNL-based expressions to eliminate most of the other elements.

  • if
  <if test="title != null">
    AND title like #{title}
  </if>
  • choose (when, otherwise)
 <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
  • trim (where, set)
  <where> 
    <if test="state != null">
         state = #{state}
    </if> 
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>

  <trim prefix="WHERE" prefixOverrides="AND |OR ">
  ... 
 </trim>

  <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  • foreach
 <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>

More dynamic SQL reference: http://www.mybatis.org/mybatis-3/en/dynamic-sql.html

The article is a little long, the source code involved in this article refers to my source code mybatis-demo module.

Source address

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325582205&siteId=291194637