Database Schema Overview
Before implementing the code examples, it's essential to understand the database table structure that we'll be working with.
Many-to-One Relationships
For our examples, we'll consider a PostContent table (primary key) as the "many" side and a Post table as the "one" side.
Appproach 1: Cascade Property Mapping
First, create the entity classes with appropriate relationships:
package com.example.mybatis.advanced.pojo;
public class PostContent {
private Integer id;
private String content;
private Post post; // Many-to-one relationship - add Post reference in the "many" side
// Constructors, getters, setters, and toString() method
}
Next, configure the mapping file:
<?xml version="1.0" encoding="UTF-8" ??>
<mapper namespace="com.example.mybatis.advanced.mapper.PostContentMapper">
<resultmap id="PostContentResultMap" type="PostContent">
<id column="id" property="id"></id>
<result column="content" property="content"></result>
<result column="postId" property="post.id"></result>
<result column="user_id" property="post.userId"></result>
<result column="title" property="post.title"></result>
<result column="summary" property="post.summary"></result>
<result column="view_count" property="post.viewCount"></result>
<result column="publish_time" property="post.publishTime"></result>
<result column="update_time" property="post.updateTime"></result>
</resultmap>
<select id="selectPostContentById" resultmap="PostContentResultMap">
SELECT
c.*, p.id postId, p.user_id, p.title, p.summary, p.view_count, p.publish_time, p.update_time
FROM
post_content c LEFT JOIN post p ON c.post_id = p.id
WHERE
c.id = #{id}
</select>
</mapper>
Implement the test case:
@Test
public void testSelectPostContentById() {
SqlSession sqlSession = SqlSessionUtil.openSqlSession();
PostContentMapper mapper = sqlSession.getMapper(PostContentMapper.class);
PostContent postContent = mapper.selectPostContentById(1);
System.out.println(postContent);
System.out.println(postContent.getPost());
sqlSession.close();
}
Approach 2: Association Mapping
Modify only the resultMap configuration to use the association tag:
<resultmap id="PostContentResultMap" type="PostContent">
<id column="id" property="id"></id>
<result column="content" property="content"></result>
<association javatype="Post" property="post">
<id column="postId" property="id"></id>
<result column="user_id" property="userId"></result>
<result column="title" property="title"></result>
<result column="summary" property="summary"></result>
<result column="view_count" property="viewCount"></result>
<result column="publish_time" property="publishTime"></result>
<result column="update_time" property="updateTime"></result>
</association>
</resultmap>
Approach 3: Step-by-Step Querying
Implement this approach in three steps:
Step 1: Add a method to the PostMapper interface:
package com.example.mybatis.advanced.mapper;
import com.example.mybatis.advanced.pojo.Post;
public interface PostMapper {
Post selectPostById(Integer id);
}
Step 2: Configure the PostMapper.xml file:
<?xml version="1.0" encoding="UTF-8" ??>
<mapper namespace="com.example.mybatis.advanced.mapper.PostMapper">
<select id="selectPostById" resulttype="Post">
SELECT * FROM post WHERE id = #{id};
</select>
</mapper>
Step 3: Update the PostContentMapper.xml file to use association with select attribute:
<?xml version="1.0" encoding="UTF-8" ??>
<mapper namespace="com.example.mybatis.advanced.mapper.PostContentMapper">
<resultmap id="PostContentResultMap" type="PostContent">
<id column="id" property="id"></id>
<result column="content" property="content"></result>
<association column="post_id" property="post" select="com.example.mybatis.advanced.mapper.PostMapper.selectPostById"></association>
</resultmap>
<select id="selectPostContentById" resultmap="PostContentResultMap">
SELECT * FROM post_content WHERE id = #{id};
</select>
</mapper>
One-to-Many Relationships
For one-to-many relationships, we typically add a List collection property to the "one" side entity:
package com.example.mybatis.advanced.pojo;
import java.util.Date;
import java.util.List;
public class Post {
private Integer id;
private Integer userId;
private String title;
private String summary;
private Integer viewCount;
private Date publishTime;
private Date updateTime;
private List<postcontent> contents;
// Constructors, getters, setters, and toString() method
}</postcontent>
Approach 1: Collection Mapping
Add a method to PostMapper interface:
public interface PostMapper {
Post selectPostById(Integer id);
}
Create the PostMapper.xml mapping file:
<?xml version="1.0" encoding="UTF-8" ??>
<mapper namespace="com.example.mybatis.advanced.mapper.PostMapper">
<resultmap id="PostResultMap" type="Post">
<id column="id" property="id"></id>
<result column="user_id" property="userId"></result>
<result column="title" property="title"></result>
<result column="summary" property="summary"></result>
<result column="view_count" property="viewCount"></result>
<result column="publish_time" property="publishTime"></result>
<result column="update_time" property="updateTime"></result>
<collection oftype="PostContent" property="contents">
<id column="contentId" property="id"></id>
<result column="content" property="content"></result>
</collection>
</resultmap>
<select id="selectPostById" resultmap="PostResultMap">
SELECT
p.*, c.id contentId, c.content
FROM
post p LEFT JOIN post_content c ON p.id = c.post_id
WHERE p.id = #{id}
</select>
</mapper>
Implement the test program:
@Test
public void testSelectPostById() {
SqlSession sqlSession = SqlSessionUtil.openSqlSession();
PostMapper mapper = sqlSession.getMapper(PostMapper.class);
Post post = mapper.selectPostById(1);
System.out.println(post);
System.out.println(post.getContents());
sqlSession.close();
}
Approach 2: Step-by-Step Querying
Similar to the many-to-one approach, modify three components:
Step 1: Update the mapping file:
<?xml version="1.0" encoding="UTF-8" ??>
<mapper namespace="com.example.mybatis.advanced.mapper.PostMapper">
<resultmap id="PostResultMap" type="Post">
<id column="id" property="id"></id>
<result column="user_id" property="userId"></result>
<result column="title" property="title"></result>
<result column="summary" property="summary"></result>
<result column="view_count" property="viewCount"></result>
<result column="publish_time" property="publishTime"></result>
<result column="update_time" property="updateTime"></result>
<collection column="id" property="contents" select="com.example.mybatis.advanced.mapper.PostContentMapper.selectContentsByPostId"></collection>
</resultmap>
<select id="selectPostById" resultmap="PostResultMap">
SELECT * FROM post WHERE id = #{id};
</select>
</mapper>
Step 2: Add a method to PostContentMapper interface:
/**
* Retrieves all content for a specific post ID
* @param postId The ID of the post
* @return List of content items for the specified post
*/
List<postcontent> selectContentsByPostId(Integer postId);
</postcontent>
Step 3: Configure the SQL statement in PostContentMapper.xml:
<select id="selectContentsByPostId" resulttype="PostContent">
SELECT * FROM post_content WHERE post_id = #{postId};
</select>
Lazy Loading
Benefits of Step-by-Step Querying
- Enhanced Reusability: Breaking down complex queries into smaller, reusable components.
- Lazy Loading Support: Enables efficient data loading only when needed.
Understanding Lazy Loading
Lazy loading operates on the principle of executing queries only when data is actually needed, not during initial loading. This approach optimizes performance by minimizing unnecessary database queries.
Implementing Lazy Loading in MyBatis
To enable lazy loading for specific associations:
<association column="post_id" fetchtype="lazy" property="post" select="com.example.mybatis.advanced.mapper.PostMapper.selectPostById"></association>
Note: By default, lazy loading is disabled. You must explicitly set fetchType="lazy" for the specific association.
Global Lazy Loading Configuration
In most applications, you'll want lazy loading enabled globally. Configure this in the MyBatis core configuration file:
<settings>
<setting name="lazyLoadingEnabled" value="true"></setting>
</settings>
Recommended Development Pattern
- Enable global lazy loading in the configuration.
- For associations that require immediate loading, set fetchType="eager".