Mybatis关联查询(转载)
原文地址: http://www.cnblogs.com/xiaolang8762400/p/7399892.html
mybatis 提供了高级的关联查询功能,可以很方便地将数据库获取的结果集映射到定义的Java Bean 中。下面通过一个实例,来展示一下Mybatis对于常见的一对多和多对一关系复杂映射是怎样处理的。
设计一个简单的博客系统,一个用户可以开多个博客,在博客中可以发表文章,允许发表评论,可以为文章加标签。博客系统主要有以下几张表构成:
Author表:作者信息表,记录作者的信息,用户名和密码,邮箱等。
Blog表 : 博客表,一个作者可以开多个博客,即Author和Blog的关系是一对多。
Post表 : 文章记录表,记录文章发表时间,标题,正文等信息;一个博客下可以有很多篇文章,Blog 和Post的关系是一对多。
Comments表:文章评论表,记录文章的评论,一篇文章可以有很多个评论:Post和Comments的对应关系是一对多。
Tag表:标签表,表示文章的标签分类,一篇文章可以有多个标签,而一个标签可以应用到不同的文章上,所以Tag和Post的关系是多对多的关系;(Tag和Post的多对多关系通过Post_Tag表体现)
Post_Tag表: 记录 文章和标签的对应关系。
一般情况下,我们会根据每一张表的结构 创建与此相对应的JavaBean(或者Pojo),来完成对表的基本CRUD操作。
上述对单个表的JavaBean定义有时候不能满足业务上的需求。在业务上,一个Blog对象应该有其作者的信息和一个文章列表,如下图所示:
如果想得到这样的类的实例,则最起码要有一下几步:
1. 通过Blog 的id 到Blog表里查询Blog信息,将查询到的blogId 和title 赋到Blog对象内;
2. 根据查询到到blog信息中的authorId 去 Author表获取对应的author信息,获取Author对象,然后赋到Blog对象内;
3. 根据 blogId 去 Post表里查询 对应的 Post文章列表,将List<Post>对象赋到Blog对象中;
这样的话,在底层最起码调用三次查询语句,请看下列的代码:
- /*
- * 通过blogId获取BlogInfo对象
- */
- public static BlogInfo ordinaryQueryOnTest(String blogId)
- {
- BigDecimal id = new BigDecimal(blogId);
- SqlSession session = sqlSessionFactory.openSession();
- BlogInfo blogInfo = new BlogInfo();
- //1.根据blogid 查询Blog对象,将值设置到blogInfo中
- Blog blog = (Blog)session.selectOne("com.foo.bean.BlogMapper.selectByPrimaryKey",id);
- blogInfo.setBlogId(blog.getBlogId());
- blogInfo.setTitle(blog.getTitle());
- //2.根据Blog中的authorId,进入数据库查询Author信息,将结果设置到blogInfo对象中
- Author author = (Author)session.selectOne("com.foo.bean.AuthorMapper.selectByPrimaryKey",blog.getAuthorId());
- blogInfo.setAuthor(author);
- //3.查询posts对象,设置进blogInfo中
- List posts = session.selectList("com.foo.bean.PostMapper.selectByBlogId",blog.getBlogId());
- blogInfo.setPosts(posts);
- //以JSON字符串的形式将对象打印出来
- JSONObject object = new JSONObject(blogInfo);
- System.out.println(object.toString());
- return blogInfo;
- }
- /*
- * 通过blogId获取BlogInfo对象
- */
- public static BlogInfo ordinaryQueryOnTest(String blogId)
- {
- BigDecimal id = new BigDecimal(blogId);
- SqlSession session = sqlSessionFactory.openSession();
- BlogInfo blogInfo = new BlogInfo();
- //1.根据blogid 查询Blog对象,将值设置到blogInfo中
- Blog blog = (Blog)session.selectOne("com.foo.bean.BlogMapper.selectByPrimaryKey",id);
- blogInfo.setBlogId(blog.getBlogId());
- blogInfo.setTitle(blog.getTitle());
- //2.根据Blog中的authorId,进入数据库查询Author信息,将结果设置到blogInfo对象中
- Author author = (Author)session.selectOne("com.foo.bean.AuthorMapper.selectByPrimaryKey",blog.getAuthorId());
- blogInfo.setAuthor(author);
- //3.查询posts对象,设置进blogInfo中
- List posts = session.selectList("com.foo.bean.PostMapper.selectByBlogId",blog.getBlogId());
- blogInfo.setPosts(posts);
- //以JSON字符串的形式将对象打印出来
- JSONObject object = new JSONObject(blogInfo);
- System.out.println(object.toString());
- return blogInfo;
- }
从上面的代码可以看出,想获取一个BlogInfo对象比较麻烦,总共要调用三次数据库查询,得到需要的信息,然后再组装BlogInfo对象。
mybatis提供了一种机制,叫做嵌套语句查询,可以大大简化上述的操作,加入配置及代码如下:
- <resultMap type="com.foo.bean.BlogInfo" id="BlogInfo">
- <id column="blog_id" property="blogId" />
- <result column="title" property="title" />
- <association property="author" column="blog_author_id"
- javaType="com.foo.bean.Author" select="com.foo.bean.AuthorMapper.selectByPrimaryKey">
- </association>
- <collection property="posts" column="blog_id" ofType="com.foo.bean.Post"
- select="com.foo.bean.PostMapper.selectByBlogId">
- </collection>
- </resultMap>
- <select id="queryBlogInfoById" resultMap="BlogInfo" parameterType="java.math.BigDecimal">
- SELECT
- B.BLOG_ID,
- B.TITLE,
- B.AUTHOR_ID AS BLOG_AUTHOR_ID
- FROM LOULUAN.BLOG B
- where B.BLOG_ID = #{blogId,jdbcType=DECIMAL}
- </select>
- <resultMap type="com.foo.bean.BlogInfo" id="BlogInfo">
- <id column="blog_id" property="blogId" />
- <result column="title" property="title" />
- <association property="author" column="blog_author_id"
- javaType="com.foo.bean.Author" select="com.foo.bean.AuthorMapper.selectByPrimaryKey">
- </association>
- <collection property="posts" column="blog_id" ofType="com.foo.bean.Post"
- select="com.foo.bean.PostMapper.selectByBlogId">
- </collection>
- </resultMap>
- <select id="queryBlogInfoById" resultMap="BlogInfo" parameterType="java.math.BigDecimal">
- SELECT
- B.BLOG_ID,
- B.TITLE,
- B.AUTHOR_ID AS BLOG_AUTHOR_ID
- FROM LOULUAN.BLOG B
- where B.BLOG_ID = #{blogId,jdbcType=DECIMAL}
- </select>
- /*
- * 通过blogId获取BlogInfo对象
- */
- public static BlogInfo nestedQueryOnTest(String blogId)
- {
- BigDecimal id = new BigDecimal(blogId);
- SqlSession session = sqlSessionFactory.openSession();
- BlogInfo blogInfo = new BlogInfo();
- blogInfo = (BlogInfo)session.selectOne("com.foo.bean.BlogMapper.queryBlogInfoById",id);
- JSONObject object = new JSONObject(blogInfo);
- System.out.println(object.toString());
- return blogInfo;
- }
- /*
- * 通过blogId获取BlogInfo对象
- */
- public static BlogInfo nestedQueryOnTest(String blogId)
- {
- BigDecimal id = new BigDecimal(blogId);
- SqlSession session = sqlSessionFactory.openSession();
- BlogInfo blogInfo = new BlogInfo();
- blogInfo = (BlogInfo)session.selectOne("com.foo.bean.BlogMapper.queryBlogInfoById",id);
- JSONObject object = new JSONObject(blogInfo);
- System.out.println(object.toString());
- return blogInfo;
- }
1.先执行 queryBlogInfoById 对应的语句从Blog表里获取到ResultSet结果集;
2.取出ResultSet下一条有效记录,然后根据resultMap定义的映射规格,通过这条记录的数据来构建对应的一个BlogInfo 对象。
4. 对BlogInfo的posts进行赋值时,也有上述类似的过程。
5. 重复2步骤,直至ResultSet. next () == false;
它的弊端也比较明显:即所谓的N+1问题。关联的嵌套查询显示得到一个结果集,然后根据这个结果集的每一条记录进行关联查询。
- <resultMap type="com.foo.bean.BlogInfo" id="BlogInfo">
- <id column="blog_id" property="blogId"/>
- <result column="title" property="title"/>
- <association property="author" column="blog_author_id" javaType="com.foo.bean.Author">
- <id column="author_id" property="authorId"/>
- <result column="user_name" property="userName"/>
- <result column="password" property="password"/>
- <result column="email" property="email"/>
- <result column="biography" property="biography"/>
- </association>
- <collection property="posts" column="blog_post_id" ofType="com.foo.bean.Post">
- <id column="post_id" property="postId"/>
- <result column="blog_id" property="blogId"/>
- <result column="create_time" property="createTime"/>
- <result column="subject" property="subject"/>
- <result column="body" property="body"/>
- <result column="draft" property="draft"/>
- </collection>
- </resultMap>
- <resultMap type="com.foo.bean.BlogInfo" id="BlogInfo">
- <id column="blog_id" property="blogId"/>
- <result column="title" property="title"/>
- <association property="author" column="blog_author_id" javaType="com.foo.bean.Author">
- <id column="author_id" property="authorId"/>
- <result column="user_name" property="userName"/>
- <result column="password" property="password"/>
- <result column="email" property="email"/>
- <result column="biography" property="biography"/>
- </association>
- <collection property="posts" column="blog_post_id" ofType="com.foo.bean.Post">
- <id column="post_id" property="postId"/>
- <result column="blog_id" property="blogId"/>
- <result column="create_time" property="createTime"/>
- <result column="subject" property="subject"/>
- <result column="body" property="body"/>
- <result column="draft" property="draft"/>
- </collection>
- </resultMap>
- <select id="queryAllBlogInfo" resultMap="BlogInfo">
- SELECT
- B.BLOG_ID,
- B.TITLE,
- B.AUTHOR_ID AS BLOG_AUTHOR_ID,
- A.AUTHOR_ID,
- A.USER_NAME,
- A.PASSWORD,
- A.EMAIL,
- A.BIOGRAPHY,
- P.POST_ID,
- P.BLOG_ID AS BLOG_POST_ID ,
- P.CREATE_TIME,
- P.SUBJECT,
- P.BODY,
- P.DRAFT
- FROM BLOG B
- LEFT OUTER JOIN AUTHOR A
- ON B.AUTHOR_ID = A.AUTHOR_ID
- LEFT OUTER JOIN POST P
- ON P.BLOG_ID = B.BLOG_ID
- </select>
- <select id="queryAllBlogInfo" resultMap="BlogInfo">
- SELECT
- B.BLOG_ID,
- B.TITLE,
- B.AUTHOR_ID AS BLOG_AUTHOR_ID,
- A.AUTHOR_ID,
- A.USER_NAME,
- A.PASSWORD,
- A.EMAIL,
- A.BIOGRAPHY,
- P.POST_ID,
- P.BLOG_ID AS BLOG_POST_ID ,
- P.CREATE_TIME,
- P.SUBJECT,
- P.BODY,
- P.DRAFT
- FROM BLOG B
- LEFT OUTER JOIN AUTHOR A
- ON B.AUTHOR_ID = A.AUTHOR_ID
- LEFT OUTER JOIN POST P
- ON P.BLOG_ID = B.BLOG_ID
- </select>
- /*
- * 获取所有Blog的所有信息
- */
- public static BlogInfo nestedResultOnTest()
- {
- SqlSession session = sqlSessionFactory.openSession();
- BlogInfo blogInfo = new BlogInfo();
- blogInfo = (BlogInfo)session.selectOne("com.foo.bean.BlogMapper.queryAllBlogInfo");
- JSONObject object = new JSONObject(blogInfo);
- System.out.println(object.toString());
- return blogInfo;
- }
- /*
- * 获取所有Blog的所有信息
- */
- public static BlogInfo nestedResultOnTest()
- {
- SqlSession session = sqlSessionFactory.openSession();
- BlogInfo blogInfo = new BlogInfo();
- blogInfo = (BlogInfo)session.selectOne("com.foo.bean.BlogMapper.queryAllBlogInfo");
- JSONObject object = new JSONObject(blogInfo);
- System.out.println(object.toString());
- return blogInfo;
- }
2. 根据结果集的信息和BlogInfo 的resultMap定义信息,对返回的结果集在内存中进行组装、赋值,构造BlogInfo;