一、Mybatis 簡介
Mybatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,並且改名為Mybatis , 2013年11月遷移到Github , iBATIS一詞來源於“internet”和“abatis”的組合,是一個基於Java的持久層框架。iBATIS提供的持久層框架包括SQL Maps和Data Access Objects(DAO)Mybatis 是支持普通 SQL查詢,存儲過程和高級映射的優秀持久層框架。Mybatis 消除了幾乎所有的JDBC代碼和參數的手工設置以及結果集的檢索。Mybatis 使用簡單的 XML或注解用於配置和原始映射,將接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java對象)映射成數據庫中的記錄。
二、Mybatis 的執行框架
1) SqlMapConfig.xml 文件 //名稱不一定是這個名稱 類似hibernate中的主配置文件
主配置文件 配置數據源,事務等運行環境,配置映射文件 (xxXmapper.xml... 多個)
2) SessionFactory
會話工廠,它是根據配置文件創建的,用來創建 SqlSession
3) SqlSession
會話 ,是一個接口,面向用戶的接口,主要用來進行數據庫操作
4) Executor
執行器,是底層封裝的一個對象,是一個接口(底層有兩個實現,基本執行器,緩存執行器)事實上 SqlSession 的內部就是通過該接口來執行sql語句的
5) MappedStatement 底層封裝的對象,封裝sql ,輸入參數,輸出結果類型等
三、第一個hello world 程序
1) 導包
asm-3.3.1.jar
cglib-2.2.2.jar
commons-logging-1.1.1.jar
javassist-3.17.1-GA.jar
log4j-1.2.17.jar
log4j-api-2.0-rc1.jar
log4j-core-2.0-rc1.jar
Mybatis-3.2.6.jar
slf4j-api-1.7.5.jar
slf4j-log4j12-1.7.5.jar
2) 在config里建一個配置文件(數據庫相關的) mydbconfig.properties , 內容如下
db.driver=com.mysql.jdbc.Driver db.url=jdbc:mysql://localhost:3306/shop db.username=admin db.password=root
3) 建立 SqlMapConfig.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> <properties resource="mydbconfig.properties" /> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> //POOLED指的是org.apache.ibatis.datasource.pooled.PooledDataSource, 一個數據源的實現類 <property name="driver" value="${db.driver}" /> <property name="url" value="${db.url}" /> <property name="username" value="${db.username}" /> <property name="password" value="${db.password}" /> </dataSource> </environment> </environments> <mappers> <mapper resource="sqlmap/UserInfo.xml" /> </mappers> </configuration>
4) 編寫映射文件 (其實就是bean 對應的映射文件 相當於 hibernate 中的 UsesrInfo.hbm.xml 之類的 )
命名規則 :
== UserInfo.xml //過去的ibatis中
== 如果使用了mapper 代理 命名規則變為 UserInfoMapper.xml
//UserInfo.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="test"> <select id="getuser_byid" parameterType="int" resultType="cat.beans.UserInfo" > select * from userInfo where id=#{id} //如是簡單類型,id可以寫成任意名字 </select> </mapper>
說明: namespace 是命名空間,用來對sql進行隔離 ,注意,如果使用mapper代理的方式開發,它就有特殊的作用
-- <select id="getuser_byid" ... > 這個id 就是sql的標識,其實將來要封裝到 MappedStatement 中
-- #{ } 代表占位
-- 如是參數是簡單類型,{} 中的名字可以任意
-- resultType 表示返回的java對象的類型,表示要將查詢結果映射成什么樣的java對象
5) 在主配置文件中引入這個映射文件
主配置文件中加入
<mappers> <mapper resource="sqlmap/UserInfo.xml" /> </mappers> //上面的主配置文件中其實已經加入了
6)測試
static void test(){ //加載主配置文件 InputStream in=Test.class.getClassLoader().getResourceAsStream("SqlMapConfig.xml"); //InputStream in= Resources.getResourceAsStream("SqlMapConfig.xml"); //Resources是mhybatis 提供的 SqlSessionFactory factor=new SqlSessionFactoryBuilder().build(in); SqlSession session =factor.openSession(); //第一個參數,是sql的id (是映射文件中的statement的id) = 名稱空間+sql的id名稱 , UserInfo user=session.selectOne("test.getuser_byid",1); //注意,這里的1 不能定成 "1" System.out.println(user); session.close(); }
在上例的基礎上,再添一個模糊查詢的例子
在 UserInfo.xml 中 加入
<select id="getuser_biname" parameterType="string" resultType="cat.beans.UserInfo"> //select * from userInfo where userName like #{value} //不行, 它會拼成like '張三' select * from userInfo where userName like '%${value}%' // ${value} 表示會把傳過來的值,直接拼到串上,有可能有sql注入問題,而且 // 如果parameterType 是簡單類型,則${ } 中間的必須寫成 value </select>
static void test() throws IOException{ InputStream in= Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory factor=new SqlSessionFactoryBuilder().build(in); SqlSession session =factor.openSession(); UserInfo user=session.selectOne("test.getuser_byid",1); //注意,這里的1 不能定成 "1" System.out.println(user); session.close(); }
總結:
#{} Mybatis 將 #{} 解釋為jdbc PreparedStatement 的一個參數標記 // 就是 select * from t where id= ? 中的 ?
${} 將它直接解釋為字符串
理解這兩者的區別是很有用的, 因為在某些SQL語句中並不能使用參數標記(parameter markers)。
那如果有like查詢要怎么辦呢? 可以這樣sql寫成第一種 #{} ,傳參的時候如下
"%張三%" selectOne //返回一個對象 selectList //返回一組對象 兩者 resultType="cat.beans.UserInfo" 都正常
在上例的基礎上,再增加一個添加用戶的功能
//對於添加這類方法,是沒有resultType的 <insert id="add_user" parameterType="cat.beans.UserInfo" > insert into userInfo (userName,password,note) values (#{userName},#{password}, #{note} ) //后面不要寫上分號 </insert> tatic void test3() throws IOException{ InputStream in= Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory factor=new SqlSessionFactoryBuilder().build(in); SqlSession session =factor.openSession(); UserInfo user=new UserInfo(); user.setUserName("陳鵬飛"); user.setPassword("admin"); user.setNote("這是備注"); int result=session.insert("test.add_user",user);
// 對於inser,雖然沒有為sql聲明反回類型,但也能正確的取到反回值的,它是整型 session.commit(); //不要忘了提交事務 System.out.println(result); //能得到正確的值 1 session.close(); }
在上面的基礎上,再添加刪除的和修改的功能
<delete id="deluser_byid" parameterType="int" > delete from userInfo where id=#{id} </delete> <update id="update_user" parameterType="cat.beans.UserInfo" > <!-- 傳過來的userInfo對象中要有id --> update userInfo set userName= #{userName},password= #{password}, note =#{note} where id= #{id} </update>
//修改 static void test4() throws IOException{ InputStream in= Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory factor=new SqlSessionFactoryBuilder().build(in); SqlSession session =factor.openSession(); UserInfo user=session.selectOne("test.getuser_byid",1); user.setUserName("english"); user.setPassword("englishxxx"); user.setNote("englishxxx"); int result= session.update("test.update_user",user); session.commit(); System.out.println("修改成功: " + result); session.close(); } //刪除 static void test5() throws IOException{ InputStream in= Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory factor=new SqlSessionFactoryBuilder().build(in); SqlSession session =factor.openSession(); int result= session.delete("test.deluser_byid",10); session.commit(); System.out.println("刪除成功: " + result); session.close(); }
四、返回自增主鍵
mysql 有一個函數 last_insert_id() 可以調用它得到最后一條插入語句生成的自增主鍵。
在上面的用戶添加的例子上
<insert id="add_user" parameterType="cat.beans.UserInfo" > <selectKey order="AFTER" keyProperty="id" resultType="int"> //resultType 必填 keyProperty的id 就是userInfo.id select last_insert_id() //elect new uuid(); //這是mysql生成uui的寫法 (32位數字或字母,4位是 -) 如果這樣,上面的AFTER要改成before //select 序列名.nextval 這是oracle中的寫法 </selectKey> insert into userInfo (userName,password,note) values (#{userName},#{password}, #{note} ) </insert>
說明:
order="AFTER" 指的是 要在這條語句執行之后取得生成的主鍵,它還有另一個取值 BEFORE
非自增主鍵,想取得,要用BEFORE,但生成主鍵的方式應該是上面的紅色的注起來的方式。
注意,如果用的是后面的方式,則下面的sql要寫上id
例子:生成 uuid型的主鍵
<insert id="add_user_new" parameterType="cat.beans.UserInfo" > <selectKey order="BEFORE" keyProperty="id" resultType="string"> //注意返回類型,注意前面要寫成 BEFORE select uuid() </selectKey> insert into userInfo (id,userName,password,note) values (#{id},#{userName},#{password}, #{note} ) //注意要把id這列也寫上 </insert> UserInfo user=new UserInfo(); //user.setId() 不用寫了,因為主鍵由Mybatis生成 user.setUserName("鄭某某"); user.setPassword("admin"); user.setNote("這是備注"); session.insert("test.add_user_new",user); session.commit(); System.out.println(user.getId()); //可以取到主鍵 369010c6-e631-1033-b927-e281dfdaa6b0
五、dao層的開發
SessionFactory 可以做成單例 (未來會交給Spring管理)
SqlSession 是線程不安全的, 用的時候,要在方法體內 , 這樣它就是一個局部變量,千萬不要把它寫成類成員
Mybatis 開發DAO 有兩種方式
原始的方式 ,手寫dao 和實現類
//接口 : public interface IUserDao { int addUser(UserInfo user); UserInfo getUserById(int id); } //實現類 public class UserDaoImplTest { private IUserDao _dao ; @Before public void setUp() throws Exception { InputStream in= Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory factor=new SqlSessionFactoryBuilder().build(in); _dao=new UserDaoImpl(factor); } @Test public void testAddUser() { UserInfo user=new UserInfo(); user.setUserName("周周"); user.setPassword("admin"); int result=_dao.addUser(user); System.out.println("ok"+result); } @Test public void testGetUserById() { UserInfo user=_dao.getUserById(2); System.out.println(user); } }
測試用例
使用 mapper 代理的方式 ,只需要mapper接口 (相當於dao接口)
public class UserDaoImpl implements IUserDao { private SqlSessionFactory _factory; public UserDaoImpl(SqlSessionFactory factory){ this._factory=factory; //由外界注入 SqlSessionFactory } //添加用戶 public int addUser(UserInfo user) { SqlSession s =_factory.openSession(); int result=s.insert("test.add_user",user); s.commit(); return result; } //查詢用戶 public UserInfo getUserById(int id) { SqlSession s =_factory.openSession(); UserInfo user=s.selectOne("test.getuser_byid",id); s.commit(); return user; } }
配置文件
<mapper namespace="test"> <select id="getuser_byid" parameterType="int" resultType="cat.beans.UserInfo" > select * from userInfo where id=#{id} </select> <insert id="add_user" parameterType="cat.beans.UserInfo" > <selectKey order="AFTER" keyProperty="id" resultType="int"> select last_insert_id() </selectKey> insert into userInfo (userName,password,note) values (#{userName},#{password}, #{note} ) </insert> ... 其他的略 </mapper>