Mybatis的关联映射之一对多

上一篇讲了一对一,那么现在来讲一对多,这里用到了两张表:

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集合了也就是不会包装结果集了。
当然这只是有意思的地方。。比较还是按规范写比较好

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值