Mybatis之兩大核心
1. mybatis框架簡述
工作一年多了,專案中一直是在使用mybatis框架作為orm框架,但始終感覺有點不是特別熟悉的味道,所以重新回顧一下,並寫下這篇博文記錄下,如果有什麼不對地方,歡迎指正。
在我們開始接觸資料庫的時候可能做過從獲取資料庫connection , 編寫sql ,向資料庫發出sql語句,獲取結果集,關閉資料庫連線 等繁瑣的過程。後來我們使用了apache下的一個工具類來JDBCUtil簡化這一過程,但是它並沒有像框架考慮的那麼全面,所以有很多功能實現不了還是要我們程式設計師去解決這個問題。所以再後來我們可能學習了Hibernate這個強大的orm框架,這個框架使得我們編碼只要告訴hibernate那個javaBean對應資料庫中的那個table,其中間的一系列過程都被封裝起來了,雖然這開始對於我們程式設計師來說是好事,但是系統到後面可能需要經過sql優化來提高系統的執行速度,但是這個sql編寫與傳送sql都是hibernate給我們封裝好的,我們根本沒法定製。但是hibernate 考慮到這個,給我們提供了一個hql 的功能模組來實現sql定製功能。這時問題就來了,這大大的增加了我們的學習任務。
為了解決這個弊端,mybatis應運而生,他把sql語句這一塊單獨提出來形成了一個xml配置檔案而其他的過程就封裝起來。mybatis 既簡化了繁瑣的過程,又將最精華的部分單獨提出來給我們自己來實現,我們就只要提高自己的sql編寫能力就可以,而沒有太大的學習負擔,同時又便於系統後期維護,綜上所述mybatis是一個非常優秀的orm框架。
1.1mybatis的配置檔案的作用
<?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>
<!--配置-->
</configuration>
每個基於 MyBatis 的應用都是以一個 SqlSessionFactory 的例項為中心的。SqlSessionFactory 的例項可以通過 SqlSessionFactoryBuilder 獲得。而 SqlSessionFactoryBuilder 則可以從 XML 配置檔案或一個預先定製的 Configuration 的例項構建出 SqlSessionFactory 的例項。
1.2配置檔案的幾個關鍵標籤
1.2.1 引入外部配置檔案內容 < properties >
<!--1.properties 標籤用來加外部properties配置檔案 resource用來載入類路徑下的, url用來載入網路或者磁碟下的-->
<properties resource="db.properties"/>
1.2.2 mybatis的設定 < settings>
<!--2. settings 是設定mybatis的-->
<settings>
<!--配置快取的全域性開關 如果要使用快取 就必須設定-->
<setting name="cacheEnabled" value="true"/>
<!--延遲載入的全域性開關,如果為true 所有關聯物件都會延遲載入。但是可以通過fetchType屬性來設定特例-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--是否允許單一語句返回多結果集(需要相容驅動)。-->
<setting name="multipleResultSetsEnabled" value="true"/>
<!--使用列標籤代替列名。不同的驅動在這方面會有不同的表現, 具體可參考相關驅動文件或通過測試這兩種不同的模式來觀察所用驅動的結果。 -->
<setting name="useColumnLabel" value="true"/>
<!--允許 JDBC 支援自動生成主鍵,需要驅動相容。 如果設定為 true 則這個設定強制使用自動生成主鍵,
儘管一些驅動不能相容但仍可正常工作(比如 Derby)。-->
<setting name="useGeneratedKeys" value="false"/>
<!--指定 MyBatis 應如何自動對映列到欄位或屬性。 NONE 表示取消自動對映;
PARTIAL 只會自動對映沒有定義巢狀結果集對映的結果集。 FULL 會自動對映任意複雜的結果集(無論是否巢狀)。-->
<setting name="autoMappingBehavior" value="PARTIAL"/>
<!-- 指定發現自動對映目標未知列(或者未知屬性型別)的行為。
NONE: 不做任何反應
WARNING: 輸出提醒日誌 ('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日誌等級必須設定為 WARN)
FAILING: 對映失敗 (丟擲 SqlSessionException)-->
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<!--配置預設的執行器。SIMPLE 就是普通的執行器;REUSE 執行器會重用預處理語句(prepared statements);
BATCH 執行器將重用語句並執行批量更新。-->
<setting name="defaultExecutorType" value="SIMPLE"/>
<!--設定超時時間,設定驅動等的資料庫響應的秒數-->
<setting name="defaultStatementTimeout" value="25"/>
<!--為驅動的結果集獲取數量(fetchSize)設定一個提示值。此引數只可以在查詢設定中被覆蓋。-->
<setting name="defaultFetchSize" value="100"/>
<!--允許在巢狀語句中使用分頁(RowBounds)。如果允許使用則設定為false。-->
<setting name="safeRowBoundsEnabled" value="false"/>
<!--這個屬性值如果為true 的話 就可以將資料庫表字段 如 emp_id 對映成javaBean 屬性的駝峰規則的屬性 empId-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--MyBatis 利用本地快取機制(Local Cache)防止迴圈引用(circular references)和加速重複巢狀查詢。
預設值為 SESSION,這種情況下會快取一個會話中執行的所有查詢。 若設定值為 STATEMENT,
本地會話僅用在語句執行上,對相同 SqlSession 的不同調用將不會共享資料。-->
<setting name="localCacheScope" value="SESSION"/>
<!--當沒有為引數提供特定的 JDBC 型別時,為空值指定 JDBC 型別。 某些驅動需要指定列的 JDBC 型別,
多數情況直接用一般型別即可,比如 NULL、VARCHAR 或 OTHER。-->
<setting name="jdbcTypeForNull" value="OTHER"/>
<!--指定哪個物件的方法觸發一次延遲載入。-->
<setting name="lazyLoadTriggerMethods"
value="equals,clone,hashCode,toString"/>
</settings>
1.2.3 別名處理器 < typeAliases >
<!--3.typeAliases 設定別名,減少全限定名的冗餘 如果沒有設定alias的值,mybatis會給個預設的值類名小寫
別名是大小寫不敏感
如果是給一個包下的某個類名與 該包下的子包中的類同名 可以使用@Alias()來設定 別名-->
<typeAliases>
<typeAlias type="com.worldly.config.entity.Employee" alias="Employee"></typeAlias>
</typeAliases>
1.2.4 型別處理器 < typeHandlers >
1.2.5物件工廠
<!--5.物件工廠(objectFactory)一般使用預設的-->
1.2.6 外掛 < plugins>
<!--6.外掛(plugins) MyBatis 允許你在已對映語句執行過程中的某一點進行攔截呼叫。預設情況下,
MyBatis 允許使用外掛來攔截的方法呼叫包括:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
這些類中方法的細節可以通過檢視每個方法的簽名來發現,或者直接檢視 MyBatis 的發行包中的原始碼。
假設你想做的不僅僅是監控方法的呼叫,那麼你應該很好的瞭解正在重寫的方法的行為。
因為如果在試圖修改或重寫已有方法的行為的時候,你很可能在破壞 MyBatis 的核心模組。 這些都是更低層的類和方法,
所以使用外掛的時候要特別當心。
通過 MyBatis 提供的強大機制,使用外掛是非常簡單的,只需實現 Interceptor 介面,並指定了想要攔截的方法簽名即可。-->
1.2.7 環境配置 < enviroments>
<!--7.environments 用來配置多環境 多環境 1.資料庫廠商不同(mysql oracle) 2.環境不同 (test dev prod)
一個屬性 default 預設選擇什麼環境
7.1 environment 代表環境物件 ID唯一標識某個環境
兩個必須屬性 transactionManager 事務管理器
dataSource 資料來源
在mybatis org.apache.ibatis.session.Configuration裡預設配置配置了
1. 兩種事務管理器 1.依賴於jdbc的 JdbcTransactionFactory 別名JDBC
2.依賴於j2ee容器的 ManagedTransactionFactory 別名為MANAGED
當然我們可以自定義 事務管理器 只要implements TransactionFactory結口 type值為自定義事務管理器的全限定名
2. 三種資料來源 1.JndiDataSourceFactory 別名JNDI
2.基於連線池的 PooledDataSourceFactory 別名POOLED
3.不基於連線池的 UnpooledDataSourceFactory 別名UNPOOLED
當然我們可以自定義 資料來源 只要implement DataSourceFactory結口就可以 type值為自定義資料來源的全限定名
-->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
<environment id="oracle">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${oracle.driver}"/>
<property name="url" value="${oracle.url}"/>
<property name="username" value="${oracle.username}"/>
<property name="password" value="${oracle.password}"/>
</dataSource>
</environment>
</environments>
1.2.7 資料庫廠商的支援 < databasedIdProvider>
<!--8. databaseIdProvider mybatis對資料庫廠商的移植性的支援
1.DB_VENDOR VendorDatabaseIdProvider的別名,是mybatis 同connection.getMetaData() ===》
得到DatabaseMetaData 然後通過getDatabaseProductName() 得到資料庫廠商標識。
2.我們如果要實現如果是oracle資料庫的 使用這個sql語句 ,如果是mysql資料庫的話 使用另一個語句。
只要我們在 sql語句中 用databaseId來標識 這個databaseId="mysql"(是別名及property 的value 或者 直接是MySQL)
2.2如果 某個sql語句 既有databaseId標識的一條 ,也有沒有databaseId標識的一條, 會選有databaseId標識的更精確的,
另外一條會捨棄
-->
<databaseIdProvider type="DB_VENDOR" >
<!--name 為資料庫廠商的唯一標識, value為別名-->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
</databaseIdProvider>
1.2.8 對映檔案註冊< mappers>
<!--9.mappers註冊對映檔案
註冊方式可以單個註冊
1.有sql配置檔案的註冊 1.1 resource sql對映檔案
1.2 class 為介面(將sql語句 通過@Select等註解寫在方法上)
2. 也可以批量註冊 package 但是使用package
如果是 批量註冊 有sql對映檔案的話, 介面 與sql對映檔案必須同名 並且在同一目錄下(在視覺上我們可以把sql對映檔案放在resources下
一個和介面包名相同的包下)
如果是沒有對映檔案的話就直接可以使用(不推薦,維護性差違背mybatis的初衷)
-->
<mappers>
<mapper resource="mapper/EmployeeMapper.xml"/>
</mappers>
2.mybatis的sql對映檔案
<?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="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
2.0 sql中的引數變化
2.0.1 單個引數
2.0.2 多個引數
2.1 sql對映檔案的基本幾個標籤
2.1.1 resultMap與resultType
這兩個都是在select 對映結果集中物件用到的。
a.如果我們資料庫欄位與javaBean的屬性名 不一樣,需要 在查詢的時候指定對映關係的話 就可以使用resultMap 來將查詢出來的欄位(包括欄位別名) 和 javaBean的屬性進行對映,就要使用resultMap
<resultMap id="唯一標識" type="JavaBean 或者VO">
<id column="主鍵或者別名" property="javaBean的唯一標識"/>
<result column="欄位或者別名" property="javaBean的普通屬性"/>
</resultMap>
b.如果一個javaBean中的屬性與資料庫表的欄位完全對映(欄位名是屬性名首字母小寫或者是以”_”分開) 的話,就可以簡單點 直接使用resultType
2.1.2 #{} 與${}
這個兩個都是在動態sql時用來取引數值得。
a.#{}預設情況下,使用#{}格式的語法會導致 MyBatis 建立預處理語句屬性並安全地設定值(比如?)。這樣做更安全,更迅速,通常也是首選做法.
b. **${}** 過有時你只是想直接在 SQL 語句中插入一個不改變的字串,ORDER BY \${columnName} 這裡 MyBatis 不會修改或轉義字串。 以這種方式接受從使用者輸出的內容並提供給語句中不變的字串是不安全的,會導致潛在的 SQL 注入攻擊,因此要麼不允許使用者輸入這些欄位,要麼自行轉義並檢驗。
比如:你要取Integer型別的id的值為2,#{id} 的結果是’2’,\${id}結果是2.
2.1.3 < sql> < where> < if> < choose> < when> < otherwise>等
< sql> 是可重用的程式碼段
< where>是智慧化的標籤,一般是結合if標籤使用, 當第一if條件不存在時,後面的if條件有效時,就會 有 where and 的情況。就會出現sql語法有問題的情況 如下程式碼:
<where>
<if test ="">
and id=#{id}
</if>
<if test ="">
and name=#{name}
</if>
</where>
當第一個if失效的情況, 就不會出現 where and name=“”的情況。
< choose>
< when>< /when>
< otherwise>< /otherwise>
< /choose>
2.1.4 insert
以前我們往資料插入一條資料後,我們可能需要獲取這條記錄在資料庫表中的位置比如id,然後通過這個id再去資料庫中取出然後在表格中顯示這條剛剛插入的記錄。通過mybatis我們這麼實現呢?
針對這種情況,mybatis分為兩種情況:
a.如果資料庫支援自增長主鍵設定 useGeneratedKeys=”true”,然後再把 keyProperty 設定到目標屬性上就OK了
<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username, password, email, bio) values
<foreach item="item" collection="list" separator=",">
(#{item.username}, #{item.password}, #{item.email}, #{item.bio})
</foreach>
</insert>
b.如果資料庫不支援自增長主鍵,就分兩步,1先查出自增序列的值id,2.把1的結果對映到keyProperty的id然後插入資料庫中
<insert id="insertAuthor">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
</selectKey>
insert into Author
(id, username, password, email,bio, favourite_section)
values
(#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
</insert>