1. 程式人生 > 其它 >CF960G-Bandit Blues【第一類斯特林數,分治,NTT】

CF960G-Bandit Blues【第一類斯特林數,分治,NTT】

mybatis學習筆記

使用流程:

1.新建資料庫表對應的bean

2.工程下新建 lib 資料夾用於存放需要匯入的jar包(buildPath—>add to buildPath)

3.再新建一個資料夾 conf 存放配置檔案

(寫好log4j相應的配置檔案 log4J.xml )

4.從 XML 中構建 SqlSessionFactory

a.建立一個全域性配置xml檔案(mybatis-config.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>
	<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://localhost:3306/mybatis" />
				<property name="username" value="root" />
				<property name="password" value="123456" />
			</dataSource>
		</environment>
	</environments>
	<!-- 將我們寫好的sql對映檔案(EmployeeMapper.xml)一定要註冊到全域性配置檔案(mybatis-config.xml)中 -->
	<mappers>
		<mapper resource="EmployeeMapper.xml" />
	</mappers>
</configuration>

b.建立一個sql對映對應的xml檔案(EmployeeMapper.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.atguigu.mybatis.EmployeeMapper">
<!-- 
namespace:名稱空間 
id:唯一標識
resultType:返回值型別 (返回的是我們想從資料庫中得到的被封裝的型別)
#{id}:從傳遞過來的引數中取出id值
-->

	<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee">
		select id,last_name lastName,email,gender from tbl_employee where id = #{id}
	</select>
</mapper>

c.根據xml檔案(sql對映對應的xml檔案)建立一個sqlSessionFactory物件

@Test
public void test() throws IOException {
 String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2、獲取sqlSession例項,能直接執行已經對映的sql語句
		// sql的唯一標識:statement: Unique identifier matching the statement to use.
		// 執行sql要用的引數:parameter: A parameter object to pass to the statement.
		try {
			Employee employee = openSession.selectOne(
                //這裡的sql唯一表示推薦寫全地址(namespace + id) ,這裡含義即為獲取上面xml中配置對應id的sql語句
					"com.atguigu.mybatis.EmployeeMapper.getEmpById", 1);
			System.out.println(employee);
		} finally {
			openSession.close();
		}
}

總結:

 * 1、根據xml配置檔案(全域性配置檔案)建立一個SqlSessionFactory物件 有資料來源一些執行環境資訊
 * 2、sql對映檔案;配置了每一個sql,以及sql的封裝規則等。 
 * 3、將sql對映檔案註冊在全域性配置檔案中
 * 4、寫程式碼:
 * 		1)、根據全域性配置檔案得到SqlSessionFactory;
 * 		2)、使用sqlSession工廠,獲取到sqlSession物件使用他來執行增刪改查
 * 			一個sqlSession就是代表和資料庫的一次會話,用完關閉
 * 		3)、使用sql的唯一標誌來告訴MyBatis執行哪個sql。sql都是儲存在sql對映檔案中的。

以上為舊版本MyBatis的操作,現在有一種更簡潔的方式:

介面式程式設計:

1.首先定義一個介面:

import com.atguigu.mybatis.bean.Employee;

public interface EmployeeMapper {
	
	public Employee getEmpById(Integer id);

}

2.重新配置sql對映對應的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.atguigu.mybatis.dao.EmployeeMapper">
<!-- 
namespace:名稱空間;指定為介面的全類名,以便將介面抽象類與這個配置檔案動態繫結 
id:唯一標識
resultType:返回值型別
#{id}:從傳遞過來的引數中取出id值


public Employee getEmpById(Integer id);
 -->
	<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee">
		select id,last_name lastName,email,gender from tbl_employee where id = #{id}
	</select>
</mapper>

3.測試:

	@Test
	public void test01() throws IOException {
		// 1、獲取sqlSessionFactory物件
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		// 2、獲取sqlSession物件
        //SqlSession的例項不是執行緒安全的,因此不能被共享,每次使用都需重新建立
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			// 3、獲取介面的實現類物件
			//會為介面自動的建立一個代理物件,代理物件去執行增刪改查方法
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			Employee employee = mapper.getEmpById(1);
			System.out.println(mapper.getClass());
			System.out.println(employee);
		} finally {
			openSession.close();
		}

	}

總結:

 * 1、介面式程式設計
 * 	原生:		Dao		====>  DaoImpl
 * 	mybatis:	Mapper	====>  xxMapper.xml
 * 
 * 2、SqlSession代表和資料庫的一次會話;用完必須關閉;
 * 3、SqlSession和connection一樣她都是非執行緒安全。每次使用都應該去獲取新的物件。
 * 4、mapper介面沒有實現類,但是mybatis會為這個介面生成一個代理物件。
 * 		(將介面和xml進行繫結)
 * 		EmployeeMapper empMapper =	sqlSession.getMapper(EmployeeMapper.class);
 * 5、兩個重要的配置檔案:
 * 		mybatis的全域性配置檔案:包含資料庫連線池資訊,事務管理器資訊等...系統執行環境資訊
 * 		sql對映檔案:儲存了每一個sql語句的對映資訊:
 * 					將sql抽取出來。	

MyBatis全域性配置檔案(mybatis-config.xml):

引入dtd約束(規定約束xml檔案的定義和程式):

有網的條件下:

直接點選以上網址,即可下載,然後點選即可實現對於相應標籤的提示;

無網的條件下:

對應情況如下(另一個dtd檔案同理):

首先找到磁碟中MyBatis jar包的位置,以壓縮檔案形式開啟,找到相應的兩個檔案:

將這兩個檔案解壓出來,放到某一 目錄下;

將上述xml檔案中給的地址複製下來 http://mybatis.org/dtd/mybatis-3-mapper.dtd

點選 windows—>preferences—>xml—>選擇add

即可看到有相應提示:

另一個同理;

properties 引入外部配置檔案:

<?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>
	<!--
		1、mybatis可以使用properties來引入外部properties配置檔案的內容;
		resource:引入類路徑下的資源(和當前檔案在同一個資料夾下)
		url:引入網路路徑或者磁碟路徑下的資源
	  -->
	<properties resource="dbconfig.properties"></properties>
</configuration>

dbconfig.properties檔案:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=123456 

settings設定:

<!-- 
		2、settings包含很多重要的設定項
		setting:用來設定每一個設定項
			name:設定項名
			value:設定項取值
	 -->
	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
	</settings>
	

更多使用參考:

typeAliases:

<!-- 3、typeAliases:別名處理器:可以為我們的java型別起別名 
			別名不區分大小寫
	-->
	<typeAliases>
		<!-- 1、typeAlias:為某個java型別起別名
				type:指定要起別名的型別全類名;預設別名就是類名小寫;employee
				alias:指定新的別名
		 -->
		<!-- <typeAlias type="com.atguigu.mybatis.bean.Employee" alias="emp"/> -->
		
		<!-- 2、package:為某個包下的所有類批量起別名 
				name:指定包名(為當前包以及下面所有的後代包的每一個類都起一個預設別名(類名小寫),)
		-->
		<package name="com.atguigu.mybatis.bean"/>
		
		<!-- 3、批量起別名的情況下,使用@Alias註解為某個型別指定新的別名 -->
	</typeAliases>

第3種情況:

@Alias("emp")
public class Employee {....}

同時它內建了一些Java型別內建的類型別名。

typeHandlers(型別處理器):

environments(執行環境)介紹(僅做了解):

<!-- 
		4、environments:環境們,mybatis可以配置多種環境 ,default指定使用某種環境。可以達到快速切換環境。
			environment:配置一個具體的環境資訊;必須有兩個標籤;id代表當前環境的唯一標識
				transactionManager:事務管理器;
					type:事務管理器的型別;JDBC(JdbcTransactionFactory) 這個配置直接使用了 JDBC 的提交和回滾設施,它依賴從資料來源獲得的連線來管理事務作用域。|MANAGED(ManagedTransactionFactory) 這個配置幾乎沒做什麼。它從不提交或回滾一個連線,而是讓容器來管理事務的整個生命週期
						自定義事務管理器:實現TransactionFactory介面.type指定為全類名
				
				dataSource:資料來源;
					type:資料來源型別;UNPOOLED(UnpooledDataSourceFactory) 這個資料來源的實現會每次請求時開啟和關閉連線。
								|POOLED(PooledDataSourceFactory)  這種資料來源的實現利用“池”的概念將 JDBC 連線物件組織起來,避免了建立新的連線例項時所必需的初始化和認證時間。
								|JNDI(JndiDataSourceFactory) 這個資料來源實現是為了能在如 EJB 或應用伺服器這類容器中使用,容器可以集中或在外部配置資料來源,然後放置一個 JNDI 上下文的資料來源引用。
					自定義資料來源:實現DataSourceFactory介面,type是全類名
		 -->
		 

		 
	<environments default="dev_mysql">
		<environment id="dev_mysql">
			<transactionManager type="JDBC"></transactionManager>
			<dataSource type="POOLED">
                <!--這是 JDBC 驅動的 Java 類全限定名(並不是 JDBC 驅動中可能包含的資料來源類)。 -->
				<property name="driver" value="${jdbc.driver}" />
                <!--這是資料庫的 JDBC URL 地址。 -->
				<property name="url" value="${jdbc.url}" />
                <!--登入資料庫的使用者名稱。 -->
				<property name="username" value="${jdbc.username}" />
                <!--登入資料庫的密碼。 -->
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	
		

databaseIdProvider (多資料庫支援)

<!-- 5、databaseIdProvider:支援多資料庫廠商的;
		 type="DB_VENDOR":VendorDatabaseIdProvider
		 	作用就是得到資料庫廠商的標識(驅動getDatabaseProductName()),mybatis就能根據資料庫廠商標識來執行不同的sql;
		 	MySQL,Oracle,SQL Server,xxxx
	  -->
	<databaseIdProvider type="DB_VENDOR">
		<!-- 為不同的資料庫廠商起別名 -->
		<property name="MySQL" value="mysql"/>
		<property name="Oracle" value="oracle"/>
		<property name="SQL Server" value="sqlserver"/>
	</databaseIdProvider>
	
	

再將新加入的Oracle資料庫的配置資訊寫入properties(需要先將Oracle資料庫連線jar包匯入)

orcl.driver=oracle.jdbc.OracleDriver
orcl.url=jdbc:oracle:thin:@localhost:1521:orcl
orcl.username=scott
orcl.password=123456

配置xml中的執行環境:

<environment id="dev_oracle">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${orcl.driver}" />
				<property name="url" value="${orcl.url}" />
				<property name="username" value="${orcl.username}" />
				<property name="password" value="${orcl.password}" />
			</dataSource>
		</environment>
	</environments>

再將xml檔案中 改為 即可實現

mappers_sql注射對映:

<!-- 將我們寫好的sql對映檔案(EmployeeMapper.xml)一定要註冊到全域性配置檔案(mybatis-config.xml)中 -->
	<!-- 6、mappers:將sql對映註冊到全域性配置中 -->
	<mappers>
		<!-- 
			mapper:註冊一個sql對映 
				註冊配置檔案
				resource:引用類路徑下的sql對映檔案
					mybatis/mapper/EmployeeMapper.xml
				url:引用網路路徑或者磁碟路徑下的sql對映檔案
					file:///var/mappers/AuthorMapper.xml
			 <mapper resource="mybatis/mapper/EmployeeMapper.xml"/> -->	
		<!--	註冊介面
				class:引用(註冊)介面,
					1、有sql對映檔案,對映檔名必須和介面同名,並且放在與介面同一目錄下;
					2、沒有sql對映檔案,所有的sql都是利用註解寫在介面上;
					推薦:
						比較重要的,複雜的Dao介面我們來寫sql對映檔案
						不重要,簡單的Dao介面為了開發快速可以使用註解;
		-->
		<!-- <mapper class="com.atguigu.mybatis.dao.EmployeeMapperAnnotation"/> -->
		
		<!-- 批量註冊: -->
		<package name="com.atguigu.mybatis.dao"/>
	</mappers>

基於註解的抽象類 :

對映檔案(xml)及介面類都放在同一目錄下可能會比較混亂,我們可以

實際上在磁碟上他們仍然是在同一個資料夾內。

MyBatis對映檔案(EmployeeMapper.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.atguigu.mybatis.dao.EmployeeMapper">

	
	<!-- public void addEmp(Employee employee); -->
	<!-- parameterType:引數型別,可以省略, 
	獲取自增主鍵的值:
		mysql支援自增主鍵,自增主鍵值的獲取,mybatis也是利用statement.getGenreatedKeys();
		useGeneratedKeys="true";使用自增主鍵獲取主鍵值策略
		keyProperty;指定對應的主鍵屬性,也就是mybatis獲取到主鍵值以後,將這個值封裝給javaBean的哪個屬性
	-->
	<insert id="addEmp" parameterType="com.atguigu.mybatis.bean.Employee"
		useGeneratedKeys="true" keyProperty="id" databaseId="mysql">
		insert into tbl_employee(last_name,email,gender) 
		values(#{lastName},#{email},#{gender})
	</insert>
	
	<!-- 
	獲取非自增主鍵的值:
		Oracle不支援自增;Oracle使用序列來模擬自增;
		每次插入的資料的主鍵是從序列中拿到的值;如何獲取到這個值;
	 -->
	<insert id="addEmp" databaseId="oracle">
		<!-- 
		keyProperty:查出的主鍵值封裝給javaBean的哪個屬性
		order="BEFORE":當前sql在插入sql之前執行
			   AFTER:當前sql在插入sql之後執行
		resultType:查出的資料的返回值型別
		
		BEFORE執行順序:(適用於執行之前得知主鍵屬性的值用以執行操作)
			先執行selectKey查詢id的sql;查出id值封裝給javaBean的id屬性
			在執行插入的sql;就可以取出id屬性對應的值
		AFTER執行順序:(適用於執行之後還需要得知主鍵屬性的值)
			先執行插入的sql(從序列中取出新值作為id);
			再執行selectKey查詢id的sql;
		 -->
		<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
			<!-- 編寫查詢主鍵的sql語句 -->
			<!-- BEFORE-->
			select EMPLOYEES_SEQ.nextval from dual 
			<!-- AFTER:
			 select EMPLOYEES_SEQ.currval from dual -->
		</selectKey>
		
		<!-- 插入時的主鍵是從序列中拿到的 -->
		<!-- BEFORE:-->
		insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL) 
		values(#{id},#{lastName},#{email<!-- ,jdbcType=NULL -->}) 
		<!-- AFTER:
		insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL) 
		values(employees_seq.nextval,#{lastName},#{email}) -->
	</insert>
	
	<!-- public void updateEmp(Employee employee);  -->
	<update id="updateEmp">
		update tbl_employee 
		set last_name=#{lastName},email=#{email},gender=#{gender}
		where id=#{id}
	</update>
	
	<!-- public void deleteEmpById(Integer id); -->
	<delete id="deleteEmpById">
		delete from tbl_employee where id=#{id}
	</delete>
	
	
</mapper>

測試類:

	/**
	 * 測試增刪改
	 * 1、mybatis允許增刪改直接定義以下型別返回值
	 * 		Integer、Long、Boolean、void
	 * 2、我們需要手動提交資料
	 * 		sqlSessionFactory.openSession();===》手動提交
	 * 		sqlSessionFactory.openSession(true);===》自動提交
	 * @throws IOException 
	 */
	@Test
	public void test03() throws IOException{
		
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		//1、獲取到的SqlSession不會自動提交資料
		SqlSession openSession = sqlSessionFactory.openSession();
		
		try{
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			//測試新增
			Employee employee = new Employee(null, "jerry4",null, "1");
			mapper.addEmp(employee);
			System.out.println(employee.getId());
			
			//測試修改
			//Employee employee = new Employee(1, "Tom", "[email protected]", "0");
			//boolean updateEmp = mapper.updateEmp(employee);
			//System.out.println(updateEmp);
			//測試刪除
			//mapper.deleteEmpById(2);
			//2、手動提交資料
			openSession.commit();
		}finally{
			openSession.close();
		}
		
	}

引數處理:

單個引數:mybatis不會做特殊處理,
	#{引數名/任意名}:取出引數值。
	
多個引數:mybatis會做特殊處理。
	多個引數會被封裝成 一個map,
		key:param1...paramN,或者引數的索引也可以
		value:傳入的引數值
	#{}就是從map中獲取指定的key的值;
	
	異常:
	org.apache.ibatis.binding.BindingException: 
	Parameter 'id' not found. 
	Available parameters are [1, 0, param1, param2]
	操作:
		方法:public Employee getEmpByIdAndLastName(Integer id,String lastName);
		取值:#{id},#{lastName}
		
	方法一:對映檔案中對應改為:
	<!--  public Employee getEmpByIdAndLastName(Integer id,String lastName);-->
 	<select id="getEmpByIdAndLastName" resultType="com.atguigu.mybatis.bean.Employee">
 		select * from tbl_employee where id = #{param1} and last_name=#{param2}
 	</select>
	

	

【命名引數】:明確指定封裝引數時map的key;@Param("id")
	方法二:對應介面改為:
	 eg:public Employee getEmpByIdAndLastName(@Param("id")Integer 	         id,@Param("lastName")String lastName);

	多個引數會被封裝成 一個map,
		key:使用@Param註解指定的值
		value:引數值
	#{指定的key}取出對應的引數值


POJO&Map&TO:

POJO:
如果多個引數正好是我們業務邏輯的資料模型,我們就可以直接傳入pojo;
	#{屬性名}:取出傳入的pojo的屬性值	

Map:
如果多個引數不是業務模型中的資料,沒有對應的pojo,不經常使用,為了方便,我們也可以傳入map
	#{key}:取出map中對應的值

TO:
如果多個引數不是業務模型中的資料,但是經常要使用,推薦來編寫一個TO(Transfer Object)資料傳輸物件
Page{
	int index;
	int size;
}

傳入map:

首先在介面類中定義一個抽象方法;

public Employee getEmpByMap(Map<String, Object> map);

在對映檔案中加入對應的select語句:

<!-- public Employee getEmpByMap(Map<String, Object> map); -->
 	<select id="getEmpByMap" resultType="com.atguigu.mybatis.bean.Employee">
 		select * from ${tableName} where id=${id} and last_name=#{lastName}
 	</select>

一些思考:

public Employee getEmp(@Param("id")Integer id,String lastName);
	取值:id==>#{id/param1}   lastName==>#{param2}

public Employee getEmp(Integer id,@Param("e")Employee emp);
	取值:id==>#{param1}    lastName===>#{param2.lastName/e.lastName}

##特別注意:如果是Collection(List、Set)型別或者是陣列,
		 也會特殊處理。也是把傳入的list或者陣列封裝在map中。
			key:Collection(collection),如果是List還可以使用這個key(list)
				陣列(array)
public Employee getEmpById(List<Integer> ids);
	取值:取出第一個id的值:   #{list[0]}

原始碼分析(沒聽懂):

引數值的獲取:

#{}:可以獲取map中的值或者pojo物件屬性的值;
${}:可以獲取map中的值或者pojo物件屬性的值;


select * from tbl_employee where id=${id} and last_name=#{lastName}
Preparing: select * from tbl_employee where id=2 and last_name=?
	區別:
		#{}:是以預編譯的形式,將引數設定到sql語句中;PreparedStatement;防止sql注入
		${}:取出的值直接拼裝在sql語句中;會有安全問題;
		大多情況下,我們去引數的值都應該去使用#{};
		
		原生jdbc不支援佔位符的地方我們就可以使用${}進行取值
		比如分表、排序。。。;按照年份分表拆分
			select * from ${year}_salary where xxx;
			select * from tbl_employee order by ${f_name} ${order}

#{}:更豐富的用法:
	規定引數的一些規則:
	javaType、 jdbcType、 mode(儲存過程)、 numericScale、
	resultMap、 typeHandler、 jdbcTypeName、 expression(未來準備支援的功能);

	jdbcType通常需要在某種特定的條件下被設定:
		在我們資料為null的時候,有些資料庫可能不能識別mybatis對null的預設處理。比如Oracle(報錯);
		
		JdbcType OTHER:無效的型別;因為mybatis對所有的null都對映的是原生Jdbc的OTHER型別,oracle不能正確處理;
		
		由於全域性配置中:jdbcTypeForNull=OTHER;oracle不支援;兩種辦法
		1、#{email,jdbcType=OTHER};
		2、jdbcTypeForNull=NULL
			<setting name="jdbcTypeForNull" value="NULL"/>
			

select 返回List:

<!-- public List<Employee> getEmpsByLastNameLike(String lastName); -->
	<!--resultType:如果返回的是一個集合,要寫集合中元素的型別  -->
	<select id="getEmpsByLastNameLike" resultType="com.atguigu.mybatis.bean.Employee">
		select * from tbl_employee where last_name like #{lastName}
	</select>

測試類中的相關程式碼

List<Employee> like = mapper.getEmpsByLastNameLike("%e%");
			for (Employee employee : like) {
				System.out.println(employee);
			}

select記錄封裝map:

 <!--多條 使用id作為map的key -->
<!--public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName);  -->
 	<select id="getEmpByLastNameLikeReturnMap" resultType="com.atguigu.mybatis.bean.Employee">
 		select * from tbl_employee where last_name like #{lastName}
 	</select>
 <!--單條-->
 	<!--public Map<String, Object> getEmpByIdReturnMap(Integer id);  -->
 	<select id="getEmpByIdReturnMap" resultType="map">//這裡時因為mybatis為常用Java型別起了別名
 		select * from tbl_employee where id=#{id}
 	</select>

介面類中的相應抽象方法定義:

//多條記錄封裝一個map:Map<Integer,Employee>:鍵是這條記錄的主鍵,值是記錄封裝後的javaBean
	//@MapKey:告訴mybatis封裝這個map的時候使用哪個屬性作為map的key
	@MapKey("id")
	public Map<id, Employee> getEmpByLastNameLikeReturnMap(String lastName);
	
	//返回一條記錄的map;key就是列名,值就是對應的值
	public Map<String, Object> getEmpByIdReturnMap(Integer id);

(查出資料庫中列名與JavaBean中屬性名不一樣時,有三種可以實現將其封裝的方法 1.查詢語句中寫一個別名 2.符合駝峰命名開啟駝峰命名 3.使用resultMap自定義結果集)

resultMap 自定義結果對映規則:

對應抽象類(EmployeeMapperPlus類):

package com.atguigu.mybatis.dao;

import java.util.List;

import com.atguigu.mybatis.bean.Employee;

public interface EmployeeMapperPlus {
	
	public Employee getEmpById(Integer id);
	
	public Employee getEmpAndDept(Integer id);
	
	public Employee getEmpByIdStep(Integer id);
	//根據部門id查詢員工放到list集合裡
	public List<Employee> getEmpsByDeptId(Integer deptId);

}

相應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.atguigu.mybatis.dao.EmployeeMapperPlus">

	<!--自定義某個javaBean的封裝規則
	type:自定義規則的Java型別
	id:唯一id方便引用
	  -->
	<resultMap type="com.atguigu.mybatis.bean.Employee" id="MySimpleEmp">
		<!--指定主鍵列的封裝規則
		id定義主鍵會底層有優化;
		column:指定哪一列
		property:指定對應的javaBean屬性
		  -->
		<id column="id" property="id"/>
		<!-- 定義普通列封裝規則 -->
		<result column="last_name" property="lastName"/>
		<!-- 其他不指定的列會自動封裝:我們只要寫resultMap就把全部的對映規則都寫上。 -->
		<result column="email" property="email"/>
		<result column="gender" property="gender"/>
	</resultMap>
	
	<!-- resultMap:自定義結果集對映規則;  -->
	<!-- public Employee getEmpById(Integer id); -->
	<select id="getEmpById"  resultMap="MySimpleEmp">
		select * from tbl_employee where id=#{id}
	</select>
	

</mapper>

關聯物件封裝:

	<!-- 
	場景一:
		查詢Employee的同時查詢員工對應的部門
		employee類中有一個department類  相應employee資料表中對應一個外來鍵指向department表
		Employee===Department
		一個員工有與之對應的部門資訊;
		id  last_name  gender    d_id     did  dept_name (private Department dept;)
	 -->
	 
	 
	<!--
		第一種方式:聯合查詢:級聯屬性封裝結果集
	  -->
	<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp">
		<id column="id" property="id"/>
		<result column="last_name" property="lastName"/>
		<result column="gender" property="gender"/>
		<result column="did" property="dept.id"/>
		<result column="dept_name" property="dept.departmentName"/>
	</resultMap>

<!--  public Employee getEmpAndDept(Integer id);-->
	<select id="getEmpAndDept" resultMap="MyDifEmp">
		SELECT e.id id,e.last_name last_name,e.gender gender,e.d_id d_id,
		d.id did,d.dept_name dept_name FROM tbl_employee e,tbl_dept d
		WHERE e.d_id=d.id AND e.id=#{id}
	</select>
	

	<!-- 
		第二種方式:使用association定義關聯的單個物件的封裝規則;
	 -->
	<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp2">
		<id column="id" property="id"/>
		<result column="last_name" property="lastName"/>
		<result column="gender" property="gender"/>
		
		<!--  association可以指定聯合的javaBean物件
		property="dept":指定哪個屬性是聯合的物件
		javaType:指定這個屬性物件的型別[不能省略]
		-->
        <!-- 在association裡定義封裝的物件的規則-->
		<association property="dept" javaType="com.atguigu.mybatis.bean.Department">
            <!-- 注意這裡的Column屬性不能寫成和上面一樣的id,因為預設為第一個id,這裡的						property="id"預設是department下的id-->
			<id column="did" property="id"/>
			<result column="dept_name" property="departmentName"/>
		</association>
	</resultMap>
	
	

association進行分步查詢:

employMapperPlus.xml檔案:

<!-- 使用association進行分步查詢:一對一
		1、先按照員工id查詢員工資訊
		2、根據查詢員工資訊中的d_id值去部門表查出部門資訊
		3、部門設定到員工中;
	 -->
	 
	 <!--  id  last_name  email   gender    d_id   -->
	 <resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpByStep">
	 	<id column="id" property="id"/>
	 	<result column="last_name" property="lastName"/>
	 	<result column="email" property="email"/>
	 	<result column="gender" property="gender"/>
	 	<!-- association定義關聯物件的封裝規則
	 		select:表明當前屬性是呼叫select指定的方法查出的結果
	 		column:指定將哪一列的值傳給這個方法
	 		
	 		流程:使用select指定的方法(傳入column指定的這列引數的值)查出物件,並封裝給property指定的屬性
	 	 -->
 		<association property="dept" 
	 		select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
	 		column="d_id">
 		</association>
	 </resultMap>
	 <!--  public Employee getEmpByIdStep(Integer id);-->
	 <select id="getEmpByIdStep" resultMap="MyEmpByStep">
	 	select * from tbl_employee where id=#{id}
	 	<if test="_parameter!=null">
	 		and 1=1
	 	</if>
	 </select>
	 
	 <!-- 可以使用延遲載入(懶載入);(按需載入)
	 	Employee==>Dept:
	 		我們每次查詢Employee物件的時候,都將一起查詢出來。
	 		部門資訊在我們使用的時候再去查詢;
	 		分段查詢的基礎之上加上兩個配置:
	  -->(在MyBatis-config.xml中配置)
	<setting name="lazyLoadingEnabled" value="true"/>
		<setting name="aggressiveLazyLoading" value="false"/>

根據需求建立departmentMapper類以及departmentMapper.xml配置檔案:

package com.atguigu.mybatis.dao;

import com.atguigu.mybatis.bean.Department;

public interface DepartmentMapper {
	
	public Department getDeptById(Integer id);
	//增強版 在獲取部門id的同時將部門員工查出來
	public Department getDeptByIdPlus(Integer id);
	//collection分步查詢
	public Department getDeptByIdStep(Integer id);
}

departmentMapper.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.atguigu.mybatis.dao.DepartmentMapper">
	<!--public Department getDeptById(Integer id);  -->
	<select id="getDeptById" resultType="com.atguigu.mybatis.bean.Department">
		select id,dept_name departmentName from tbl_dept where id=#{id}
	</select>
</mapper>

test測試類:

public void test05() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		SqlSession openSession = sqlSessionFactory.openSession();
		try{
			EmployeeMapperPlus mapper = openSession.getMapper(EmployeeMapperPlus.class);
			/*Employee empById = mapper.getEmpById(1);
			System.out.println(empById);*/
			/*Employee empAndDept = mapper.getEmpAndDept(1);
			System.out.println(empAndDept);
			System.out.println(empAndDept.getDept());*/
			Employee employee = mapper.getEmpByIdStep(3);
			System.out.println(employee);
			//System.out.println(employee.getDept());
			System.out.println(employee.getDept()); 
			openSession.close();
		}

collection定義關聯集合封裝規則:一對多

對應xml檔案(departmentMapper.xml)配置:

<!-- 
	public class Department {
			private Integer id;
			private String departmentName;
			private List<Employee> emps;
	  did  dept_name  ||  eid  last_name  email   gender  
	 -->
	 
	<!--巢狀結果集的方式,使用collection標籤定義關聯的集合型別的屬性封裝規則  -->
	<resultMap type="com.atguigu.mybatis.bean.Department" id="MyDept">
		<id column="did" property="id"/>
		<result column="dept_name" property="departmentName"/>
		<!-- 
			collection定義關聯集合型別的屬性的封裝規則 
			ofType:指定集合裡面元素的型別
			注:這裡是將分割號後面的employee中封裝成list集合
		-->
		<collection property="emps" ofType="com.atguigu.mybatis.bean.Employee">
			<!-- 定義這個集合中元素的封裝規則 -->
			<id column="eid" property="id"/>
			<result column="last_name" property="lastName"/>
			<result column="email" property="email"/>
			<result column="gender" property="gender"/>
		</collection>
	</resultMap>
	<!-- public Department getDeptByIdPlus(Integer id); -->
	<select id="getDeptByIdPlus" resultMap="MyDept">
		SELECT d.id did,d.dept_name dept_name,
				e.id eid,e.last_name last_name,e.email email,e.gender gender
		FROM tbl_dept d
		LEFT JOIN tbl_employee e
		ON d.id=e.d_id
		WHERE d.id=#{id}
	</select>
	
	

collection分佈查詢延遲載入:

departmentMapper.xml

<!-- collection:分段查詢 -->
	<resultMap type="com.atguigu.mybatis.bean.Department" id="MyDeptStep">
		<id column="id" property="id"/>
		<id column="dept_name" property="departmentName"/>
		<collection property="emps" 
			select="com.atguigu.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId"
			<!-- column = "id" 為原始,以下為拓展演示案例) -->
                    column="{deptId=id}" fetchType="lazy"></collection>
	</resultMap>
	<!-- public Department getDeptByIdStep(Integer id); -->
	<select id="getDeptByIdStep" resultMap="MyDeptStep">
		select id,dept_name from tbl_dept where id=#{id}
	</select>
	
	<!-- 擴充套件:多列的值傳遞過去:
			將多列的值封裝map傳遞;
			column="{key1=column1,key2=column2}"
		fetchType="lazy":表示使用延遲載入;
				- lazy:延遲
				- eager:立即
	 -->

EmployMapperPlus.xml檔案配置:

<!-- 
	場景二:
		查詢部門的時候將部門對應的所有員工資訊也查詢出來:註釋在DepartmentMapper.xml中
	 -->
	<!-- public List<Employee> getEmpsByDeptId(Integer deptId); -->
	<select id="getEmpsByDeptId" resultType="com.atguigu.mybatis.bean.Employee">
		select * from tbl_employee where d_id=#{deptId}
	</select>
	

對應測試類:

@Test
	public void test06() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		SqlSession openSession = sqlSessionFactory.openSession();
		
		try{
			DepartmentMapper mapper = openSession.getMapper(DepartmentMapper.class);
			/*Department department = mapper.getDeptByIdPlus(1);
			System.out.println(department);
			System.out.println(department.getEmps());*/
			Department deptByIdStep = mapper.getDeptByIdStep(1);
			System.out.println(deptByIdStep.getDepartmentName());
			System.out.println(deptByIdStep.getEmps());
		}finally{
			openSession.close();
		}
	}

discriminator鑑別器:
EmployMapperPlus.xml檔案配置:

	<!-- <discriminator javaType=""></discriminator>
		鑑別器:mybatis可以使用discriminator判斷某列的值,然後根據某列的值改變封裝行為
		封裝Employee:
			如果查出的是女生:就把部門資訊查詢出來,否則不查詢;
			如果是男生,把last_name這一列的值賦值給email;
	 -->
	 <resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpDis">
	 	<id column="id" property="id"/>
	 	<result column="last_name" property="lastName"/>
	 	<result column="email" property="email"/>
	 	<result column="gender" property="gender"/>
	 	<!--
	 		column:指定判定的列名
	 		javaType:列值對應的java型別  -->
	 	<discriminator javaType="string" column="gender">
	 		<!--女生  resultType:指定封裝的結果型別;不能缺少。/resultMap-->
	 		<case value="0" resultType="com.atguigu.mybatis.bean.Employee">
	 			<association property="dept" 
			 		select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
			 		column="d_id">
		 		</association>
	 		</case>
	 		<!--男生 ;如果是男生,把last_name這一列的值賦值給email; -->
	 		<case value="1" resultType="com.atguigu.mybatis.bean.Employee">
		 		<id column="id" property="id"/>
			 	<result column="last_name" property="lastName"/>
			 	<result column="last_name" property="email"/>
			 	<result column="gender" property="gender"/>
	 		</case>
	 	</discriminator>
	 </resultMap>

動態sql(動態拼裝sql):

抽象介面類:

package com.atguigu.mybatis.dao;

import java.util.List;

import org.apache.ibatis.annotations.Param;

import com.atguigu.mybatis.bean.Employee;

public interface EmployeeMapperDynamicSQL {
	
	public List<Employee> getEmpsTestInnerParameter(Employee employee);
	
	//攜帶了哪個欄位查詢條件就帶上這個欄位的值
	public List<Employee> getEmpsByConditionIf(Employee employee);
	
	public List<Employee> getEmpsByConditionTrim(Employee employee);
	
	public List<Employee> getEmpsByConditionChoose(Employee employee);
	
	public void updateEmp(Employee employee);
	
	//查詢員工id'在給定集合中的
	public List<Employee> getEmpsByConditionForeach(@Param("ids")List<Integer> ids);
	
	public void addEmps(@Param("emps")List<Employee> emps);

}

EmployeeMapperDynamicSQL.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.atguigu.mybatis.dao.EmployeeMapperDynamicSQL">
	<!-- 
• if:判斷
• choose (when, otherwise):分支選擇;帶了break的swtich-case
	如果帶了id就用id查,如果帶了lastName就用lastName查;只會進入其中一個
• trim 字串擷取(where(封裝查詢條件), set(封裝修改條件))
• foreach 遍歷集合
	 -->
	 <!-- 查詢員工,要求,攜帶了哪個欄位查詢條件就帶上這個欄位的值 -->
	 <!-- public List<Employee> getEmpsByConditionIf(Employee employee); -->
	 <select id="getEmpsByConditionIf" resultType="com.atguigu.mybatis.bean.Employee">
	 	select * from tbl_employee
	 	<!-- where -->
	 	<where>
		 	<!-- test:判斷表示式(OGNL)
		 	OGNL參照PPT或者官方文件。
		 	  	 c:if  test
		 	從引數中取值進行判斷
		 	
		 	遇見特殊符號應該去寫轉義字元:
		 	&&:&amp;&amp;
		 	"":&quot;&quot;
		 	具體可參考 w3cschool html 8859-1符號實體
		 	-->
		 	<if test="id!=null">
		 		id=#{id}
		 	</if>
		 	<if test="lastName!=null &amp;&amp; lastName!=&quot;&quot;">
		 		and last_name like #{lastName}
		 	</if>
		 	<if test="email!=null and email.trim()!=&quot;&quot;">
		 		and email=#{email}
		 	</if> 
		 	<!-- ognl會進行字串與數字的轉換判斷  "0"==0 -->
		 	<if test="gender==0 or gender==1">
		 	 	and gender=#{gender}
		 	</if>
	 	</where>
	 </select>
    
    
    <!-- 自定義字串擷取-->
     <!--public List<Employee> getEmpsByConditionTrim(Employee employee);  -->
	 <select id="getEmpsByConditionTrim" resultType="com.atguigu.mybatis.bean.Employee">
	 	select * from tbl_employee
	 	<!-- 後面多出的and或者or where標籤不能解決 
	 	prefix="":字首:trim標籤體中是整個字串拼串 後的結果。
	 			prefix給拼串後的整個字串加一個字首 
	 	prefixOverrides="":
	 			字首覆蓋: 去掉整個字串前面多餘的字元
	 	suffix="":字尾
	 			suffix給拼串後的整個字串加一個字尾 
	 	suffixOverrides=""
	 			字尾覆蓋:去掉整個字串後面多餘的字元
	 			
	 	-->
	 	<!-- 自定義字串的擷取規則 -->
	 	<trim prefix="where" suffixOverrides="and">
	 		<if test="id!=null">
		 		id=#{id} and
		 	</if>
		 	<if test="lastName!=null &amp;&amp; lastName!=&quot;&quot;">
		 		last_name like #{lastName} and
		 	</if>
		 	<if test="email!=null and email.trim()!=&quot;&quot;">
		 		email=#{email} and
		 	</if> 
		 	<!-- ognl會進行字串與數字的轉換判斷  "0"==0 -->
		 	<if test="gender==0 or gender==1">
		 	 	gender=#{gender}
		 	</if>
		 </trim>
	 </select>
 
    
    <!--choose分支選擇-->
	 <!-- public List<Employee> getEmpsByConditionChoose(Employee employee); -->
	 <select id="getEmpsByConditionChoose" resultType="com.atguigu.mybatis.bean.Employee">
	 	select * from tbl_employee 
	 	<where>
	 		<!-- 如果帶了id就用id查,如果帶了lastName就用lastName查;只會進入其中一個 -->
	 		<choose>
	 			<when test="id!=null">
	 				id=#{id}
	 			</when>
	 			<when test="lastName!=null">
	 				last_name like #{lastName}
	 			</when>
	 			<when test="email!=null">
	 				email = #{email}
	 			</when>
	 			<otherwise>
	 				gender = 0
	 			</otherwise>
	 		</choose>
	 	</where>
	 </select>
	 
	 <!--public void updateEmp(Employee employee);  -->
	 <update id="updateEmp">
	 	<!-- Set標籤的使用:set 元素會動態地在行首插入 SET 關鍵字,並會刪掉額外的逗號 -->
	 	update tbl_employee 
		<set>
			<if test="lastName!=null">
				last_name=#{lastName},
			</if>
			<if test="email!=null">
				email=#{email},
			</if>
			<if test="gender!=null">
				gender=#{gender}
			</if>
		</set>
		where id=#{id} 
<!-- 		
		Trim:更新拼串
		update tbl_employee 
		<trim prefix="set" suffixOverrides=",">
			<if test="lastName!=null">
				last_name=#{lastName},
			</if>
			<if test="email!=null">
				email=#{email},
			</if>
			<if test="gender!=null">
				gender=#{gender}
			</if>
		</trim>
		where id=#{id}  -->
	 </update>
	 
    
    <!-- foreach遍歷集合-->
	 <!--public List<Employee> getEmpsByConditionForeach(List<Integer> ids);  -->
	 <select id="getEmpsByConditionForeach" resultType="com.atguigu.mybatis.bean.Employee">
	 	select * from tbl_employee
	 	<!--
	 		collection:指定要遍歷的集合:
	 			list型別的引數會特殊處理封裝在map中,map的key就叫list
	 		item:將當前遍歷出的元素賦值給指定的變數
	 		separator:每個元素之間的分隔符
	 		open:遍歷出所有結果拼接一個開始的字元
	 		close:遍歷出所有結果拼接一個結束的字元
	 		index:索引。遍歷list的時候是index就是索引,item就是當前值
	 				      遍歷map的時候index表示的就是map的key,item就是map的值
	 		
	 		#{變數名}就能取出變數的值也就是當前遍歷出的元素
	 	  -->
	 	<foreach collection="ids" item="item_id" separator=","
	 		open="where id in(" close=")">
	 		#{item_id}
	 	</foreach>
	 </select>
	 
</mapper>	 

測試類:

@Test
	public void testDynamicSql() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		SqlSession openSession = sqlSessionFactory.openSession();
		try{
			EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class);
			//select * from tbl_employee where id=? and last_name like ?
			//測試if\where
			Employee employee = new Employee(1, "Admin", null, null);
		/*	List<Employee> emps = mapper.getEmpsByConditionIf(employee );
			for (Employee emp : emps) {
				System.out.println(emp);
			}*/
			
			//查詢的時候如果某些條件沒帶可能sql拼裝會有問題
			//1、給where後面加上1=1,以後的條件都and xxx.
			//2、mybatis使用where標籤來將所有的查詢條件包括在內。mybatis就會將where標籤中拼裝的sql,多出來的and或者or去掉
				//where只會去掉第一個多出來的and或者or。
			
			//測試Trim
			/*List<Employee> emps2 = mapper.getEmpsByConditionTrim(employee);
			for (Employee emp : emps2) {
				System.out.println(emp);
			}*/
			
			
			//測試choose
			/*List<Employee> list = mapper.getEmpsByConditionChoose(employee);
			for (Employee emp : list) {
				System.out.println(emp);
			}*/
			
			//測試set標籤
			/*mapper.updateEmp(employee);
			openSession.commit();*/
			
			List<Employee> list = mapper.getEmpsByConditionForeach(Arrays.asList(1,2));
			for (Employee emp : list) {
				System.out.println(emp);
			}
			
		}finally{
			openSession.close();
		}
	}

foreach批量插入:

 <!-- 批量儲存 -->
	 <!--public void addEmps(@Param("emps")List<Employee> emps);  -->
	
	<insert id="addEmps">
	 	insert into tbl_employee(
	 		<include refid="insertColumn"></include>
	 	) 
		values
		<foreach collection="emps" item="emp" separator=",">
			(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
		</foreach>
         <!--MySQL下批量儲存:可以foreach遍歷   mysql支援values(),(),()語法-->
	 </insert><!--   -->
	 
	 <!-- 這種方式需要資料庫連線屬性allowMultiQueries=true;(在dbconfig.properties裡 jdbc.url里加入)
	 	這種分號分隔多個sql可以用於其他的批量操作(刪除,修改) -->
	 <!-- <insert id="addEmps">
	 	<foreach collection="emps" item="emp" separator=";">
	 		insert into tbl_employee(last_name,email,gender,d_id)
	 		values(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
	 	</foreach>
	 </insert> -->
	 
	 <!-- Oracle資料庫批量儲存: 
	 	Oracle不支援values(),(),()
	 	Oracle支援的批量方式
	 	1、多個insert放在begin - end裡面
	 		begin
			    insert into employees(employee_id,last_name,email) 
			    values(employees_seq.nextval,'test_001','[email protected]');
			    insert into employees(employee_id,last_name,email) 
			    values(employees_seq.nextval,'test_002','[email protected]');
			end;
		2、利用中間表:
			insert into employees(employee_id,last_name,email)
		       select employees_seq.nextval,lastName,email from(
		              select 'test_a_01' lastName,'test_a_e01' email from dual
		              union
		              select 'test_a_02' lastName,'test_a_e02' email from dual
		              union
		              select 'test_a_03' lastName,'test_a_e03' email from dual
		       )	
	 -->
	 <insert id="addEmps" databaseId="oracle">
	 	<!-- oracle第一種批量方式 -->
	 	<!-- <foreach collection="emps" item="emp" open="begin" close="end;">
	 		insert into employees(employee_id,last_name,email) 
			    values(employees_seq.nextval,#{emp.lastName},#{emp.email});
	 	</foreach> -->
	 	
	 	<!-- oracle第二種批量方式  -->
	 	insert into employees(
	 		<!-- 引用外部定義的sql -->
	 		<include refid="insertColumn">
	 			<property name="testColomn" value="abc"/>
	 		</include>
	 	)
	 			<foreach collection="emps" item="emp" separator="union"
	 				open="select employees_seq.nextval,lastName,email from("
	 				close=")">
	 				select #{emp.lastName} lastName,#{emp.email} email from dual
	 			</foreach>
	 </insert>
	 

<!--內建引數-->
	 <!-- 兩個內建引數:
	 	不只是方法傳遞過來的引數可以被用來判斷,取值。。。
	 	mybatis預設還有兩個內建引數:
	 	_parameter:代表整個引數
	 		單個引數:_parameter就是這個引數
	 		多個引數:引數會被封裝為一個map;_parameter就是代表這個map
	 	
	 	_databaseId:如果配置了databaseIdProvider標籤。
	 		_databaseId就是代表當前資料庫的別名oracle
	  -->
	  
	  <!--public List<Employee> getEmpsTestInnerParameter(Employee employee);  -->
	  <select id="getEmpsTestInnerParameter" resultType="com.atguigu.mybatis.bean.Employee">
	  		<!-- bind:可以將OGNL表示式的值繫結到一個變數中,方便後來引用這個變數的值 -->
	  		<bind name="_lastName" value="'%'+lastName+'%'"/>
	  		<if test="_databaseId=='mysql'">
	  			select * from tbl_employee
                <!-- "_parameter!=null"即可保證查詢的employee不為空-->
	  			<if test="_parameter!=null">
	  				where last_name like #{lastName}
	  			</if>
	  		</if>
	  		<if test="_databaseId=='oracle'">
	  			select * from employees
	  			<if test="_parameter!=null">
	  				where last_name like #{_parameter.lastName}
	  			</if>
	  		</if>
	  </select>
	  
	  <!-- 
	  	抽取可重用的sql片段。方便後面引用 
	  	1、sql抽取:經常將要查詢的列名,或者插入用的列名抽取出來方便引用
	  	2、include來引用已經抽取的sql:
	  	3、include還可以自定義一些property,sql標籤內部就能使用自定義的屬性
	  			include-property:取值的正確方式${prop},
	  			#{不能使用這種方式}
	  -->
	  <sql id="insertColumn">
	  		<if test="_databaseId=='oracle'">
	  			employee_id,last_name,email
	  		</if>
	  		<if test="_databaseId=='mysql'">
	  			last_name,email,gender,d_id
	  		</if>
	  </sql>
	  

測試類:

@Test
	public void testBatchSave() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		SqlSession openSession = sqlSessionFactory.openSession();
		try{
			EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class);
			List<Employee> emps = new ArrayList<>();
			emps.add(new Employee(null, "smith0x1", "[email protected]", "1",new Department(1)));
			emps.add(new Employee(null, "allen0x1", "[email protected]", "0",new Department(1)));
			mapper.addEmps(emps);
			openSession.commit();
		}finally{
			openSession.close();
		}
	}

MyBatis快取機制:

mybatis作為一個持久化的框架定義了二級快取。

一級快取:

 	 * 一級快取:(本地快取):sqlSession級別的快取。一級快取是一直開啟的;SqlSession級別的一個Map
	 * 		與資料庫同一次會話期間查詢到的資料會放在本地快取中。
	 * 		以後如果需要獲取相同的資料,直接從快取中拿,沒必要再去查詢資料庫;
	 * 
	 * 		一級快取失效情況(沒有使用到當前一級快取的情況,效果就是,還需要再向資料庫發出查詢):
	 * 		1、sqlSession不同。
	 * 		2、sqlSession相同,查詢條件不同.(當前一級快取中還沒有這個資料)
	 * 		3、sqlSession相同,兩次查詢之間執行了增刪改操作(這次增刪改可能對當前資料有影響)
	 * 		4、sqlSession相同,手動清除了一級快取(快取清空)

測試類:

@Test
	public void testFirstLevelCache() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		SqlSession openSession = sqlSessionFactory.openSession();
		try{
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			Employee emp01 = mapper.getEmpById(1);
			System.out.println(emp01);
			Employee emp02 = mapper.getEmpById(1);
            System.out.println(emp01==emp02);//true
            
			

一級快取失效的情況:

	 * 		一級快取失效情況(沒有使用到當前一級快取的情況,效果就是,還需要再向資料庫發出查詢):
	 * 		1、sqlSession不同。
	 * 		2、sqlSession相同,查詢條件不同.(當前一級快取中還沒有這個資料)
	 * 		3、sqlSession相同,兩次查詢之間執行了增刪改操作(這次增刪改可能對當前資料有影響)
	 * 		4、sqlSession相同,手動清除了一級快取(快取清空)
	 
	 //xxxxx
			//1、sqlSession不同。
			//SqlSession openSession2 = sqlSessionFactory.openSession();
			//EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
			//System.out.println(emp01==emp02);//false
			//2、sqlSession相同,查詢條件不同
			//Employee emp03 = mapper.getEmpById(3);
            //System.out.println(emp01==emp0);//false
			//3、sqlSession相同,兩次查詢之間執行了增刪改操作(這次增刪改可能對當前資料有影響)
			//mapper.addEmp(new Employee(null, "testCache", "cache", "1"));
			//System.out.println("資料新增成功");
			
         	//Employee emp02 = mapper.getEmpById(1);
			//System.out.println(emp02);
			//System.out.println(emp01==emp02);//false
         
			//4、sqlSession相同,手動清除了一級快取(快取清空)
			//openSession.clearCache();
		
			//Employee emp02 = mapper.getEmpById(1);
			//System.out.println(emp02);
			//System.out.println(emp01==emp02);//false
			
			//openSession2.close();
		}finally{
			openSession.close();
		}
	}
}

二級快取:

	 * 二級快取:(全域性快取):基於namespace級別的快取:一個namespace對應一個二級快取:
	 * 		工作機制:
	 * 		1、一個會話,查詢一條資料,這個資料就會被放在當前會話的一級快取中;
	 * 		2、如果會話關閉;一級快取中的資料會被儲存到二級快取中;新的會話查詢資訊,就可以參照二級快取中的內容;
	 * 		3、sqlSession===EmployeeMapper==>Employee
	 * 						DepartmentMapper===>Department
	 * 			不同namespace查出的資料會放在自己對應的快取中(map)
	 * 			效果:資料會從二級快取中獲取
	 * 				查出的資料都會被預設先放在一級快取中。
	 * 				只有會話提交或者關閉以後,一級快取中的資料才會轉移到二級快取中
	 * 		使用:
	 * 			1)、開啟全域性二級快取配置:<setting name="cacheEnabled" value="true"/>
	 * 			2)、去mapper.xml中配置使用二級快取:
	 * 				<cache></cache>
	 * 			3)、我們的POJO需要實現序列化介面(department類和employee類)
	 * 	
	 * 和快取有關的設定/屬性:
	 * 			1)、cacheEnabled=true:false:關閉快取(二級快取關閉)(一級快取一直可用的)
	 * 			2)、每個select標籤都有useCache="true":
	 * 					false:不使用快取(一級快取依然使用,二級快取不使用)
	 * 			3)、【每個增刪改標籤的:flushCache="true":(一級二級都會清除)】
	 * 					增刪改執行完成後就會清楚快取;
	 * 					測試:flushCache="true":一級快取就清空了;二級也會被清除;
	 * 					查詢標籤:flushCache="false":
	 * 						如果flushCache=true;每次查詢之後都會清空快取;快取是沒有被使用的;
	 * 			4)、sqlSession.clearCache();只是清除當前session的一級快取;與二級無關。
	 * 			5)、localCacheScope:本地快取作用域:(一級快取SESSION);當前會話的所有資料儲存在會話快取中;
	 * 								STATEMENT:可以禁用一級快取;		
	 * 				
	

departmentMapper.xml中的檔案配置:

	
 <cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache> 
	<!--  
	eviction:快取的回收策略:
		• LRU – 最近最少使用的:移除最長時間不被使用的物件。
		• FIFO – 先進先出:按物件進入快取的順序來移除它們。
		• SOFT – 軟引用:移除基於垃圾回收器狀態和軟引用規則的物件。
		• WEAK – 弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的物件。
		• 預設的是 LRU。
	flushInterval:快取重新整理間隔
		快取多長時間清空一次,預設不清空,設定一個毫秒值
	readOnly:是否只讀:
		true:只讀;mybatis認為所有從快取中獲取資料的操作都是隻讀操作,不會修改資料。
				 mybatis為了加快獲取速度,直接就會將資料在快取中的引用交給使用者。不安全,速度快
		false:非只讀:mybatis覺得獲取的資料可能會被修改。
				mybatis會利用序列化&反序列的技術克隆一份新的資料給你。安全,速度慢
	size:快取存放多少元素;
	type="":指定自定義快取的全類名;
			實現Cache介面即可;
	-->

測試類:

@Test
	public void testSecondLevelCache02() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		SqlSession openSession = sqlSessionFactory.openSession();
		SqlSession openSession2 = sqlSessionFactory.openSession();
		try{
			//1、
			DepartmentMapper mapper = openSession.getMapper(DepartmentMapper.class);
			DepartmentMapper mapper2 = openSession2.getMapper(DepartmentMapper.class);
			
			Department deptById = mapper.getDeptById(1);
			System.out.println(deptById);
			openSession.close();
			
			
			
			Department deptById2 = mapper2.getDeptById(1);
			System.out.println(deptById2);
			openSession2.close();
			//第二次查詢是從二級快取中拿到的資料,並沒有傳送新的sql
			
		}finally{
			
		}
	}

第三方cache包:

*第三方快取整合:
	 *		1)、匯入第三方快取包即可;
	 *		2)、匯入與第三方快取整合的適配包;官方有;
	 *		3)、mapper.xml中使用自定義快取
	 *		<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>

首先匯入這三個包:

修改相應的xxxMapper.xml檔案

<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>

配置ehcache.xml檔案

<?xml version="1.0" encoding="UTF-8"?>

-<ehcache xsi:noNamespaceSchemaLocation="../config/ehcache.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<!-- 屬性說明: l diskStore:指定資料在磁碟中的儲存位置。 l defaultCache:當藉助CacheManager.add("demoCache")建立Cache時,EhCache便會採用<defalutCache/>指定的的管理策略 以下屬性是必須的: l maxElementsInMemory - 在記憶體中快取的element的最大數目 l maxElementsOnDisk - 在磁碟上快取的element的最大數目,若是0表示無窮大 l eternal - 設定快取的elements是否永遠不過期。如果為true,則快取的資料始終有效,如果為false那麼還要根據timeToIdleSeconds,timeToLiveSeconds判斷 l overflowToDisk - 設定當記憶體快取溢位的時候是否將過期的element快取到磁碟上 以下屬性是可選的: l timeToIdleSeconds - 當快取在EhCache中的資料前後兩次訪問的時間超過timeToIdleSeconds的屬性取值時,這些資料便會刪除,預設值是0,也就是可閒置時間無窮大 l timeToLiveSeconds - 快取element的有效生命期,預設是0.,也就是element存活時間無窮大 diskSpoolBufferSizeMB 這個引數設定DiskStore(磁碟快取)的快取區大小.預設是30MB.每個Cache都應該有自己的一個緩衝區. l diskPersistent - 在VM重啟的時候是否啟用磁碟儲存EhCache中的資料,預設是false。 l diskExpiryThreadIntervalSeconds - 磁碟快取的清理執行緒執行間隔,預設是120秒。每個120s,相應的執行緒會進行一次EhCache中資料的清理工作 l memoryStoreEvictionPolicy - 當記憶體快取達到最大,有新的element加入的時候, 移除快取中element的策略。預設是LRU(最近最少使用),可選的有LFU(最不常使用)和FIFO(先進先出) -->

<!-- 磁碟儲存路徑 -->


<diskStore path="D:\44\ehcache"/>

<defaultCache 
              memoryStoreEvictionPolicy="LRU" 
              diskExpiryThreadIntervalSeconds="120" 
              timeToLiveSeconds="120" 
              timeToIdleSeconds="120" 
              overflowToDisk="true" 
              eternal="false" 
              maxElementsOnDisk="10000000" 
              maxElementsInMemory="1"> 
    </defaultCache>

</ehcache>

mybatis工作原理圖:

MyBatis整合Spring:

這裡由於還沒學spring放到後續!

逆向工程:

首先下載generator,解壓壓縮包將其匯入:

配置mgb.xml檔案:

<?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>

	<!-- 
		targetRuntime="MyBatis3Simple":生成簡單版的CRUD
		MyBatis3:豪華版
	
	 -->
  <context id="DB2Tables" targetRuntime="MyBatis3">
  	<!-- jdbcConnection:指定如何連線到目標資料庫 -->
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
        connectionURL="jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true"
        userId="root"
        password="123456">
    </jdbcConnection>

	<!--  -->
    <javaTypeResolver >
      <property name="forceBigDecimals" value="false" />
    </javaTypeResolver>

	<!-- javaModelGenerator:指定javaBean的生成策略 
	targetPackage="test.model":目標包名
	targetProject="\MBGTestProject\src":目標工程
	-->
    <javaModelGenerator targetPackage="com.atguigu.mybatis.bean" 
    		targetProject=".\src">
      <property name="enableSubPackages" value="true" />
      <property name="trimStrings" value="true" />
    </javaModelGenerator>

	<!-- sqlMapGenerator:sql對映生成策略: -->
    <sqlMapGenerator targetPackage="com.atguigu.mybatis.dao"  
    	targetProject=".\conf">
      <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>

	<!-- javaClientGenerator:指定mapper介面所在的位置 -->
    <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mybatis.dao"  
    	targetProject=".\src">
      <property name="enableSubPackages" value="true" />
    </javaClientGenerator>

	<!-- 指定要逆向分析哪些表:根據表要建立javaBean -->
    <table tableName="tbl_dept" domainObjectName="Department"></table>
    <table tableName="tbl_employee" domainObjectName="Employee"></table>
  </context>
</generatorConfiguration>

用程式碼方式讓generrator跑起來:

@Test
	public void testMbg() throws Exception {
		List<String> warnings = new ArrayList<String>();
		boolean overwrite = true;
		File configFile = new File("mbg.xml");
		ConfigurationParser cp = new ConfigurationParser(warnings);
		Configuration config = cp.parseConfiguration(configFile);
		DefaultShellCallback callback = new DefaultShellCallback(overwrite);
		MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
				callback, warnings);
		myBatisGenerator.generate(null);
	}

測試MyBatis簡單版:

@Test
	public void testMyBatis3Simple() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		SqlSession openSession = sqlSessionFactory.openSession();
		try{
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			List<Employee> list = mapper.selectByExample(null);
			for (Employee employee : list) {
				System.out.println(employee.getId());
			}
		}finally{
			openSession.close();
		}
	}

測試MyBatis3豪華版:

@Test
	public void testMyBatis3() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		SqlSession openSession = sqlSessionFactory.openSession();
		try{
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			//xxxExample就是封裝查詢條件的
			//1、查詢所有
			//List<Employee> emps = mapper.selectByExample(null);
			//2、查詢員工名字中有e字母的,和員工性別是1的
			//封裝員工查詢條件的example
			EmployeeExample example = new EmployeeExample();
			//建立一個Criteria,這個Criteria就是拼裝查詢條件
			//select id, last_name, email, gender, d_id from tbl_employee 
			//WHERE ( last_name like ? and gender = ? ) or email like "%e%"
			Criteria criteria = example.createCriteria();
			criteria.andLastNameLike("%e%");
			criteria.andGenderEqualTo("1");
			
			Criteria criteria2 = example.createCriteria();
			criteria2.andEmailLike("%e%");
			example.or(criteria2);
			
			List<Employee> list = mapper.selectByExample(example);
			for (Employee employee : list) {
				System.out.println(employee.getId());
			}
			
		}finally{
			openSession.close();
		}
	}

執行原理(沒有聽懂,已放後續補):