1. 程式人生 > >Mybatis複習整理

Mybatis複習整理

Mybatis 知識點整理,有些問題不是不會,也不是不懂,只是沒有見過!
<?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="mapper.BlogMapper">
	
	<!--單獨的cache作用: 
		所有的對映檔案裡的select都會被快取
		所有的對映檔案裡的insert、update和delete語句執行都會被清空快取
		快取使用最近最少使用演算法LRU來回收
		快取不會被設定的時間所清空
		每個快取可以儲存1024個列表或物件的引用(不管查詢方法返回的是什麼)
		快取將作為“讀/寫”快取,意味著檢索的物件不是共享裡的且可以被呼叫者安全的修改,而不會被其他呼叫者或執行緒干擾
	 -->
	<cache/>
	<!-- eviction:快取回收演算法
			LRU:最近最少使用:移出最近最長事件內都沒有被使用的物件
			FIFO:先進先出:移出最先進入快取的物件
			SOFI:軟引用:基於垃圾回收機制和軟引用規則來移除物件(空間記憶體不足時才回收物件)
			WEAK:弱引用:基於垃圾回收機制和弱引用規則來移除物件(垃圾回收器掃描到才進行回收)
		 flushInterval:重新整理時間間隔?
		 size:快取物件的大小以及環境中的可用記憶體空間
		 readOnly:只讀快取將對所有呼叫者返回同一個例項。
		 	因此這些物件都不能被修改,這可以極大的提高效能。
		 	可寫的快取將通過序列化來返回一個快取物件的拷貝。這會比較慢,但是比較安全。所以預設值都是false -->
	<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" />
	<!-- 引用另外一個名稱空間配置的快取 -->
	<cache-ref namespace=""/>
	
	<!-- 使用自定義快取==>如果想實現自定義快取,必須實現org.mybatis.cache.Cache這個介面
		可以通過自己的快取實現來完全重寫快取行為,或者通過建立第三方快取解決方案的介面卡 -->
	<cache type="something.MyBlogCache">
		<!-- 要配置自己的快取,簡單的新增一個公共的JavaBeans屬性到快取實現中,然後通過cache元素設定屬性進行傳遞 -->
		<property name="cacheFile" value="/tmp/my-blog-cache.tmp"/>
	</cache>
	
	<!-- 描述如何從資料庫結果集裡面載入物件 -->
	<resultMap type="Blog" id="blog"></resultMap>
	<!-- 能夠被其他語句重用的sql塊 -->
	<sql id="columns"> 
		ID as id,
		AUTHOR as author,
		TITLE as title,
		CONTENT as content,
		CLICKTIMES as clicktimes 
	</sql>
	
	<!-- 根據主鍵查詢 -->
	<select id="selectBlogById" parameterType="int" resultType="Blog">
		select <include refid="columns"/> from Blog where id = #{id}
	</select>
	
	<!--parameterType:引數型別
		resultType:返回值型別的具體類名
		resultMap:返回值型別的結果集
		flushCache:如果為true,每次該語句呼叫都會清空快取 
		useCache:如果為true,則該語句的結果集將被快取
		timeout:請求超時時間
		fetchSize:從資料庫獲得記錄條數
		statementType:STATEMENT/~PREPARED/CALLABLE
		resultSetType:FORWARD_ONLY/SCROLL_SENSITIVE/SCROLL_INSENSITIVE -->
	<select id="exampleSelect" 
		parameterType="引數型別"  
		resultType="Blog"
		resultMap="Blog"
		flushCache="true"
		useCache="true"
		timeout="1000"
		fetchSize="256"
		statementType="STATEMENT"
		resultSetType="FORWARD_ONLY"></select>
		
	<!-- useGeneratedKeys : 資料庫自動生成主鍵
		 keyProperty : 主鍵欄位 -->
	<insert id="exampleInsert" useGeneratedKeys="true" keyProperty="id"></insert>
	
	<insert id="add" parameterType="Blog">
		insert into blog (id,author,title,content)
			values (#{id} , #{author} , #{title} , #{content} , #{clicktimes})
	</insert>
	
	<!-- 自定義資料型別轉換,並指定轉換類
		numericScale:指定小數位精度 -->
	<insert id="add2" parameterType="Blog">
		insert into blog (id,author,title,content)
			values (
			#{id , javaType=int , jdbcType=NUMERIC , typeHandler=ExampleTypeHandler } , 
			#{author} , 
			#{title} , 
			#{content},
			#{clicktimes,javaType=int,jdbcType=NUMERIC,numericScale=0})
	</insert>
	
	<!-- 這種申明結果集的方法可以解決列名不匹配的問題,和columns的效果相同 -->
	<resultMap type="Blog" id="blogs">
		<!-- 各屬性解釋
			property:對映資料庫列的欄位或屬性(對應javaBean)
			column:資料庫中的列名或者列標籤的別名(對應資料庫列)
			javaType:完整的java類名或者列標籤名
			jdbcType:資料庫中該列的欄位型別(這個屬性在insert、update或delete的時候針對允許空的列有用)
			typeHandler:資料型別處理器
		 -->
		<id column="ID" property="id" />
		<result column="AUTHOR" property="author"/>
		<result column="TITLE" property="title"/>
		<result column="CONTENT" property="content"/>
		<result column="CLICKTIMES" property="clicktimes"/>
	</resultMap>
	
	
	<!-- 一個多表關聯的複合查詢 -->
	<select id="selectBlogDetails" parameterType="int" resultMap="detailedBlogResultMap">
		select
			B.id as blog_id,
			B.title as blog_title,
			B.author_id as blog_author_id,
			A.id as author_id,
			A.username as author_username,
			A.password as author_password,
			A.email as author_email,
			A.bio as author_bio,
			A.favourite_section as author_favourite_section,
			P.id as post_id,
			P.blog_id as post_blog_id,
			P.author_id as post_author_id,
			P.created_on as post_created_on,
			P.section as post_section,
			P.subject as post_subject,
			P.draft as draft,
			P.body as post_body,
			C.id as comment_id,
			C.post_id as comment_post_id,
			C.name as comment_name,
			C.comment as comment_text,
			T.id as tag_id,
			T.name as tag_name
		from Blog B
			left outer join Author A on B.author_id = A.id
			left outer join Post P on B.id = P.blog_id
			left outer join Comment C on P.id = C.post_id
			left outer join Post_Tag PT on PT.post_id = P.id
			left outer join Tag T on PT.tag_id = T.id
		where B.id = #{id}
	</select>
	
	<!-- 將上一步的多表關聯查詢對映到一個智慧的物件模型中 -->
	<resultMap id="detailedBlogResultMap" type="Blog">
		<!-- 例項化的時候通過構造器將結果注入到類中 -->
		<constructor>
			<!-- idArg:將結果集標記為ID,以方便全域性呼叫
					arg:注入構造器的結果集 
				使用構造器將主表資料注入類中 -->
			<idArg column="blog_id" javaType="int"/>
		</constructor>
		<!-- 注入一個欄位或者javaBean屬性的結果
			主表資料單獨用result申明 -->
		<result property="title" column="blog_title"/>
		<!-- 複雜型別聯合:許多查詢結果合成這個型別
			使用blog_author_id外來鍵關聯author表中需要查詢的欄位 -->
		<association property="author" column="blog_author_id" javaType=" Author">
			<id property="id" column="author_id"/>
			<result property="username" column="author_username"/>
			<result property="password" column="author_password"/>
			<result property="email" column="author_email"/>
			<result property="bio" column="author_bio"/>
			<result property="favouriteSection" column="author_favourite_section"/>
		</association>
		<!-- 複雜型別集合
			使用post_id外來鍵關聯post表中需要的欄位 -->
		<collection property="posts" ofType="Post">
			<!-- post.id -->
			<id property="id" column="post_id"/>
			<!-- post.subject -->
			<result property="subject" column="post_subject"/>
			<!-- post與author表關聯的欄位 -->
			<association property="author" column="post_author_id" javaType="Author"/>
			<!-- comment_id作為外來鍵與post關聯,所以申明在post的關聯物件中 -->
			<collection property="comments" column="post_id" ofType=" Comment">
				<id property="id" column="comment_id"/>
			</collection>
			<!-- tag_id 作為外來鍵與post關聯 -->
			<collection property="tags" column="post_id" ofType=" Tag" >
				<id property="id" column="tag_id"/>
			</collection>
			<!-- 使用一個結果值,以確定使用哪個resultMap -->
			<discriminator javaType="int" column="draft">
				<!-- 基於不同的結果對映 -->
				<case value="1" resultType="DraftPost"/>
			</discriminator>
		</collection>
	</resultMap>
	
	<!-- 一對一關聯配置 -->
	<!-- 使用association和select屬性做巢狀查詢
		使用這種配置方法會導致N+1選擇問題的產生
			執行單條sql語句獲得一個表的記錄
			對獲得的列表中的每條記錄,在執行一次聯合查詢用以載入詳細資訊
		這樣會導致成千上萬條sql執行,並非可取 -->
	<resultMap type="Blog" id="blogResult">
		<association property="author" column="blog_author_id" 
			javaType="Author" select="selectAuthor" />
	</resultMap>
	<select id="selectBlog" resultMap="blogResult" parameterType="int">
		select * from blog where id = #{id}
	</select>
	<select id="selectAuthor" resultMap="author" parameterType="int">
		select * from author where id = #{id}
	</select>
	<!-- 以上的結果相當於sql:
		select b.*,a.* from blog b
			left join author a on b.blog_author_id = a.id
			where b.id = #{id}  -->
			
	<!-- 第二種對映結果集的方式 -->
	<select id="selectBlog" parameterType="int" resultMap="blogResult">
		select
			B.id 		as blog_id,
			B.title 	as blog_title,
			B.author_id as blog_author_id,
			A.id 		as author_id,
			A.username  as author_username,
			A.password  as author_password,
			A.email 	as author_email,
			A.bio 		as author_bio
		from Blog B 
			left outer join Author A on B.author_id = A.id
		where B.id = #{id}
	</select>
	<resultMap id="blogResult" type="Blog">
		<id property="id" column=" blog_id" />
		<result property="title" column="blog_title"/>
		<association property="author" column="blog_author_id" javaType="Author"
			resultMap="authorResult"/>
			<!-- lable209 -->
	</resultMap>
	<!-- 這樣寫便於查詢結果集重用,如果只要用一次的話,直接將resultMap標籤中的內容移動到lable209 -->
	<resultMap id="authorResult" type="Author">
		<id property="id" column="author_id"/>
		<result property="username" column="author_username"/>
		<result property="password" column="author_password"/>
		<result property="email" column="author_email"/>
		<result property="bio" column="author_bio"/>
	</resultMap>
	
	<!-- 一對多關聯配置 -->
	<resultMap id="blogResult" type="Blog">
		<collection property="posts" javaType="ArrayList" column="blog_id"
			ofType="Post" select="selectPostsForBlog"/>
	</resultMap>
	<select id="selectBlog" parameterType="int" resultMap="blogResult">
		SELECT * FROM BLOG WHERE ID = #{id}
	</select>
	<select id="selectPostsForBlog" parameterType="int" resultType="Author">
		SELECT * FROM POST WHERE BLOG_ID = #{id}
	</select>
	
	<!-- Discriminator:識別器~switch -->
	<resultMap id="vehicleResult" type="Vehicle">
		<id property="id" column="id" />
		<result property="vin" column="vin"/>
		<result property="year" column="year"/>
		<result property="make" column="make"/>
		<result property="model" column="model"/>
		<result property="color" column="color"/>
		<discriminator javaType="int" column="vehicle_type">
			<!-- 每當匹配中一個case,其餘case將被忽略 -->
			<case value="1" resultMap="carResult"/>
			<case value="2" resultMap="truckResult"/>
			<case value="3" resultMap="vanResult"/>
			<case value="4" resultMap="suvResult"/>
		</discriminator>
	</resultMap>
	
	<!-- 動態sql -->
	<!-- if -->
	<select id="findBlogTitleLike" parameterType="Blog">
		select * from blog where 1=1 
		<if test="title != null">
			and title like #{title}
		</if>
	</select>
	
	<!-- choose -->
	<select id="findBlogLike" parameterType="Blog">
		select * from blog where 1=1 
		<choose>
			<when test="title != null">
				and title like #{title}
			</when>
			<when test="author != null and author.name != null">
				and title like #{author.name}
			</when>
			<otherwise>
				and featured = 1
			</otherwise>
		</choose>
	</select>
	
	<!-- where : 如果第一個條件不滿足,但是第二個條件滿足,則會忽略第二個條件的and -->
	<select id="findBlogWithWhere" parameterType="Blog">
		select * from blog
		<where>
			<if test="title != null">
				title like #{title}
			</if>
			<if test="author != null and author.name != null">
				and author like #{author.name}
			</if>
		</where>
	</select>
	
	<!-- trim==>where : 使用trim實現where同樣的功能 -->
	<select id="findBlogWithTrim" parameterType="Blog">
		select * from blog 
		<trim prefix="where" prefixOverrides="and/or">
			<if test="title != null">
				title like #{title}
			</if>
			<if test="author != null and author.name != null">
				and author like #{author.name}
			</if>
		</trim>
	</select>
	
	<!-- set -->
	<update id="updateWithSet" parameterType="Blog">
		update blog
		<set>
			<if test="author != null">author = #{author},</if>
			<if test="content != null">content = #{content}</if>
		</set>
		where id = #{id}
	</update>
	
	<!-- trim==>set -->
	<update id="updateWithTrim" parameterType="Blog">
		update blog
		<trim prefix="set" prefixOverrides=",">
			<if test="author != null">author = #{author},</if>
			<if test="content != null">content = #{content}</if>
		</trim>
		where id = #{id}
	</update>
	
	<!-- foreach -->
	<!-- 注意:當傳遞一個集合給mybatis時,mybatis會自動將引數包裝成map並且以名字作為key
			list==》list,array==》array -->
	<select id="selectBlogIn">
		select * from blog
		where id in
		<foreach collection="list" item="item" index="index" open="(" close=")" separator=",">
			#{item}
		</foreach>
	</select>
	
	
</mapper>
使用註解方式繫結sql
public interface BlogMapper {


	@Select("select * from Blog where id = #{id}")
	public Blog selectBlogById(int id);
	
	public void update(Blog blog);
	
}

自定義的型別處理器
/**
 * @author Administrator
 *	自己的資料型別處理器
 */
public class ExampleTypeHandler implements TypeHandler{
	
	@Override
	public Object getResult(ResultSet rs, String columnName) throws SQLException {
		return rs.getString(columnName);
	}


	@Override
	public Object getResult(CallableStatement cs, int columnIndex)
			throws SQLException {
		return cs.getString(columnIndex);
	}


	@Override
	public void setParameter(PreparedStatement ps, int i, Object parameter,
			JdbcType jdbcType) throws SQLException {
		ps.setString(i, (String) parameter);
	}


}

自定義外掛
@Intercepts(
	{@Signature(type = Executor.class, method = "update",
			args = {BlogMapper.class,Object.class})}
		)
public class ExamplePlugin implements Interceptor{


	@Override
	public Object intercept(Invocation arg0) throws Throwable {
		return arg0.proceed();
	}


	@Override
	public Object plugin(Object arg0) {
		return Plugin.wrap(arg0, this);
	}


	@Override
	public void setProperties(Properties arg0) {
		
	}


}

自定義ObjectFactory
public class ExampleObjectFactory extends DefaultObjectFactory{


	@Override
	public Object create(Class type, List<Class> constructorArgTypes,
			List<Object> constructorArgs) {
		return super.create(type, constructorArgTypes, constructorArgs);
	}


	@Override
	public Object create(Class type) {
		return super.create(type);
	}


	/* 用來配置ExampleObjectFactory
	 * 在初始化ExampleObjectFactory例項後,定義在ExampleObjectFactory元素主體中的屬性會以引數的形式傳遞到此方法中
	 * @see org.apache.ibatis.reflection.factory.DefaultObjectFactory#setProperties(java.util.Properties)
	 */
	@Override
	public void setProperties(Properties properties) {
		super.setProperties(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>

<!-- 引入配置檔案 -->
<properties resource="config/config.properties">
<!-- 如果config.properties檔案中有username和password屬性,再次配置將會覆蓋檔案中的值 -->
<property name="username" value="bikeeverywhere"/>
<property name="password" value="bikeeverywhere"/>
</properties>

<!-- 設定和改變Mybatis執行中的行為 -->
<settings>
<!-- 全域性性的啟用或者禁用所有在mapper配置檔案中配置的快取 -->
<setting name="cacheEnable" value="true"/>
<!-- 全域性性的啟用或禁用延遲載入。當禁用時,所有關聯的配置都會立即載入 -->
<setting name="lazyLoadingEnable" value="true"/>
<!-- 當啟用時,一個有延遲載入屬性的物件的任何一個延遲屬性被載入時,該物件的所有的屬性都會被載入。
否則所有屬性都是按需載入 -->
<setting name="aggressiveLazyLoading" value="true"/>
<!-- 允許或禁止從單一的語句返回多個結果集(需要驅動程式相容) -->
<setting name="multipleResultSetsEnable" value="true"/>
<!-- 使用列的標籤而不是列的名稱 -->
<setting name="userColumnLable" value="true"/>
<!-- 允許JDBC自動生成主鍵(需要驅動程式相容) -->
<setting name="userGeneratedKeys" value="false"/>
<!-- 指定mybatis是否以及如何自動將列對映到欄位/屬性
partial:自動對映簡單的、非巢狀的結果集
full:自動對映任何複雜的(巢狀或非巢狀的結果集) -->
<setting name="autoMappingBehavior" value="partial"/>
<!-- 配置預設的執行器(executor)
simple:簡單的執行器
reuse:重用prepared statements的執行器
batch:重用statements並且進行批量更新的執行器 -->
<setting name="defaultExecutorType" value="simple"/>
<!-- 設定資料庫查詢超時時間 -->
<setting name="defaultStatementTimeout" value="10000"/>
</settings>

<!-- 實體類設定別名 -->
<typeAliases>
<typeAlias type="entity.Author" alias="author"/>
<typeAlias type="entity.Blog" alias="blog"/>
<typeAlias type="entity.Comment" alias="comment"/>
<typeAlias type="entity.Post" alias="post"/>
<typeAlias type="entity.Section" alias="section"/>
<typeAlias type="entity.Tag" alias="tag"/>
</typeAliases>

<!-- 資料庫資料與java資料做型別轉換使用 -->
<typeHandlers>
<!-- 可以重寫型別處理器,或者建立自己的型別處理器去處理沒有被支援的或非標準的型別
要做到這一點,只要實現TypeHandler介面(org.mybatis.type),
並且將TypeHandler類對映到java型別和可選的JDBC型別即可 -->
<!-- 指定自己的資料型別轉換,將VARCHAR轉成String -->
<typeHandler handler="handler.ExampleTypeHandler" javaType="String" jdbcType="VARCHAR"/>
</typeHandlers>

<!-- Mybatis每次建立一個結果物件都會使用objectFactory例項。
使用預設的objectFactory和使用預設的建構函式來例項化目標類並沒有什麼區別 -->
<objectFactory type="factory.ExampleObjectFactory">
<property name="someProperty" value="100"/>
</objectFactory>

<!-- 自定義外掛,在對映語句執行的某個時間點攔截方法呼叫 -->
<plugins>
<!-- 允許攔截的方法 -->
<!-- Executor:
query,update,flushStatements,commit,rollback,getTransaction,close,isClosed -->
<!-- ParameterHandler:
getParameterObject,setParameters -->
<!-- ResultSetHandler:
handlerResultSets,handlerOutputParameters -->
<!-- StatementHandler:
prepare,parameterize,batch,update,query -->
<plugin interceptor="plugin.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>

<!-- 配置執行環境 
可能出現一個sql語句向兩個資料庫發起請求,所以要配置多個數據源
但是:每個sqlsessionFactory只能選擇一個執行環境
所以如果連線多個數據庫,就要配置多個sqlsessionFactory-->
<environments default="development">
<environment id="development">
<!-- 事物管理:JDBC/MANAGED -->
<!-- JDBC:直接使用JDBC的提交和回滾功能,它依賴於從資料來源獲得連線來管理事物的生命週期  -->
<transactionManager type="JDBC" />
<!-- 只有使用了延遲載入才需要資料來源? -->
<!-- Mybatis內建了3種資料來源型別 -->
<!--UNPOOLED:每次需要的時候簡單的開啟和關閉連線
 雖然有點慢,但是對於不需要立即響應的簡單的應用來說,不失為一種好的選擇-->
<dataSource type="UNPOOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<environment id="product">
<!-- MANAGED:什麼都不做,而是讓容器來管理事務的生命週期
如果需要通過關閉連線來停止事務,將屬性closeConnection設定為false-->
<transactionManager type="MANAGED" >
<property name="closeConnection" value="false"/>
</transactionManager>
<!-- POOLED:這個資料來源的實現快取了JDBC連線物件(池化技術) -->
<dataSource type="POOLED">
<property name="driver" value="${driver1}"/>
<property name="url" value="${url1}"/>
<property name="username" value="${username1}"/>
<property name="password" value="${password1}"/>
<!-- 池化技術相關引數設定 -->
<!-- 最大使用數量,預設為10 -->
<property name="poolMaximumActiveConnections" value="10"/>
<!-- 在特定時間內空閒的連線數(當沒有人訪問的時候也保持一定數量的連線) -->
<property name="poolMaximumIdleConnections" value="5"/>
<!-- 在連線池被強行返回前,一個連線能夠“檢出”的總時間,預設是20000ms -->
<property name="poolMaximumCheckoutTime" value="20000"/>
<!-- 這是一個比較底層的設定,如果連線佔用了很長的時間,能夠給連線池一個去列印日誌的機會,並嘗試重新連線,預設是20000ms -->
<property name="poolTimeToWait" value="2000"/>
<!-- ping query 是傳送給資料庫的ping資訊,測試資料庫是否良好和是否準備好了接受請求
預設值是 NO PING QUERY SET,讓大部分資料庫都不適用ping,返回一個友好的錯誤資訊 -->
<property name="poolPingQuery" value=""/>
<!-- ping query 的開關,如果允許,需要設定一條可用的(最高效的)sql語句來設定pingquery的值 -->
<property name="poolPingEnable" value="false"/>
<!-- 這個屬性設定ping query的間隔時間。通常設定為資料庫連線的超時時間,避免不必要的ping
只有poolPingEnable=true時才生效 -->
<property name="poolPingConnectionsNotUsedFor" value="0"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC" />
<dataSource type="JNDI">
<property name="driver" value="${driver2}"/>
<property name="url" value="${url2}"/>
<property name="username" value="${username2}"/>
<property name="password" value="${password2}"/>
<!-- 這個屬性用來從InitalContext中查詢一個上下文 -->
<property name="initial_context" value=""/>
<!-- 這個屬性是引用一個能夠被找到的資料來源例項的上下文路徑
它會查詢根據 initial_context 從 initialContext 中搜尋返回的上下文。
或者在initial_context 沒有提供的情況下直接在 InitialContext 中進行查詢。 -->
<property name="data_source" value=""/>
</dataSource>
</environment>
</environments>

<!-- 此處用於配置sql對映 -->
<mappers>
<!-- 引入根目錄下的配置檔案 -->
<mapper resource="mapper/BlogMapper.xml"/>
<!-- 使用資源路徑引入 -->
<mapper url="file:///var/sqlmaps/AuthorMapper.xml"/>
</mappers>
</configuration>