mybatis的瞭解

一、mybatis的框架結構



過程說明:

1、 mybatis配置

SqlMapConfig.xml,此文件作爲mybatis的全局配置文件,配置了mybatis的運行環境等信息。

mapper.xml文件即sql映射文件,文件中配置了操作數據庫的sql語句。此文件需要在SqlMapConfig.xml中加載。

2、 通過mybatis環境等配置信息構造SqlSessionFactory即會話工廠

3、 由會話工廠創建sqlSession即會話,操作數據庫需要通過sqlSession進行。

4、 mybatis底層自定義了Executor執行器接口操作數據庫,Executor接口有兩個實現,一個是基本執行器、一個是緩存執行器。

5、 Mapped Statement也是mybatis一個底層封裝對象,它包裝了mybatis配置信息及sql映射信息等。mapper.xml文件中一個sql對應一個Mapped Statement對象,sqlid即是Mapped statementid

6、 Mapped Statementsql執行輸入參數進行定義,包括HashMap、基本類型、pojoExecutor通過Mapped Statement在執行sql前將輸入的java對象映射至sql中,輸入參數映射就是jdbc編程中對preparedStatement設置參數。

7、 Mapped Statementsql執行輸出結果進行定義,包括HashMap、基本類型、pojoExecutor通過Mapped Statement在執行sql後將輸出結果映射至java對象中,輸出結果映射過程相當於jdbc編程中對結果的解析處理過程。

注意事項:

1.代碼說明

public class User_select {
	public static void main(String[] args) throws IOException {
		
		//加載配置文件
		String resource = "SqlMapConfig.xml";
		InputStream inputStream  = Resources.getResourceAsStream(resource);
		
		//根據mytais的配置創建SqlSessionFactory
		
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		
		//根據SqlSessionFactory創建SqlSession
		
		SqlSession sqlSession = sqlSessionFactory.openSession();
		
		//通過sqlSession查詢用戶信息(發起數據庫操作)
		//第一個參數statement:指定mapper映射文件中statement的id,指定 時需要前邊加上statement所屬的命名空間
		//第二個參數parameter,指定 輸入參數
		//selectOne返回的是單條記錄,如果select返回多條記錄(list集合),使用selectOne會報錯
		//根據映射文件中的resultType指定輸出類型
		User user = sqlSession.selectOne("test.findUserById", 10);
		
		//遍歷查詢結果
		System.out.println(user);
		
		//查詢用戶列表
		//selectList表示查詢一個結果集(可以是一條或多條)
		List<User> list = sqlSession.selectList("test.findUserList", "張");
		
		System.out.println(list.size());
		
		//關閉sqlSession
		
		sqlSession.close();
		
	}
}

2、sqlSession的使用注意事項

SqlSession使用方法

SqlSessionFactoryBuilder:用於創建SqlSessionFactory,將SqlSessionFactoryBuilder當成工具類使用。

 

SqlSessionFactory:會話工廠,用於創建SqlSessionSqlSessionFactory一旦創建成功,不用每次創建工廠,建議單例模式使用工廠。如果和spring整合後,由spring來管理SqlSessionFactoryspring容器中SqlSessionFactory是一個單例對象

 

SqlSession(重點)是一個面向用戶的接口,通過SqlSessionFactory獲取SqlSession,每次數據操作都需要創建新的SqlSessionSqlSession 不是線程安全,最佳應用場合是方法體內,在方法中定義一個SqlSession局部變量。


二、mybatis作爲持久層框架在開發過程中常用的兩種配置方式

1、原始dao層的使用方式

這種情況:需要開發dao接口和dao的實現類。

public interface UserDao {
	
	//根據用戶id查詢用戶信息
	public User findUserById(int id) throws Exception;
	
	//添加用戶
	public void insertUser(User user) throws Exception;
	
	//查詢用戶列表
	public List<User> findUserList()throws Exception;
}
public class UserDaoImpl implements UserDao {

	// 注入SqlSessionFactory
	private SqlSessionFactory sqlSessionFactory;

	public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
		this.sqlSessionFactory = sqlSessionFactory;
	}

	@Override
	public User findUserById(int id) throws Exception {

		// 根據SqlSessionFactory創建SqlSession

		SqlSession sqlSession = sqlSessionFactory.openSession();

		// 通過sqlSession查詢用戶信息(發起數據庫操作)
		// 第一個參數statement:指定mapper映射文件中statement的id,指定 時需要前邊加上statement所屬的命名空間
		// 第二個參數parameter,指定 輸入參數
		// selectOne返回的是單條記錄,如果select返回多條記錄(list集合),使用selectOne會報錯
		// 根據映射文件中的resultType指定輸出類型
		User user = sqlSession.selectOne("test.findUserById", id);

		// 遍歷查詢結果
		// System.out.println(user);

		return user;
	}

	@Override
	public void insertUser(User user) throws Exception {
		// 根據SqlSessionFactory創建SqlSession

		SqlSession sqlSession = sqlSessionFactory.openSession();

		sqlSession.insert("test.insertUser", user);

		sqlSession.commit();

		sqlSession.close();

	}

	@Override
	public List<User> findUserList() throws Exception {
		// 根據SqlSessionFactory創建SqlSession

		SqlSession sqlSession = sqlSessionFactory.openSession();

		// 通過sqlSession查詢用戶信息(發起數據庫操作)
		// 第一個參數statement:指定mapper映射文件中statement的id,指定 時需要前邊加上statement所屬的命名空間
		// 第二個參數parameter,指定 輸入參數
		// selectOne返回的是單條記錄,如果select返回多條記錄(list集合),使用selectOne會報錯
		// 根據映射文件中的resultType指定輸出類型

		// 查詢用戶列表
		// selectList表示查詢一個結果集(可以是一條或多條)
		List<User> list = sqlSession.selectList("test.findUserList", "張");

		System.out.println(list.size());
		return list;
	}

}

具體代碼參考:https://github.com/libolibolibo/mybatis1217_1.git

2、使用mybatis的動態代理的方法

2.1、使用mybatis動態代理的好處

在開發過程中,只需要開發dao接口、接口對應的mapper.xml文件就可以了;

2.2、使用mybatis動態代理開發的原理

通過mybatis動態代理規則的配置,根據映射文件會生成dao接口實現類的代理對象,這個就相當於mybatis自己通過映射文件的配置,自己生成類dao接口的實現類,進而動態的調用statement語句;

2.3、使用mybatis的動態代理的規則配置

2.31、 mapper.xml中將namespace設置爲mapper.java接口的全限定名

2.32、 mapper.java接口的方法名和mapper.xmlstatementid保持一致。

2.33、 mapper.java接口的方法輸入參數類型和mapper.xmlstatementparameterType保持一致

2.34、 mapper.java接口的方法輸出 結果類型和mapper.xmlstatementresultType保持一致

2.4注意:mybatis在調用查詢的時候是調用selectOne還是selectList取決於dao接口的返回值,但是resultType保持不變;

Mybatis生成代理對象時,根據statement的標籤決定調用 SqlSession的方法(selectinsertupdate..)

根據上邊接口方法返回值 類型來決定 是調用 selectOne還是selectList,如果返回的是單個對象,動態代理調用selectOne(),如果返回的是集合對象,動態代理調用selectList()

<select id="findUserById" parameterType="int" resultType="user">
		
		SELECT * FROM USER WHERE id = #{id}
	
	</select>
<select id="findUserList" parameterType="java.lang.String" resultType="cn.itcast.mybatis.po.User" >
	  SELECT * FROM USER WHERE username LIKE '%${value}%'
	</select>
public interface UserMapper {
	
	//根據用戶id查詢用戶信息
	public User findUserById(int id) throws Exception;
	
	//查詢用戶列表
	public List<User> findUserList(String username)throws Exception;

}

具體的代碼示例:https://github.com/libolibolibo/mybatis1217_2.git

三、mybatis框架的全局的配置文件(一般名稱爲SqlMapConfig.xml)說明,這個在開發過程中使用比較少,一般在與spring整合後就不使用該文件了,但是要了解

1、全局配置文件的作用

SqlMapConfig.xml作爲mybatis的全局配置文件,配置內容包括:數據庫環境、mapper定義、全局參數設置......

  • properties(屬性)

properties屬性文件一般使用在,將數據庫連接參數單獨在一個properties文件中配置,好處是:方便系統升級維護。

  • settings(全局配置參數)

mybaits框架運行設置一些全局配置參數,比如:開啓二級緩存 ,開啓延遲載。。。

ibatis中有一些設置性能參數(最大線程數、最大請求數。。),在mybatis中沒有這些性能參數。

注意:設置全局參數會影響mybatis框架運行,謹慎設置。

  • typeAliases(類型別名)

在parameterTyperesultType設置時,爲了方便編碼,定義別名代替pojo的全路徑。

  • typeHandlers(類型處理器)

類型處理器用於java類型和jdbc類型映射:

Mybatis提供 的類型處理器滿足日常需要。

  • objectFactory(對象工廠)

  • plugins(插件)

  • environments(環境集合屬性對象)

  • environment(環境子屬性對象)

  • transactionManager(事務管理)

  • dataSource(數據源)

  • mappers(映射器)


<?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="db.properties" />
	
	<!-- 全局配置參數 -->
	<!-- settings></settings> -->
	
	<!-- 定義別名 -->
	<typeAliases>
	    <!-- 單個別名定義
		type:pojo的路徑 
		alias:別名的名稱
		-->
		<!-- <typeAlias type="cn.itcast.mybatis.po.User" alias="user"/> -->
		<!-- 批量別名定義
		name:指定包名,將包下邊的所有pojo定義別名 ,別名爲類名(首字母大寫或小寫都行)
		 -->
		<package name="cn.itcast.mybatis.po"/>
	</typeAliases>
	
	<!-- 和spring整合後 environments配置將廢除 -->
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}"/>
				<property name="url" value="${jdbc.url}"/>
				<property name="username" value="${jdbc.username}"/>
				<property name="password" value="${jdbc.password}"/>
			</dataSource>
		</environment>
	</environments>

	<!-- 配置mapper映射文件 -->
	<mappers>
	    <!-- resource方式
	    在UserMapper.xml,定義namespace爲mapper接口的地址,映射文件通過namespace找到對應的mapper接口文件
	     -->
		<!-- <mapper resource="sqlmap/UserMapper.xml" /> -->
		<!-- class方式
		class:指定 mapper接口的地址
		遵循規則:將mapper.xml和mapper.java文件放在一個目錄 且文件名相同
		 -->
		<!-- <mapper class="cn.itcast.mybatis.mapper.UserMapper"/> -->
	
		
		<!--批量mapper掃描
		遵循規則:將mapper.xml和mapper.java文件放在一個目錄 且文件名相同
		  -->
		<package name="cn.itcast.mybatis.mapper"/>
		
		
	</mappers>
</configuration>

四、mybatis的映射文件配置說明(一般命名爲xxxMapper.xml)

(一)、主鍵返回問題

需求 :對於新增的記錄,需要將主鍵返回到pojo中,就可以從pojo中獲取新添加的記錄id

自增主鍵生成 Uuid主鍵生成時機區別:

  • 自增主鍵在insert語句執行後生成 的。
  • Uuid主鍵在insert語句執行前生成 的。

1、Mysql自增主鍵獲取

<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
	
	<!-- 
	keyProperty:將主鍵設置到pojo中哪個屬性中
	order:selectKey中sql執行的時機
	resultType:selectKey中sql執行的結果類型
	LAST_INSERT_ID:是insert後獲取自增主鍵值 
	 -->
	<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
	     select LAST_INSERT_ID()
	</selectKey>
	insert into user(username,birthday,sex,address,detail,score)
	 values(#{username},#{birthday},#{sex},#{address},#{detail},#{score})
	</insert>

2、 Uuid主鍵獲取

Uuid主鍵在insert語句執行前生成 的

 

如果使用uuid獲取主鍵,定義selectkey

 

<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<!--
keyProperty:將主鍵設置到pojo中哪個屬性中
order:selectKey中sql執行的時機
resultType:selectKey中sql執行的結果類型
LAST_INSERT_ID:是insert後獲取自增主鍵值
 -->
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
     select uuid()
</selectKey>
insert into user(id,username,birthday,sex,address,detail,score)
 values(#{id},#{username},#{birthday},#{sex},#{address},#{detail},#{score})
</insert>

如果不用selectKey,也可以在調用SqlSession.insert()前,在輸入參數設置id(生成uuid,設置到userid屬性中。)


3、Oracle主鍵返回

Oracle沒有自增主鍵,使用oracle的序列(可以生成流水號,類似 自增主鍵)生成主鍵。

 

通過序列獲取流水號方法:

Select 序列名.next.val from dual

 

<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<!--
keyProperty:將主鍵設置到pojo中哪個屬性中
order:selectKey中sql執行的時機
resultType:selectKey中sql執行的結果類型
LAST_INSERT_ID:是insert後獲取自增主鍵值
 -->
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
     Select 序列名.next.val from dual
</selectKey>
insert into user(id,username,birthday,sex,address,detail,score)
 values(#{id},#{username},#{birthday},#{sex},#{address},#{detail},#{score})
</insert>

(二)、parameterType(輸入類型)

parameterType:用於設置輸入參數的類型。


1、#{}與${}

 

#{}:表示佔位符,如果獲取簡單類型,#{}中可以使用value或其它名稱 。有效防止sql注入。使用#{}設置參數無需考慮參數的類型。

如果使用#{}比較日期字段,select * from tablename where birthday >=#{birthday}

 

${}:表示sql拼接,如果獲取簡單類型,${}中只能使用value 。無法防止sql注入。使用${}設置參數必須考慮參數的類型,比如:使用oracle查詢條件是日期類型,如果使用${},必須人爲將${}兩邊加單引號通過to_date轉日期。(只是表示簡單的字符串拼接,而沒有mybatis的映射關係)

Select * from table where birthday >=to_date(‘${birthday}’,’yyyy-MM-dd’)

 

在沒有特殊要求的情況下,建議使用#{}佔位符

有些情況必須使用${}

比如:需要動態拼接表名,Select * from ${tablename}

動態拼接排序字段:select * from tablename order by ${username} desc

2、 傳遞pojo對象

2.1、parameterType指定 輸入參數爲pojo自定義對象時,在sql中使用${}#{}獲取pojo的屬性。

 

2.2、 包裝對象使用

開發中使用pojo傳遞查詢條件 ,查詢條件是綜合的查詢條件,不僅包括用戶查詢條件還包括其它的查詢條件(是另一個pojo),使用包裝對象傳遞輸入參數。

 

定義包裝對象將查詢條件(pojo)以類組合的方式包裝起來。

 

parameterType使用包裝對象:

包裝類

public class QueryVo {
	
	//用戶查詢條件
	//爲了查詢條件擴展方便,基於po的基礎上自定義的pojo,繼承於Po
	private UserCustom userCustom;
	
	private User user;
	
	private int[] ids;

	public UserCustom getUserCustom() {
		return userCustom;
	}

	public void setUserCustom(UserCustom userCustom) {
		this.userCustom = userCustom;
	}

	public User getUser() {
		return user;
	}

	public void setUser(User user) {
		this.user = user;
	}

	public int[] getIds() {
		return ids;
	}

	public void setIds(int[] ids) {
		this.ids = ids;
	}
	
	//基它的查詢條件

}

public class UserCustom extends User {
	
	//學生類型,擴展字段
	private String groupid;

	public String getGroupid() {
		return groupid;
	}

	public void setGroupid(String groupid) {
		this.groupid = groupid;
	}
	
	

}

映射文件.xml

<!-- 查詢用戶列表 根據用戶名稱和用戶性別查詢用戶列表 -->
	<select id="findUserList" parameterType="queryVo" resultType="user">
		select id,username username_ from user
		<!-- where自動將第一個and去掉 -->
		<where>
	     <!-- 這裏調用 queryVo的getUser方法獲 取user的值 -->
			<if test="user!=null">
				<!-- 這裏調用 queryVo的user的getUsername方法獲取username的值 -->
				<if test="user.username!=null and user.username!=''">
					and user.username = #{user.username}
				</if>
				<if test="user.sex!=null and user.sex!=''">
					and user.sex = #{user.sex}
				</if>
			</if>
	<if test="ids!=null">
		<foreach collection="ids" item="id" open="AND ("
			separator="OR" close=")">
			id =#{id} 
		</foreach>

		<!-- AND id IN (10,89,16) -->
		<!-- <foreach collection="ids" item="id" open="AND id IN ("
			separator="," close=")">
			#{id} 
		</foreach> -->
	</if>		
		</where>
	</select>


2.3、 傳遞hashmap

parameterType指定 hashmap傳遞輸入參數,#{}${}中引用mapkey

 

Sql映射文件定義如下:

<!-- 傳遞hashmap綜合查詢用戶信息 -->
<select id="selectUserByHashmap" parameterType="hashmap" resultType="user">
   select * from user where id=#{id} and username like '%${username}%'
</select>




(三)、 resultType輸出映射到java對象上


1、返回pojo

resultType:將sql查詢結果集映射爲java對象。要求sql查詢的字段名和resultType指定pojo的屬性名一致,才能映射成功。

如果全部字段和pojo的屬性名不一致,映射生成 的java對象爲空,只要有一個字段和pojo屬性名一致,映射生成 的java對象不爲空。

 

結論:sql查詢字段名和pojo的屬性名一致才能映射成功。

 

 

不管select返回的是單個 對象還是集合對象,resultType要指定單條記錄映射的java對象。



2、返回簡單類型

如果 sql查詢的結果集只有一行且一列,resultType可以返回簡單類型。


3、 返回hashmap

輸出pojo對象可以改用hashmap輸出類型,將輸出的字段名稱作爲mapkeyvalue爲字段值。

 

l Mapper.xml
<select id="findUserListReturnMap" parameterType="queryVo"  resultType="hashmap">
 	select id,username username_ from user where username = #{user.username} and sex=#{user.sex}
 </select>
l Mapper.java
Public Map  findUserListReturnMap(QueryVo queryVo);

建議不使用map作爲返回值 ,因爲需要對key在代碼中硬編碼。


(四)、mybatis的動態sql

Mybatis提供 了很多標籤,用於拼接sql語句。

  • if標籤
  • where標籤
  • set標籤
  • foreach標籤

<?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="cn.itcast.mybatis.mapper.UserMapperCustom">

	<!-- sql片段 -->
	<!-- 
	用戶查詢條件
	id:在namespace唯 一標識 
	建議以單表抽取查詢條件 
	 -->
	 <sql id="query_user_where">
	 
	     <!-- 這裏調用 queryVo的getUser方法獲 取user的值 -->
			<if test="user!=null">
				<!-- 這裏調用 queryVo的user的getUsername方法獲取username的值 -->
				<if test="user.username!=null and user.username!=''">
					and user.username = #{user.username}
				</if>
				<if test="user.sex!=null and user.sex!=''">
					and user.sex = #{user.sex}
				</if>
				
				
				
				
			</if>
	<if test="ids!=null">
		<!-- 根據傳入id數組構造查詢條件 -->
		<!-- AND (id =10 OR id =89 OR id=16) -->
		<!-- 遍歷ids數組 
		
		collection:集合,ids數組
		item:遍歷的每個對象
		open:開始遍歷時拼接的sql
		separator:遍歷的間隔符號
		close:結束 遍歷時拼接的sql
		 -->
		<foreach collection="ids" item="id" open="AND ("
			separator="OR" close=")">
			id =#{id} 
		</foreach>

		<!-- AND id IN (10,89,16) -->
		<!-- <foreach collection="ids" item="id" open="AND id IN ("
			separator="," close=")">
			#{id} 
		</foreach> -->
	</if>
			
	 </sql>
	 
	 <!-- 定義resultMap,將用戶查詢的字段和user這個pojo的屬性名作一個對應關係  -->
	 <!-- 
	 type:最終映射的java對象。
	 id:resultMap的唯一標識 
	  -->
	 <resultMap type="user" id="userListResultMap">
	 	<!-- id標籤:查詢結果集的唯 一標識 列(主鍵或唯 一標識 )
	 	column:sql查詢字段名(列名)
	 	property:pojo的屬性名
	 	
	 	result標籤:普通列
	 	 -->
	 	
	 	<id column="id_" property="id"/>
	 	<result column="username_" property="username"/>
	 	<result column="birthday_" property="birthday"/>
	 	
	 
	 </resultMap>

	<!-- 查詢用戶列表 根據用戶名稱和用戶性別查詢用戶列表 -->
	<select id="findUserList" parameterType="queryVo" resultType="user">
		select id,username username_ from user

		<!-- where自動將第一個and去掉 -->
		<where>
		    <!-- 
		    refid:指定 sql片段的id,如果要引用其它命名空間的sql片段,需要前邊加namespace
		    
		     -->
			<include refid="query_user_where"/>
		</where>
	</select>
	
	<!-- 查詢用戶列表 根據用戶名稱和用戶性別查詢用戶列表 -->
	<select id="findUserListResultMap" parameterType="queryVo" resultMap="userListResultMap">
		select id id_,username username_,birthday birthday_ from user

		<!-- where自動將第一個and去掉 -->
		<where>
		    <!-- 
		    refid:指定 sql片段的id,如果要引用其它命名空間的sql片段,需要前邊加namespace
		    
		     -->
			<include refid="query_user_where"/>
		</where>
	</select>
	

	<!-- 查詢用戶列表總數 用於分頁查詢 -->
	<select id="findUserCount" parameterType="queryVo" resultType="int">
		select count(*) from user
		<!-- where自動將第一個and去掉 -->
		<where>
		    <!-- 
		    refid:指定 sql片段的id,如果要引用其它命名空間的sql片段,需要前邊加namespace
		    
		     -->
			<include refid="query_user_where"/>
		</where>
	</select>



</mapper>

具體代碼示例,看上面兩個項目的代碼吧!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章