MyBatis簡介(三)
(4)typeHandler型別轉換器 typeHandler作用是轉換jdbcType和javaType,MyBatis中存在系統定義typeHandler和自定義typeHandler,MyBatis會根據jdbcType和javaType型別自動選擇typeHandler型別; –系統:大部分無需顯式宣告 –使用者自定義typeHandler 對於一些特殊的轉換規則,如列舉,就需要自定義一個tyleHandler了,可以選擇implements typeHandler或entends BaseTypeHandler;示例如下: 實現類:
public class MyTypeHandler implements TypeHandler<String> { @Override public void setParameter(PreparedStatement ps, int i, String parameter,JdbcType jdbcType) throws SQLException { logger.info("設定string引數【" + parameter+"】"); ps.setString(i, parameter); } @Override public String getResult(ResultSet rs, String columnName) throws SQLException { String result = rs.getString(columnName); logger.info("在返回的結果集根據列名讀取string引數1【" + result+"】"); return result; } @Override public String getResult(ResultSet rs, int columnIndex) throws SQLException { String result = rs.getString(columnIndex); logger.info("在返回的結果集根據下標讀取string引數2【" + result+"】"); return result; } @Override public String getResult(CallableStatement cs, int columnIndex) throws SQLException { String result = cs.getString(columnIndex); logger.info("在儲存過程讀取string引數3【" + result+"】"); return result; } }
配置typeHandler:單個配置或批量配置(包掃描)
<typeHandlers>
<typeHandler jdbcType="VARCHAR" javaType="string" handler="cn.infocore.mybatis.typehandler.MyTypeHandler" />
</typeHandlers>
<typeHandlers> <package name="cn.infocore.mybatis.typehandler" /> </typeHandlers> //啟用包掃描註冊的時候需要註解: @MappedTypes(String.class) @MappedJdbcTypes(JdbcType.VARCHAR) public class MyTypeHandler implements TypeHandler<String> {}
使用自定義typeHandler: 1)指定與定義一致的jdbcType和javaType; 2)直接使用實現類
<resultMap id="roleMapper" type="role"> <result property="id" column="id" /> <result property="roleName" column="role_name" jdbcType="VARCHAR" javaType="string" /> <result property="note" column="note" typeHandler="cn.infocore.mybatis.typehandler.MyTypeHandler" /> </resultMap> <select id="getRole" parameterType="long" resultMap="roleMapper"> selectid, role_name, note from t_role where id = #{id} </select> <select id="findRoles" parameterType="string" resultMap="roleMapper"> select id, role_name, note from t_role where role_name like concat('%', #{roleName, jdbcType=VARCHAR,javaType=string}, '%') </select> <select id="findRoles2" parameterType="string" resultMap="roleMapper"> select id, role_name, note from t_role where note like concat('%', #{note,typeHandler=cn.infocore.mybatis.typehandler.MyTypeHandler}, '%') </select>
列舉typeHandler:雖然系統自定義了兩個,但一般不常用,還是需要自己定義; 系統: -EnumOrdinalTypeHandler根據列舉陣列下標索引匹配; -EnumTypeHandler根據名稱轉換; -自定義:同上String的例子;
(5)ObjectFactory物件工廠 在預設和大部分情況下,MyBatis會使用其定義的物件工廠DefaultObjectFactory來完成建立結果集例項;自定義ObjectFactory,需要implements ObjectFactory或者extends DefaultObjectFactory;
public class MyObjectFactory extends DefaultObjectFactory{
private static final long serialVersionUID = 7820951972813645768L;
private Object temp=null;
//方法2
@Override
public <T> T create(Class<T> type) {
T result=super.create(type);
System.out.println("建立物件:"+result.toString());
System.out.println("是否是同一個物件:"+(temp==result)); //true
return result;
}
//方法1
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
T result=super.create(type, constructorArgTypes, constructorArgs);
System.out.println("建立物件:"+result.toString());
temp=result;
return result;
}
@Override
public <T> boolean isCollection(Class<T> type) {
return super.isCollection(type);
}
@Override
public void setProperties(Properties props) {
super.setProperties(props);
System.out.println("初始化引數:【"+props.toString()+"】");
}
}
配置MyObjectFactory:這樣MyBatis就會採用配置的MyObjectFactory來生成結果集物件;
<objectFactory type="cn.infocore.mybatis.objectFactory.MyObjectFactory">
<property name="prop1" value="value1"/>
</objectFactory>
(6)外掛 外掛很靈活和強大,但也十分危險,因為它會覆蓋MyBatis底層物件的核心方法和屬性;
(7)environments(執行環境) 主要作用是配置資料庫,支援配置多個數據庫,但一般只需要一個;可分為事務管理器(transactionManager)和資料來源(dataSource);
<environments default="development">
<environment id="development">
<!--事務管理器,採用的是JDBC管理器方式和MANAGED方式-->
<transactionManager type="JDBC"/>
<dataSource type="POOLED"><!--POOLED代表採用MyBatis內部提供的連線池方式-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/xx_db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
JDBC:使用JdbcTransactionFactory生成的JdbcTransaction物件實現,以JDBC的方式對資料庫的提交和回滾進行操作; MANAGED:使用ManagedTransactionFactory生成的ManagedTransaction物件實現,它的提交和回滾方法不用任何操作,而是把事務交給容器處理,預設情況下,它會關閉連線,而一些容器並不希望這麼做,因此需要將claseConnection設定為false;
當然,也可以自定義TransactionFactory,滿足特殊要求;只需要定義類並配置即可; -自定義事務類、事務工廠
public class MyTransaction extends JdbcTransaction implements Transaction{
public MyTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
super(ds, desiredLevel, desiredAutoCommit);
}
public MyTransaction(Connection conn) {
super(conn);
}
@Override
public void close() throws SQLException {
super.close();
}
@Override
public void commit() throws SQLException {
super.commit();
}
@Override
public Connection getConnection() throws SQLException {
return super.getConnection();
}
@Override
public Integer getTimeout() throws SQLException {
return super.getTimeout();
}
@Override
public void rollback() throws SQLException {
super.rollback();
}
}
public class MyTransactionFactory implements TransactionFactory{
@Override
public Transaction newTransaction(Connection conn) {
return new MyTransaction(conn);
}
@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new MyTransaction(ds, level, autoCommit);
}
@Override
public void setProperties(Properties props) {}
}
-配置:
<transactionManager type="cn.infocore.mybatis.MyTransactionFactory"/>
資料來源型別: UNPOOLED:由UnpooledDataSourceFactory工廠類產生UnpooledDataSource類物件;採用非資料庫池的管理方式,每次請求都會開啟一個新的資料庫連線,所以建立比較慢;可配置資料庫驅動名driver、連線資料庫url、username、password、事務隔離級別defaultTransactionIsolationLevel,驅動屬性可通過DriverManager.getConnection(url,props)設定; POOLED: 由PooledDataSourceFactory工廠類產生PooledDataSource類物件;利用池將JDBC的Connection物件組織起來,開始會有一些空置和已經連線好的資料庫連線,所以請求時,不需要再建立和驗證,比較快;還能空置最大連線數,避免過多連線導致系統瓶頸;除了UNPOOLED的屬性外,還可以配置: poolMaximumActiveConnections:最大活動連線數,預設10; poolMaximumIdleConnections:最大空閒連線數,預設5,最好設定為0; poolMaximumCheckoutTime:最大可回收時間,即當達到最大活動連線數時,此時如果有程式獲取連線,則檢查最先使用的連線,看其是否超出了該時間,如果超出了該時間,則可以回收該連線,預設20s; poolTimeToWait:沒有連線時,重新嘗試獲取連線以及列印日誌的時間間隔(預設20s; poolPingQuery:檢查連線正確的語句,預設為"NO PING QUERY SET",即沒有,使用會導致拋異常 poolPingEnabled:是否開啟ping檢測,啟用需設定可執行的SQL,預設false; poolPingConnectionsNotUsedFor:設定ping檢測時間間隔,通常用於檢測超時連線,預設為0,即所有連線每一時刻都被偵測,當然僅當 poolPingEnabled 為 true 時適用; JNDI:由JndiDataSourceFactory工廠類根據JNDI的資訊拿到外部容器實現的資料庫連線物件;適用於EJB或應用伺服器;屬性: initial_context用來在InitialContext中尋找上下文,如忽略,data_source屬性將直接從InitialContext中尋找; data_source:引用資料來源例項位置上下文的路徑; 與其他資料來源配置類似,可以通過字首“env.”直接把屬性傳遞給初始上下文; 也支援第三方資料庫,需要提供一個自定義的DataSourceFactory(implements DataSourceFactory),然後配置;
(8)databaseIdProvider資料庫廠商標識 —系統定義: 支援多種不同廠商的資料庫,支援MyBatis的移植性,便於在多資料庫環境下使用;
<databaseIdProvider type="DB_VENDOR">
<property name="Oracle" value="oracle" />
<property name="MySQL" value="mysql" />
<property name="DB2" value="db2" />
</databaseIdProvider>
property元素的屬性name是資料庫名稱,名字可以通過Connection的getMetaData().getDatabaseProductName()獲取;value是別名,在MyBatis中可以通過這個別名標識一條SQL使用哪種資料庫執行;示例如下:表示使用Oracle資料庫;
<delete id="deleteRole" parameterType="long" databaseId="oracle">
delete from t_role where t_id=#{id}
</delete>
注意:我們在使用多資料庫SQL時需要配置databaseIdProvidertype屬性,配置後,系統優先取得和資料庫配置一致的SQL;如果未配置,則取沒有databaseId的SQL,可以當做預設值;如果databaseId與系統資料庫匹配不到,那就會丟擲異常;
—自定義:
public class MyDatabaseIdProvider implements DatabaseIdProvider {
private static final String DATEBASE_TYPE_DB2 = "DB2";
private static final String DATEBASE_TYPE_MYSQL = "MySQL";
private static final String DATEBASE_TYPE_ORACLE = "Oralce";
private Logger log = Logger.getLogger(MyDatabaseIdProvider.class);
@Override //讀取配置的引數
public void setProperties(Properties props) {
log.info(props);
}
@Override
public String getDatabaseId(DataSource dataSource) throws SQLException {
Connection connection = dataSource.getConnection();//獲取當前資料庫連線
//當前資料庫連線名字
String dbProductName = connection.getMetaData().getDatabaseProductName();
if (MyDatabaseIdProvider.DATEBASE_TYPE_DB2.equals(dbProductName)) {
return "db2";
} else if (MyDatabaseIdProvider.DATEBASE_TYPE_MYSQL
.equals(dbProductName)) {
return "mysql"; //匹配SQL中指定的databaseId
} else if (MyDatabaseIdProvider.DATEBASE_TYPE_ORACLE
.equals(dbProductName)) {
return "oracle";
} else {
return null;
}
}
}
<databaseIdProvider type="cn.infocore.mybatis.MyDatabaseIdProvider ">
<property name="msg" value="自定義DatabaseIdProvider " /> //配置的引數
</databaseIdProvider>
總之,系統規則是根據資料庫的name對應的value去匹配SQL的databaseId,不匹配就異常;自定義規則就是根據自定義DatabaseIdProvider中getDatabaseId()返回的值去匹配SQL的databaseId,不匹配,那就異常;