Mybatis實(shí)戰(zhàn)教程之入門到精通(經(jīng)典)
什么是mybatis
MyBatis是支持普通SQL查詢,存儲(chǔ)過(guò)程和高級(jí)映射的優(yōu)秀持久層框架。MyBatis消除了幾乎所有的JDBC代碼和參數(shù)的手工設(shè)置以及結(jié)果集的檢索。MyBatis使用簡(jiǎn)單的XML或注解用于配置和原始映射,將接口和Java的POJOs(Plan Old Java Objects,普通的Java對(duì)象)映射成數(shù)據(jù)庫(kù)中的記錄.
orm工具的基本思想
無(wú)論是用過(guò)的hibernate,mybatis,你都可以法相他們有一個(gè)共同點(diǎn):
1. 從配置文件(通常是XML配置文件中)得到 sessionfactory.
2. 由sessionfactory 產(chǎn)生 session
3. 在session 中完成對(duì)數(shù)據(jù)的增刪改查和事務(wù)提交等.
4. 在用完之后關(guān)閉session 。
5. 在java 對(duì)象和 數(shù)據(jù)庫(kù)之間有做mapping 的配置文件,也通常是xml 文件。
mybatis實(shí)戰(zhàn)教程(mybatis in action)之一:開發(fā)環(huán)境搭建
mybatis 的開發(fā)環(huán)境搭建,選擇: eclipse j2ee 版本,mysql 5.1 ,jdk 1.7,mybatis3.2.0.jar包。這些軟件工具均可以到各自的官方網(wǎng)站上下載。
首先建立一個(gè)名字為 MyBaits 的 dynamic web project
1. 現(xiàn)階段,你可以直接建立java 工程,但一般都是開發(fā)web項(xiàng)目,這個(gè)系列教程最后也是web的,所以一開始就建立web工程。
2. 將 mybatis-3.2.0-SNAPSHOT.jar,mysql-connector-java-5.1.22-bin.jar 拷貝到 web工程的lib目錄.
3. 創(chuàng)建mysql 測(cè)試數(shù)據(jù)庫(kù)和用戶表,注意,這里采用的是 utf-8 編碼
創(chuàng)建用戶表,并插入一條測(cè)試數(shù)據(jù)
程序代碼
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=2 DEFAULT CHARSET=utf8; Insert INTO `user` VALUES ('1', 'summer', '100', 'shanghai,pudong');
到此為止,前期準(zhǔn)備工作就完成了。下面開始真正配置mybatis項(xiàng)目了。
1. 在MyBatis 里面創(chuàng)建兩個(gè)源碼目錄,分別為 src_user,test_src, 用如下方式建立,鼠標(biāo)右鍵點(diǎn)擊 JavaResource.
2. 設(shè)置mybatis 配置文件:Configuration.xml, 在src_user目錄下建立此文件,內(nèi)容如下:
程序代碼
< ?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> <typeAliases> <typeAlias alias="User" type="com.yihaomen.mybatis.model.User"/> </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="password"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/yihaomen/mybatis/model/User.xml"/> </mappers> < /configuration>
3. 建立與數(shù)據(jù)庫(kù)對(duì)應(yīng)的 java class,以及映射文件.
在src_user下建立package:com.yihaomen.mybatis.model ,并在這個(gè) package 下建立 User 類:
程序代碼
package com.yihaomen.mybatis.model; public class User { private int id; private String userName; private String userAge; private String userAddress; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserAge() { return userAge; } public void setUserAge(String userAge) { this.userAge = userAge; } public String getUserAddress() { return userAddress; } public void setUserAddress(String userAddress) { this.userAddress = userAddress; } }
同時(shí)建立這個(gè)User 的映射文件 User.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.yihaomen.mybatis.models.UserMapper"> <select id="selectUserByID" parameterType="int" resultType="User"> select * from `user` where id = #{id} </select> < /mapper>
下面對(duì)這幾個(gè)配置文件解釋下:
1.Configuration.xml 是 mybatis 用來(lái)建立 sessionFactory 用的,里面主要包含了數(shù)據(jù)庫(kù)連接相關(guān)東西,還有 java 類所對(duì)應(yīng)的別名,比如 <typeAlias alias="User" type="com.yihaomen.mybatis.model.User"/> 這個(gè)別名非常重要,你在 具體的類的映射中,比如User.xml 中 resultType 就是對(duì)應(yīng)這里的。要保持一致,當(dāng)然這里的 resultType 還有另外單獨(dú)的定義方式,后面再說(shuō)。
2. Configuration.xml 里面 的<mapper resource="com/yihaomen/mybatis/model/User.xml"/>是包含要映射的類的xml配置文件。
3. 在User.xml 文件里面 主要是定義各種SQL 語(yǔ)句,以及這些語(yǔ)句的參數(shù),以及要返回的類型等.
開始測(cè)試
在test_src 源碼目錄下建立com.yihaomen.test這個(gè)package,并建立測(cè)試類Test:
程序代碼
package com.yihaomen.test; import java.io.Reader; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import com.yihaomen.mybatis.model.User; public class Test { private static SqlSessionFactory sqlSessionFactory; private static Reader reader; static{ try{ reader = Resources.getResourceAsReader("Configuration.xml"); sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); }catch(Exception e){ e.printStackTrace(); } } public static SqlSessionFactory getSession(){ return sqlSessionFactory; } public static void main(String[] args) { SqlSession session = sqlSessionFactory.openSession(); try { User user = (User) session.selectOne("com.yihaomen.mybatis.models.UserMapper.selectUserByID", 1); System.out.println(user.getUserAddress()); System.out.println(user.getUserName()); } finally { session.close(); } } }
現(xiàn)在運(yùn)行這個(gè)程序,是不是得到查詢結(jié)果了。恭喜你,環(huán)境搭建配置成功,接下來(lái)第二章,將講述基于接口的操作方式,增刪改查。
整個(gè)工程目錄結(jié)構(gòu)如下:
mybatis實(shí)戰(zhàn)教程(mybatis in action)之二:以接口的方式編程
前面一章,已經(jīng)搭建好了eclipse,mybatis,mysql的環(huán)境,并且實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的查詢。請(qǐng)注意,這種方式是用SqlSession實(shí)例來(lái)直接執(zhí)行已映射的SQL語(yǔ)句:
session.selectOne("com.yihaomen.mybatis.models.UserMapper.selectUserByID", 1)
其實(shí)還有更簡(jiǎn)單的方法,而且是更好的方法,使用合理描述參數(shù)和SQL語(yǔ)句返回值的接口(比如IUserOperation.class),這樣現(xiàn)在就可以至此那個(gè)更簡(jiǎn)單,更安全的代碼,沒(méi)有容易發(fā)生的字符串文字和轉(zhuǎn)換的錯(cuò)誤.下面是詳細(xì)過(guò)程:
在src_user源碼目錄下建立 com.yihaomen.mybatis.inter 這個(gè)包,并建立接口類 IUserOperation , 內(nèi)容如下:
程序代碼
package com.yihaomen.mybatis.inter; import com.yihaomen.mybatis.model.User; public interface IUserOperation { public User selectUserByID(int id); }
請(qǐng)注意,這里面有一個(gè)方法名 selectUserByID 必須與 User.xml 里面配置的 select 的id 對(duì)應(yīng)(<select id="selectUserByID")
重寫測(cè)試代碼
public static void main(String[] args) { SqlSession session = sqlSessionFactory.openSession(); try { IUserOperation userOperation=session.getMapper(IUserOperation.class); User user = userOperation.selectUserByID(1); System.out.println(user.getUserAddress()); System.out.println(user.getUserName()); } finally { session.close(); } }
整個(gè)工程結(jié)構(gòu)圖現(xiàn)在如下:
運(yùn)行這個(gè)測(cè)試程序,就可以看到結(jié)果了。
mybatis實(shí)戰(zhàn)教程(mybatis in action)之三:實(shí)現(xiàn)數(shù)據(jù)的增刪改查
前面已經(jīng)講到用接口的方式編程。這種方式,要注意的一個(gè)地方就是。在User.xml 的配置文件中,mapper namespace="com.yihaomen.mybatis.inter.IUserOperation" ,命名空間非常重要,不能有錯(cuò),必須與我們定義的package 和 接口一致。如果不一致就會(huì)出錯(cuò),這一章主要在上一講基于接口編程的基礎(chǔ)上完成如下事情:
1. 用 mybatis 查詢數(shù)據(jù),包括列表
2. 用 mybatis 增加數(shù)據(jù)
3. 用 mybatis 更新數(shù)據(jù).
4. 用 mybatis 刪除數(shù)據(jù).
查詢數(shù)據(jù),前面已經(jīng)講過(guò)簡(jiǎn)單的,主要看查詢出列表的
查詢出列表,也就是返回list, 在我們這個(gè)例子中也就是 List<User> , 這種方式返回?cái)?shù)據(jù),需要在User.xml 里面配置返回的類型 resultMap, 注意不是 resultType, 而這個(gè)resultMap 所對(duì)應(yīng)的應(yīng)該是我們自己配置的
程序代碼
< !-- 為了返回list 類型而定義的returnMap --> <resultMap type="User" id="resultListUser"> <id column="id" property="id" /> <result column="userName" property="userName" /> <result column="userAge" property="userAge" /> <result column="userAddress" property="userAddress" /> </resultMap>
查詢列表的語(yǔ)句在 User.xml
程序代碼
< !-- 返回list 的select 語(yǔ)句,注意 resultMap 的值是指向前面定義好的 --> <select id="selectUsers" parameterType="string" resultMap="resultListUser"> select * from user where userName like #{userName} </select>
在 IUserOperation 接口中增加方法:public List<User> selectUsers(String userName);
現(xiàn)在在 Test 類中做測(cè)試
程序代碼
public void getUserList(String userName){ SqlSession session = sqlSessionFactory.openSession(); try { IUserOperation userOperation=session.getMapper(IUserOperation.class); List<User> users = userOperation.selectUsers(userName); for(User user:users){ System.out.println(user.getId()+":"+user.getUserName()+":"+user.getUserAddress()); } } finally { session.close(); } }
現(xiàn)在在main 方法中可以測(cè)試:
程序代碼
public static void main(String[] args) { Test testUser=new Test(); testUser.getUserList("%"); }
可以看到,結(jié)果成功查詢出來(lái)。如果是查詢單個(gè)數(shù)據(jù)的話,用第二講用過(guò)的方法就可以了。
用mybatis 增加數(shù)據(jù)
在 IUserOperation 接口中增加方法:public void addUser(User user);
在 User.xml 中配置
程序代碼
< !--執(zhí)行增加操作的SQL語(yǔ)句。id和parameterType 分別與IUserOperation接口中的addUser方法的名字和 參數(shù)類型一致。以#{name}的形式引用Student參數(shù) 的name屬性,MyBatis將使用反射讀取Student參數(shù) 的此屬性。#{name}中name大小寫敏感。引用其他 的gender等屬性與此一致。seGeneratedKeys設(shè)置 為"true"表明要MyBatis獲取由數(shù)據(jù)庫(kù)自動(dòng)生成的主 鍵;keyProperty="id"指定把獲取到的主鍵值注入 到Student的id屬性--> <insert id="addUser" parameterType="User" useGeneratedKeys="true" keyProperty="id"> insert into user(userName,userAge,userAddress) values(#{userName},#{userAge},#{userAddress}) </insert>
然后在 Test 中寫測(cè)試方法:
程序代碼
/** * 測(cè)試增加,增加后,必須提交事務(wù),否則不會(huì)寫入到數(shù)據(jù)庫(kù). */ public void addUser(){ User user=new User(); user.setUserAddress("人民廣場(chǎng)"); user.setUserName("飛鳥"); user.setUserAge(80); SqlSession session = sqlSessionFactory.openSession(); try { IUserOperation userOperation=session.getMapper(IUserOperation.class); userOperation.addUser(user); session.commit(); System.out.println("當(dāng)前增加的用戶 id為:"+user.getId()); } finally { session.close(); } }
用mybatis 更新數(shù)據(jù)
方法類似,先在 IUserOperation 中增加方法:public void addUser(User user);
然后配置 User.xml
程序代碼
<update id="updateUser" parameterType="User" > update user set userName=#{userName},userAge=#{userAge},userAddress=#{userAddress} where id=#{id} </update>
Test 類總的測(cè)試方法如下:
程序代碼
public void updateUser(){ //先得到用戶,然后修改,提交。 SqlSession session = sqlSessionFactory.openSession(); try { IUserOperation userOperation=session.getMapper(IUserOperation.class); User user = userOperation.selectUserByID(4); user.setUserAddress("原來(lái)是魔都的浦東創(chuàng)新園區(qū)"); userOperation.updateUser(user); session.commit(); } finally { session.close(); } }
用mybatis 刪除數(shù)據(jù)
同理,IUserOperation 增加方法:public void deleteUser(int id);
配置User.xml
程序代碼
<delete id="deleteUser" parameterType="int"> delete from user where id=#{id} </delete>
然后在Test類中寫測(cè)試方法:
程序代碼
/** * 刪除數(shù)據(jù),刪除一定要 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(); } }
這樣,所有增刪改查都完成了,注意在增加,更改,刪除的時(shí)候要調(diào)用session.commit(),這樣才會(huì)真正對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作,否則是沒(méi)有提交的。
到此為止,簡(jiǎn)單的單表操作,應(yīng)該都會(huì)了,接下來(lái)的時(shí)間了,我會(huì)講多表聯(lián)合查詢,以及結(jié)果集的選取。
mybatis實(shí)戰(zhàn)教程(mybatis in action)之四:實(shí)現(xiàn)關(guān)聯(lián)數(shù)據(jù)的查詢
有了前面幾章的基礎(chǔ),對(duì)一些簡(jiǎn)單的應(yīng)用是可以處理的,但在實(shí)際項(xiàng)目中,經(jīng)常是關(guān)聯(lián)表的查詢,比如最常見到的多對(duì)一,一對(duì)多等。這些查詢是如何處理的呢,這一講就講這個(gè)問(wèn)題。我們首先創(chuàng)建一個(gè)Article 這個(gè)表,并初始化數(shù)據(jù).
程序代碼
Drop TABLE IF EXISTS `article`; Create TABLE `article` ( `id` int(11) NOT NULL auto_increment, `userid` int(11) NOT NULL, `title` varchar(100) NOT NULL, `content` text NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; -- ---------------------------- -- 添加幾條測(cè)試數(shù)據(jù) -- ---------------------------- Insert INTO `article` VALUES ('1', '1', 'test_title', 'test_content'); Insert INTO `article` VALUES ('2', '1', 'test_title_2', 'test_content_2'); Insert INTO `article` VALUES ('3', '1', 'test_title_3', 'test_content_3'); Insert INTO `article` VALUES ('4', '1', 'test_title_4', 'test_content_4');
你應(yīng)該發(fā)現(xiàn)了,這幾個(gè)文章對(duì)應(yīng)的userid都是1,所以需要用戶表user里面有id=1的數(shù)據(jù)。可以修改成滿足自己條件的數(shù)據(jù).按照orm的規(guī)則,表已經(jīng)創(chuàng)建了,那么肯定需要一個(gè)對(duì)象與之對(duì)應(yīng),所以我們?cè)黾右粋€(gè) Article 的class
程序代碼
package com.yihaomen.mybatis.model; 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; } }
注意一下,文章的用戶是怎么定義的,是直接定義的一個(gè)User對(duì)象。而不是int類型。
多對(duì)一的實(shí)現(xiàn)
場(chǎng)景:在讀取某個(gè)用戶發(fā)表的所有文章。當(dāng)然還是需要在User.xml 里面配置 select 語(yǔ)句, 但重點(diǎn)是這個(gè) select 的resultMap 對(duì)應(yīng)什么樣的數(shù)據(jù)呢。這是重點(diǎn),這里要引入 association 看定義如下:
程序代碼
< !-- User 聯(lián)合文章進(jìn)行查詢 方法之一的配置 (多對(duì)一的方式) --> <resultMap id="resultUserArticleList" type="Article"> <id property="id" column="aid" /> <result property="title" column="title" /> <result property="content" column="content" /> <association property="user" javaType="User"> <id property="id" column="id" /> <result property="userName" column="userName" /> <result property="userAddress" column="userAddress" /> </association> </resultMap> < select id="getUserArticles" parameterType="int" resultMap="resultUserArticleList"> select user.id,user.userName,user.userAddress,article.id aid,article.title,article.content from user,article where user.id=article.userid and user.id=#{id} </select>
這樣配置之后,就可以了,將select 語(yǔ)句與resultMap 對(duì)應(yīng)的映射結(jié)合起來(lái)看,就明白了。用association 來(lái)得到關(guān)聯(lián)的用戶,這是多對(duì)一的情況,因?yàn)樗械奈恼露际峭粋€(gè)用戶的。
還有另外一種處理方式,可以復(fù)用我們前面已經(jīng)定義好的 resultMap ,前面我們定義過(guò)一個(gè) resultListUser ,看這第二種方法如何實(shí)現(xiàn):
程序代碼
<resultMap type="User" id="resultListUser"> <id column="id" property="id" /> <result column="userName" property="userName" /> <result column="userAge" property="userAge" /> <result column="userAddress" property="userAddress" /> </resultMap> <!-- User 聯(lián)合文章進(jìn)行查詢 方法之二的配置 (多對(duì)一的方式) --> <resultMap id="resultUserArticleList-2" type="Article"> <id property="id" column="aid" /> <result property="title" column="title" /> <result property="content" column="content" /> <association property="user" javaType="User" resultMap="resultListUser" /> </resultMap> <select id="getUserArticles" parameterType="int" resultMap="resultUserArticleList"> select user.id,user.userName,user.userAddress,article.id aid,article.title,article.content from user,article where user.id=article.userid and user.id=#{id} </select>
將 association 中對(duì)應(yīng)的映射獨(dú)立抽取出來(lái),可以達(dá)到復(fù)用的目的。
好了,現(xiàn)在在Test 類中寫測(cè)試代碼:
程序代碼
public void getUserArticles(int userid){ SqlSession session = sqlSessionFactory.openSession(); try { IUserOperation userOperation=session.getMapper(IUserOperation.class); List<Article> articles = userOperation.getUserArticles(userid); for(Article article:articles){ System.out.println(article.getTitle()+":"+article.getContent()+ ":作者是:"+article.getUser().getUserName()+":地址:"+ article.getUser().getUserAddress()); } } finally { session.close(); } }
漏掉了一點(diǎn),我們一定要在 IUserOperation 接口中,加入 select 對(duì)應(yīng)的id 名稱相同的方法:
public List<Article> getUserArticles(int id);
然后運(yùn)行就可以測(cè)試。
mybatis實(shí)戰(zhàn)教程(mybatis in action)之五:與spring3集成
在這一系列文章中,前面講到純粹用mybatis 連接數(shù)據(jù)庫(kù),然后 進(jìn)行增刪改查,以及多表聯(lián)合查詢的的例子,但實(shí)際項(xiàng)目中,通常會(huì)用 spring 這個(gè)沾合劑來(lái)管理 datasource 等。充分利用spring 基于接口的編程,以及aop ,ioc 帶來(lái)的方便。用spring 來(lái)管理 mybatis 與管理hibernate 有很多類似的地方。今天的重點(diǎn)就是數(shù)據(jù)源管理以及 bean的配置。
你可以下載源碼后,對(duì)比著看,源代碼沒(méi)有帶jar包,太大了,空間有限. 有截圖,你可以看到用到哪些jar包,源碼在本文最后.
1. 首先對(duì)前面的工程結(jié)構(gòu)做一點(diǎn)改變,在src_user源代碼目錄下建立文件夾config ,并將原來(lái)的 mybatis 配置文件 Configuration.xml 移動(dòng)到這個(gè)文件夾中, 并在config 文家夾中建立 spring 配置文件:applicationContext.xml ,這個(gè)配置文件里最主要的配置:
程序代碼
< !--本示例采用DBCP連接池,應(yīng)預(yù)先把DBCP的jar包復(fù)制到工程的lib目錄下。 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--dataSource屬性指定要用到的連接池--> <property name="dataSource" ref="dataSource"/> <!--configLocation屬性指定mybatis的核心配置文件--> <property name="configLocation" value="config/Configuration.xml"/> </bean> <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <!--sqlSessionFactory屬性指定要用到的SqlSessionFactory實(shí)例--> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> <!--mapperInterface屬性指定映射器接口,用于實(shí)現(xiàn)此接口并生成映射器對(duì)象--> <property name="mapperInterface" value="com.yihaomen.mybatis.inter.IUserOperation" /> </bean>
[b]這里面的重點(diǎn)就是 org.mybatis.spring.SqlSessionFactoryBean 與 org.mybatis.spring.mapper.MapperFactoryBean[b] 實(shí)現(xiàn)了 spring 的接口,并產(chǎn)生對(duì)象。詳細(xì)可以查看 mybatis-spring 代碼。(http://code.google.com/p/mybatis/),如果僅僅使用,固定模式,這樣配置就好。
然后寫測(cè)試程序
package com.yihaomen.test; import java.util.List; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.yihaomen.mybatis.inter.IUserOperation; import com.yihaomen.mybatis.model.Article; import com.yihaomen.mybatis.model.User; public class MybatisSprintTest { private static ApplicationContext ctx; static { ctx = new ClassPathXmlApplicationContext("config/applicationContext.xml"); } public static void main(String[] args) { IUserOperation mapper = (IUserOperation)ctx.getBean("userMapper"); //測(cè)試id=1的用戶查詢,根據(jù)數(shù)據(jù)庫(kù)中的情況,可以改成你自己的. System.out.println("得到用戶id=1的用戶信息"); User user = mapper.selectUserByID(1); System.out.println(user.getUserAddress()); //得到文章列表測(cè)試 System.out.println("得到用戶id為1的所有文章列表"); List<Article> articles = mapper.getUserArticles(1); for(Article article:articles){ System.out.println(article.getContent()+"--"+article.getTitle()); } } }
運(yùn)行即可得到相應(yīng)的結(jié)果.
工程圖:
用到的jar包,如下圖:
mybatis實(shí)戰(zhàn)教程(mybatis in action)之六:與Spring MVC 的集成
前面幾篇文章已經(jīng)講到了mybatis與spring 的集成。但這個(gè)時(shí)候,所有的工程還不是web工程,雖然我一直是創(chuàng)建的web 工程。今天將直接用mybatis與Spring mvc 的方式集成起來(lái),源碼在本文結(jié)尾處下載.主要有以下幾個(gè)方面的配置
1. web.xml 配置 spring dispatchservlet ,比如為:mvc-dispatcher
2. mvc-dispatcher-servlet.xml 文件配置
3. spring applicationContext.XML文件配置(與數(shù)據(jù)庫(kù)相關(guān),與mybatis sqlSessionFaction 整合,掃描所有mybatis mapper 文件等.)
4. 編寫controller 類
5. 編寫頁(yè)面代碼.
先有個(gè)大概映像,整個(gè)工程圖如下:
1. web.xml 配置 spring dispatchservlet ,比如為:mvc-dispatcher
程序代碼 程序代碼
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:config/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <listener> <listener-class> org.springframework.web.context.ContextCleanupListener</listener-class> </listener> <servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
2. 在web.xml 同目錄下配置 mvc-dispatcher-servlet.xml 文件,這個(gè)文件名前面部分必須與你在web.xml里面配置的DispatcherServlet 的servlet名字對(duì)應(yīng).其內(nèi)容為:
程序代碼
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <context:component-scan base-package="com.yihaomen.controller" /> <mvc:annotation-driven /> <mvc:resources mapping="/static/**" location="/WEB-INF/static/"/> <mvc:default-servlet-handler/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix"> <value>/WEB-INF/pages/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean> < /beans>
3. 在源碼目錄 config 目錄下配置 spring 配置文件 applicationContext.xml
程序代碼
< !--本示例采用DBCP連接池,應(yīng)預(yù)先把DBCP的jar包復(fù)制到工程的lib目錄下。 --> <context:property-placeholder location="classpath:/config/database.properties" /> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=utf8" p:username="root" p:password="password" p:maxActive="10" p:maxIdle="10"> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--dataSource屬性指定要用到的連接池--> <property name="dataSource" ref="dataSource"/> <!--configLocation屬性指定mybatis的核心配置文件--> <property name="configLocation" value="classpath:config/Configuration.xml" /> <!-- 所有配置的mapper文件 --> <property name="mapperLocations" value="classpath*:com/yihaomen/mapper/*.xml" /> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.yihaomen.inter" /> </bean>
不知道為什么,一旦我用了 MapperScannerConfigurer 去掃描所有的mapper 接口時(shí),數(shù)據(jù)庫(kù)配置datasource 就不能用讀取database.properties文件了。報(bào)錯(cuò): Cannot load JDBC driver class '${jdbc.driverClassName}',網(wǎng)上有人說(shuō)在spring 3.1.1 下用 sqlSessionFactionBean 注入可以解決,但我用 spring 3.1.3 還是有問(wèn)題,所以只好把數(shù)據(jù)庫(kù)連接信息直接配置在了XML 文件里面。
4. 編寫 controller 層
程序代碼
package com.yihaomen.controller; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import com.yihaomen.inter.IUserOperation; import com.yihaomen.model.Article; @Controller @RequestMapping("/article") public class UserController { @Autowired IUserOperation userMapper; @RequestMapping("/list") public ModelAndView listall(HttpServletRequest request,HttpServletResponse response){ List<Article> articles=userMapper.getUserArticles(1); ModelAndView mav=new ModelAndView("list"); mav.addObject("articles",articles); return mav; } }
5. 頁(yè)面文件:
< c:forEach items="${articles}" var="item"> ${item.id }--${item.title }--${item.content }<br /> </c:forEach>
運(yùn)行結(jié)果:
當(dāng)然還有 mybatis 的Configure.xml 配置文件,與上一講的差不多,唯一不同的就是不用再配置類似如下的: <mapper resource="com/yihaomen/mapper/User.xml"/> ,所有這些都交給 在配置 sqlSessionFactory 的時(shí)候,由 <property name="mapperLocations" value="classpath*:com/yihaomen/mapper/*.xml" /> 去導(dǎo)入了。
數(shù)據(jù)庫(kù)下載:
下載文件spring mvc 數(shù)據(jù)庫(kù)測(cè)試文件
mybatis實(shí)戰(zhàn)教程(mybatis in action)之七:實(shí)現(xiàn)mybatis分頁(yè)(源碼下載)
上一篇文章里已經(jīng)講到了mybatis與spring MVC的集成,并且做了一個(gè)列表展示,顯示出所有article 列表,但沒(méi)有用到分頁(yè),在實(shí)際的項(xiàng)目中,分頁(yè)是肯定需要的。而且是物理分頁(yè),不是內(nèi)存分頁(yè)。對(duì)于物理分頁(yè)方案,不同的數(shù)據(jù)庫(kù),有不同的實(shí)現(xiàn)方法,對(duì)于mysql 來(lái)說(shuō) 就是利用 limit offset,pagesize 方式來(lái)實(shí)現(xiàn)的。oracle 是通過(guò)rownum 來(lái)實(shí)現(xiàn)的,如果你熟悉相關(guān)數(shù)據(jù)庫(kù)的操作,是一樣的很好擴(kuò)展,本文以mysql 為例子來(lái)講述.先看一下效果圖(源代碼在文章最后提供下載):
實(shí)現(xiàn)mybatis 物理分頁(yè),一個(gè)最簡(jiǎn)單的方式是,是在你的mapper的SQL語(yǔ)句中直接寫類似如下方式 :
程序代碼
<select id="getUserArticles" parameterType="Your_params" resultMap="resultUserArticleList"> select user.id,user.userName,user.userAddress,article.id aid,article.title,article.content from user,article where user.id=article.userid and user.id=#{id} limit #{offset},#{pagesize} </select>
請(qǐng)注意這里的 parameterType 是你傳入的參數(shù)類,或者map ,里面包含了offset,pagesize ,和其他你需要的參數(shù),用這種方式,肯定可以實(shí)現(xiàn)分頁(yè)。這是簡(jiǎn)單的一種方式。但更通用的一種方式是用 mybatis 插件的方式. 參考了網(wǎng)上的很多資料 ,mybatis plugin 方面的資料。寫自己的插件.
程序代碼
package com.yihaomen.util; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.Map; import java.util.Properties; import javax.xml.bind.PropertyException; import org.apache.ibatis.builder.xml.dynamic.ForEachSqlNode; import org.apache.ibatis.executor.ErrorContext; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.ExecutorException; import org.apache.ibatis.executor.statement.BaseStatementHandler; import org.apache.ibatis.executor.statement.RoutingStatementHandler; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.mapping.ParameterMode; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.property.PropertyTokenizer; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.TypeHandlerRegistry; @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) }) public class PagePlugin implements Interceptor { private static String dialect = ""; private static String pageSqlId = ""; @SuppressWarnings("unchecked") public Object intercept(Invocation ivk) throws Throwable { if (ivk.getTarget() instanceof RoutingStatementHandler) { RoutingStatementHandler statementHandler = (RoutingStatementHandler) ivk .getTarget(); BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper .getValueByFieldName(statementHandler, "delegate"); MappedStatement mappedStatement = (MappedStatement) ReflectHelper .getValueByFieldName(delegate, "mappedStatement"); if (mappedStatement.getId().matches(pageSqlId)) { BoundSql boundSql = delegate.getBoundSql(); Object parameterObject = boundSql.getParameterObject(); if (parameterObject == null) { throw new NullPointerException("parameterObject error"); } else { Connection connection = (Connection) ivk.getArgs()[0]; String sql = boundSql.getSql(); String countSql = "select count(0) from (" + sql + ") myCount"; System.out.println("總數(shù)sql 語(yǔ)句:"+countSql); PreparedStatement countStmt = connection .prepareStatement(countSql); BoundSql countBS = new BoundSql( mappedStatement.getConfiguration(), countSql, boundSql.getParameterMappings(), parameterObject); setParameters(countStmt, mappedStatement, countBS, parameterObject); ResultSet rs = countStmt.executeQuery(); int count = 0; if (rs.next()) { count = rs.getInt(1); } rs.close(); countStmt.close(); PageInfo page = null; if (parameterObject instanceof PageInfo) { page = (PageInfo) parameterObject; page.setTotalResult(count); } else if(parameterObject instanceof Map){ Map<String, Object> map = (Map<String, Object>)parameterObject; page = (PageInfo)map.get("page"); if(page == null) page = new PageInfo(); page.setTotalResult(count); }else { Field pageField = ReflectHelper.getFieldByFieldName( parameterObject, "page"); if (pageField != null) { page = (PageInfo) ReflectHelper.getValueByFieldName( parameterObject, "page"); if (page == null) page = new PageInfo(); page.setTotalResult(count); ReflectHelper.setValueByFieldName(parameterObject, "page", page); } else { throw new NoSuchFieldException(parameterObject .getClass().getName()); } } String pageSql = generatePageSql(sql, page); System.out.println("page sql:"+pageSql); ReflectHelper.setValueByFieldName(boundSql, "sql", pageSql); } } } return ivk.proceed(); } private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql, Object parameterObject) throws SQLException { ErrorContext.instance().activity("setting parameters") .object(mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = boundSql .getParameterMappings(); if (parameterMappings != null) { Configuration configuration = mappedStatement.getConfiguration(); TypeHandlerRegistry typeHandlerRegistry = configuration .getTypeHandlerRegistry(); MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject); for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); PropertyTokenizer prop = new PropertyTokenizer(propertyName); if (parameterObject == null) { value = null; } else if (typeHandlerRegistry .hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else if (boundSql.hasAdditionalParameter(propertyName)) { value = boundSql.getAdditionalParameter(propertyName); } else if (propertyName .startsWith(ForEachSqlNode.ITEM_PREFIX) && boundSql.hasAdditionalParameter(prop.getName())) { value = boundSql.getAdditionalParameter(prop.getName()); if (value != null) { value = configuration.newMetaObject(value) .getValue( propertyName.substring(prop .getName().length())); } } else { value = metaObject == null ? null : metaObject .getValue(propertyName); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); if (typeHandler == null) { throw new ExecutorException( "There was no TypeHandler found for parameter " + propertyName + " of statement " + mappedStatement.getId()); } typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType()); } } } } private String generatePageSql(String sql, PageInfo page) { if (page != null && (dialect !=null || !dialect.equals(""))) { StringBuffer pageSql = new StringBuffer(); if ("mysql".equals(dialect)) { pageSql.append(sql); pageSql.append(" limit " + page.getCurrentResult() + "," + page.getShowCount()); } else if ("oracle".equals(dialect)) { pageSql.append("select * from (select tmp_tb.*,ROWNUM row_id from ("); pageSql.append(sql); pageSql.append(") tmp_tb where ROWNUM<="); pageSql.append(page.getCurrentResult() + page.getShowCount()); pageSql.append(") where row_id>"); pageSql.append(page.getCurrentResult()); } return pageSql.toString(); } else { return sql; } } public Object plugin(Object arg0) { // TODO Auto-generated method stub return Plugin.wrap(arg0, this); } public void setProperties(Properties p) { dialect = p.getProperty("dialect"); if (dialect ==null || dialect.equals("")) { try { throw new PropertyException("dialect property is not found!"); } catch (PropertyException e) { // TODO Auto-generated catch block e.printStackTrace(); } } pageSqlId = p.getProperty("pageSqlId"); if (dialect ==null || dialect.equals("")) { try { throw new PropertyException("pageSqlId property is not found!"); } catch (PropertyException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
此插件有兩個(gè)輔助類:PageInfo,ReflectHelper,你可以下載源代碼參考。
寫了插件之后,當(dāng)然需要在 mybatis 的配置文件Configuration.xml 里配置這個(gè)插件
程序代碼
<plugins> <plugin interceptor="com.yihaomen.util.PagePlugin"> <property name="dialect" value="mysql" /> <property name="pageSqlId" value=".*ListPage.*" /> </plugin> </plugins>
請(qǐng)注意,這個(gè)插件定義了一個(gè)規(guī)則,也就是在mapper中sql語(yǔ)句的id 必須包含ListPage才能被攔截。否則將不會(huì)分頁(yè)處理.
插件寫好了,現(xiàn)在就可以在 spring mvc 中的controller 層中寫一個(gè)方法來(lái)測(cè)試這個(gè)分頁(yè):
程序代碼
@RequestMapping("/pagelist") public ModelAndView pageList(HttpServletRequest request,HttpServletResponse response){ int currentPage = request.getParameter("page")==null?1:Integer.parseInt(request.getParameter("page")); int pageSize = 3; if (currentPage<=0){ currentPage =1; } int currentResult = (currentPage-1) * pageSize; System.out.println(request.getRequestURI()); System.out.println(request.getQueryString()); PageInfo page = new PageInfo(); page.setShowCount(pageSize); page.setCurrentResult(currentResult); List<Article> articles=iUserOperation.selectArticleListPage(page,1); System.out.println(page); int totalCount = page.getTotalResult(); int lastPage=0; if (totalCount % pageSize==0){ lastPage = totalCount % pageSize; } else{ lastPage =1+ totalCount / pageSize; } if (currentPage>=lastPage){ currentPage =lastPage; } String pageStr = ""; pageStr=String.format("<a href=\"%s\">上一頁(yè)</a> <a href=\"%s\">下一頁(yè)</a>", request.getRequestURI()+"?page="+(currentPage-1),request.getRequestURI()+"?page="+(currentPage+1) ); //制定視圖,也就是list.jsp ModelAndView mav=new ModelAndView("list"); mav.addObject("articles",articles); mav.addObject("pageStr",pageStr); return mav; }
然后運(yùn)行程序,進(jìn)入分頁(yè)頁(yè)面,你就可以看到結(jié)果了:
相關(guān)jar 包下載,請(qǐng)到下載這里例子中的jar
http://www.yihaomen.com/article/java/318.htm (文章最后有源代碼下載,里面有jar 包,拷貝到上面源代碼里面所需要的lib 目錄下.)
另外,你還得在前面提到的數(shù)據(jù)庫(kù)artilce表里面,多插入一些記錄,分頁(yè)效果就更好。
mybatis實(shí)戰(zhàn)教程(mybatis in action)之八:mybatis 動(dòng)態(tài)sql語(yǔ)句
mybatis 的動(dòng)態(tài)sql語(yǔ)句是基于OGNL表達(dá)式的??梢苑奖愕脑?sql 語(yǔ)句中實(shí)現(xiàn)某些邏輯. 總體說(shuō)來(lái)mybatis 動(dòng)態(tài)SQL 語(yǔ)句主要有以下幾類:
1. if 語(yǔ)句 (簡(jiǎn)單的條件判斷)
2. choose (when,otherwize) ,相當(dāng)于java 語(yǔ)言中的 switch ,與 jstl 中的choose 很類似.
3. trim (對(duì)包含的內(nèi)容加上 prefix,或者 suffix 等,前綴,后綴)
4. where (主要是用來(lái)簡(jiǎn)化sql語(yǔ)句中where條件判斷的,能智能的處理 and or ,不必?fù)?dān)心多余導(dǎo)致語(yǔ)法錯(cuò)誤)
5. set (主要用于更新時(shí))
6. foreach (在實(shí)現(xiàn) mybatis in 語(yǔ)句查詢時(shí)特別有用)
下面分別介紹這幾種處理方式
1. mybaits if 語(yǔ)句處理
程序代碼
<select id="dynamicIfTest" parameterType="Blog" resultType="Blog"> select * from t_blog where 1 = 1 <if test="title != null"> and title = #{title} </if> <if test="content != null"> and content = #{content} </if> <if test="owner != null"> and owner = #{owner} </if> </select>
這條語(yǔ)句的意思非常簡(jiǎn)單,如果你提供了title參數(shù),那么就要滿足title=#{title},同樣如果你提供了Content和Owner的時(shí)候,它們也需要滿足相應(yīng)的條件,之后就是返回滿足這些條件的所有Blog,這是非常有用的一個(gè)功能,以往我們使用其他類型框架或者直接使用JDBC的時(shí)候, 如果我們要達(dá)到同樣的選擇效果的時(shí)候,我們就需要拼SQL語(yǔ)句,這是極其麻煩的,比起來(lái),上述的動(dòng)態(tài)SQL就要簡(jiǎn)單多了
2.2. choose (when,otherwize) ,相當(dāng)于java 語(yǔ)言中的 switch ,與 jstl 中的choose 很類似
程序代碼
<select id="dyamicChooseTest" parameterType="Blog" resultType="Blog"> select * from t_blog where 1 = 1 <choose> <when test="title != null"> and title = #{title} </when> <when test="content != null"> and content = #{content} </when> <otherwise> and owner = "owner1" </otherwise> </choose> </select>
when元素表示當(dāng)when中的條件滿足的時(shí)候就輸出其中的內(nèi)容,跟JAVA中的switch效果差不多的是按照條件的順序,當(dāng)when中有條件滿足的時(shí)候,就會(huì)跳出choose,即所有的when和otherwise條件中,只有一個(gè)會(huì)輸出,當(dāng)所有的我很條件都不滿足的時(shí)候就輸出otherwise中的內(nèi)容。所以上述語(yǔ)句的意思非常簡(jiǎn)單, 當(dāng)title!=null的時(shí)候就輸出and titlte = #{title},不再往下判斷條件,當(dāng)title為空且content!=null的時(shí)候就輸出and content = #{content},當(dāng)所有條件都不滿足的時(shí)候就輸出otherwise中的內(nèi)容。
3.trim (對(duì)包含的內(nèi)容加上 prefix,或者 suffix 等,前綴,后綴)
程序代碼
<select id="dynamicTrimTest" parameterType="Blog" resultType="Blog"> select * from t_blog <trim prefix="where" prefixOverrides="and |or"> <if test="title != null"> title = #{title} </if> <if test="content != null"> and content = #{content} </if> <if test="owner != null"> or owner = #{owner} </if> </trim> </select>
trim元素的主要功能是可以在自己包含的內(nèi)容前加上某些前綴,也可以在其后加上某些后綴,與之對(duì)應(yīng)的屬性是prefix和suffix;可以把包含內(nèi)容的首部某些內(nèi)容覆蓋,即忽略,也可以把尾部的某些內(nèi)容覆蓋,對(duì)應(yīng)的屬性是prefixOverrides和suffixOverrides;正因?yàn)閠rim有這樣的功能,所以我們也可以非常簡(jiǎn)單的利用trim來(lái)代替where元素的功能
4. where (主要是用來(lái)簡(jiǎn)化sql語(yǔ)句中where條件判斷的,能智能的處理 and or 條件
程序代碼
<select id="dynamicWhereTest" parameterType="Blog" resultType="Blog"> select * from t_blog <where> <if test="title != null"> title = #{title} </if> <if test="content != null"> and content = #{content} </if> <if test="owner != null"> and owner = #{owner} </if> </where> </select>
where元素的作用是會(huì)在寫入where元素的地方輸出一個(gè)where,另外一個(gè)好處是你不需要考慮where元素里面的條件輸出是什么樣子的,MyBatis會(huì)智能的幫你處理,如果所有的條件都不滿足那么MyBatis就會(huì)查出所有的記錄,如果輸出后是and 開頭的,MyBatis會(huì)把第一個(gè)and忽略,當(dāng)然如果是or開頭的,MyBatis也會(huì)把它忽略;此外,在where元素中你不需要考慮空格的問(wèn)題,MyBatis會(huì)智能的幫你加上。像上述例子中,如果title=null, 而content != null,那么輸出的整個(gè)語(yǔ)句會(huì)是select * from t_blog where content = #{content},而不是select * from t_blog where and content = #{content},因?yàn)镸yBatis會(huì)智能的把首個(gè)and 或 or 給忽略。
5.set (主要用于更新時(shí))
程序代碼
<update id="dynamicSetTest" parameterType="Blog"> update t_blog <set> <if test="title != null"> title = #{title}, </if> <if test="content != null"> content = #{content}, </if> <if test="owner != null"> owner = #{owner} </if> </set> where id = #{id} </update>
set元素主要是用在更新操作的時(shí)候,它的主要功能和where元素其實(shí)是差不多的,主要是在包含的語(yǔ)句前輸出一個(gè)set,然后如果包含的語(yǔ)句是以逗號(hào)結(jié)束的話將會(huì)把該逗號(hào)忽略,如果set包含的內(nèi)容為空的話則會(huì)出錯(cuò)。有了set元素我們就可以動(dòng)態(tài)的更新那些修改了的字段
6. foreach (在實(shí)現(xiàn) mybatis in 語(yǔ)句查詢時(shí)特別有用)
foreach的主要用在構(gòu)建in條件中,它可以在SQL語(yǔ)句中進(jìn)行迭代一個(gè)集合。foreach元素的屬性主要有item,index,collection,open,separator,close。item表示集合中每一個(gè)元素進(jìn)行迭代時(shí)的別名,index指定一個(gè)名字,用于表示在迭代過(guò)程中,每次迭代到的位置,open表示該語(yǔ)句以什么開始,separator表示在每次進(jìn)行迭代之間以什么符號(hào)作為分隔符,close表示以什么結(jié)束,在使用foreach的時(shí)候最關(guān)鍵的也是最容易出錯(cuò)的就是collection屬性,該屬性是必須指定的,但是在不同情況下,該屬性的值是不一樣的,主要有一下3種情況:
如果傳入的是單參數(shù)且參數(shù)類型是一個(gè)List的時(shí)候,collection屬性值為list
如果傳入的是單參數(shù)且參數(shù)類型是一個(gè)array數(shù)組的時(shí)候,collection的屬性值為array
如果傳入的參數(shù)是多個(gè)的時(shí)候,我們就需要把它們封裝成一個(gè)Map了,當(dāng)然單參數(shù)也可以封裝成map,實(shí)際上如果你在傳入?yún)?shù)的時(shí)候,在MyBatis里面也是會(huì)把它封裝成一個(gè)Map的,map的key就是參數(shù)名,所以這個(gè)時(shí)候collection屬性值就是傳入的List或array對(duì)象在自己封裝的map里面的key
1.1.單參數(shù)List的類型
程序代碼
<select id="dynamicForeachTest" resultType="Blog"> select * from t_blog where id in <foreach collection="list" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </select>
上述collection的值為list,對(duì)應(yīng)的Mapper是這樣的
程序代碼
public List<Blog> dynamicForeachTest(List<Integer> ids);
測(cè)試代碼
@Test public void dynamicForeachTest() { SqlSession session = Util.getSqlSessionFactory().openSession(); BlogMapper blogMapper = session.getMapper(BlogMapper.class); List<Integer> ids = new ArrayList<Integer>(); ids.add(1); ids.add(3); ids.add(6); List<Blog> blogs = blogMapper.dynamicForeachTest(ids); for (Blog blog : blogs) System.out.println(blog); session.close(); }
2.數(shù)組類型的參數(shù)
程序代碼
<select id="dynamicForeach2Test" resultType="Blog"> select * from t_blog where id in <foreach collection="array" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </select>
對(duì)應(yīng)mapper
程序代碼
public List<Blog> dynamicForeach2Test(int[] ids);
3. Map 類型的參數(shù)
程序代碼
<select id="dynamicForeach3Test" resultType="Blog"> select * from t_blog where title like "%"#{title}"%" and id in <foreach collection="ids" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </select>
mapper 應(yīng)該是這樣的接口:
程序代碼
public List<Blog> dynamicForeach3Test(Map<String, Object> params);
通過(guò)以上方法,就能完成一般的mybatis 的 動(dòng)態(tài)SQL 語(yǔ)句.最常用的就是 if where foreach這幾個(gè),一定要重點(diǎn)掌握.
mybatis實(shí)戰(zhàn)教程(mybatis in action)之九:mybatis 代碼生成工具的使用
mybatis 應(yīng)用程序,需要大量的配置文件,對(duì)于一個(gè)成百上千的數(shù)據(jù)庫(kù)表來(lái)說(shuō),完全手工配置,這是一個(gè)很恐怖的工作量. 所以mybatis 官方也推出了一個(gè)mybatis代碼生成工具的jar包. 今天花了一點(diǎn)時(shí)間,按照 mybatis generator 的doc 文檔參考,初步配置出了一個(gè)可以使用的版本,我把源代碼也提供下載,mybatis 代碼生成工具,主要有一下功能:
1.生成pojo 與 數(shù)據(jù)庫(kù)結(jié)構(gòu)對(duì)應(yīng)
2.如果有主鍵,能匹配主鍵
3.如果沒(méi)有主鍵,可以用其他字段去匹配
4.動(dòng)態(tài)select,update,delete 方法
5.自動(dòng)生成接口(也就是以前的dao層)
6.自動(dòng)生成sql mapper,增刪改查各種語(yǔ)句配置,包括動(dòng)態(tài)where語(yǔ)句配置
7.生成Example 例子供參考
下面介紹下詳細(xì)過(guò)程
1. 創(chuàng)建測(cè)試工程,并配置mybatis代碼生成jar包
下載地址:http://code.google.com/p/mybatis/downloads/list?can=3&q=Product%3DGenerator
mysql 驅(qū)動(dòng)下載:http://dev.mysql.com/downloads/connector/j/
這些jar包,我也會(huì)包含在源代碼里面,可以在文章末尾處,下載源代碼,參考。
用 eclipse 建立一個(gè)dynamic web project。
解壓下載后的 mybatis-generator-core-1.3.2-bundle.zip 文件,其中有兩個(gè)目錄:一個(gè)目錄是文檔目錄docs,主要介紹這個(gè)代碼生成工具如何使用,另一個(gè)是lib目錄,里面的內(nèi)容主要是jar 包,這里我們需要 mybatis-generator-core-1.3.2.jar,這個(gè) jar 包. 將它拷貝到我們剛剛創(chuàng)建的 web工程的 WebContent/WEB-INF/lib 目錄下.在這個(gè)目錄下也放入 mysql 驅(qū)動(dòng)jar包.因?yàn)橛?mysql 做測(cè)試的.
2.在數(shù)據(jù)庫(kù)中創(chuàng)建測(cè)試表
在mybatis數(shù)據(jù)庫(kù)中創(chuàng)建 用來(lái)測(cè)試的category表(如果沒(méi)有mybatis這個(gè)數(shù)據(jù)庫(kù),要?jiǎng)?chuàng)建,這是基于前面這個(gè)系列文章而寫的,已經(jīng)有了mybatis 這個(gè)數(shù)據(jù)庫(kù))
程序代碼
Drop TABLE IF EXISTS `category`; Create TABLE `category` ( `id` int(11) NOT NULL AUTO_INCREMENT, `catname` varchar(50) NOT NULL, `catdescription` varchar(200) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3. 配置mybatis 代碼生成工具的配置文件
在創(chuàng)建的web工程中,創(chuàng)建相應(yīng)的package 比如 :
com.yihaomen.inter 用來(lái)存放mybatis 接口對(duì)象.
com.yihaomen.mapper用來(lái)存放sql mapper對(duì)應(yīng)的映射,sql語(yǔ)句等.
com.yihaomen.model 用來(lái)存放與數(shù)據(jù)庫(kù)對(duì)應(yīng)的model 。
在用mybatis 代碼生成工具之前,這些目錄必須先創(chuàng)建好,作為一個(gè)好的應(yīng)用程序,這些目錄的創(chuàng)建也是有規(guī)律的。
根據(jù)mybatis代碼生成工具文檔,需要一個(gè)配置文件,這里命名為:mbgConfiguration.xml 放在src 目錄下. 配置文件內(nèi)容如下:
程序代碼
< ?xml version="1.0" encoding="UTF-8"?> < !DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> < generatorConfiguration> <!-- 配置mysql 驅(qū)動(dòng)jar包路徑.用了絕對(duì)路徑 --> <classPathEntry location="D:\Work\Java\eclipse\workspace\myBatisGenerator\WebContent\WEB-INF\lib\mysql-connector-java-5.1.22-bin.jar" /> <context id="yihaomen_mysql_tables" targetRuntime="MyBatis3"> <!-- 為了防止生成的代碼中有很多注釋,比較難看,加入下面的配置控制 --> <commentGenerator> <property name="suppressAllComments" value="true" /> <property name="suppressDate" value="true" /> </commentGenerator> <!-- 注釋控制完畢 --> <!-- 數(shù)據(jù)庫(kù)連接 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=utf8" userId="root" password="password"> </jdbcConnection> <javaTypeResolver > <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- 數(shù)據(jù)表對(duì)應(yīng)的model 層 --> <javaModelGenerator targetPackage="com.yihaomen.model" targetProject="src"> <property name="enableSubPackages" value="true" /> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- sql mapper 隱射配置文件 --> <sqlMapGenerator targetPackage="com.yihaomen.mapper" targetProject="src"> <property name="enableSubPackages" value="true" /> </sqlMapGenerator> <!-- 在ibatis2 中是dao層,但在mybatis3中,其實(shí)就是mapper接口 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.yihaomen.inter" targetProject="src"> <property name="enableSubPackages" value="true" /> </javaClientGenerator> <!-- 要對(duì)那些數(shù)據(jù)表進(jìn)行生成操作,必須要有一個(gè). --> <table schema="mybatis" tableName="category" domainObjectName="Category" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> </context> < /generatorConfiguration>
用一個(gè)main 方法來(lái)測(cè)試能否用mybatis 成生成剛剛創(chuàng)建的`category`表對(duì)應(yīng)的model,sql mapper等內(nèi)容.
創(chuàng)建一個(gè)com.yihaomen.test 的package ,并在此package 下面建立一個(gè)測(cè)試的類GenMain:
程序代碼
package com.yihaomen.test; import java.io.File; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.mybatis.generator.api.MyBatisGenerator; import org.mybatis.generator.config.Configuration; import org.mybatis.generator.config.xml.ConfigurationParser; import org.mybatis.generator.exception.InvalidConfigurationException; import org.mybatis.generator.exception.XMLParserException; import org.mybatis.generator.internal.DefaultShellCallback; public class GenMain { public static void main(String[] args) { List<String> warnings = new ArrayList<String>(); boolean overwrite = true; String genCfg = "/mbgConfiguration.xml"; File configFile = new File(GenMain.class.getResource(genCfg).getFile()); ConfigurationParser cp = new ConfigurationParser(warnings); Configuration config = null; try { config = cp.parseConfiguration(configFile); } catch (IOException e) { e.printStackTrace(); } catch (XMLParserException e) { e.printStackTrace(); } DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = null; try { myBatisGenerator = new MyBatisGenerator(config, callback, warnings); } catch (InvalidConfigurationException e) { e.printStackTrace(); } try { myBatisGenerator.generate(null); } catch (SQLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }
到此為止,eclipse項(xiàng)目工程圖應(yīng)該如下:
4.運(yùn)行測(cè)試的main 方法,生成mybatis 相關(guān)代碼
運(yùn)行GenMain類里的main方法,并刷新 工程,你會(huì)發(fā)現(xiàn) 各自package 目錄下已經(jīng)響應(yīng)生成了對(duì)應(yīng)的文件,完全符合mybatis 規(guī)則,效果圖如下:
5.注意事項(xiàng)
如果你想生成example 之類的東西,需要在<table></table>里面去掉
程序代碼
enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"
這部分配置,這是生成Example而用的,一般來(lái)說(shuō)對(duì)項(xiàng)目沒(méi)有用.
另外生成的sql mapper 等,只是對(duì)單表的增刪改查,如果你有多表join操作,你就可以手動(dòng)配置,如果調(diào)用存儲(chǔ)過(guò)程,你也需要手工配置. 這時(shí)工作量已經(jīng)少很多了。
如果你想用命令行方式處理,也是可以的.
程序代碼
比如:
java -jar mybatis-generator-core-1.3.2.jar -mbgConfiguration.xm -overwrite
這時(shí),要用絕對(duì)路徑才行. 另外mbgConfiguration.xml 配置文件中targetProject 的配置也必須是絕對(duì)路徑了。
mybatis SqlSessionDaoSupport的使用
前面的系列mybatis 文章,已經(jīng)基本講到了mybatis的操作,但都是基于mapper隱射操作的,在mybatis 3中這個(gè)mapper 接口貌似充當(dāng)了以前在ibatis 2中的 DAO 層的作用。但事實(shí)上,如果有這個(gè)mapper接口不能完成的工作,或者需要更復(fù)雜的擴(kuò)展的時(shí)候,你就需要自己的DAO 層. 事實(shí)上 mybatis 3 也是支持DAO 層設(shè)計(jì)的,類似于ibatis 2 .下面介紹下.
首先創(chuàng)建一個(gè)com.yihaomen.dao的package.然后在里面分別創(chuàng)建接口UserDAO,以及實(shí)現(xiàn)該接口的UserDAOImpl
程序代碼
package com.yihaomen.dao; import java.util.List; import com.yihaomen.model.Article; public interface UserDAO { public List<Article> getUserArticles(int userid); }
程序代碼
package com.yihaomen.dao; import java.util.List; import org.mybatis.spring.support.SqlSessionDaoSupport; import org.springframework.stereotype.Repository; import com.yihaomen.model.Article; @Repository public class UserDAOImpl extends SqlSessionDaoSupport implements UserDAO { @Override public List<Article> getUserArticles(int userid) { return this.getSqlSession().selectList("com.yihaomen.inter.IUserOperation.getUserArticles",userid); } }
執(zhí)行的SQL 語(yǔ)句采用了命名空間+sql 語(yǔ)句id的方式,后面是參數(shù).
注意繼承了 "SqlSessionDaoSupport" ,利用方法 getSqlSession() 可以得到 SqlSessionTemplate ,從而可以執(zhí)行各種sql語(yǔ)句,類似于hibernatetemplate一樣,至少思路一樣.
如果與spring 3 mvc 集成要用 autowire的話,在daoimpl 類上 加上注解 “@Repository” ,另外還需要在spring 配置文件中加入<context:component-scan base-package="com.yihaomen.dao" /> 這樣在需要調(diào)用的地方,就可以使用autowire自動(dòng)注入了。
當(dāng)然,你也可以按一般程序的思路,創(chuàng)建一個(gè)service 的package, 用service 去調(diào)用 dao層,我這里就沒(méi)有做了,因?yàn)楸容^簡(jiǎn)單,用類似的方法,也機(jī)注意自動(dòng)注入時(shí),也要配置 <context:component-scan base-package="com.yihaomen.service" /> 等這樣的。
在controller層中測(cè)試,直接調(diào)用dao層方法
在controller中加入方法:
程序代碼
@Autowired UserDAO userDAO; ....... @RequestMapping("/daolist") public ModelAndView listalldao(HttpServletRequest request,HttpServletResponse response){ List<Article> articles=userDAO.getUserArticles(1); //制定視圖,也就是list.jsp ModelAndView mav=new ModelAndView("list"); mav.addObject("articles",articles); return mav; }
這樣可以得到同樣的結(jié)果,而且滿足了一般程序的設(shè)計(jì)方法.代碼結(jié)構(gòu)如下:
以上所述是本文給大家介紹的Mybatis實(shí)戰(zhàn)教程之入門到精通(經(jīng)典)的相關(guān)知識(shí),希望對(duì)大家有所幫助。
相關(guān)文章
SpringBoot實(shí)現(xiàn)熱部署的方式總結(jié)
所謂熱部署,就是在應(yīng)用正在運(yùn)行的時(shí)候升級(jí)軟件,卻不需要重新啟動(dòng)應(yīng)用,對(duì)于Java來(lái)說(shuō),熱部署就是在運(yùn)行時(shí)更新Java類文件,本文將深入探討SpringBoot有哪些方式可以實(shí)現(xiàn)熱部署,感興趣的朋友可以小編一探討學(xué)習(xí)2023-06-06IDEA自動(dòng)生成TestNG的testng.xml的插件方法
這篇文章主要介紹了IDEA自動(dòng)生成TestNG的testng.xml的插件方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04MyBatisPlus自定義JsonTypeHandler實(shí)現(xiàn)自動(dòng)轉(zhuǎn)化JSON問(wèn)題
這篇文章主要介紹了MyBatisPlus自定義JsonTypeHandler實(shí)現(xiàn)自動(dòng)轉(zhuǎn)化JSON問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12