转载请注明: TheViper http://www.cnblogs.com/TheViper html
一对多的关系,好比文章和评论,这时须要展现文章和评论,并按文章分组。不少时候,页面只显示评论数,若是用户要看具体评论,就要点击评论数的连接。java
好比微博sql
知乎mongodb
若是必定要在展现文章(或微博)时,显示前几条评论该怎么作呢?(这里不讨论mongodb)数据库
一个常见的作法是对一方,外链接多方,一并取出,而后手工遍历分组。apache
这里仍是以文章评论和回复为例mybatis
评论表app
回复表post
两个表的id字段分别表示评论的用户id和回复的用户id,articlereplay表的comment_id是外键.另外还有个user表(id,name)。ui
外链接
SELECT articlecomment.articlecomment_id,articlecomment.commenttime,articlecomment.commentcontent,articlecomment.id as commentuser_id ,articlereply.articlereply_id,articlereply.replytime,articlereply.replycontent,articlereply.id as replyuser_id ,u1.name AS comment_user,u2.name AS reply_user FROM articlecomment LEFT JOIN articlereply ON articlereply.comment_id=articlecomment.articlecomment_id INNER JOIN USER AS u1 ON u1.id=articlecomment.id INNER JOIN USER AS u2 ON u2.id=articlereply.id ORDER BY commenttime DESC,replytime DESC
实际上这里不用写代码遍历结果,进行分组。用mybatis的resultmap就能够很优雅的帮咱们按评论分好组。
ArticleComment类
public class ArticleComment { private int articlecomment_id; private String commentcontent; private String commenttime; private User user; private Article article; private List<ArticleReply> replys; //getter,setter }
ArticleReply类
public class ArticleReply { private int articlereply_id; private String replycontent; private String replytime; private User user; //setter,getter }
resultmap
<resultMap id="ArticleCommentResult" type="ArticleComment"> <id property="articlecomment_id" column="articlecomment_id" /> <association property="user" javaType="User"> <id property="id" column="commentuser_id"/> <result property="name" column="comment_user"/> </association> <collection property="replys" ofType="ArticleReply"> <association property="user" javaType="User"> <id property="id" column="replyuser_id" /> <result property="name" column="reply_user"/> </association> </collection> </resultMap>
从“一”(articlecomment)那边取"多"(articlereply).注意,<association>要放到<collection>前面,不然报错。
org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: org.xml.sax.SAXParseException; lineNumber: 16; columnNumber: 14; 元素类型为 "resultMap" 的内容必须匹配 "(constructor?,id*,result*,association*,collection*,discriminator?)"。
pojo的类属性名尽可能和数据库相应表的字段名一致,参见本屌的文章mybatis3 autoMappingBehavior。
另外这里的两个表都内链接User表获取用户名,所以在sql中分别重命名为comment_user,reply_user.id也同样。这里重命名为commentuser_id,replyuser_id.这就须要用<result>调整,使被重命名的字段和pojo类属性名相对应。
效果
问题又来了,上面是把回复所有选出来了,实际需求多是只选取离当前时间最近的5条回复,若是用户想看到更多回复,就须要点“加载更多”。这又该怎么作?
参见本屌翻译的(译)如何在sql中选取每一组的第一行/最后行/前几行。
SELECT articlecomment.*,articlereply1.* FROM articlecomment LEFT JOIN (SELECT * FROM articlereply WHERE ( SELECT COUNT(*) FROM articlereply AS f WHERE f.comment_id = articlereply.comment_id AND f.replytime >= articlereply.replytime ) <= 2 ) AS articlereply1 ON articlereply1.comment_id=articlecomment.articlecomment_id ORDER BY articlereply1.replytime DESC
left join一个子查询,从articlereply表中选出每组离当前世界最近的2条回复,其余的和前面的外链接查询同样。
另外,若是用(译)如何在sql中选取每一组的第一行/最后行/前几行里面提到的user variables方法,实际上就是把上面sql里面left join后articlereply子查询写成user variables形式。但这就有个问题,每组的row_number会依次递增,而咱们须要的是order by replytime desc,和row_number的顺序相反.
结果
上面查询没有对row_number进行过滤,这时若是WHERE x.row_number<=2,获得的结果与咱们想要的相反。
这时很容易想到把row_number的顺序反过来,不过本屌不知道怎么修改if(@type = type, @num + 1, 1)。没办法只有用子查询了。
SELECT * FROM ( SELECT articlereply.*, @num := IF(@type =comment_id, @num + 1, 1) AS row_number, @type := comment_id AS dummy FROM articlereply ) AS X WHERE x.row_number>=(SELECT COUNT(*) FROM articlereply WHERE articlereply.comment_id=x.comment_id GROUP BY articlereply.comment_id)-1
结果