天天看點

Spring Mybatis詳解

介紹

MyBatis 是支援定制化 SQL、存儲過程以及進階映射的優秀的持久層架構,它支援定制化 SQL、存儲過程以及進階映射,它避免了幾乎所有的 JDBC 代碼和手動設定參數以及擷取結果集,它可以使用簡單的 XML 或注解來配置和映射原生資訊,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成資料庫中的記錄。

架構

構造由資料源配置檔案、SQL映射配置檔案、會話工廠、會話、執行器、底層封裝對象組成!

資料源:

負責連接配接資料庫,并對資料進行操作的一套架構,連接配接資料庫是最重要的一步!!!

1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
 3 <configuration>
 4 
 5     <!-- 引用db.properties配置檔案 -->
 6     <properties resource="db.properties"/>
 7     <!-- 
 8         development : 開發模式
 9         work : 工作模式
10      -->
11     <environments default="development">
12         <environment id="development">
13             <transactionManager type="JDBC" />
14             <!-- 配置資料庫連接配接資訊 -->
15             <dataSource type="POOLED">
16                 <!-- value屬性值引用db.properties配置檔案中配置的值 -->
17                 <property name="driver" value="${driver}" />
18                 <property name="url" value="${url}" />
19                 <property name="username" value="${name}" />
20                 <property name="password" value="${password}" />
21             </dataSource>
22         </environment>
23     </environments>
24     
25 </configuration>
           

SQL映射配置檔案:

Mapper配置檔案,可以配置任何類型的SQL語句,包括select、update、delete、insert。

INSERT
我們在Mapper XML的檔案中使用<insert>來配置INSERT的語言。如下:

<insert id="insertStudent" parameterType="Student">  
INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL, PHONE)  
VALUES(#{studId},#{name},#{email},#{phone})  
</insert>  
在這兒,我們給出了inSertStudent作為ID,這個可以關聯的獨有的命名空間是:com.owen.mybatis.mappers.StuentMapper.insertStudent.
           
我們定義的parameterType的屬性值必須是完整的限定名,或是别名。我們可以執行上面的代碼如下:

 int count =  
sqlSession.insert("com.owen.mybatis.mappers.StudentMapper.insertStuden  
t", student);  
這個sqlSession.insert()方法傳回的數值就是插入的行數。取代上面通過命名空間和聲明的ID,你可以建立一個Mapper的接口和調用的方式如下:

 package com.owen.mybatis.mappers;  
public interface StudentMapper  
{  
int insertStudent(Student student);  
}  
你可以調用的insertStudent映射聲明如下:

StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);  
int count = mapper.insertStudent(student);  
這樣的方式比Hibernate好用了,不是使用固定的格式,而是通過自己的想法至尊命名,然後去調用。
           
自動生成Key
在前面的INSERT聲明中,我們插入STUDENT的表時,使用了自己手動的STUD_ID作為主鍵的列值。其實我們可以應用一個useGeneratedKeys和keyProperty屬性來生成auto_increment列的值,而将生成的值賦予給STUD_ID.

<insert id="insertStudent" parameterType="Student"  
useGeneratedKeys="true" keyProperty="studId">  
INSERT INTO STUDENTS(NAME, EMAIL, PHONE)  
VALUES(#{name},#{email},#{phone})  
</insert>  
這裡的STUD_ID值将會由MySQL資料庫自動生成,而且生成的值将會給student對象的參數studId。

StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);  
mapper.insertStudent(student);  
現在你可以擷取STUD_ID的值:

int studentId = student.getStudId();  
一些資料庫,如Oracle不能夠提供AUTO_INCREMENT列和運用SEQUENCE去建立主鍵的值。現在,我們擁有一個SEQUENCE名稱為STUD_ID_SEQ去生成STUD_ID的主鍵值。運用的代碼如下:

<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>  
這裡我們運用了<selectKey>的子元素去生成一個主鍵的值,并且儲蓄它在Student對象的studId的屬性中。我們設定屬性order=”BEFORE”,這個說明MyBatis在執行INSERT之前,會先對已有的主鍵進行排序,然後将主鍵儲蓄到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>  
           
聲明UPDATE
UPDATE的聲明可以使用<update>的元素配置在MapperXML檔案中。

<update id="updateStudent" parameterType="Student">  
UPDATE STUDENTS SET NAME=#{name}, EMAIL=#{email}, PHONE=#{phone}  
WHERE STUD_ID=#{studId}  
</update>  
我們可以使用如下的代碼來調用聲明:

int noOfRowsUpdated =  
sqlSession.update("com.owen.mybatis.mappers.StudentMapper.updateStudent",  
student);  
通過UPDATE的聲明,sqlSession().update()方法将會傳回受影響的行。

這裡我們就不使用聲明的命名空間和聲明的id來執行,我們使用Mapper的接口和調用 方法如下:

package com.owen.mybatis.mappers;  
public interface StudentMapper  
{  
int updateStudent(Student student);  
}  
  你可發調用updateStudent聲明使用Mapper接口。
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);  
int noOfRowsUpdated = mapper.updateStudent(student);  
           

會話工廠、會話:

SqlSessionFactory類會根據Resource資源資訊加載對象,擷取開發人員在項目中配置的資料庫連接配接池配置檔案SqlMapConfig.xml資訊,進而産生一種可與資料庫互動的會話執行個體SqlSession.

//MyBatis的配置檔案  
      Stringresource= "SqlMapConfig.xml";  
      InputStreaminputStream= Resources.getResourceAsStream(resource);  
       //建立會話工廠  
      SqlSessionFactorysqlSessionFactory= newSqlSessionFactoryBuilder().build(inputStream);  
      //從會話工廠中得到會話  
      SqlSessionsqlSession= sqlSessionFactory.openSession();  
      Useruser= sqlSession.selectOne("test.findUserByName","jasber");  
   System.out.println("-------------------------------------result-----------------------------------");  
      System.out.println(user);  
   System.out.println("------------------------------------------------------------------------------");  
      //關閉session  
      sqlSession.close();  
           

執行器:

SimpleExecutor繼承BaseExecutor
主要實作:doUpdate、doQuery、doFlushStatements
可以看代碼主要是:
1、擷取聲明處理類:StatementHandler handler
2、擷取聲明類:stmt = prepareStatement(handler, ms.getStatementLog());
3、處理聲明,執行SQL處理
           

底層封裝對象:

<resultMap id="SmsHouseholdOrderDto" type="com.dzmsoft.sms.base.dto.SmsHouseholdOrderDto">
    <id column="id_smsOrder" jdbcType="VARCHAR" property="id" />
    <association column="id_smsHouseholdOrder" javaType="com.dzmsoft.sms.base.pojo.SmsHouseholdOrder" property="smsHouseholdOrder" resultMap="com.dzmsoft.sms.base.dao.SmsHouseholdOrderMapper.BaseResultUdfMap">
    </association>   
    <association column="id_smsOrder" javaType="com.dzmsoft.sms.base.pojo.SmsOrder" property="smsOrder" resultMap="com.dzmsoft.sms.base.dao.SmsOrderMapper.BaseResultUdfMap">
    </association> 
  </resultMap>
           
public class SmsHouseholdOrderDto {
    private String id;
    private SmsHouseholdOrder smsHouseholdOrder;
    private SmsOrder smsOrder;

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public SmsHouseholdOrder getSmsHouseholdOrder() {
        return smsHouseholdOrder;
    }
    public void setSmsHouseholdOrder(SmsHouseholdOrder smsHouseholdOrder) {
        this.smsHouseholdOrder = smsHouseholdOrder;
    }
    public SmsOrder getSmsOrder() {
        return smsOrder;
    }
    public void setSmsOrder(SmsOrder smsOrder) {
        this.smsOrder = smsOrder;
    }
}
           
自定義字段
<sql id="Base_Column_List_Udf">
    smsOrder.id id_smsOrder, smsOrder.order_time order_time_smsOrder, smsOrder.appointment_time appointment_time_smsOrder, smsOrder.director director_smsOrder, smsOrder.status status_smsOrder, smsOrder.member member_smsOrder
    , smsOrder.address address_smsOrder, smsOrder.individual_needs individual_needs_smsOrder, 
    smsOrder.order_type order_type_smsOrder, smsOrder.member_phone member_phone_smsOrder,smsOrder.member_name member_name_smsOrder
  </sql>
           
<sql id="Base_Column_List_Udf">
    smsHouseholdOrder.id id_smsHouseholdOrder, smsHouseholdOrder.order_id order_id_smsHouseholdOrder, smsHouseholdOrder.times times_smsHouseholdOrder, smsHouseholdOrder.price price_smsHouseholdOrder
    , smsHouseholdOrder.discount discount_smsHouseholdOrder, smsHouseholdOrder.amount amount_smsHouseholdOrder
  </sql>
           
層次架構
Spring Mybatis詳解

MyBatis的主要成員

Configuration        MyBatis所有的配置資訊都儲存在Configuration對象之中,配置檔案中的大部配置設定置都會存儲到該類中

SqlSession            作為MyBatis工作的主要頂層API,表示和資料庫互動時的會話,完成必要資料庫增删改查功能

Executor               MyBatis執行器,是MyBatis 排程的核心,負責SQL語句的生成和查詢緩存的維護

StatementHandler 封裝了JDBC Statement操作,負責對JDBC statement 的操作,如設定參數等

ParameterHandler  負責對使用者傳遞的參數轉換成JDBC Statement 所對應的資料類型

ResultSetHandler   負責将JDBC傳回的ResultSet結果集對象轉換成List類型的集合

TypeHandler          負責java資料類型和jdbc資料類型(也可以說是資料表列類型)之間的映射和轉換

MappedStatement  MappedStatement維護一條<select|update|delete|insert>節點的封裝

SqlSource              負責根據使用者傳遞的parameterObject,動态地生成SQL語句,将資訊封裝到BoundSql對象中,并傳回

BoundSql              表示動态生成的SQL語句以及相應的參數資訊

以上主要成員在一次資料庫操作中基本都會涉及,在SQL操作中重點需要關注的是SQL參數什麼時候被設定和結果集怎麼轉換為JavaBean對象的,這兩個過程正好對應StatementHandler和ResultSetHandler類中的處理邏輯。

運作流程

1 使用連接配接池

2 統一sql 存取xml

3 參數封裝和結果映射

4 sql語句的複用封裝

(1)加載配置并初始化:觸發條件:加載配置檔案.配置來源于兩個地方,一處是配置檔案,一處是Java代碼的注解,将SQL的配置資訊加載成為一個個MappedStatement對象(包括了傳入參數映射配置、執行的SQL語句、結果映射配置),存儲在記憶體中。
(2)接收調用請求:觸發條件:調用Mybatis提供的API。傳入參數:為SQL的ID和傳入參數對象。處理過程:将請求傳遞給下層的請求處理層進行處理。
(3)處理操作請求 觸發條件:API接口層傳遞請求過來。傳入參數:為SQL的ID和傳入參數對象。
(4)傳回處理結果将最終的處理結果傳回。
           
配置檔案詳解
名稱    含義 簡介
configuration  頂級配置 頂級配置
properties  屬性 這些屬性都是可外部配置且可動态替換的,既可以在典型的 Java 屬性檔案中配置,亦可通過 properties 元素的子元素來傳遞。
settings  設定 這是 MyBatis 中極為重要的調整設定,它們會改變 MyBatis 的運作時行為。下表描述了設定中各項的意圖、預設值等。
typeAliases  類型别名 類型别名是為 Java 類型設定一個短的名字。它隻和 XML 配置有關,存在的意義僅在于用來減少類完全限定名的備援。
typeHandlers  類型處理器 無論是 MyBatis 在預處理語句(PreparedStatement)中設定一個參數時,還是從結果集中取出一個值時, 都會用類型處理器将擷取的值以合适的方式轉換成 Java 類型。下表描述了一些預設的類型處理器。
objectFactory  對象工廠 MyBatis 每次建立結果對象的新執行個體時,它都會使用一個對象工廠(ObjectFactory)執行個體來完成。 預設的對象工廠需要做的僅僅是執行個體化目标類,要麼通過預設構造方法,要麼在參數映射存在的時候通過參數構造方法來執行個體化。 如果想覆寫對象工廠的預設行為,則可以通過建立自己的對象工廠來實作。
plugins  插件 MyBatis 允許你在已映射語句執行過程中的某一點進行攔截調用。
environments  環境 MyBatis 可以配置成适應多種環境,這種機制有助于将 SQL 映射應用于多種資料庫之中, 現實情況下有多種理由需要這麼做。
environment  環境變量 同上
transactionManager  事務管理器 這個配置就是直接使用了 JDBC 的送出和復原設定,它依賴于從資料源得到的連接配接來管理事務作用域。
dataSource  資料源 dataSource 元素使用标準的 JDBC 資料源接口來配置 JDBC 連接配接對象的資源。
databaseIdProvider  資料庫廠商辨別 MyBatis 可以根據不同的資料庫廠商執行不同的語句,這種多廠商的支援是基于映射語句中的 databaseId 屬性。 
mappers  映射器 既然 MyBatis 的行為已經由上述元素配置完了,我們現在就要定義 SQL 映射語句了。

Setting

設定參數	描述	有效值	預設值
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: 輸出提醒日志 ('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等級必須設定為 WARN)
FAILING: 映射失敗 (抛出 SqlSessionException)
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 常量. 大多都為: NULL, VARCHAR and OTHER	OTHER
lazyLoadTriggerMethods	指定哪個對象的方法觸發一次延遲加載。	用逗号分隔的方法清單。	equals,clone,hashCode,toString
defaultScriptingLanguage	指定動态 SQL 生成的預設語言。	一個類型别名或完全限定類名。	org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler	指定 Enum 使用的預設 TypeHandler 。 (從3.4.5開始)	一個類型别名或完全限定類名。	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 增加到日志名稱的字首。	任何字元串	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
           
<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
           

typeAliases

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
當這樣配置時,Blog可以用在任何使用domain.blog.Blog的地方。

也可以指定一個包名,MyBatis 會在包名下面搜尋需要的 Java Bean,比如:

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>
每一個在包 domain.blog 中的 Java Bean,在沒有注解的情況下,會使用 Bean 的首字母小寫的非限定類名來作為它的别名。 比如 domain.blog.Author 的别名為 author;若有注解,則别名為其注解值。看下面的例子:

@Alias("author")
public class Author {
    ...
}
           

typeHandlers:

// ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String> {

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

  @Override
  public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
    return rs.getString(columnName);
  }

  @Override
  public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    return rs.getString(columnIndex);
  }

  @Override
  public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    return cs.getString(columnIndex);
  }
}
<!-- mybatis-config.xml -->
<typeHandlers>
  <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
           

使用這個的類型處理器将會覆寫已經存在的處理 Java 的 String 類型屬性和 VARCHAR 參數及結果的類型處理器。 要注意 MyBatis 不會窺探資料庫元資訊來決定使用哪種類型,是以你必須在參數和結果映射中指明那是 VARCHAR 類型的字段, 以使其能夠綁定到正确的類型處理器上。 這是因為:MyBatis 直到語句被執行才清楚資料類型。

通過類型處理器的泛型,MyBatis 可以得知該類型處理器處理的 Java 類型,不過這種行為可以通過兩種方法改變:

在類型處理器的配置元素(typeHandler element)上增加一個 javaType 屬性(比如:javaType="String");

在類型處理器的類上(TypeHandler class)增加一個 @MappedTypes 注解來指定與其關聯的 Java 類型清單。 如果在 javaType 屬性中也同時指定,則注解方式将被忽略。

可以通過兩種方式來指定被關聯的 JDBC 類型:

<!-- mybatis-config.xml -->
<typeHandlers>
  <package name="org.mybatis.example"/>
</typeHandlers>
           

對象工廠(objectFactory)

// ExampleObjectFactory.java
public class ExampleObjectFactory extends DefaultObjectFactory {
  public Object create(Class type) {
    return super.create(type);
  }
  public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) {
    return super.create(type, constructorArgTypes, constructorArgs);
  }
  public void setProperties(Properties properties) {
    super.setProperties(properties);
  }
  public <T> boolean isCollection(Class<T> type) {
    return Collection.class.isAssignableFrom(type);
  }}
<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
  <property name="someProperty" value="100"/>
</objectFactory>
           

插件(plugins)

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  public Object intercept(Invocation invocation) throws Throwable {
    return invocation.proceed();
  }
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  public void setProperties(Properties properties) {
  }
}
<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>
           

配置環境(environments)

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

事務管理器(transactionManager)

JDBC – 這個配置就是直接使用了 JDBC 的送出和復原設定,它依賴于從資料源得到的連接配接來管理事務作用域。
MANAGED – 這個配置幾乎沒做什麼。它從來不送出或復原一個連接配接,而是讓容器來管理事務的整個生命周期(比如 JEE 應用伺服器的上下文)。 預設情況下它會關閉連接配接,然而一些容器并不希望這樣,是以需要将 closeConnection 屬性設定為 false 來阻止它預設的關閉行為。例如:
<transactionManager type="MANAGED">
  <property name="closeConnection" value="false"/>
</transactionManager>
提示如果你正在使用 Spring + MyBatis,則沒有必要配置事務管理器, 因為 Spring 子產品會使用自帶的管理器來覆寫前面的配置。

這兩種事務管理器類型都不需要任何屬性。它們不過是類型别名,換句話說,你可以使用 TransactionFactory 接口的實作類的完全限定名或類型别名代替它們。

public interface TransactionFactory {
  void setProperties(Properties props);  
  Transaction newTransaction(Connection conn);
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);  
}
任何在 XML 中配置的屬性在執行個體化之後将會被傳遞給 setProperties() 方法。你也需要建立一個 Transaction 接口的實作類,這個接口也很簡單:

public interface Transaction {
  Connection getConnection() throws SQLException;
  void commit() throws SQLException;
  void rollback() throws SQLException;
  void close() throws SQLException;
  Integer getTimeout() throws SQLException;
}
           

一對一查詢

public interface UserMapper {
    User getUser(int userId);
}
public interface ArticleMapper {
    List<Article> getArticleByUserId(int userId);
}
           
<resultMap id="article" type="com.mybatis.model.Article">
        <id property="articleId" column="article_id" ></id>
        <result property="title" column="title"></result>
        <result property="content" column="content"></result>
        <result property="createDate" column="create_date" jdbcType="TIMESTAMP"></result>
        <association property="user" column="user_id" select="com.mybatis.mapper.UserMapper.getUser"></association>
    </resultMap>
<select id="getArticleByUserId" parameterType="int" resultMap="article">
    SELECT
    a.article_id,
    a.user_id,
    a.title,
    a.content,
    a.create_date
    FROM
    t_artile a
    where a.user_id = #{id}
</select>
           
<select id="getUser" parameterType="int" resultType="com.mybatis.model.User">
        select user_id as userId,
                username,
                password,
                age
         from t_user where user_id=#{id}
    </select>
           

ArticleMapper.xml 中使用的是UserMapper的查詢方法的接口,這樣有兩個查詢語句,一個查詢文章,一個查詢使用者。

這種方式很簡單, 但是對于大型資料集合和清單将不會表現很好。 問題就是我們熟知的 “N+1 查詢問題”。概括地講,N+1 查詢問題可以是這樣引起的:

你執行了一個單獨的 SQL 語句來擷取結果清單(就是“+1”)。對傳回的每條記錄,你執行了一個查詢語句來為每個加載細節(就是“N”)。這個問題會導緻成百上千的 SQL 語句被執行。這通常不是期望的。

MyBatis 能延遲加載這樣的查詢就是一個好處,是以你可以分散這些語句同時運作的消 耗。然而,如果你加載一個清單,之後迅速疊代來通路嵌套的資料,你會調用所有的延遲加 載,這樣的行為可能是很糟糕的。

<resultMap id="article" type="com.mybatis.model.Article">
    <id property="articleId" column="article_id" ></id>
    <result property="title" column="title"></result>
    <result property="content" column="content"></result>
    <result property="createDate" column="create_date" jdbcType="TIMESTAMP"></result>
    <association property="user" column="user_id" resultMap="user"></association>
</resultMap>

<resultMap id="user" type="com.mybatis.model.User">
    <id property="userId" column="user_id"></id>
    <result property="username" column="username"></result>
    <result property="password" column="password"></result>
    <result property="age" column="age"></result>
</resultMap>
--------------OR-------------------------
<resultMap id="article" type="com.mybatis.model.Article">
    <id property="articleId" column="article_id" ></id>
    <result property="title" column="title"></result>
    <result property="content" column="content"></result>
    <result property="createDate" column="create_date" jdbcType="TIMESTAMP"></result>
    <association property="user" javaType="com.mybatis.model.User" >
        <id property="userId" column="user_id"></id>
        <result property="username" column="username"></result>
        <result property="password" column="password"></result>
        <result property="age" column="age"></result>
    </association>
</resultMap>

<select id="getArticleByUserId" parameterType="int" resultMap="article">
   SELECT
        a.article_id,
        a.user_id,
        a.title,
        a.content,
        a.create_date,
        b.username,
        b.password,
        b.age
    FROM
        t_artile a
    INNER JOIN t_user b ON a.user_id = b.user_id
    where a.user_id = #{id}
</select>
           
一對多查詢

一對多關聯和多對一關聯的差別:是從一的一端取多的一端,還是從多的一端取一的一端。

-- 使用者表  
CREATE TABLE `user1`(  
    `id` INT PRIMARY KEY AUTO_INCREMENT,  
    `user_name` VARCHAR(20),-- 使用者姓名  
    `address` VARCHAR(60)-- 聯系位址  
) ENGINE INNODB CHARSET utf8;  
INSERT INTO `user1` VALUES(1,'陳大','深圳市南山區');  
INSERT INTO `user1` VALUES(2,'王二','深圳市福田區');  
INSERT INTO `user1` VALUES(3,'張三','深圳市龍華新區');  
INSERT INTO `user1` VALUES(4,'李四','深圳市龍崗區');  
  
  
-- 卡表  
CREATE TABLE `card1`(  
    `id` INT PRIMARY KEY AUTO_INCREMENT,  
    `card_no` VARCHAR(18),  
    `remark` VARCHAR(100),  
    `user_id` INT-- 用于關聯user1的主鍵id(不設定外鍵,避免級聯問題)  
) ENGINE=INNODB CHARSET=utf8;  
INSERT INTO `card1` VALUES(1,'420001','工資卡',1);  
INSERT INTO `card1` VALUES(2,'420002','工資卡',2);  
INSERT INTO `card1` VALUES(3,'420003','工資卡',3);  
INSERT INTO `card1` VALUES(4,'420004','工資卡',3);  
  
  
-- SELECT * FROM `user1`;  
-- SELECT * FROM `card1`;  
           
package com.chensan.o2m.entity;  
  
public class Card1 {  
    private int id;  
    private String cardNo;  
    private String remark;  
      
    //...省略setter、getter方法  
}  
           
package com.chensan.o2m.entity;  
  
import java.util.List;  
  
public class User1 {  
    private int id;  
    private String userName;  
    private String address;  
    private List<Card1> cards;  
      
    public String toString(){  
        return "[ id = " + id  + ", userName = "   
            + userName + ", address = " + address + "]";  
    }  
    //...省略setter、getter方法  
}  
           
<?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.chensan.o2m.entity.User1Mapper">  
  <resultMap id="user1" type="com.chensan.o2m.entity.User1">  
    <id property="id" column="user_id"/>  
    <result property="userName" column="user_name"/>  
    <result property="address" column="address"/>  
      
    <collection property="cards" column="user_id" ofType="com.chensan.o2m.entity.Card1">  
        <id property="id" column="id"/>  
        <result property="cardNo" column="card_no"/>  
        <result property="remark" column="remark"/>  
    </collection>  
  </resultMap>  
    
  <select id="query" parameterType="int" resultMap="user1">  
    SELECT t1.`id` `user_id`,t1.`user_name`,t1.`address`,t2.`id`,t2.`card_no`,t2.`remark`   
    FROM `user1` t1,`card1` t2   
    WHERE t1.`id`=t2.`user_id` AND t1.`id`=#{id}  
  </select>  
</mapper>  
           
<?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>  
  <!-- 和spring整合後 environments配置将廢除-->  
  <environments default="development">  
    <environment id="development">  
      <!-- 使用jdbc事務管理  或者JTA事務管理-->  
      <transactionManager type="JDBC" />  
      <!-- 資料庫連接配接池  第三方元件:c3p0-->  
      <dataSource type="POOLED">  
        <property name="driver" value="com.mysql.jdbc.Driver"/>  
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis01"/>  
        <property name="username" value="root"/>  
        <property name="password" value="123456"/>  
      </dataSource>  
    </environment>  
  </environments>  
  
  <!-- 加載實體類的映射檔案 -->  
  <mappers>  
    <mapper resource="com/chensan/o2m/mapper/User1Mapper.xml"/>  
  </mappers>  
</configuration>  
           
public class TestO2M {  
    private static SqlSessionFactory sqlSessionFactory;  
    private static Reader resource;  
      
    //建立會話工廠  
    static{  
        try{  
            resource = Resources.getResourceAsReader("myBatisConfig.xml");  
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);  
        }catch(Exception e){  
            e.printStackTrace();  
        }  
    }  
      
    public static SqlSessionFactory getSession(){  
        return sqlSessionFactory;  
    }  
      
    //一對多:查詢使用者對應卡(銀行卡)  
    public void getUserCard(){  
        SqlSession sqlSession = sqlSessionFactory.openSession();  
        User1 user = sqlSession.selectOne("com.chensan.o2m.entity.User1Mapper.query", 3);  
        System.out.println(user);  
        for(Card1 card : user.getCards()){  
            System.out.println(  
                "[ " +   
                "userId = " + user.getId() + ", " +   
                "userName = " + user.getUserName() + ", " +   
                "address = " + user.getAddress() + ", " +   
                "cardId = " + card.getId() + ", " +   
                "cardNo = " + card.getCardNo() + ", " +   
                "remark = " + card.getRemark() +   
                " ]"  
            );  
        }  
          
        sqlSession.close();  
    }  
  
    public static void main(String[] args) {  
        TestO2M testMyBatisOneToMany = new TestO2M();  
        testMyBatisOneToMany.getUserCard();  
    }  
}  
           
多對多查詢
SELECT 
  orders.*,
  user.username,
  user.sex,
  user.address,
  orderdetail.id orderdetail_id,
  orderdetail.items_id,
  orderdetail.items_num,
  orderdetail.orders_id,
  items.name items_name,
  items.detail items_detail,
  items.price items_price
FROM
  orders,
  user,
  orderdetail,
  items
WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id AND orderdetail.items_id = items.id
           
<!-- 查詢使用者及購買的商品資訊,使用resultmap -->
<select id="findUserAndItemsResultMap" resultMap="UserAndItemsResultMap">
   SELECT
      orders.*,
      user.username,
      user.sex,
      user.address,
      orderdetail.id orderdetail_id,
      orderdetail.items_id,
      orderdetail.items_num,
      orderdetail.orders_id,
      items.name items_name,
      items.detail items_detail,
      items.price items_price
    FROM
      orders,
      user,
      orderdetail,
      items
    WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id AND orderdetail.items_id = items.id
</select>
           
<!-- 查詢使用者及購買的商品 -->
<resultMap type="com.iot.mybatis.po.User" id="UserAndItemsResultMap">
    <!-- 使用者資訊 -->
    <id column="user_id" property="id"/>
    <result column="username" property="username"/>
    <result column="sex" property="sex"/>
    <result column="address" property="address"/>

    <!-- 訂單資訊
    一個使用者對應多個訂單,使用collection映射
     -->
    <collection property="ordersList" ofType="com.iot.mybatis.po.Orders">
        <id column="id" property="id"/>
        <result column="user_id" property="userId"/>
        <result column="number" property="number"/>
        <result column="createtime" property="createtime"/>
        <result column="note" property="note"/>

        <!-- 訂單明細
         一個訂單包括 多個明細
         -->
        <collection property="orderdetails" ofType="com.iot.mybatis.po.Orderdetail">
            <id column="orderdetail_id" property="id"/>
            <result column="items_id" property="itemsId"/>
            <result column="items_num" property="itemsNum"/>
            <result column="orders_id" property="ordersId"/>

            <!-- 商品資訊
             一個訂單明細對應一個商品
             -->
            <association property="items" javaType="com.iot.mybatis.po.Items">
                <id column="items_id" property="id"/>
                <result column="items_name" property="name"/>
                <result column="items_detail" property="detail"/>
                <result column="items_price" property="price"/>
            </association>

        </collection>

    </collection>
</resultMap>
           
//查詢使用者購買商品資訊
public List<User>  findUserAndItemsResultMap()throws Exception;
           
resultType

作用:将查詢結果按照sql列名pojo屬性名一緻性映射到pojo中。
場合:常見一些明細記錄的展示,比如使用者購買商品明細,将關聯查詢資訊全部展示在頁面時,此時可直接使用resultType将每一條記錄映射到pojo中,在前端頁面周遊list(list中是pojo)即可。
resultMap

使用association和collection完成一對一和一對多進階映射(對結果有特殊的映射要求)。

association:

作用:将關聯查詢資訊映射到一個pojo對象中。
場合:為了友善查詢關聯資訊可以使用association将關聯訂單資訊映射為使用者對象的pojo屬性中,比如:查詢訂單及關聯使用者資訊。
使用resultType無法将查詢結果映射到pojo對象的pojo屬性中,根據對結果集查詢周遊的需要選擇使用resultType還是resultMap。

collection:

作用:将關聯查詢資訊映射到一個list集合中。
場合:為了友善查詢周遊關聯資訊可以使用collection将關聯資訊映射到list集合中,比如:查詢使用者權限範圍子產品及子產品下的菜單,可使用collection将子產品映射到子產品list中,将菜單清單映射到子產品對象的菜單list屬性中,這樣的作的目的也是友善對查詢結果集進行周遊查詢。如果使用resultType無法将查詢結果映射到list集合中。
           
延遲加載

延遲加載的條件:resultMap可以實作進階映射(使用association、collection實作一對一及一對多映射),association、collection具備延遲加載功能。

延遲加載的好處:

先從單表查詢、需要時再從關聯表去關聯查詢,大大提高 資料庫性能,因為查詢單表要比關聯查詢多張表速度要快。

延遲加載的執行個體:

如果查詢訂單并且關聯查詢使用者資訊。如果先查詢訂單資訊即可滿足要求,當我們需要查詢使用者資訊時再查詢使用者資訊。把對使用者資訊的按需去查詢就是延遲加載。

<settings>
    <!--開啟延遲加載-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--關閉積極加載-->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>
           

lazyLoadingEnabled:全局性設定懶加載。如果設為‘false’,則所有相關聯的都會被初始化加載。預設為false

aggressiveLazyLoading:當設定為‘true’的時候,懶加載的對象可能被任何懶屬性全部加載。否則,每個屬性都按需加載。預設為true

延遲加載的resultMap

在resultMap中使用association或者collection,即可使用延遲加載。

延遲加載需要兩個statement語句來完成

在resultMap中使用association或者collection來配置兩個statement直接的管理。

延遲加載的mapper檔案

必須要有兩個statement

兩個statement直接必須存在關聯的資料列

<select id="findOrdersByLazyLoad" resultMap="OrderAndUserLazyLoad">
    SELECT * FROM orders
</select>

<select id="findUser" parameterType="int" resultType="User">
    SELECT * FROM User WHERE id = #{value}
</select>
           
Mapper動态代理

Mapper接口開發方法隻需要程式員編寫Mapper接口(相當于Dao接口),由Mybatis架構根據接口定義建立接口的動态代理對象,代理對象的方法體同上邊Dao接口實作類方法。

Mapper接口開發需要遵循以下規範:

                1、 Mapper.xml檔案中的namespace與mapper接口的類路徑相同

                2、  Mapper接口方法名和Mapper.xml中定義的每個statement的id相同 

                3、 Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql 的parameterType的類型相同

                4、 Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同

<?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.UserMapper">  
<!-- 根據id擷取使用者資訊 -->  
    <select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.po.User">  
        select * from user where id = #{id}  
    </select>  
<!-- 自定義條件查詢使用者清單 -->  
    <select id="findUserByUsername" parameterType="java.lang.String"   
            resultType="cn.itcast.mybatis.po.User">  
       select * from user where username like '%${value}%'   
    </select>  
<!-- 添加使用者 -->  
    <insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">  
    <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">  
        select LAST_INSERT_ID()   
    </selectKey>  
      insert into user(username,birthday,sex,address)   
      values(#{username},#{birthday},#{sex},#{address})  
    </insert>  
  
</mapper>  
           
/** 
 * 使用者管理mapper 
 */  
Public interface UserMapper {  
    //根據使用者id查詢使用者資訊  
    public User findUserById(int id) throws Exception;  
    //查詢使用者清單  
    public List<User> findUserByUsername(String username) throws Exception;  
    //添加使用者資訊  
    public void insertUser(User user)throws Exception;   
}  
           
<!-- 加載映射檔案 -->  
 <mappers>  
   <mapper resource="mapper/UserMapper.xml"/>  
 </mappers>  
           
Public class UserMapperTest extends TestCase {  
  
    private SqlSessionFactory sqlSessionFactory;  
      
    protected void setUp() throws Exception {  
        //mybatis配置檔案  
        String resource = "sqlMapConfig.xml";  
        InputStream inputStream = Resources.getResourceAsStream(resource);  
        //使用SqlSessionFactoryBuilder建立sessionFactory  
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);  
    }  
  
      
    Public void testFindUserById() throws Exception {  
        //擷取session  
        SqlSession session = sqlSessionFactory.openSession();  
        //擷取mapper接口的代理對象  
        UserMapper userMapper = session.getMapper(UserMapper.class);  
        //調用代理對象方法  
        User user = userMapper.findUserById(1);  
        System.out.println(user);  
        //關閉session  
        session.close();  
          
    }  
    @Test  
    public void testFindUserByUsername() throws Exception {  
        SqlSession sqlSession = sqlSessionFactory.openSession();  
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);  
        List<User> list = userMapper.findUserByUsername("張");  
        System.out.println(list.size());  
  
    }  
Public void testInsertUser() throws Exception {  
        //擷取session  
        SqlSession session = sqlSessionFactory.openSession();  
        //擷取mapper接口的代理對象  
        UserMapper userMapper = session.getMapper(UserMapper.class);  
        //要添加的資料  
        User user = new User();  
        user.setUsername("張三");  
        user.setBirthday(new Date());  
        user.setSex("1");  
        user.setAddress("北京市");  
        //通過mapper接口添加使用者  
        userMapper.insertUser(user);  
        //送出  
        session.commit();  
        //關閉session  
        session.close();  
    }  
}  
           
一級查詢緩存

是SQlSession級别的緩存。在操作資料庫時需要構造SqlSession對象,在對象中有一個資料結構(HashMap)用于存儲緩存資料。不同的SqlSession之間的緩存資料區域(HashMap)是互相不影響的。其預設是開啟的,無法關閉,作用範圍為namespace,生命周期同整個SqlSession相同。

public class UserMapperTest {  
  
    private SqlSessionFactory sqlSessionFactory;  
  
    // 此方法是在執行testFindUserById之前執行  
    @Before  
    public void setUp() throws Exception {  
        // 建立sqlSessionFactory  
  
        // mybatis配置檔案  
        String resource = "SqlMapConfig.xml";  
        // 得到配置檔案流  
        InputStream inputStream = Resources.getResourceAsStream(resource);  
  
        // 建立會話工廠,傳入mybatis的配置檔案資訊  
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);  
    }  
  
    @Test  
    public void testFindUserById() throws Exception {  
  
        SqlSession sqlSession = sqlSessionFactory.openSession();  
  
        // 建立UserMapper對象,mybatis自動生成mapper代理對象  
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);  
  
        // 調用userMapper的方法  
        // 第一次查詢(先去緩存中查找,有則直接擷取,沒有則執行SQL語句查詢)  
        User user = userMapper.findUserById(1);  
  
        System.out.println(user);  
        // 第二次查詢(先去緩存中查找,有則直接擷取,沒有則執行SQL語句查詢)  
        User user2 = userMapper.findUserById(1);  
  
        System.out.println(user1);  
  
    }  
}
           
Spring Mybatis詳解
public class UserMapperTest {  
  
    private SqlSessionFactory sqlSessionFactory;  
  
    // 此方法是在執行testFindUserById之前執行  
    @Before  
    public void setUp() throws Exception {  
        // 建立sqlSessionFactory  
  
        // mybatis配置檔案  
        String resource = "SqlMapConfig.xml";  
        // 得到配置檔案流  
        InputStream inputStream = Resources.getResourceAsStream(resource);  
  
        // 建立會話工廠,傳入mybatis的配置檔案資訊  
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);  
    }  
  
    @Test  
    public void testFindUserById() throws Exception {  
  
        SqlSession sqlSession = sqlSessionFactory.openSession();  
  
        // 建立UserMapper對象,mybatis自動生成mapper代理對象  
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);  
  
        // 調用userMapper的方法  
        // ①第一次查詢(先去緩存中查找,有則直接擷取,沒有則執行SQL語句查詢)  
        User user1 = userMapper.findUserById(1);  
  
        System.out.println(user1);  
  
        // ②插入使用者對象  
        User user = new User();  
        user.setUsername("王二小");  
        user.setBirthday(new Date());  
        user.setSex("1");  
        user.setAddress("河北廊坊");  
  
        sqlSession.insert("test.insertUser", user);  
  
        // 送出事務  
        sqlSession.commit();  
          
        // ③第二次查詢(先去緩存中查找,有則直接擷取,沒有則執行SQL語句查詢)  
        User user2 = userMapper.findUserById(1);  
  
        System.out.println(user2);  
  
    }  
}
           
Spring Mybatis詳解
二級查詢緩存

是mapper級别的緩存,多個SqlSession去操作同一個mapper的sql語句,多個SqlSession可以共用二級緩存,二級緩存是跨SqlSession的。其預設是開啟的,無法關閉,作用範圍為namespace,生命周期同整個SqlSession相同。

Spring Mybatis詳解
在核心配置檔案SQLMapConfig.xml中的全局設定中開啟二級緩存,将value設為true。如下:

<span style="font-family:'Comic Sans MS';"><settings>  
    <!-- 開啟二級緩存 -->  
    <setting name="cacheEnabled" value="true"/>  
</settings></span>
           
<span style="font-family:'Comic Sans MS';"><?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">  
  
<!-- namespace命名空間,作用就是對sql進行分類化管理,了解sql隔離   
注意:使用mapper代理方法開發,namespace有特殊重要的作用,namespace等于mapper接口位址  
-->  
<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">  
  
    <!-- 開啟本mapper的namespace下的二緩存  
    type:指定cache接口的實作類的類型,mybatis預設使用PerpetualCache  
    要和ehcache整合,需要配置type為ehcache實作cache接口的類型  
     -->  
    <cache />  
<!-- 下面的一些SQL語句暫時略 -->  
</mapper></span>  
           
public class UserMapperTest {  
  
    private SqlSessionFactory sqlSessionFactory;  
  
    // 此方法是在執行testFindUserById之前執行  
    @Before  
    public void setUp() throws Exception {  
        // 建立sqlSessionFactory  
  
        // mybatis配置檔案  
        String resource = "SqlMapConfig.xml";  
        // 得到配置檔案流  
        InputStream inputStream = Resources.getResourceAsStream(resource);  
  
        // 建立會話工廠,傳入mybatis的配置檔案資訊  
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);  
    }  
  
    @Test  
    public void testFindUserById() throws Exception {  
  
        SqlSession sqlSession1 = sqlSessionFactory.openSession();  
        SqlSession sqlSession2 = sqlSessionFactory.openSession();  
        SqlSession sqlSession3 = sqlSessionFactory.openSession();  
        // 建立代理對象  
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);  
        // 第一次發起請求,查詢id為1的使用者  
        User user1 = userMapper1.findUserById(1);  
        System.out.println(user1);  
          
        //這裡執行關閉操作,将sqlsession中的資料寫到二級緩存區域  
        sqlSession1.close();  
  
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);  
        // 第二次發起請求,查詢id為1的使用者  
        User user2 = userMapper2.findUserById(1);  
        System.out.println(user2);  
  
        sqlSession2.close();  
  
  
    }  
}
           
Spring Mybatis詳解
逆向工程

MyBatis的一個主要的特點就是需要程式員自己編寫sql,那麼如果表太多的話,難免會很麻煩,是以mybatis官方提供了一個逆向工程,可以針對單表自動生成mybatis執行所需要的代碼(包括mapper.xml、mapper.java、po..)。一般在開發中,常用的逆向工程方式是通過資料庫的表生成代碼。

使用MyBatis的逆向工程,需要導入逆向工程的jar包 mybatis-generator-core-1.3.2.jar。

配置逆向工程的配置檔案

<?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>
    <context id="testTables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自動生成的注釋 true:是 : false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--資料庫連接配接的資訊:驅動類、連接配接位址、使用者名、密碼 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
            connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root"
            password="yezi">
        </jdbcConnection>
        <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
            connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" 
            userId="yycg"
            password="yycg">
        </jdbcConnection> -->

        <!-- 預設false,把JDBC DECIMAL 和 NUMERIC 類型解析為 Integer,為 true時把JDBC DECIMAL 和 
            NUMERIC 類型解析為java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- targetProject:生成PO類的位置 -->
        <javaModelGenerator targetPackage="com.itheima.mybatis.po"
            targetProject=".\src">
            <!-- enableSubPackages:是否讓schema作為包的字尾 -->
            <property name="enableSubPackages" value="false" />
            <!-- 從資料庫傳回的值被清理前後的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- targetProject:mapper映射檔案生成的位置 -->
        <sqlMapGenerator targetPackage="com.itheima.mybatis.mapper" 
            targetProject=".\src">
            <!-- enableSubPackages:是否讓schema作為包的字尾 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- targetPackage:mapper接口生成的位置 -->
        <javaClientGenerator type="XMLMAPPER"
            targetPackage="com.itheima.mybatis.mapper" 
            targetProject=".\src">
            <!-- enableSubPackages:是否讓schema作為包的字尾 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>
        <!-- 指定資料庫表 -->
        <table schema="" tableName="user"></table>
        <table schema="" tableName="orders"></table>

        <!-- 有些表的字段需要指定java類型
         <table schema="" tableName="">
            <columnOverride column="" javaType="" />
        </table> -->
    </context>
</generatorConfiguration>
           
public class GeneratorSqlmap {

    public void generator() throws Exception{

        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        //指定 逆向工程配置檔案
        File configFile = new File("generatorConfig.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);

    } 
    public static void main(String[] args) throws Exception {
        try {
            GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
            generatorSqlmap.generator();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
           
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!-- 配置要掃描的包,如果掃描多個包使用半形逗號分隔 -->
    <!-- <property name="basePackage" value="cn.itheima.mybatis.mapper,com.itheima.mybatis.mapper" /> -->
    <property name="basePackage" value="com.itheima.mybatis.mapper" />
</bean>
           
public class UserMapperTest {

    private ApplicationContext applicationContext;

    @Before
    public void init() {
        // 初始化Spring容器
        applicationContext = new ClassPathXmlApplicationContext("classpath:spring/application-context.xml");
    }

    @Test
    public void testDeleteByPrimaryKey() {
        // 請讀者自行測試...
    }

    @Test
    public void testInsert() {
        UserMapper userMapper = applicationContext.getBean(UserMapper.class);
        User user = new User();
        user.setUsername("武大郎");
        user.setSex("1");
        user.setBirthday(new Date());
        user.setAddress("河北清河縣");
        userMapper.insert(user);
    }

    @Test
    public void testSelectByExample() {
        UserMapper userMapper = applicationContext.getBean(UserMapper.class);
        UserExample example = new UserExample();
        // Criteria類是UserExample類裡面的内部類,Criteria類是幹什麼用的呢?它專門用于封裝自定義查詢條件的
        // Criteria criteria = example.createCriteria();
        // criteria.andUsernameLike("%張%");
        // 執行查詢
        List<User> list = userMapper.selectByExample(example);
        for (User user : list) {
            System.out.println(user);
        }
    }

    @Test
    public void testSelectByPrimaryKey() {
        UserMapper userMapper = applicationContext.getBean(UserMapper.class);
        User user = userMapper.selectByPrimaryKey(10);
        System.out.println(user);
    }

    @Test
    public void testUpdateByPrimaryKey() {
        // 請讀者自行測試...
    }

}
           

……