[Java Zero to Architect Season 3] [08] MyBatis-Delayed Loading

Posted by turtlefox on Mon, 03 Jan 2022 13:35:58 +0100

Continuous Learning & Continuous Updating...

Break away

Why use delayed loading

Delayed loading: reload when needed

  • Many times, we don't have all the properties of an object temporarily, especially those that are complex and huge
  • For example, I only want to know the id and name of the Person object right now, but I can't use List <BankCard>, List <Job> of the Person object for the time being.
  • Using the previous query method will find out all the information of Person, which will cause a great waste
  • Many times, we want to load an object's properties when they are used (typically those of the collection type), rather than rushing to get all its information out at once
  • That is, simple attributes are loaded first, and complex and bulky attributes are loaded when they are to be used.
  • This is the time to use delayed loading

Immediate Loading and Delayed Loading

Load Now

  1. A SQL statement
  2. May cause waste of resources
  3. If it's not a collection type, common, and small, consider loading it now

Delayed Loading

  1. Multiple SQL statements
  2. These SQL statements execute at different times (when called. Example: when person.getBankCards() loads the BankCard-related data for this person object)
  3. No waste of resources
  4. Delayed loading is recommended if the collection type is unusual and large.

Delayed Loading of MyBatis

Implement Delayed Loading - Requirement 1

  • Requirement: Query out the corresponding Person object through Person's id, and delay load the three member variables idCard, bankCards and jobs of this Person object

  • Analysis

    1. Since the three attributes idCard, bankCards, and jobs of the Person object are to be delayed loaded, the previous SQL L statement needs to be modified, and not all the contents can be directly queried at once as before.
    2. Initialize the query by simply querying the id and name of Person
    3. When person is called in Java. When getIdCard(), load the idCard of the corresponding person object;
    4. When person is called in Java. When getBankCards(), load the bankCards corresponding to the person object;
    5. When person is called in Java. When getJobs (), the jobs corresponding to the person object are loaded;
    6. All three member variables that need to be delayed need to be queried separately by the id of the corresponding person
    7. So these three must have their own SQL query statements
  • id_card.xml Mapping file:

	<mapper namespace="id_card">
	    <select id="getByPersonId" parameterType="Int" resultType="IdCard">
	        SELECT id, no, address FROM id_card WHERE person_id = #{personId}
	    </select>
	</mapper>
  • bank_card.xml Mapping file:
	<mapper namespace="bank_card">
	    <select id="listByPersonId" parameterType="Int" resultType="BankCard">
	        SELECT id, no, amout FROM bank_card WHERE person_id = #{personId}
	    </select>
	</mapper>
  • job.xml Mapping file:
	<mapper namespace="job">
	    <select id="listByPersonId" parameterType="Int" resultType="Job">
	        SELECT id, name, duty
	        FROM job
	        JOIN person_job
	        ON 
	        	person_job.job_id = id AND person_job.person_id = #{personId}
	    </select>
	</mapper>
  • person.xml Mapping 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="person">
	    <sql id="sqlListAll">
	        SELECT id, name FROM person
	    </sql>
	    <resultMap id="rmList" type="Person">
	        <id property="id" column="id"/>
	        <result property="name" column="name"/>
	        <association property="idCard" javaType="IdCard"
	                     fetchType="lazy"
	                     column="id"
	                     select="id_card.getByPersonId"/>
	        <collection property="bankCards" ofType="BankCard"
	                    fetchType="lazy"
	                    column="id"
	                    select="bank_card.listByPersonId"/>
	        <collection property="jobs" ofType="Job"
	                    fetchType="lazy"
	                    column="id"
	                    select="job.listByPersonId"/>
	    </resultMap>
	    <select id="get" resultMap="rmList">
	        <include refid="sqlListAll"/> WHERE id = #{personId}
	    </select>
	</mapper>

Implement Delayed Loading - Requirement 2

  • Requirement: Query out the corresponding Person object through Person's id, and delay load only the two member variables bankCards and jobs of this Person object

  • Analysis

    1. Since only the bankCards and jobs attributes of the Person object are lazily loaded
    2. That means you need to query Person's id, name, idCard attributes when initializing the query
    3. When person is called in Java. When getBankCards(), load the bankCards corresponding to the person object;
    4. When person is called in Java. When getJobs (), the jobs corresponding to the person object are loaded;
    5. Both member variables that require delayed loading need to be queried separately by the id of the corresponding person
    6. So these two must have their own SQL queries
  • bank_card.xml:

	<mapper namespace="bank_card">
	    <select id="listByPersonId" parameterType="Int" resultType="BankCard">
	        SELECT id, no, amout FROM bank_card WHERE person_id = #{personId}
	    </select>
	</mapper>
  • job.xml:
	<mapper namespace="job">
	    <select id="listByPersonId" parameterType="Int" resultType="Job">
	        SELECT id, name, duty
	        FROM job
	        JOIN person_job
	        ON 
	        	person_job.job_id = id AND person_job.person_id = #{personId}
	    </select>
	</mapper>
  • person.xml:
	<mapper namespace="person">
	    <sql id="sqlListAll">
	        SELECT person.id       person_id,
	               person.name     person_name,
	               id_card.id      id_card_id,
	               id_card.no      id_card_no,
	               id_card.address id_card_address
	        FROM person
	        JOIN id_card ON id_card.person_id = person.id
	    </sql>
	    <resultMap id="rmList" type="Person">
	        <id property="id" column="person_id"/>
	        <result property="name" column="person_name"/>
	        <association property="idCard" javaType="IdCard">
	            <id property="id" column="id_card_id"/>
	            <result property="no" column="id_card_no"/>
	            <result property="address" column="id_card_address"/>
	        </association>
	        <collection property="bankCards" ofType="BankCard"
	                    fetchType="lazy"
	                    column="person_id"
	                    select="bank_card.listByPersonId"/>
	        <collection property="jobs" ofType="Job"
	                    fetchType="lazy"
	                    column="person_id"
	                    select="job.listByPersonId"/>
	    </resultMap>
	    <select id="get" resultMap="rmList">
	        <include refid="sqlListAll"/> WHERE person.id = #{personId}
	    </select>
	</mapper>

Global Delay Load Switch

  • Whether delayed loading is controlled when a related object has a select attribute configured:

    • Delayed loading when fetchType="lazy"
    • When fetchType="eager", load it immediately
    	<collection property="bankCards" ofType="BankCard"
    	            fetchType="lazy|eager"
    	            column="person_id"
    	            select="bank_card.listByPersonId"/>
    

Attention and some suggestions

  • If you don't want to delay loading, query "Person" when you query it, as in the example above
  • If you want to delay loading, instead of checking out Person when you query it, make another SQl statement (select tag) and let it query when it is needed.

Reference resources

Xiao Ma Ge-Li Mingjie: Java from 0 to Architect 3 Advanced Internet Architect.

Thank you for your attention and support!

Topics: Java Back-end