上一篇讲了一对一,那么现在来讲一对多,这里用到了两张表:
CREATE TABLE `article` (
`article_id` int(11) NOT NULL AUTO_INCREMENT,
`date` datetime DEFAULT NULL,
`content` text COLLATE utf8_bin,
`blog_id` int(11) DEFAULT NULL,
PRIMARY KEY (`article_id`),
KEY `FK_article` (`blog_id`),
CONSTRAINT `FK_article` FOREIGN KEY (`blog_id`) REFERENCES `blog` (`blog_id`)
)
CREATE TABLE `blog` (
`blog_id` int(11) NOT NULL,
`blog_name` varchar(20) COLLATE utf8_bin DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
PRIMARY KEY (`blog_id`),
KEY `FK_blog` (`user_id`),
CONSTRAINT `FK_blog` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
)
按关系即:一个博客可以发布多篇文章,而一片文章只能属于一个博客,OK现在看看转换到POJO对象的样子:
文章:
public class Article {
private int id;
private Date date;
private String content;
public Article() {
super();
// TODO Auto-generated constructor stub
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "Article [id=" + id + ", date=" + date + ", content=" + content + "]";
}
}
博客:
public class Blog {
private int id;
private String blogName;
private List<Article> articles;
public Blog() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getBlogName() {
return blogName;
}
public void setBlogName(String blogName) {
this.blogName = blogName;
}
public List<Article> getArticles() {
return articles;
}
public void setArticles(List<Article> articles) {
this.articles = articles;
}
@Override
public String toString() {
return "Blog [id=" + id + ", blogName=" + blogName + ", articles=" + articles + "]";
}
映射文件:
<?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="dao.BlogMapper">
<resultMap type="bean.Blog" id="BlogMap">
<id property="id" column="blog_id"/>
<result property="blogName" column="blog_name"/>
<collection property="articles" ofType="Article">
<id property="id" column="article_id"/>
<result property="date" column="date"/>
<result property="content" column="content"/>
</collection>
</resultMap>
<select id="getBlogInfByBlogId" parameterType="bean.Blog" resultMap="BlogMap">
SELECT blog.blog_id,blog_name,article_id,DATE,content
FROM blog INNER JOIN article ON blog.blog_id=article.blog_id
WHERE blog.blog_id=#{id}
</select>
</mapper>
在这里用到了collection标签即集合标签可以通过该标签管理博客和文章集合,但既然是集合那么就必须声明集合里对象的类型这里便用到了collection中的ofType属性。其实就结果而言collection与association的很相近了。OK看看结果:
articles的集合也正确的关联到了。
既然前面说了collection与association相似那么还可以这样写:
<?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="dao.BlogMapper">
<resultMap type="bean.Blog" id="BlogMap">
<id property="id" column="blog_id"/>
<result property="blogName" column="blog_name"/>
<collection property="articles" ofType="Article" resultMap="ArticleMap"/>
</resultMap>
<resultMap type="Article" id="ArticleMap">
<id property="id" column="article_id"/>
<id property="date" column="date"/>
<id property="content" column="content"/>
</resultMap>
<select id="getBlogInfByBlogId" parameterType="bean.Blog" resultMap="BlogMap">
SELECT blog.blog_id,blog_name,article_id,DATE,content
FROM blog INNER JOIN article ON blog.blog_id=article.blog_id
WHERE blog.blog_id=#{id}
</select>
</mapper>
最后还是那个不推荐的方法:
<?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="dao.BlogMapper">
<resultMap type="bean.Blog" id="BlogMap">
<id property="id" column="blog_id"/>
<result property="blogName" column="blog_name"/>
<collection property="articles" ofType="Article" select="getArticleById" column="blog_id"/>
</resultMap>
<resultMap type="Article" id="ArticleMap">
<id property="id" column="article_id"/>
<id property="date" column="date"/>
<id property="content" column="content"/>
</resultMap>
<select id="getArticleById" parameterType="int" resultMap="ArticleMap">
select article_id,date,content from article where article.blog_id = #{id}
</select>
<select id="getBlogInfByBlogId" parameterType="bean.Blog" resultMap="BlogMap">
SELECT blog.blog_id,blog_name
FROM blog WHERE blog.blog_id=#{id}
</select>
</mapper>
这个依然存在N+1的问题
不过在这之中也有一个很有意思的事情那就是,association时对于结果集也可以识别出集合并包装:
<?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="dao.BlogMapper">
<resultMap type="bean.Blog" id="BlogMap">
<id property="id" column="blog_id"/>
<result property="blogName" column="blog_name"/>
<association property="articles" select="getArticleById" column="blog_id"/>
</resultMap>
<resultMap type="Article" id="ArticleMap">
<id property="id" column="article_id"/>
<id property="date" column="date"/>
<id property="content" column="content"/>
</resultMap>
<select id="getArticleById" parameterType="int" resultMap="ArticleMap">
select article_id,date,content from article where article.blog_id = #{id}
</select>
<select id="getBlogInfByBlogId" parameterType="bean.Blog" resultMap="BlogMap">
SELECT blog.blog_id,blog_name
FROM blog WHERE blog.blog_id=#{id}
</select>
</mapper>
这种情况下也可以:
<?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="dao.BlogMapper">
<resultMap type="bean.Blog" id="BlogMap">
<id property="id" column="blog_id"/>
<result property="blogName" column="blog_name"/>
<association property="articles" resultMap="ArticleMap"/>
</resultMap>
<resultMap type="Article" id="ArticleMap">
<id property="id" column="article_id"/>
<id property="date" column="date"/>
<id property="content" column="content"/>
</resultMap>
<select id="getBlogInfByBlogId" parameterType="bean.Blog" resultMap="BlogMap">
SELECT blog.blog_id,blog_name,article_id,DATE,content
FROM blog INNER JOIN article ON blog.blog_id=article.blog_id
WHERE blog.blog_id=#{id}
</select>
</mapper>
但是这种情况就不行:
<?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="dao.BlogMapper">
<resultMap type="bean.Blog" id="BlogMap">
<id property="id" column="blog_id"/>
<result property="blogName" column="blog_name" />
<association property="articles" javaType="Article">
<id property="id" column="article_id"/>
<result property="date" column="date"/>
<result property="content" column="content"/>
</association>
</resultMap>
<select id="getBlogInfByBlogId" parameterType="bean.Blog" resultMap="BlogMap">
SELECT blog.blog_id,blog_name,article_id,DATE,content
FROM blog INNER JOIN article ON blog.blog_id=article.blog_id
WHERE blog.blog_id=#{id}
</select>
</mapper>
报错:
Could not set property 'articles' of 'class bean.Blog' with value 'Article [id=1, date=Mon Jan 01 00:00:00 CST 2018, content=123]' Cause: java.lang.IllegalArgumentException: argument type mismatch
这就有意思了。不过因为与源码相关自己看的不够深就暂时不讨论,不过可以猜测第一种方式反复调用select的方式mybatis会根据结果集判断出是一个集合所以会包装然后调用反射调用setArticles方法,第二种方式利用了resultMap的复用形式mybatis依然可以识别出结果集。最后一种在使用反射调用了方法setArticles,而配置文件声明的时Arcticle类,在反射出的方法是要求是一个List集合,但是传入却是Article对象所以会报错(PS:mybati有一个特点是:如果没有set方法会直接用反射操作实例域赋值,但这种情况下依然会报错)。因此我们可以尝试用改写一下articles的set方法:
原:
public void setArticles(List<Article> articles) {
this.articles = articles;
}
修改后
public void setArticles(Article articles) {
System.out.println("执行了set方法:"+articles);
}
运行后:
可以看到mybatis不会再生成一个list集合了也就是不会包装结果集了。
当然这只是有意思的地方。。比较还是按规范写比较好