SoFunction
Updated on 2025-05-19

MyBatis Lazy Loading and Caching Implementation

1. Lazy loading strategy: load on demand, optimize performance

1. Lazy Load vs Load Now: Core Difference

  • Load now: When the main query (such as querying the user) is executed,Actively associated loading of associated data(such as all users’ accounts).
    • Scene: For many-to-one query (such as an account associated with a user), you need to obtain the associated data immediately.
  • Delay loading: When the main query is executedThe associated data is not loaded yet, only when the program accesses the associated data, a subquery will be triggered.
    • Scene: One-to-many query (such as the user associated with multiple accounts), reducing the initial query pressure.

To give a small example:

Scene: The user information is not loaded immediately when querying, and the query is only triggered when the order needs to be viewed.
Example: The e-commerce user details page first displays the user's name and address. Only when the user's order list is loaded is loaded when the "View Order" button is clicked.

2. Many-to-one lazy loading implementation (Account → User)

Step analysis:

1. Define the association query: The main query only checks the account table, and the associated user information is delayed by subquery.

<!-- Main query:Check the account only -->
<select  resultMap="accountMap">
  SELECT * FROM account
</select>

<!-- Subquery:By userIDCheck user information -->
<select  parameterType="int" resultType="User">
  SELECT * FROM user WHERE id = #{id}
</select>

2. Configure delayed loading:passassociationTags specify subquery paths and parameters.

<resultMap type="Account" >
  <association 
    property="user"          <!-- AccountIn the classUserproperty -->
    javaType="User"          <!-- Associated object type -->
    select="findById"        <!-- Subquery method name -->
    column="uid"             <!-- The column used for association in the main query result(Account tableuid) -->
  />
</resultMap>

3. Globally enable delayed loading:existConfiguration.

<settings>
  <setting name="lazyLoadingEnabled" value="true"/>   <!-- Turn on delayed loading -->
  <setting name="aggressiveLazyLoading" value="false"/> <!-- Close Active Loading(All associated data will be loaded by default) -->
</settings>

Test verification:

@Test
public void testLazyLoading() {
  List<Account> accounts = ();
  for (Account account : accounts) {
    ("Account Amount:" + ()); // Only the amount is output when the main query is executed    ("User Name:" + ().getUsername()); // Trigger subquery when accessing user for the first time  }
}

3. One-to-many lazy loading implementation (User → Accounts)

Core configuration:

<!-- Main query:Check the user table only -->
<select  resultMap="userMap">
  SELECT * FROM user
</select>

<resultMap type="User" >
  <collection 
    property="accounts"       <!-- UserAccount list attributes in class -->
    ofType="Account"          <!-- Collection element type -->
    select="" <!-- Subquery:By userIDCheck the account -->
    column="id"                <!-- Main query结果中的用户ID -->
  />
</resultMap>

<!-- Subquery:According to the userIDCheck the account -->
<select  parameterType="int" resultType="Account">
  SELECT * FROM account WHERE uid = #{uid}
</select>

Key Differences:

  • Many-to-one useassociation(Single object), one-to-multi-purposecollection(gather).
  • Subquery parameters passcolumnPass the fields in the main query result (such as the user tableid)。

2. MyBatis caching mechanism: reduce database access

1. The core value of cache

  • definition: Storing frequently queried data in memory temporarily to avoid repeated access to the database and improve query speed.
  • Applicable scenarios: Data that reads more and writes less and updates infrequently (such as dictionary tables, configuration information).

2. Level 1 cache: SqlSession-level cache

(1) Essence and function

  • Scope:based onSqlSessionObject, sameSqlSessionThe same query inside will get the results directly from the cache.
  • Implementation principleSqlSessionMaintain an internal oneHashMap, the key is the unique identifier of the query (SQL + parameter), and the value is the query result object.

(2) Verify Level 1 cache

@Test
public void testFirstLevelCache() {
  // Two identical queries within the same SqlSession  User user1 = (1);
  User user2 = (1); // Get it directly from the cache without executing SQL  (user1 == user2); // Output true (the object reference is the same)}

(3) Cache failure scenario

  • SqlSessionClose or submit (commit)。
  • implementupdate/insert/deleteOperation (the cache will be cleared).
  • Manual call()Clear the cache.

3. Level 2 cache: SqlSessionFactory-level cache

(1) Core concept

  • Scope:based onSqlSessionFactory, crossSqlSessionShared cache (such as multipleSqlSessionExecute the same query).
  • Implementation conditions

    Entity class needs to be implementedSerializableInterface (serialized storage is supported).

    existEnable Level 2 cache (it is enabled by default).

    Configure in Mapper<cache/>Label.

(2) Configuration steps

1、 Entity class serialization

public class User implements Serializable {
  // Omit properties and methods}

2. Enable cache in Mapper

&lt;mapper namespace=""&gt;
  &lt;cache/&gt; &lt;!-- Enable Level 2 Cache --&gt;
  
  &lt;select  resultType="User" useCache="true"&gt;
    SELECT * FROM user WHERE id = #{id}
  &lt;/select&gt;
&lt;/mapper&gt;

3. Configure cache policy (optional)

&lt;cache 
  eviction="LRU"       &lt;!-- Cache Elimination Strategy:LRU(Least recently used) --&gt;
  flushInterval="60000" &lt;!-- Automatic refresh interval(millisecond) --&gt;
  size="512"           &lt;!-- Maximum number of cached objects --&gt;
  readOnly="true"      &lt;!-- Read only:true(Shared Objects)/ false(Copy the object) --&gt;
/&gt;

(3) Cache priority and refresh

  • Priority: Level 2 Cache > Level 1 Cache > Database Query.
  • Refresh mechanism:implementupdate/insert/deleteWhen  , the corresponding Mapper's level 2 cache will be cleared.

(4) Test and Verification

@Test
public void testSecondLevelCache() {
  try (SqlSession session1 = ()) {
    UserMapper mapper1 = ();
    User user1 = (1); // The first query is to hit the database  }

  try (SqlSession session2 = ()) {
    UserMapper mapper2 = ();
    User user2 = (1); // The second query hits the second level cache and does not execute SQL  }
}

3. Summary: Core points of performance optimization

technology Core role Key configurations
Delay loading Reduce the initial query data volume and improve response speed lazyLoadingEnabledassociation/collectionofselectAttributes
Level 1 cache Reduce duplicate queries within the same session Automatically take effect, no additional configuration is required (noteSqlSessionlife cycle)
Level 2 cache Share cache across sessions to reduce database pressure Entity class serialization,<cache/>Tags and cache policy configuration

Rational use of delayed loading and caching can significantly improve the performance of MyBatis applications, but it needs to be flexibly selected according to the business scenario to avoid data inconsistency or memory overflow caused by excessive use.

This is the end of this article about the implementation of MyBatis lazy loading and caching. For more related content on MyBatis lazy loading and caching, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!