实例讲解Java的MyBatis框架对MySQL中数据的关联查询

网友投稿 209 2023-07-15


实例讲解Java的MyBatis框架对MySQL中数据的关联查询

mybatis 提供了高级的关联查询功能,可以很方便地将数据库获取的结果集映射到定义的java Bean 中。下面通过一个实例,来展示一下Mybatis对于常见的一对多和多对一关系复杂映射是怎样处理的。

设计一个简单的博客系统,一个用户可以开多个博客,在博客中可以发表文章,允许发表评论,可以为文章加标签。博客系统主要有以下几张表构成:

Post表  : 文章记录表,记录文章发表时间,标题,正文等信息;一个博客下可以有很多篇文章,Blog 和Post的关系是一对多。

Comments表:文章评论表,记录文章的评论,一篇文章可以有很多个评论:Post和Comments的对应关系是一对多。

Tag表:标签表,表示文章的标签分类,一篇文章可以有多个标签,而一个标签可以应用到不同的文章上,所以Tag和Post的关系是多对多的关系;(Tag和Post的多对多关系通过Post_Tag表体现)

Post_Tag表: 记录 文章和标签的对应关系。

一般情况下,我们会根据每一张表的结构 创建与此相对应的JavaBean(或者Pojo),来完成对表的基本CRUD操作。

如果想得到这样的类的实例,则最起码要有一下几步:

1. 通过Blog 的id 到Blog表里查询Blog信息,将查询到的blogId 和title 赋到Blog对象内;

2. 根据查询到到blog信息中的authorId 去 Author表获取对应的author信息,获取Author对象,然后赋到Blog对象内;

3. 根据 blogId 去 Post表里查询 对应的 Post文章列表,将List对象赋到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;

}

从上面的代码可以看出,想获取一个BlogInfo对象比较麻烦,总共要调用三次数据库查询,得到需要的信息,然后再组装BlogInfo对象。

嵌套语句查询

mybatis提供了一种机制,叫做嵌套语句查询,可以大大简化上述的操作,加入配置及代码如下:

javaType="com.foo.bean.Author" select="com.foo.bean.AuthorMapper.selectByPrimaryKey">

javaType="com.foo.bean.Author" select="com.foo.bean.AuthorMapper.selectByPrimaryKey">

select="com.foo.bean.PostMapper.selectByBlogId">

select="com.foo.bean.PostMapper.selectByBlogId">

SELECT

B.BLOG_ID,

B.TITLE,

B.AUTHOR_ID AS BLOG_AUTHOR_ID

FROM LOULUAN.BLOG B

where B.BLOG_ID = #{blogId,jdbcType=DECIMAL}

/*

* 通过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;

}

通过上述的代码完全可以实现前面的那个查询。这里我们在代码里只需要 blogInfo = (BlogInfo)session.selectOne("com.foo.bean.BlogMapper.queryBlogInfoById",id);一句即可获取到复杂的blogInfo对象。

嵌套语句查询的原理

在上面的代码中,Mybatis会执行以下流程:

1.先执行 queryBlogInfoById 对应的语句从Blog表里获取到ResultSet结果集;

2.取出ResultSet下一条有效记录,然后根据resultMap定义的映射规格,通过这条记录的数据来构建对应的一个BlogInfo 对象。

3. 当要对BlogInfo中的author属性进行赋值的时候,发现有一个关联的查询,此时Mybatis会先执行这个select查询语句,得到返回的结果,将结果设置到BlogInfo的author属性上;

4. 对BlogInfo的posts进行赋值时,也有上述类似的过程。

5. 重复2步骤,直至ResultSet. next () == false;

以下是blogInfo对象构造赋值过程示意图:

这种关联的嵌套查询,有一个非常好的作用就是:可以重用select语句,通过简单的select语句之间的组合来构造复杂的对象。上面嵌套的两个select语句com.foo.bean.AuthorMapper.selectByPrimaryKey和com.foo.bean.PostMapper.selectByBlogId完全可以独立使用。

N+1问题

它的弊端也比较明显:即所谓的N+1问题。关联的嵌套查询显示得到一个结果集,然后根据这个结果集的每一条记录进行关联查询。

现在假设嵌套查询就一个(即resultMap 内部就一个association标签),现查询的结果集返回条数为N,那么关联查询语句将会被执行N次,加上自身返回结果集查询1次,共需要访问数据库N+1次。如果N比较大的话,这样的数据库访问消耗是非常大的!所以使用这种嵌套语句查询的使用者一定要考虑慎重考虑,确保N值不会很大。

以上面的例子为例,select 语句本身会返回com.foo.bean.BlogMapper.queryBlogInfoById 条数为1 的结果集,由于它有两条关联的语句查询,它需要共访问数据库 1*(1+1)=3次数据库。

嵌套结果查询

嵌套语句的查询会导致数据库访问次数不定,进而有可能影响到性能。Mybatis还支持一种嵌套结果的查询:即对于一对多,多对多,多对一的情况的查询,Mybatis通过联合查询,将结果从数据库内一次性查出来,然后根据其一对多,多对一,多对多的关系和ResultMap中的配置,进行结果的转换,构建需要的对象。

重新定义BlogInfo的结果映射 resultMap

对应的sql语句如下:

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

/*

* 获取所有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;

}

嵌套结果查询的执行步骤:

1.根据表的对应关系,进行join操作,获取到结果集;

2. 根据结果集的信息和BlogInfo 的resultMap定义信息,对返回的结果集在内存中进行组装、赋值,构造BlogInfo;

3. 返回构造出来的结果List 结果。

对于关联的结果查询,如果是多对一的关系,则通过形如 进行配置,Mybatis会通过column属性对应的author_id 值去从内存中取数据,并且封装成Author对象;

如果是一对多的关系,就如Blog和Post之间的关系,通过形如 进行配置,MyBatis通过 blog_Id去内存中取Post对象,封装成List

对于关联结果的查询,只需要查询数据库一次,然后对结果的整合和组装全部放在了内存中。

以上是通过查询Blog所有信息来演示了一对多和多对一的映射对象处理。

ps:自身关联映射示例:

实体类

public class Module {

private int id;

private String key;

private String name;

private Module parentModule;

private List childrenModules;

private String url;

private int sort;

private String show;

private String del;

public int getId() {

return id;

}

IpMqEvxrpublic void setId(int id) {

this.id = id;

}

public String getKey() {

return key;

}

public void setKey(String key) {

this.key = key;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Module getParentModule() {

return parentModule;

}

public void setParentModule(Module parentModule) {

this.parentModule = parentModule;

}

public String getUrl() {

return url;

}

public void setUrl(String url) {

this.url = url;

http:// }

public int getSort() {

return sort;

}

public void setSort(int sort) {

this.sort = sort;

}

public String getShow() {

return show;

}

public void setShow(String show) {

this.show = show;

}

public String getDel() {

return del;

}

public void setDel(String del) {

this.del = del;

}

public List getChildrenModules() {

return childrenModules;

}

public void setChildrenModules(List childrenModules) {

this.childrenModules = childrenModules;

}

}

XML代码:

select * from tb_module where module_id=2

select * from tb_module where module_id = #{module_id}

select * from tb_module where module_parent_id = #{module_id}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:深入理解node exports和module.exports区别
下一篇:Bootstrap 折叠(Collapse)插件用法实例详解
相关文章

 发表评论

暂时没有评论,来抢沙发吧~