在云笔记学习过程中,又简单的介绍了MyBatis关联映射查询,以发帖作为例子,开始了对它的学习,不过本文只是查询,不涉及其他类型的数据库操作。由于查询帖子不是简单查询,简单的一一映射将不能满足需求,因此涉及到了复杂查询,需要使用关联查询来解决问题,以下将从数据库结构,配置文件等进行简单说明。
数据库结构
需要建立三张表,P_PERSON,P_POST,P_COMMENT,其中P_PERSON用于保存发帖人的基本信息,P_POST保存发帖内容,并且字段person_id作为外键关联P_PERSON的主键id,最后P_COMMENT用于保存回帖内容,其中字段post_id作为外键关联P_POST的主键id。以下是在Navicat中,三张表的关系图。
建表&数据插入准备
使用Eclipse在云笔记数据库中建立了以上三张表,以下是具体代码:
(1)建立P_PERSON
1 --MyBatis数据自增,MySQL中使用AUTO_INCREMENT,ORACLE中使用SEQUENCE 2 CREATE TABLE P_PERSON( 3 id int not null AUTO_INCREMENT, 4 name VARCHAR(100), 5 PRIMARY KEY(id) 6 );
(2)建立P_POST
1 --为了学习MyBatis关系映射,再建立P_POST表和P_COMMENT表,其中P_POST表保存用户发的帖子,P_COMMENT表保存用户回复的帖子 2 CREATE TABLE P_POST( 3 id int not null AUTO_INCREMENT, 4 title VARCHAR(100), 5 person_id int, 6 PRIMARY KEY(id) 7 );
(3)建立P_COMMENT
1 CREATE TABLE P_COMMENT( 2 id int not null AUTO_INCREMENT, 3 title VARCHAR(100), 4 post_id int, 5 PRIMARY KEY(id) 6 );
(4)建表完成后创建约束,关联表
--使用P_POST的person_id作为外键,关联P_PERSON的主键id,将两张表关联起来 ALTER TABLE P_POST ADD CONSTRAINT FK_ID FOREIGN KEY(person_id) REFERENCES P_PERSON(id); --类似的,关联P_POST和P_COMMENT ALTER TABLE P_COMMENT ADD CONSTRAINT FK_ID_COMMENT FOREIGN KEY(post_id) REFERENCES P_POST(id);
创建数据表对应实体类+DAO接口
需要建立三个实体类,用于保存数据表对应信息,其中POST实体类不仅仅跟表信息一一对应,还包含person和comments属性,因此需要用到MyBatis关联映射查询。
Person类,对应P_PERSON
package com.boe.Entity; import java.io.Serializable; /** * 验证MyBatis返回自增类型,数据表对应实体类
*/ public class Person implements Serializable{ private static final long serialVersionUID = 4185328190674358099L; Integer id; //使用包装类,因为数据库中id有null的可能,包装类也可以为null,两者可以一一对应,如果使用基本数据类型将无法表示null String name; //如果查询的结果不完整,使用此构造方法初始化 public Person() { super(); }
public Person(Integer id, String name) { super(); this.id = id; this.name = name; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + "]"; } ...省略Get Set方法 }
Post实体类,对应表P_POST
package com.boe.Entity; import java.io.Serializable; import java.util.ArrayList; import java.util.List; public class Post implements Serializable{ private static final long serialVersionUID = -2929218418809381846L; Integer id; String title;//帖子内容 //添加人和评论 Person person; List<Comment> comments=new ArrayList<Comment>();//实体内部有集合属性,集合最好初始化,防止空指针异常 @Override public String toString() { return "Post [id=" + id + ", title=" + title + ", person=" + person + ", comments=" + comments + "]"; } //如果查询不到person和comment,就调用此构造方法 public Post() { super(); } public Post(Integer id, String title, Person person, List<Comment> comments) { super(); this.id = id; this.title = title; this.person = person; this.comments = comments; } ...省略Get Set方法 }
实体类Comment对应数据表P_COMMENT
package com.boe.Entity; import java.io.Serializable; public class Comment implements Serializable{ private static final long serialVersionUID = -3407225418917489641L; Integer id; String title;//评论内容 Integer post_id; public Comment(Integer id, String title, Integer post_id) { super(); this.id = id; this.title = title; this.post_id = post_id; } @Override public String toString() { return "Comment [id=" + id + ", title=" + title + ", post_id=" + post_id + "]"; } ...省略Get Set方法 }
DAO接口的话,需要建立一个方法,根据id来查找帖子,帖子内容包含帖子id,帖子内容title,发帖人person和评论内容comments。
1 package com.boe.Dao; 2 3 import org.springframework.stereotype.Repository; 4 5 import com.boe.Entity.Post; 6 7 @Repository("postDAO") 8 public interface PostDAO { 9 10 //public int addPost(Post post); 11 12 public Post findPostById(Integer id); 13 14 }
Mapper文件中创建复杂映射关系
刚开始只查询P_POST表中的字段,但是只有id和title可以直接映射到实体类,person和comments无从得知,但是三张表之间有关联,可以通过查询到的person_id,关联查询PERSON表中的对应信息,通过查询到的id,关联查询P_COMMENT中的title,这样目的就达成了。
其中映射person属性时,其为实体类属性,因此使用association标签,而映射comments属性时,由于其为集合类型属性,因此使用collection标签。
最后查询comment时,再创建了一个单独的查询,其中传入的参数为P_POST表的主键id,不是其他表的,最后查询结果映射到comments属性。
<?xml version="1.0" en评论coding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.boe.Dao.PostDAO"> <!-- 以下查询方法查询不到person,和comment --> <!-- <select id="findPostById" parameterType="int" resultType="com.boe.Entity.Post"> select id, title, person_id from p_post where id=#{id} </select> --> <!-- 如果需要查询得到person和comment,需要使用resultMap,进行处理 --> <select id="findPostById" parameterType="int" resultMap="postMap"> select p_post.id, title, person_id, p_person.name from p_post left outer join p_person on p_person.id=person_id where p_post.id=#{id} </select> <resultMap type="com.boe.Entity.Post" id="postMap"> <!-- 逐一映射每个属性 --> <!-- 主键采用id映射,其他采用result映射 --> <id column="id" property="id"></id> <result column="title" property="title"></result> <!-- 映射person属性,由于person是个实体类属性,因此需要用association标签 --> <association property="person" javaType="com.boe.Entity.Person"> <id column="person_id" property="id"></id> <result column="name" property="name"></result> </association> <!-- 映射comments属性,由于comments是一个集合,因此需要使用collection标签 --> <collection property="comments" column="id" select="findCommentsByPostId"> </collection> </resultMap> <!-- 查询comments,单独写一个查询 --> <select id="findCommentsByPostId" parameterType="int" resultType="com.boe.Entity.Comment"> select id, title, post_id as postId from p_comment where post_id=#{id} </select> </mapper>
测试
基于云笔记项目中的测试类,建立单独的测试,测试结果能正常返回帖子详细信息。
1 package Test; 2 3 import org.junit.Before; 4 import org.junit.Test; 5 6 import com.boe.Dao.PostDAO; 7 import com.boe.Entity.Post; 8 9 public class testPost extends baseTest{ 10 11 PostDAO dao; 12 13 @Before 14 public void initTestPost() { 15 dao=ac.getBean("postDAO",PostDAO.class); 16 } 17 18 19 //通过post id来查找帖子 20 @Test 21 public void testFindPost() { 22 Post post=dao.findPostById(1); 23 System.out.println(post); 24 } 25 26 }
测试结果展示:
结论
(1)当查询结果包含实体类和集合时,简单的一一映射不能满足要求,需要使用MyBatis关联查询,返回类型需要使用resultMap,并在resultMap里逐一配置,映射每个属性。
(2)如果需要映射实体类属性,使用association标签,需要映射集合类型属性,使用collection标签。