上一篇文章介紹了基於Mybatis對數據庫的增、刪、改、查。這一篇介紹下關聯查詢(join query)。
三張表:user article blog
表的存儲sql文件:
/* Navicat MySQL Data Transfer Source Server : localhost Source Server Version : 50620 Source Host : localhost:3306 Source Database : mybatis Target Server Type : MYSQL Target Server Version : 50620 File Encoding : 65001 Date: 2014-10-19 18:27:31 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `user` -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userName` varchar(50) DEFAULT NULL, `userAge` int(11) DEFAULT NULL, `userAddress` varchar(200) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES ('1', 'summer', '30', 'shanghai'); INSERT INTO `user` VALUES ('2', 'test1', '22', 'suzhou'); INSERT INTO `user` VALUES ('3', 'test1', '29', 'some place'); INSERT INTO `user` VALUES ('4', 'lu', '28', 'some place'); INSERT INTO `user` VALUES ('5', 'xiaoxun', '27', 'nanjing'); -- ---------------------------- -- Table structure for `article` -- ---------------------------- DROP TABLE IF EXISTS `article`; CREATE TABLE `article` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userid` int(11) NOT NULL, `title` varchar(100) DEFAULT NULL, `content` text, `blogid` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of article -- ---------------------------- INSERT INTO `article` VALUES ('1', '1', 'test_title_1', 'test_content_1', '1'); INSERT INTO `article` VALUES ('2', '1', 'test_title_2', 'test_content_2', '1'); INSERT INTO `article` VALUES ('3', '1', 'test_title_3', 'test_content_3', '2'); INSERT INTO `article` VALUES ('4', '1', 'test_title_4', 'test_content_4', '2'); INSERT INTO `article` VALUES ('5', '2', 'test_title_5', 'test_content_5', '2'); -- ---------------------------- -- Table structure for `blog` -- ---------------------------- DROP TABLE IF EXISTS `blog`; CREATE TABLE `blog` ( `id` int(11) NOT NULL AUTO_INCREMENT, `title` varchar(200) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of blog -- ---------------------------- INSERT INTO `blog` VALUES ('1', 'xiaoxun_blog'); INSERT INTO `blog` VALUES ('2', 'zhang_blog');
配置文件Configuration.xml
復制代碼 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- mybatis別名定義 --> <typeAliases> <typeAlias alias="User" type="com.mybatis.test.User"/> <typeAlias alias="Article" type="com.mybatis.test.Article"/> <typeAlias alias="Blog" type="com.mybatis.test.Blog"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis" /> <property name="username" value="root"/> <property name="password" value="admin"/> </dataSource> </environment> </environments> <!-- mybatis的mapper文件,每個xml配置文件對應一個接口 --> <mappers> <mapper resource="com/mybatis/test/User.xml"/> <mapper resource="com/mybatis/test/Article.xml"/> <mapper resource="com/mybatis/test/Blog.xml"/> </mappers> </configuration>
User類的定義和User.xml的配置見上一文章。
Article類定義:
package com.mybatis.test; public class Article { private int id; private User user; private String title; private String content; public int getId() { return id; } public void setId(int id) { this.id = id; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }
Article類中有一個User類。
Article.xml的配置:
<?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="com.mybatis.test.IArticleOperation"> <resultMap type="User" id="userResultMap"> <!-- 屬性名和數據庫列名映射 --> <id property="id" column="user_id" /> <result property="userName" column="user_userName" /> <result property="userAge" column="user_userAge" /> <result property="userAddress" column="user_userAddress" /> </resultMap> <!-- User join Article進行聯合查詢 (一對一)--> <resultMap id="articleResultMap" type="Article"> <id property="id" column="article_id" /> <result property="title" column="article_title" /> <result property="content" column="article_content" /> <!-- 將article的user屬性映射到userResultMap --> <association property="user" javaType="User" resultMap="userResultMap"/> </resultMap> <!-- 使用別名來映射匹配 --> <select id="getUserArticles" parameterType="int" resultMap="articleResultMap"> select user.id user_id,user.userName user_userName,user.userAddress user_userAddress, article.id article_id,article.title article_title,article.content article_content from user,article where user.id=article.userid and user.id=#{id} </select> <!-- 另一種聯合查詢 (一對一)的實現,但是這種方式有“N+1”的問題 --> <!-- <resultMap id="articleResultMap" type="Article"> <id property="id" column="article_id" /> <result property="title" column="article_title" /> <result property="content" column="article_content" /> <association property="user" javaType="User" column="userid" select="selectUser"/> </resultMap> <select id="selectUser" parameterType="int" resultType="User"> select * from user where id = #{id} </select> --> </mapper>
Blog類定義:
package com.mybatis.test; import java.util.List; public class Blog { private int id; private String title; private List<Article> articles; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public List<Article> getArticles() { return articles; } public void setArticles(List<Article> articles) { this.articles = articles; } }
Blog類中有一個List<Article>。
Blog.xml配置:
<?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="com.mybatis.test.IBlogOperation"> <resultMap id="userResultMap" type="User"> <id property="id" column="user_id" /> <result property="userName" column="user_userName" /> <result property="userAge" column="user_userAge" /> <result property="userAddress" column="user_userAddress" /> </resultMap> <resultMap id="articleResultMap" type="Article"> <id property="id" column="article_id" /> <result property="title" column="article_title" /> <result property="content" column="article_content" /> <association property="user" javaType="User" resultMap="userResultMap"/> </resultMap> <resultMap id="blogResultMap" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title" /> <!-- 將article list屬性映射到collection --> <collection property="articles" ofType="Article" resultMap="articleResultMap"/> </resultMap> <!-- select語句 --> <select id="getBlogByID" parameterType="int" resultMap="blogResultMap"> select user.id user_id,user.userName user_userName,user.userAddress user_userAddress, article.id article_id,article.title article_title,article.content article_content, blog.id blog_id, blog.title blog_title from user,article,blog where user.id=article.userid and blog.id=article.blogid and blog.id=#{id} </select> </mapper>
IArticleOperation定義:
package com.mybatis.test; import java.util.List; public interface IArticleOperation { public List<Article> getUserArticles(int userID); }
IBlogOperation定義:
package com.mybatis.test; public interface IBlogOperation { Blog getBlogByID(int id); }
Test類:
package com.mybatis.test; import java.io.Reader; import java.util.List; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public class Test { private static SqlSessionFactory sqlSessionFactory; private static Reader reader; static { try { //通過配置文件初始化sqlSessionFactory reader = Resources.getResourceAsReader("Configuration.xml"); sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); } catch (Exception e) { e.printStackTrace(); } } public static SqlSessionFactory getSession() { return sqlSessionFactory; } public void getUserByID(int userID) { SqlSession session = sqlSessionFactory.openSession(); try { IUserOperation userOperation = session .getMapper(IUserOperation.class); User user = userOperation.selectUserByID(userID); if (user != null) { System.out.println(user.getId() + ":" + user.getUserName() + ":" + user.getUserAddress()); } } finally { session.close(); } } public void getUserList(String userName) { SqlSession session = sqlSessionFactory.openSession(); try { IUserOperation userOperation = session .getMapper(IUserOperation.class); List<User> users = userOperation.selectUsersByName(userName); for (User user : users) { System.out.println(user.getId() + ":" + user.getUserName() + ":" + user.getUserAddress()); } } finally { session.close(); } } /** * 增加后要commit */ public void addUser() { User user = new User(); user.setUserAddress("place"); user.setUserName("test_add"); user.setUserAge(30); SqlSession session = sqlSessionFactory.openSession(); try { IUserOperation userOperation = session .getMapper(IUserOperation.class); userOperation.addUser(user); session.commit(); System.out.println("新增用戶ID:" + user.getId()); } finally { session.close(); } } /** * 修改后要commit */ public void updateUser() { SqlSession session = sqlSessionFactory.openSession(); try { IUserOperation userOperation = session .getMapper(IUserOperation.class); User user = userOperation.selectUserByID(1); if (user != null) { user.setUserAddress("A new place"); userOperation.updateUser(user); session.commit(); } } finally { session.close(); } } /** * 刪除后要commit. * * @param id */ public void deleteUser(int id) { SqlSession session = sqlSessionFactory.openSession(); try { IUserOperation userOperation = session .getMapper(IUserOperation.class); userOperation.deleteUser(id); session.commit(); } finally { session.close(); } } public void getUserArticles(int userid) { SqlSession session = sqlSessionFactory.openSession(); try { IArticleOperation articleOperation = session .getMapper(IArticleOperation.class); List<Article> articles = articleOperation.getUserArticles(userid); for (Article article : articles) { System.out.println(article.getTitle() + ":" + article.getContent() + "用戶名:" + article.getUser().getUserName() + "用戶地址:" + article.getUser().getUserAddress()); } } finally { session.close(); } } public void getBlogArticles(int blogid) { SqlSession session = sqlSessionFactory.openSession(); try { IBlogOperation blogOperation = session .getMapper(IBlogOperation.class); Blog blog = blogOperation.getBlogByID(blogid); System.out.println(blog.getTitle() + ":"); List<Article> articles = blog.getArticles(); for (Article article : articles) { System.out.println(article.getTitle() + ":" + article.getContent() + "用戶名:" + article.getUser().getUserName() + "用戶地址:" + article.getUser().getUserAddress()); /*System.out.println(article.getTitle() + ":" + article.getContent());*/ } } finally { session.close(); } } public static void main(String[] args) { try { Test test = new Test(); // test.getUserByID(1); // test.getUserList("test1"); // test.addUser(); // test.updateUser(); // test.deleteUser(6); //test.getUserArticles(1); test.getBlogArticles(1); } catch (Exception e) { System.out.println(e.getMessage()); } } }
Mybatis的“N+1查詢問題”
<resultMap id=”blogResult” type=”Blog”> <association property="author" column="blog_author_id" javaType="Author" select=”selectAuthor” /> </resultMap> <select id=”selectBlog” parameterType=”int” resultMap=”blogResult”> SELECT * FROM BLOG WHERE ID = #{id} </select> <select id=”selectAuthor” parameterType=”int” resultType="Author"> SELECT * FROM AUTHOR WHERE ID = #{id} </select>
有兩個查詢語句:一個來加載博客,另外一個來加載作者,而且博客的結果映射描述了“selectAuthor”語句應該被用來加載它的 author 屬性。
其他所有的屬性將會被自動加載,假設它們的列和屬性名相匹配。
這種方式很簡單,但是對於大型數據集合和列表將不會表現很好。問題就是我們熟知的“N+1 查詢問題”。概括地講,N+1 查詢問題可以是這樣引起的:
你執行了一個單獨的 SQL 語句來獲取結果列表(就是“+1”)。
對返回的每條記錄,你執行了一個查詢語句來為每個加載細節(就是“N”)。
這個問題會導致成百上千的 SQL 語句被執行。這通常不是期望的。
可以采用關聯的嵌套結果來解決這個問題:
<resultMap id="blogResult" type="Blog"> <id property=”id” column="blog_id" /> <result property="title" column="blog_title" /> <association property="author" column="blog_author_id" javaType="Author" resultMap=”authorResult” /> </resultMap> <resultMap id="authorResult" type="Author"> <id property="id" column="author_id" /> <result property="username" column="author_username" /> <result property="password" column="author_password" /> <result property="email" column="author_email" /> <result property="bio" column="author_bio" /> </resultMap>
resultMap 這是結果映射的 ID,可以映射關聯的嵌套結果到一個合適的對象圖中。這是一種替代方法來調用另外一個查詢語句。
代碼下載:http://download.csdn.net/detail/luxiaoxun/8056559
參考:
http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html