(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,不匹配,那就异常;