<?xml version="1.0" encoding=UTF-8">
<configuration><!--配置-->
<properties/><!--屬性-->
<settings/><!--設置-->
<typeAliases><!--類型命名-->
<typeHandler><!--類型處理器-->
<objectFactory><!--對象工廠-->
<plugins><!--插件-->
<!-- 配置全局屬性 -->
<environments><!--配置環境-->
<environment><!--環境變量-->
<transactionManager/><!--事務管理器-->
<dataSource/><!--數據源-->
</environment>
</environments>
<databaseIdProvider/><!--數據庫廠商標識-->
<mappers/><!--映射器-->
</configuration>
properties元素
properties是一個配置屬性的元素,讓我們能在配置文件的上下文中使用它。
MyBatis提供3中配置方式:
- property子元素
- properties配置文件
- 程序參數傳遞
property子元素
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/study?useUnicode=true&characterEncoding=utf8" />
<property name="username" value="root" />
<property name="password" value="root" />
這樣我們就可以在上下文中使用已經配置好的屬性值了。我們配置數據庫時就可以按照以下方式配置
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
properties
properties還可以加載配置文件,如jdbc.properties,並以比較簡潔的方式進行數據庫的配置
<properties resource="jdbc.properties" />
<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.passWd}" />
</dataSource>
jdbc.properties配置文件內容如下:
jdbc.Driver=com.mysql.jdbc.Driver
jdbc.userName=root
jdbc.passWd=root
jdbc.url=jdbc:mysql://localhost:3306/study?useUnicode=true&characterEncoding=utf8
由於以上兩種爲常用的配置,所以只分析它們。
優先級
MyBatis支持的3中配置方式可能同時出現,並且屬性還會重複配置,其實這三種方式是存在優先級的,MyBatis將按照下面的順序來進行加載
- 在properties元素體內指定的屬性首先被讀取
- 根據properties元素中的resource屬性讀取類路徑下的屬性文件;或者根據url屬性指定的路徑讀取屬性文件,並覆蓋已讀取的同名屬性
- 讀取作爲方法參數傳遞的屬性,並覆蓋已讀取的同名屬性
因此,通過方法參數傳遞的屬性具有最高優先級,resource/url屬性中指定的配置文件次之,最低優先級是properties屬性中指定的屬性
推薦使用properties文件的形式
Settings
settings在Mybatis中是最複雜的配置,同時也是最爲重要的配置內容之一,它會改變MyBatis運行時的行爲。即使不配置settings,MyBatis也可以正常工作。
typeAliases
別名是一個指點的名稱,因爲我們遇到的類全限定名過長,所以我們希望用一個簡短的名稱去指代它,這個名稱可以在MyBatis上下文重視會用。別名可以分爲系統定義別名和自定義別名兩類。在Mybatis中別名是不區分大小寫的。一個typeAliases的實例是在解析配置文件時生成的,然後長期保存在Configuration對象中,當我們使用它時,再把它拿出來,這樣就沒有必要運行的時候再次生成它的實例了。
系統定義別名
MyBatis系統定義了一些經常使用的類型的別名,如數值,字符串,日期和集合等,我們可以在MyBatis中直接使用它們,在使用時不要重複定義把它們給覆蓋了
其實我們可以在 org.apache.ibatis.type.TypeAliasRegistry這個類中查看別名信息。
registerAlias("string", String.class);
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class);
...
自定義別名
因爲不同的應用有不同的需要,系統所定義的別名往往不夠用,所以MyBatis允許自定義的別名。我們可以用typeAliases配置別名。也可以使用@Alias方法註冊別名。如
<typeAliases>
<typeAlias type="com.wojiushiwo.mybatis_demo.User" alias="user" />
</typeAliases>
這樣就在MyBatis的上下文中使用”user”來代替其全路徑,減少配置的複雜度
如果POJO類較多的話,以上面的方式配置也稍顯麻煩。我們可以通過掃描包的方式。如下
<typeAliases>
<package name="com.wojiushiwo.mybatis_demo" />
</typeAliases>
在實體類上添加上註解,定義別名
@Alias(value="user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
這樣,MyBatis就能夠掃描到POJO類,並且指定其別名
如果配置了包掃描,而POJO類沒有註解@alias,它也會被裝載,只是它會把類的第一個字母變爲小寫,然後把改變後的類名作爲MyBatis的別名。
定義別名時要特別注意,別名不要重複
typeHandler
Mybatis在預處理語句中設置一個參數時。或者從結果集中取出一個值時,都會使用到typeHandler。typeHandler允許根據項目的需要自定義設置Java傳遞到數據庫的參數中,或者從數據庫讀出數據,這些都可以在自定義的typeHandler中處理。
typeHandler也分爲系統定義和用戶自定義兩種。
typeHandler常用的配置爲Java類型(javaType)、JDBC類型(jdbcType).typeHandler的作用就是將參數從JavaType轉化爲jdbcType;或者從數據庫取出結果時把jdbcType轉換爲javaType
系統定義的typeHandler,可以通過org.apache.ibatis.type.TypeHandlerRegistry查看,如下
...
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
...
自定義TypeHandler
當我們需要特殊的處理Java的那些類型和對應處理數據庫的那些類型時,系統定義的typeHandler已經應付不了的時候,我們考慮自定義typeHandler。
自定義typeHandler需要實現TypeHandler接口
@MappedTypes(value = { String.class })
@MappedJdbcTypes(value = { JdbcType.VARCHAR })
public class MyStringTypeHandler implements TypeHandler<String> {
private Logger log = Logger.getLogger(MyStringTypeHandler.class);
@Override
public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
log.info("使用我的typeHandler");
ps.setString(i, parameter);
}
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
log.info("使用我的typeHandler,ResultSet列名獲取字符串");
return rs.getString(columnName);
}
@Override
public String getResult(ResultSet rs, int columnIndex) throws SQLException {
log.info("使用我的typeHandler,ResultSet下標獲取字符串");
return rs.getString(columnIndex);
}
@Override
public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
log.info("使用我的typeHandler,CallableStatement下標獲取字符串");
return cs.getString(columnIndex);
}
}
重寫TypeHandler的方法,方法大概有:爲預編譯語句設置參數、根據列名從結果集中獲取數據、根據列索引從結構集中獲取數據、從存儲過程中獲取數據
自定義typeHandler裏用註解配置JdbcType和JavaType,這兩個註解是:
- @MappedTypes定義的是JavaType類型,可以指定哪些Java類型被攔截
@MappedJdbcTypes定義的是JdbcType類型,它需要滿足枚舉類org.apache.ibatis.type.JdbcType所列的枚舉類型
自定義typeHandler在mybatis-config.xml中的使用:
<typeHandlers>
<typeHandler javaType="String" jdbcType="VARCHAR"
handler="com.example.demo.handler.MyStringTypeHandler" />
</typeHandlers>
<!--也可以使用包掃描的方式-->
<!--<typeHandlers>
<package name="com.example.demo.handler.MyStringTypeHandler"/>
</typeHandlers>
-->
常見的使用自定義typeHandler的方式:
<resultMap type="com.example.demo.entity.User" id="User"> <!-- 方式一 使用自定義的typeHandler --> <!-- <result column="name" property="name" javaType="string" jdbcType="VARCHAR" /> --> <!-- 方式二使用自定義的typeHandler --> <result column="name" property="name" typeHandler="com.example.demo.handler.MyStringTypeHandler" /> </resultMap>
枚舉類型處理器
先定義一個枚舉類:
public enum Sex {
MALE(1, "男"), FEMALE(2, "女");
private int id;
private String name;
private Sex(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static Sex getSex(int id) {
if (id == 1) {
return Sex.MALE;
} else if (id == 2) {
return Sex.FEMALE;
}
return null;
}
}
EnumTypeHandler、EnumOrdinalTypeHandler均可作爲枚舉類型處理器,但兩者存在區別:
EnumOrdinalTypeHandler獲取的是枚舉定義的下標,如1,2;EnumTypeHandler獲取的是枚舉定義的name,如男,女
截取一部分源碼說明問題:
EnumTypeHandler
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
if (jdbcType == null) {
ps.setString(i, parameter.name());
} else {
ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE); // see r3589
}
}
EnumOrdinalTypeHandler
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i, parameter.ordinal());
}
使用方式:
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
javaType="com.example.demo.entity.Sex" />
</typeHandlers>
<!--user類中定義了該Sex枚舉屬性-->
<resultMap type="com.example.demo.entity.User" id="User">
...
<result column="sex" property="sex"
typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler" />
...
</resultMap>
ObjectFactory
當MyBatis從數據庫取得數據返回的時候,都會使用ObjectFactory去構建POJO;在MyBatis中可以定製自己的對象工廠。一般來說我麼們使用默認的ObjectFactory(org.apache.ibatis.reflection.factory.DefaultObjectFactory)即可。當然我們也可以自己自定義。
public class MyObjectFactory extends DefaultObjectFactory {
private static final long serialVersionUID = 6016076182957642243L;
private Logger log = Logger.getLogger(MyObjectFactory.class);
@Override
public <T> T create(Class<T> type) {
log.info("使用定製對象工廠 創建單個對象");
return super.create(type);
}
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
log.info("使用定製對象工廠 創建列表對象");
return super.create(type, constructorArgTypes, constructorArgs);
}
@Override
public void setProperties(Properties properties) {
log.info("定製屬性:" + properties);
super.setProperties(properties);
}
@Override
public <T> boolean isCollection(Class<T> type) {
return super.isCollection(type);
}
}
我們可以通過實現ObjectFactory接口來構建ObjectFactory。但是由於DefaultObjectFactory已經實現了ObjectFactory的接口,我們可以通過繼承DefaultObjectFactory來簡化編程。一般我們不需要使用自己配置的ObjectFactory,使用系統默認即可
代碼中setProperties方法可以使我們如何去處理設置進去的屬性,而create方法可以分別處理單個對象和列表對象。
在mybatis-config.xml中使用
<objectFactory type="com.example.demo.factory.MyObjectFactory">
<property name="name" value="MyObjectFactory" />
</objectFactory>
environments
<!-- 配置全局屬性 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<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.passWd}" />
</dataSource>
</environment>
</environments>
配置環境可以註冊多個數據源,每個數據源可以分爲兩大部分:一個是數據源的配置,一個是數據庫事務的配置。
- environments中的屬性default,表明在缺省的情況下,我們將啓動哪個數據源配置
- environment元素是配置一個數據源的開始;屬性id是設置這個數據源的標識,以便MyBatis上下文使用它
transactionManager配置的是數據庫事務,其中type屬性有3種配置方式:
JDBC:採用JDBC方式管理事務,在獨立編碼中經常使用
MANAGED:採用容器方式管理事務,在JNDI數據源中常用
自定義,由使用者自定義數據庫事務管理辦法,適用於特殊應用property元素則可以配置數據源的各類屬性,autoCommit=false則表示數據源不自動提交
- dataSource:配置數據源連接的信息,type屬性是提供我們對數據庫連接方式的配置:
UNPOOLED 非連接池數據庫
POOLED 連接池數據庫
JNDI JNDI數據源
自定義數據源
數據源
- UNPOOLED 非連接池 使用 org.apache.ibatis.datasource.unpooled.UnpooledDataSource實現
- POOLED 連接池 使用org.apache.ibatis.datasource.pooled.PooledDataSource實現
- JNDI 使用org.apache.ibatis.datasource.jndi.JndiDataSourceFactory來獲取數據源
如果我們需要自定義數據源,必須實現org.apache.ibatis.datasource.DataSourceFactory接口
Mapper
引入mapper的方式:
<mappers>
<!--使用文件路徑引入-->
<mapper resource="com.wojiushiwo/UserMapper.xml"/>
<mapper resource="com.wojiushiwo/ArticleMapper.xml"/>
</mappers>
<mappers>
<!--使用類註冊引入-->
<mapper class="com.wojiushiwo.mybatis_demo.UserMapper"/>
</<mappers>
<mappers>
<!--使用包名引入-->
<package name="com.wojiushiwo.mybatis_demo"/>
</<mappers>
參考文獻:
深入淺出MyBatis技術原理與實戰