一、框架基本介紹
1、概念
支持普通SQL查詢、存儲過程和高級映射,簡化和實現了Java 數據持久化層的的開源框架,主要流行的原因在於他的簡單性和易使用性。
2、特點
持久層 、ORM(對象關系映射) 、輕量級、支持SQL語句。
3、工作原理
- 應用程序讀取mybatis的配置文件,形成配置對象。
- 通過sqlsessionfactorybuilder讀取配置對象,產生sqlsessionfactory
- 通過sqlsessionfactory得到sqlsession
- 通過sqlsession得到mapper映射器
- 通過mapper讀取對應的映射文件從而操作數據庫
- 處理事務
- 釋放sqlsession
二、配置文件含義
1、environment
<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/test" /> <property name="username" value="root" /> <property name="password" value="admin" /> </dataSource> </environment> <environment id="test"> <transactionManager type="JDBC"></transactionManager> <dataSource type="JNDI"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="admin" /> </dataSource> </environment> </environments>
主要用來配置多個dataSource環境;在同一個環境中切換不同的dataSource時,需要為每個數據庫創建一個SqlSessionFactory。對於每個環境 environment,我們需要配置 dataSource 和 transactionManager。
1.1 DateSource
-
- UNPOOLED:為每一個數據庫操作創建一個新的連接,使用結束就關閉他。該方式適用於小規模數量的並發用戶的簡單應用程序上。
- POOLED:Mybatis會創建一個數據庫連接池,連接池中的一個連接將會被用作數據庫操作。一旦數據庫操作完成,MyBatis 會將此連接返回給連接池。
- JNDI:MyBatis 從在應用服務器向配置好的 JNDI 數據源 dataSource 獲取數據庫連接。
1.2 TransactionManager
- JDBC:應用程序自己管理事務(Tomcat)
- MANAGED:應用本身不去管理事務,而把事務管理交給應用所在的服務器進行管理。(JBoss、WebLogic、GlassFish)
2、properties
<properties resource="application.properties"> <property name="jdbc.username" value="db_user" /> <property name="jdbc.password" value="verysecurepwd" /> </properties>
這里,如果 application.properties 文件包含值 jdbc.username 和 jdbc.password,則上述定義的 username 和password 的值 db_user 和 verysecurepwd 將會被 application.properties 中定義的對應的 jdbc.username 和jdbc.password 值覆蓋。
3、typeAliases
如果沒有設置typeAliases的類型別名,對於resultType和parameterType屬性值,我們需要使用JavaBean的完全限定名。所以,我們可以為完全限定名使用別名,通過typeAliases設置:
3.1 為每個JavaBean單獨起別名
<typeAliases> <typeAlias alias="Student" type="com.mybatis3.domain.Student" /> <typeAlias alias="Tutor" type="com.mybatis3.domain.Tutor" /> </typeAliases>
3.2 為JavaBean 所在的包起別名
當然也可以不用為每一個JavaBean 單獨定義別名,你可以為提供需要起別名的JavaBean 所在的包,Mybatis會自動掃描包內定義的JavaBeans,然后分別為JavaBean注冊一個小寫字母開頭的非完全限定的類名形式的別名。
<typeAliases> <package name="com.mybatis3.domain" /> </typeAliases>
如果 Student.java 和 Tutor.java 的 Bean 定義在 com.mybatis3.domain 包中,則 com.mybatis3.domain.Student的別名會被注冊為 student。而 com.mybatis3.domain.Tutor 別名將會被注冊為 tutor。
3.3 利用注解的方式起別名
使用注解@Alias起別名,@Alias 注解將會覆蓋配置文件中的<typeAliases>定義:
@Alias("StudentAlias")
public class Student
{ }
4、typeHandlers
MyBatis 對於以下的類型使用內建的類型處理器:所有的基本數據類型、基本類型的包裝類型、 byte[]、java.util.Date、 java.sql.Date、 java,sql.Time、 java.sql.Timestamp、 java 枚舉類型等。所以當 MyBatis 發現屬性的類型屬於上述類型,他會使用對應的類型處理器將值設置到PreparedStatement 中,同樣地,當從 SQL 結果集構建 JavaBean 時,也有類似的過程。
如果,我們可以自定義一個類型處理器為我們子定義的Class服務。一旦我們實現了自定義的類型處理器,我們需要在 mybatis-config.xml 中注冊它:
*/ public class PhoneTypeHandler extends BaseTypeHandler<PhoneNumber> { public void setNonNullParameter(PreparedStatement preparedStatement, int i, PhoneNumber phoneNumber, JdbcType jdbcType) throws SQLException { preparedStatement.setString(i, phoneNumber.toString()); } public PhoneNumber getNullableResult(ResultSet resultSet, String s) throws SQLException { return new PhoneNumber(resultSet.getString(s)); } public PhoneNumber getNullableResult(ResultSet resultSet, int i) throws SQLException { return new PhoneNumber(resultSet.getString(i)); } public PhoneNumber getNullableResult(CallableStatement callableStatement, int i) throws SQLException { return new PhoneNumber(callableStatement.getString(i)); } }
<typeHandlers> <typeHandler handler="com.summersoft.ts.util.PhoneTypeHandler"/> </typeHandlers>
時間類型:Mybatis 會將java.util.Data 類型轉換成java.sql.Timestamp(時間戳)並設置。
5、Mappers
<mappers> <mapper resource="com/mybatis3/mappers/StudentMapper.xml" /> <mapper url="file:///D:/mybatisdemo/app/mappers/TutorMapper.xml" /> <mapper class="com.mybatis3.mappers.TutorMapper" /> <package name="com.mybatis3.mappers" /> </mappers>
- resource 屬性用來指定在 classpath 中的 mapper 文件。
- url 屬性用來通過完全文件系統路徑或者 web URL 地址來指向 mapper 文件
- class 屬性用來指向一個 mapper 接口
- package 屬性用來指向可以找到 Mapper 接口的包名
6、Settings
這是 MyBatis 中極為重要的調整設置,它們會改變 MyBatis 的運行時行為。下表描述了設置中各項的意圖、默認值等。
| 設置參數 | 描述 | 有效值 | 默認值 |
|---|---|---|---|
| cacheEnabled | 該配置影響的所有映射器中配置的緩存的全局開關。 | true | false | true |
| lazyLoadingEnabled | 延遲加載的全局開關。當開啟時,所有關聯對象都會延遲加載。 特定關聯關系中可通過設置fetchType屬性來覆蓋該項的開關狀態。 | true | false | false |
| aggressiveLazyLoading | 當開啟時,任何方法的調用都會加載該對象的所有屬性。否則,每個屬性會按需加載(參考lazyLoadTriggerMethods). | true | false | false (true in ≤3.4.1) |
| multipleResultSetsEnabled | 是否允許單一語句返回多結果集(需要兼容驅動)。 | true | false | true |
| useColumnLabel | 使用列標簽代替列名。不同的驅動在這方面會有不同的表現, 具體可參考相關驅動文檔或通過測試這兩種不同的模式來觀察所用驅動的結果。 | true | false | true |
| useGeneratedKeys | 允許 JDBC 支持自動生成主鍵,需要驅動兼容。 如果設置為 true 則這個設置強制使用自動生成主鍵,盡管一些驅動不能兼容但仍可正常工作(比如 Derby)。 | true | false | False |
| autoMappingBehavior | 指定 MyBatis 應如何自動映射列到字段或屬性。 NONE 表示取消自動映射;PARTIAL 只會自動映射沒有定義嵌套結果集映射的結果集。 FULL 會自動映射任意復雜的結果集(無論是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
| autoMappingUnknownColumnBehavior | 指定發現自動映射目標未知列(或者未知屬性類型)的行為。
|
NONE, WARNING, FAILING | NONE |
| defaultExecutorType | 配置默認的執行器。SIMPLE 就是普通的執行器;REUSE 執行器會重用預處理語句(prepared statements); BATCH 執行器將重用語句並執行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
| defaultStatementTimeout | 設置超時時間,它決定驅動等待數據庫響應的秒數。 | 任意正整數 | Not Set (null) |
| defaultFetchSize | 為驅動的結果集獲取數量(fetchSize)設置一個提示值。此參數只可以在查詢設置中被覆蓋。 | 任意正整數 | Not Set (null) |
| safeRowBoundsEnabled | 允許在嵌套語句中使用分頁(RowBounds)。如果允許使用則設置為false。 | true | false | False |
| safeResultHandlerEnabled | 允許在嵌套語句中使用分頁(ResultHandler)。如果允許使用則設置為false。 | true | false | True |
| mapUnderscoreToCamelCase | 是否開啟自動駝峰命名規則(camel case)映射,即從經典數據庫列名 A_COLUMN 到經典 Java 屬性名 aColumn 的類似映射。 | true | false | False |
| localCacheScope | MyBatis 利用本地緩存機制(Local Cache)防止循環引用(circular references)和加速重復嵌套查詢。 默認值為 SESSION,這種情況下會緩存一個會話中執行的所有查詢。 若設置值為 STATEMENT,本地會話僅用在語句執行上,對相同 SqlSession 的不同調用將不會共享數據。 | SESSION | STATEMENT | SESSION |
| jdbcTypeForNull | 當沒有為參數提供特定的 JDBC 類型時,為空值指定 JDBC 類型。 某些驅動需要指定列的 JDBC 類型,多數情況直接用一般類型即可,比如 NULL、VARCHAR 或 OTHER。 | JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER | OTHER |
| lazyLoadTriggerMethods | 指定哪個對象的方法觸發一次延遲加載。 | A method name list separated by commas | equals,clone,hashCode,toString |
| defaultScriptingLanguage | 指定動態 SQL 生成的默認語言。 | A type alias or fully qualified class name. | org.apache.ibatis.scripting.xmltags.XMLLanguageDriver |
| defaultEnumTypeHandler | Specifies the TypeHandler used by default for Enum. (Since: 3.4.5) | A type alias or fully qualified class name. | org.apache.ibatis.type.EnumTypeHandler |
| callSettersOnNulls | 指定當結果集中值為 null 的時候是否調用映射對象的 setter(map 對象時為 put)方法,這對於有 Map.keySet() 依賴或 null 值初始化的時候是有用的。注意基本類型(int、boolean等)是不能設置成 null 的。 | true | false | false |
| returnInstanceForEmptyRow | 當返回行的所有列都是空時,MyBatis默認返回null。 當開啟這個設置時,MyBatis會返回一個空實例。 請注意,它也適用於嵌套的結果集 (i.e. collectioin and association)。(從3.4.2開始) | true | false | false |
| logPrefix | 指定 MyBatis 增加到日志名稱的前綴。 | Any String | Not set |
| logImpl | 指定 MyBatis 所用日志的具體實現,未指定時將自動查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | Not set |
| proxyFactory | 指定 Mybatis 創建具有延遲加載能力的對象所用到的代理工具。 | CGLIB | JAVASSIST | JAVASSIST (MyBatis 3.3 or above) |
| vfsImpl | 指定VFS的實現 | 自定義VFS的實現的類全限定名,以逗號分隔。 | Not set |
| useActualParamName | 允許使用方法簽名中的名稱作為語句參數名稱。 為了使用該特性,你的工程必須采用Java 8編譯,並且加上-parameters選項。(從3.4.1開始) | true | false | true |
| configurationFactory | 指定一個提供Configuration實例的類. 這個被返回的Configuration實例是用來加載被反序列化對象的懶加載屬性值. 這個類必須包含一個簽名方法static Configuration getConfiguration(). (從 3.2.3 版本開始) | 類型別名或者全類名. | Not set |
三、映射文件講解
我們通過映射器配置文件配置了映射語句,同時創建一個完全對應的一個映射器接口。接口名跟配置文件名相同。接口所在包名也跟配置文件所在包名完全一 樣。在配置文件中,其命名空間namespace 應該和接口的完全限定名保持一致。
1、INSERT
id: 對應接口的方法名
parameterType: 輸入參數
useGeneratedKeys="true" :讓數據庫生成自增長的列
keyProperty="屬性名": 將生成的值設置到其中一個輸入對象屬性內
如果是Oracle數據庫,沒有像MySQL那樣的自增機制:
<insert id="insertStudent" parameterType="Student"> <selectKey keyProperty="studId" resultType="int" order="BEFORE"> SELECT ELEARNING.STUD_ID_SEQ.NEXTVAL FROM DUAL </selectKey> INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL, PHONE) VALUES(#{studId},#{name},#{email},#{phone}) </insert>
order="BEFORE" 表示在插入語句發生前產生studId的值就賦給插入語句。
<insert id="insertStudent" parameterType="Student"> INSERT INTO STUDENTS(NAME,EMAIL, PHONE) VALUES(#{name},#{email},#{phone}) <selectKey keyProperty="studId" resultType="int" order="AFTER"> SELECT ELEARNING.STUD_ID_SEQ.CURRVAL FROM DUAL </selectKey> </insert>
order="AFTER" 表示在插入語句之后采用觸發器(trigger)來設置主鍵值
2、SELECT
2.1、 resultType='Student' 當 column名和對象property名一致時,會自動把相應的column填充上對象的property,如果查詢記錄返回多條,Mybatis 自動用 集合類來接收。
2.2、Mybatis根據集合的類型,會采用適當的集合實現:
對於 List,Collection,Iterable 類型,MyBatis 將返回 java.util.ArrayList
對於 Map 類型,MyBatis 將返回 java.util.HashMap
對於 Set 類型,MyBatis 將返回 java.util.HashSet
對於 SortedSet 類型,MyBatis 將返回 java.util.TreeSet
2.3、ResultType 和 ResuleMap 不能同時使用,ResultMap的 id 在此命名空間內是唯一的
2.4、Resulttype="java.util.HashMap"。在這種情況下,結果集中的列名將會作為Map中的key值,而列值作值將會作為Map的value值。如果查詢記錄返回多條,Mybatis自動用集合類來接收。
2.5、我們可以從另外一個<resultMap>,擴展出一個新的<resultMap>,這樣原來的屬性值可以擴展過來
2.6、一對一關聯查詢
<resultMap type="Student" id="StudentWithAddressResult"> <id property="studId" column="stud_id" /> <result property="name" column="name" /> <result property="email" column="email" /> <result property="phone" column="phone" /> <result property="address.addrId" column="addr_id" /> <result property="address.street" column="street" /> <result property="address.city" column="city" /> <result property="address.state" column="state" /> <result property="address.zip" column="zip" /> <result property="address.country" column="country" /> </resultMap>
<resultMap type="Address" id="AddressResult"> <id property="addrId" column="addr_id" /> <result property="street" column="street" /> <result property="city" column="city" /> <result property="state" column="state" /> <result property="zip" column="zip" /> <result property="country" column="country" /> </resultMap> <resultMap type="Student" id="StudentWithAddressResultIn"> <id property="studId" column="stud_id" /> <result property="name" column="name" /> <result property="email" column="email" /> <association property="address" resultMap="AddressResult" /> </resultMap>
<resultMap type="Student" id="StudentWithAddressResultHasOne"> <id property="studId" column="stud_id" /> <result property="name" column="name" /> <result property="email" column="email" /> <association property="address" javaType="Address"> <id property="addrId" column="addr_id" /> <result property="street" column="street" /> <result property="city" column="city" /> <result property="state" column="state" /> <result property="zip" column="zip" /> <result property="country" column="country" /> </association> </resultMap>
2.7、一對多關聯查詢
<resultMap type="Course" id="CourseResult"> <id column="course_id" property="courseId" /> <result column="name" property="name" /> <result column="description" property="description" /> <result column="start_date" property="startDate" /> <result column="end_date" property="endDate" /> </resultMap> <resultMap type="Tutor" id="TutorResult"> <id column="tutor_id" property="tutorId" /> <result column="tutor_name" property="name" /> <result column="email" property="email" /> <collection property="courses" resultMap="CourseResult" /> </resultMap>
2.8、在MyBatis的配置文件中,可以不指定輸入參數。而采用#{param1}和#{param2}引用接口中方法的形參。
3、動態SQL
3.1 if 條件
<select id="searchCourses" parameterType="hashmap" resultMap="CourseResult"> SELECT * FROM COURSES WHERE TUTOR_ID= #{tutorId} <if test="courseName != null"> AND NAME LIKE #{courseName} </if> <if test="startDate != null"> AND START_DATE > #{startDate} </if> <if test="endDate != null"> AND END_DATE < #{endDate} </if> </select>
3.2 choose when條件
<select id="searchCoursesTwo" parameterType="hashmap" resultMap="CourseResult"> SELECT * FROM COURSES <choose> <when test="searchBy == 'Tutor'"> WHERE TUTOR_ID= #{tutorId} </when> <when test="searchBy == 'CourseName'"> WHERE name like #{courseName} </when> <otherwise> WHERE TUTOR start_date >= now() </otherwise> </choose> </select>
<choose>測試條件的值,且使用第一個值為TRUE的子句,如果沒有條件為True,則使用<otherwise>內的字句
3.3 where 條件
<select id="searchCourses" parameterType="hashmap" resultMap="CourseResult"> SELECT * FROM COURSES <where> <if test=" tutorId != null "> TUTOR_ID= #{tutorId} </if> <if test="courseName != null"> AND name like #{courseName} </if> <if test="startDate != null"> AND start_date >= #{startDate} </if> <if test="endDate != null"> AND end_date <= #{endDate} </if> </where> </select>
<where>元素只有在內部標簽有返回內容時才會在動態語句上插入WHERE條件語句。並且,如果Where子句以AND 或者 OR 打頭,則打頭的AND 或 OR 將會被移除。
3.4 trim 條件
<select id="searchCourses" parameterType="hashmap" resultMap="CourseResult"> SELECT * FROM COURSES <trim prefix="WHERE" prefixOverrides="AND | OR"> /*拼接成的字句前面加上WHERE,如果WHERE后面有AND 或者 OR,將去掉。suffix末尾加上,suffixOverrides 末尾去掉*/ <if test=" tutorId != null "> TUTOR_ID= #{tutorId} </if> <if test="courseName != null"> AND name like #{courseName} </if> </trim> </select>
<trim> 元素和 <where>元素類似,如果任意一個<if>條件為true,<trim>元素會插入WHERE,並且移除緊跟WHERE 后面的AND 或 OR
3.5 foreach 循環
<select id="searchCoursesByTutors" parameterType="map" resultMap="CourseResult"> SELECT * FROM COURSES <if test="tutorIds != null"> <where> <foreach item="tutorId" collection="tutorIds"> OR tutor_id=#{tutorId} </foreach> </where> </if> </select>
它可以迭代遍歷一個數組或者列表,構造AND/OR條件或者一個IN子句。foreach 的參數 open、close、separator(列表中的每個元素以什么分隔)。
3.6 set 條件
<update id="updateStudent" parameterType="Student"> update students <set> <if test="name != null">name=#{name},</if> <if test="email != null">email=#{email},</if> <if test="phone != null">phone=#{phone},</if> </set> where stud_id=#{id} </update>
如果<if>條件返回了任何文本內容,<set>將會插入set關鍵字和其文本內容。並且會剔除末尾的","
4、緩存
4.1、第一級緩存:如果你使用同一個SqlSession 接口對象調用了相同的SELECT語句,則直接會從緩存中返回結果,而不是再查詢一次數據庫。
4.2、第二級緩存:我們可以在SQL映射器XML配置文件中使用<cache />元素添加全局二級緩存。
-
- 所有的在映射語句文件定義的<select>語句的查詢結果都會被緩存
- 所有的在映射語句文件定義的<insert>,<update> 和<delete>語句將會刷新緩存
- 緩存根據最近最少被使用(Least Recently Used,LRU)算法管理
- 緩存不會被任何形式的基於時間表的刷新(沒有刷新時間間隔),即不支持定時刷新機制
- 緩存將存儲 1024 個 查詢方法返回的列表或者對象的引用
- 緩存是線程安全的
當然我們也可以 復寫默認屬性來自定義緩存的行為:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
eviction:LRU、FIFO、SOFT、WEAK
readOnly:一個只讀的緩存會向調用者返回被緩存對象的一份引用。一個讀/寫緩存cache將會返回改對象的一份拷貝。
四、其他
1. Mybatis 的查詢結果集對屬性 set 的時候,如果子類和父類有相同的屬性,會調用誰的 setter 方法呢?
答:會調用父類的 setter 方法。所以調用子類的相同相同屬性 getter 方法,會導致 get 到的數據 為 null。這也體現了繼承中的思想。屬性只會被隱藏,不會被重寫。方法相反,不會被隱藏,只會被重寫。
2. 通過對MyBatis源碼進行分析,查詢函數返回的列表和數據項都不為空,在代碼中可以不用進行空指針判斷。
