大部分時候,我們都是在Spring 里面去集成MyBatis。因為Spring 對MyBatis 的一些操作進行的封裝,我們不能直接看到它的本質,所以先看下不使用容器的時候,也就是編程的方式,MyBatis 怎么使用。先引入mybatis jar 包。
首先我們要創建一個全局配置文件,這里面是對MyBatis 的核心行為的控制,比如mybatis-config.xml。
第二個就是我們的映射器文件,Mapper.xml,通常來說一張表對應一個,我們會在這個里面配置我們增刪改查的SQL 語句,以及參數和返回的結果集的映射關系。跟JDBC 的代碼一樣,我們要執行對數據庫的操作,必須創建一個會話,這個在MyBatis 里面就是SqlSession。SqlSession 又是工廠類根據全局配置文件創建的。所以整個的流程就是這樣的(如下代碼)。最后我們通過SqlSession 接口上的方法,傳入我們的Statement ID 來執行SQL。這是第一種方式。
這種方式有一個明顯的缺點,就是會對Statement ID 硬編碼,而且不能在編譯時進行類型檢查,所以通常我們會使用第二種方式,就是定義一個Mapper 接口的方式。這個接口全路徑必須跟Mapper.xml 里面的namespace 對應起來,方法也要跟StatementID 一一對應。
public void testMapper() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); try { BlogMapper mapper = session.getMapper(BlogMapper.class); Blog blog = mapper.selectBlogById(1); System.out.println(blog); } finally { session.close(); } }
這個就是我們單獨使用MyBatis 的基本流程。
核心對象的生命周期
在編程式使用的這個demo 里面,我們看到了MyBatis 里面的幾個核心對象:SqlSessionFactoryBuiler、SqlSessionFactory、SqlSession 和Mapper 對象。這幾個核心對象在MyBatis 的整個工作流程里面的不同環節發揮作用。如果說我們不用容器,自己去管理這些對象的話,我們必須思考一個問題:什么時候創建和銷毀這些對象?在一些分布式的應用里面,多線程高並發的場景中,如果要寫出高效的代碼,必須了解這四個對象的生命周期。這四個對象的聲明周期的描述在官網上面也可以找到。我們從每個對象的作用的角度來理解一下,只有理解了它們是干什么的,才知道什么時候應該創建,什么時候應該銷毀。
SqlSessionFactoryBuilder
這個類可以被實例化、使用和丟棄,一旦創建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 實例的最佳作用域是方法作用域(也就是局部方法變量)。 你可以重用 SqlSessionFactoryBuilder 來創建多個 SqlSessionFactory 實例,但是最好還是不要讓其一直存在,以保證所有的 XML 解析資源可以被釋放給更重要的事情。
SqlSessionFactory
SqlSessionFactory 一旦被創建就應該在應用的運行期間一直存在,沒有任何理由丟棄它或重新創建另一個實例。 使用 SqlSessionFactory 的最佳實踐是在應用運行期間不要重復創建多次,多次重建 SqlSessionFactory 被視為一種代碼“壞味道(bad smell)”。因此 SqlSessionFactory 的最佳作用域是應用作用域。 有很多方法可以做到,最簡單的就是使用單例模式或者靜態單例模式。
SqlSession
每個線程都應該有它自己的 SqlSession 實例。SqlSession 的實例不是線程安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。 絕對不能將 SqlSession 實例的引用放在一個類的靜態域,甚至一個類的實例變量也不行。 也絕不能將 SqlSession 實例的引用放在任何類型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你現在正在使用一種 Web 框架,要考慮 SqlSession 放在一個和 HTTP 請求對象相似的作用域中。 換句話說,每次收到的 HTTP 請求,就可以打開一個 SqlSession,返回一個響應,就關閉它。 這個關閉操作是很重要的,你應該把這個關閉操作放到 finally 塊中以確保每次都能執行關閉。 下面的示例就是一個確保 SqlSession 關閉的標准模式:
SqlSession session = sqlSessionFactory.openSession(); try { // 你的應用邏輯代碼 } finally { session.close(); }
在你的所有的代碼中一致地使用這種模式來保證所有數據庫資源都能被正確地關閉。
映射器實例(Mapper)
映射器是一些由你創建的、綁定你映射的語句的接口。映射器接口的實例是從 SqlSession 中獲得的。因此從技術層面講,任何映射器實例的最大作用域是和請求它們的 SqlSession 相同的。盡管如此,映射器實例的最佳作用域是方法作用域。 也就是說,映射器實例應該在調用它們的方法中被請求,用過之后即可丟棄。 並不需要顯式地關閉映射器實例,盡管在整個請求作用域保持映射器實例也不會有什么問題,但是你很快會發現,像 SqlSession 一樣,在這個作用域上管理太多的資源的話會難於控制。 為了避免這種復雜性,最好把映射器放在方法作用域內。下面的示例就展示了這個實踐:
SqlSession session = sqlSessionFactory.openSession(); try { BlogMapper mapper = session.getMapper(BlogMapper.class); // 你的應用邏輯代碼 } finally { session.close(); }
這個就是我們在編程式的使用里面看到的四個對象的生命周期的總結。
核心配置解讀
第一個是config 文件。大部分時候我們只需要很少的配置就可以讓MyBatis 運行起來。其實MyBatis 里面提供的配置項非常多,我們沒有配置的時候使用的是系統的默認值。
目前最新的版本是3.5.1,大家可以從官方上下載到最新的源碼。中文地址:http://www.mybatis.org/mybatis-3/zh/index.html
<?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="db.properties"></properties> <settings> <!-- 打印查詢語句 --> <setting name="logImpl" value="STDOUT_LOGGING" /> <!-- 控制全局緩存(二級緩存)--> <setting name="cacheEnabled" value="true"/> <!-- 延遲加載的全局開關。當開啟時,所有關聯對象都會延遲加載。默認 false --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 當開啟時,任何方法的調用都會加載該對象的所有屬性。默認 false,可通過select標簽的 fetchType來覆蓋--> <setting name="aggressiveLazyLoading" value="false"/> <!-- Mybatis 創建具有延遲加載能力的對象所用到的代理工具,默認JAVASSIST --> <!--<setting name="proxyFactory" value="CGLIB" />--> <!-- STATEMENT級別的緩存,使一級緩存,只針對當前執行的這一statement有效 --> <!-- <setting name="localCacheScope" value="STATEMENT"/> --> <setting name="localCacheScope" value="SESSION"/> </settings> <typeAliases> <typeAlias alias="blog" type="com.wuzz.domain.Blog" /> </typeAliases> <!-- <typeHandlers> <typeHandler handler="com.wuzz.type.MyTypeHandler"></typeHandler> </typeHandlers>--> <!-- 對象工廠 --> <!-- <objectFactory type="com.wuzz.objectfactory.GPObjectFactory"> <property name="wuzz" value="666"/> </objectFactory>--> <!-- <plugins> <plugin interceptor="com.wuzz.interceptor.SQLInterceptor"> <property name="wuzz" value="betterme" /> </plugin> <plugin interceptor="com.wuzz.interceptor.MyPageInterceptor"> </plugin> </plugins>--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/><!-- 單獨使用時配置成MANAGED沒有事務 --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="BlogMapper.xml"/> <mapper resource="BlogMapperExt.xml"/> </mappers> </configuration>
configuration
configuration 是整個配置文件的根標簽,實際上也對應着MyBatis 里面最重要的配置類Configuration。它貫穿MyBatis 執行流程的每一個環節。這里面有很多的屬性,跟其他的子標簽也能對應上。
注意:MyBatis 全局配置文件順序是固定的,否則啟動的時候會報錯。
properties
第一個是properties 標簽,用來配置參數信息,比如最常見的數據庫連接信息。為了避免直接把參數寫死在xml 配置文件中,我們可以把這些參數單獨放在properties 文件中,用properties 標簽引入進來,然后在xml 配置文件中用${}引用就可以了。可以用resource 引用應用里面的相對路徑,也可以用url 指定本地服務器或者網絡的絕對路徑。
我們為什么要把這些配置獨立出來?有什么好處?或者說,公司的項目在打包的時候,有沒有把properties 文件打包進去?
- 提取,利於多處引用,維護簡單;
- 把配置文件放在外部,避免修改后重新編譯打包,只需要重啟應用;
- 程序和配置分離,提升數據的安全性,比如生產環境的密碼只有運維人員掌握。
settings
這是 MyBatis 中極為重要的調整設置,它們會改變 MyBatis 的運行時行為。 下表描述了設置中各項的意圖、默認值等。
設置名 | 描述 | 有效值 | 默認值 |
---|---|---|---|
cacheEnabled | 全局地開啟或關閉配置文件中的所有映射器已經配置的任何緩存。 | true | false | true |
lazyLoadingEnabled | 延遲加載的全局開關。當開啟時,所有關聯對象都會延遲加載。 特定關聯關系中可通過設置 fetchType屬性來覆蓋該項的開關狀態。 | true | false | false |
aggressiveLazyLoading | 當開啟時,任何方法的調用都會加載該對象的所有屬性。 否則,每個屬性會按需加載(參考 lazyLoadTriggerMethods)。 | true | false | false (在 3.4.1 及之前的版本默認值為 true) |
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 | 設置超時時間,它決定驅動等待數據庫響應的秒數。 | 任意正整數 | 未設置 (null) |
defaultFetchSize | 為驅動的結果集獲取數量(fetchSize)設置一個提示值。此參數只可以在查詢設置中被覆蓋。 | 任意正整數 | 未設置 (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 或 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會返回一個空實例。 請注意,它也適用於嵌套的結果集 (如集合或關聯)。(新增於 3.4.2) | true | false | false |
logPrefix | 指定 MyBatis 增加到日志名稱的前綴。 | 任何字符串 | 未設置 |
logImpl | 指定 MyBatis 所用日志的具體實現,未指定時將自動查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未設置 |
proxyFactory | 指定 Mybatis 創建具有延遲加載能力的對象所用到的代理工具。 | CGLIB | JAVASSIST | JAVASSIST (MyBatis 3.3 以上) |
vfsImpl | 指定 VFS 的實現 | 自定義 VFS 的實現的類全限定名,以逗號分隔。 | 未設置 |
useActualParamName | 允許使用方法簽名中的名稱作為語句參數名稱。 為了使用該特性,你的項目必須采用 Java 8 編譯,並且加上 -parameters 選項。(新增於 3.4.1) | true | false | true |
configurationFactory | 指定一個提供 Configuration 實例的類。 這個被返回的 Configuration 實例用來加載被反序列化對象的延遲加載屬性值。 這個類必須包含一個簽名為static Configuration getConfiguration() 的方法。(新增於 3.2.3) | 類型別名或者全類名. | 未設置 |
typeAliases
TypeAlias 是類型的別名,跟Linux 系統里面的alias 一樣,主要用來簡化全路徑類名的拼寫。比如我們的參數類型和返回值類型都可能會用到我們的Bean,如果每個地方都配置全路徑的話,那么內容就比較多,還可能會寫錯。我們可以為自己的Bean 創建別名,既可以指定單個類,也可以指定一個package,自動轉換。配置了別名以后,只需要寫別名就可以了,比如com.gupaoedu.domain.Blog都只要寫blog 就可以了。MyBatis 里面有系統預先定義好的類型別名,在TypeAliasRegistry 中。
<typeAliases> <typeAlias alias="Author" type="domain.blog.Author"/> <typeAlias alias="Blog" type="domain.blog.Blog"/> </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 *
由於Java 類型和數據庫的JDBC 類型不是一一對應的(比如String 與varchar),所以我們把Java 對象轉換為數據庫的值,和把數據庫的值轉換成Java 對象,需要經過一定的轉換,這兩個方向的轉換就要用到TypeHandler。有的同學可能會有疑問,我沒有做任何的配置,為什么實體類對象里面的一個String屬性,可以保存成數據庫里面的varchar 字段,或者保存成char 字段?這是因為MyBatis 已經內置了很多TypeHandler(在type 包下),它們全部全部注冊在TypeHandlerRegistry 中,他們都繼承了抽象類BaseTypeHandler,泛型就是要處理的Java 數據類型。
當我們做數據類型轉換的時候,就會自動調用對應的TypeHandler 的方法。如果我們需要自定義一些類型轉換規則,或者要在處理類型的時候做一些特殊的動作,就可以編寫自己的TypeHandler,跟系統自定義的TypeHandler 一樣,繼承抽象類BaseTypeHandler<T>。有4 個抽象方法必須實現,我們把它分成兩類:set 方法從Java 類型轉換成JDBC 類型的,get 方法是從JDBC 類型轉換成Java 類型的。
比如我們想要在獲取或者設置String 類型的時候做一些特殊處理,我們可以寫一個String 類型的TypeHandler
public class MyTypeHandler extends BaseTypeHandler<String> { public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { // 設置 String 類型的參數的時候調用,Java類型到JDBC類型 // 注意只有在字段上添加typeHandler屬性才會生效 // insertBlog name字段 System.out.println("---------------setNonNullParameter1:"+parameter); ps.setString(i, parameter); } public String getNullableResult(ResultSet rs, String columnName) throws SQLException { // 根據列名獲取 String 類型的參數的時候調用,JDBC類型到java類型 // 注意只有在字段上添加typeHandler屬性才會生效 System.out.println("---------------getNullableResult1:"+columnName); return rs.getString(columnName); } public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { // 根據下標獲取 String 類型的參數的時候調用 System.out.println("---------------getNullableResult2:"+columnIndex); return rs.getString(columnIndex); } public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { System.out.println("---------------getNullableResult3:"); return cs.getString(columnIndex); } }
第二步,在mybatis-config.xml 文件中注冊:
<typeHandlers> <typeHandler handler="com.wuzz.type.MyTypeHandler"></typeHandler> </typeHandlers>
第三步,在我們需要使用的字段上指定,比如:插入值的時候,從Java 類型到JDBC 類型,在字段屬性中指定typehandler:
<insert id="insertBlog" parameterType="com.wuzz.domain.Blog"> insert into blog (bid, name, author_id) values (#{bid,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR,typeHandler=com.wuzz.type.MyTypeHandler}, #{authorId,jdbcType=INTEGER}) </insert>
返回值的時候,從JDBC 類型到Java 類型,在resultMap 的列上指定typehandler:
<result column="name" property="name" jdbcType="VARCHAR" typeHandler="com.wuzz.type.MyTypeHandler"/>
處理枚舉類型
若想映射枚舉類型 Enum,則需要從 EnumTypeHandler 或者 EnumOrdinalTypeHandler 中選一個來使用。
比如說我們想存儲取近似值時用到的舍入模式。默認情況下,MyBatis 會利用 EnumTypeHandler 來把 Enum 值轉換成對應的名字。
注意 EnumTypeHandler 在某種意義上來說是比較特別的,其他的處理器只針對某個特定的類,而它不同,它會處理任意繼承了 Enum 的類。
不過,我們可能不想存儲名字,相反我們的 DBA 會堅持使用整形值代碼。那也一樣輕而易舉: 在配置文件中把 EnumOrdinalTypeHandler 加到 typeHandlers 中即可, 這樣每個 RoundingMode 將通過他們的序數值來映射成對應的整形數值。
objectFactory *
當我們把數據庫返回的結果集轉換為實體類的時候,需要創建對象的實例,由於我們不知道需要處理的類型是什么,有哪些屬性,所以不能用new 的方式去創建。在MyBatis 里面,它提供了一個工廠類的接口,叫做ObjectFactory,專門用來創建對象的實例,里面定義了4 個方法。
public interface ObjectFactory { void setProperties(Properties var1); <T> T create(Class<T> var1); <T> T create(Class<T> var1, List<Class<?>> var2, List<Object> var3); <T> boolean isCollection(Class<T> var1); }
ObjectFactory 有一個默認的實現類DefaultObjectFactory,創建對象的方法最終都調用了instantiateClass(),是通過反射來實現的。如果想要修改對象工廠在初始化實體類的時候的行為,就可以通過創建自己的對象工廠,繼承DefaultObjectFactory 來實現(不需要再實現ObjectFactory 接口)。
public class MyObjectFactory extends DefaultObjectFactory { @Override public Object create(Class type) { System.out.println("創建對象方法:" + type); if (type.equals(Blog.class)) { Blog blog = (Blog) super.create(type); blog.setName("object factory"); blog.setBid(1111); blog.setAuthorId(2222); return blog; } Object result = super.create(type); return result; } }
我們可以直接用自定義的工廠類來創建對象:
public class ObjectFactoryTest { public static void main(String[] args) { MyObjectFactory factory = new MyObjectFactory(); Blog myBlog = (Blog) factory.create(Blog.class); System.out.println(myBlog); } }
應用場景舉例:
比如有一個新銳手機品牌在一個電商平台上面賣貨,為了讓預約數量好看一點,只要有人預約,預約數量就自動乘以3。這個時候就可以創建一個ObjectFactory,只要是查詢銷量,就把它的預約數乘以3 返回這個實體類。
1、什么時候調用了objectFactory.create()?
創建DefaultResultSetHandler 的時候,和創建對象的時候。
2、創建對象后,已有的屬性為什么被覆蓋了?
在DefaultResultSetHandler 類的395 行getRowValue()方法里面里面調用了applyPropertyMappings()。
3、返回結果的時候,ObjectFactory 和TypeHandler 哪個先工作?
先是ObjectFactory,再是TypeHandler。肯定是先創建對象。PS:step out 可以看到一步步調用的層級。
插件(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 接口,並指定想要攔截的方法簽名即可。
// 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>
上面的插件將會攔截在 Executor 實例中所有的 “update” 方法調用, 這里的 Executor 是負責執行低層映射語句的內部對象。
environments、environment
environments 標簽用來管理數據庫的環境,比如我們可以有開發環境、測試環境、生產環境的數據庫。可以在不同的環境中使用不同的數據庫地址或者類型。
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/><!-- 單獨使用時配置成MANAGED沒有事務 --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments>
一個environment 標簽就是一個數據源,代表一個數據庫。這里面有兩個關鍵的標簽,一個是事務管理器,一個是數據源。
transactionManager
如果配置的是JDBC,則會使用Connection 對象的commit()、rollback()、close()管理事務。如果配置成MANAGED,會把事務交給容器來管理,比如JBOSS,Weblogic。因為我們跑的是本地程序,如果配置成MANAGE 不會有任何事務。如果是Spring + MyBatis , 則沒有必要配置, 因為我們會直接在applicationContext.xml 里面配置數據源,覆蓋MyBatis 的配置。
mappers
<mappers>標簽配置的是我們的映射器,也就是Mapper.xml 的路徑。這里配置的目的是讓MyBatis 在啟動的時候去掃描這些映射器,創建映射關系。我們有四種指定Mapper 文件的方式:
- 使用相對於類路徑的資源引用(resource)
- 使用完全限定資源定位符(絕對路徑)(URL)
- 使用映射器接口實現類的完全限定類名
- 將包內的映射器接口實現全部注冊為映射器(最常用)
<!-- 使用相對於類路徑的資源引用 --> <mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <mapper resource="org/mybatis/builder/BlogMapper.xml"/> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers> <!-- 使用完全限定資源定位符(URL) --> <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <mapper url="file:///var/mappers/BlogMapper.xml"/> <mapper url="file:///var/mappers/PostMapper.xml"/> </mappers> <!-- 使用映射器接口實現類的完全限定類名 --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> <mapper class="org.mybatis.builder.BlogMapper"/> <mapper class="org.mybatis.builder.PostMapper"/> </mappers> <!-- 將包內的映射器接口實現全部注冊為映射器 --> <mappers> <package name="org.mybatis.builder"/> </mappers>