1. 程式人生 > >MyBatis簡介(三)

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、連線資料庫urlusernamepassword、事務隔離級別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,不匹配,那就異常;