二、MyBatis系列:全局配置文件
MyBatis 全局映射配置文件
MyBatis 的配置文档的顶层结构如下:
- configuration 配置
- properties 属性
- settings 设置
- typeAliases 类型命名
- typeHandlers 类型处理器
- objectFactory 对象工厂
- plugins 插件
- environments 环境
- environment 环境变量
- ransactionManager 事务管理器
- dataSource 数据源
- environment 环境变量
- databaseIdProvider 数据库厂商标识
- mappers 映射器
configuration 配置节
全局配置文件的根节点,该配置必需符合 mybatis-3-config.dtd 语法规则。
该文件也可以在 org.apache.ibatis.builder.xml 命名空间下找到。
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 3 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 4 <configuration> 5 </configuration>
properties 属性
可引用一个java属性文件中的配置,也可以通过其子元素 property 来定义属性。
1 <properties resource="db.properties"> 2 <property name="username1" value="dev_user" /> 3 </properties>
其中 db.properties 文件属性定义如下:
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis jdbc.username=root jdbc.password=123456
在配置文件中的其它位置通过 ${propertyName} 来引用该属性,例如:
1 <dataSource type="POOLED"> 2 <property name="driver" value="${jdbc.driver}" /> 3 <property name="url" value="${jdbc.url}" /> 4 <property name="username" value="${jdbc.username}" /> 5 <property name="password" value="${jdbc.password}" /> 6 </dataSource>
MyBatis将从3处位置读取属性数据,它们的读取顺序如下:
- 首选会读取 properties 的子元素属性值;
- 再读取 resource 或 url 指定的属性文件内容,并覆盖 1 中同名的属性值;
- 最后调用 sqlSessionFactoryBuilder.build 方法时,可传入属性参数值,再覆盖已有同名属性值;
优先级为:3 > 2 > 1
settings 设置
用于定义 MyBatis 的内部参数值,它将指导 MyBatis 如何运行;
1 <settings> 2 <!-- 已设置启用缓存,默认为true --> 3 <setting name="cacheEnabled" value="true" /> 4 <!-- 已设置启用延时加载,默认为false --> 5 <setting name="lazyLoadingEnabled" value="true" /> 6 <!-- 已设置启用单一SQL语句多个返回集,默认为true. --> 7 <setting name="multipleResultSetsEnabled" value="true" /> 8 </settings>
下面的列表是 MyBatis 接受的所有参数值及其描述信息。
设置参数 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 该配置影响的所有映射器中配置的缓存的全局开关。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。 | true | false | false |
aggressiveLazyLoading | 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载。 | true | false | 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 |
defaultExecutorType | 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定驱动等待数据库响应的秒数。 | Any positive integer | Not Set (null) |
safeRowBoundsEnabled | 允许在嵌套语句中使用分页(RowBounds)。 | true | false | False |
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 enumeration. Most common are: NULL, VARCHAR and OTHER | OTHER |
lazyLoadTriggerMethods | 指定哪个对象的方法触发一次延迟加载。 | A method name list separated by commas | equals,clone,hashCode,toString |
defaultScriptingLanguage | 指定动态 SQL 生成的默认语言。 | A type alias or fully qualified class name. | org.apache.ibatis.scripting.xmltags.XMLDynamicLanguageDriver |
callSettersOnNulls | 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null 的。 | true | false | false |
logPrefix | 指定 MyBatis 增加到日志名称的前缀。 | Any String | Not set |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | Not set |
proxyFactory | 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。 | CGLIB | JAVASSIST | CGLIB |
typeAliases 类型命名
用于将一个 java 类型定义为一个简短的别名,用于减少类完全限定名的冗余。
比如在 environment 标签内所使用到的 JDBC, POOLED 就内置定义的别名.
1 <typeAliases> 2 <typeAlias alias="user" type="cn.xleos.mybatis.po.User" /> 3 <package name="cn.xleos.mybatis.po" /> 4 </typeAliases>
使用 typeAlias 子元素定一单个类型别名;
使用 package 子元素定义一个命名空间,并扫描所有java 类型生成批量的别名。并使用类名字母小写做为其别名. 若存在多个包,只需要定义多个 package 标签.
你也可以使用 @Alias 来为类添加一个注解,指定其生成的别名。
1 @Alias("user") 2 public class User { 3 }
需要注意的是,别名是大小写不敏感的。
因为在MyBatis内部注册别名与解析别名,均会将字母转换为小写。
1 public <T> Class<T> resolveAlias(String string) { 2 try { 3 if (string == null) return null; 4 String key = string.toLowerCase(Locale.ENGLISH); // 转换为小写字母。 5 Class<T> value; 6 if (TYPE_ALIASES.containsKey(key)) { 7 value = (Class<T>) TYPE_ALIASES.get(key); 8 } else { 9 value = (Class<T>) Resources.classForName(string); 10 } 11 return value; 12 } catch (ClassNotFoundException e) { 13 throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e); 14 } 15 } 16 public void registerAlias(String alias, Class<?> value) { 17 if (alias == null) throw new TypeException("The parameter alias cannot be null"); 18 String key = alias.toLowerCase(Locale.ENGLISH); // 转换为小写字母。 19 if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) { 20 throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'."); 21 } 22 TYPE_ALIASES.put(key, value); 23 }
MyBatis 已经内置了非常多的类型别名,通过查看源码,可以清楚的知道内置的所有默认别名。
在 org.apache.ibatis.type.TypeAliasRegistry 类中的初始化别名:
1 registerAlias("string", String.class); 2 registerAlias("byte", Byte.class); 3 registerAlias("long", Long.class); 4 registerAlias("short", Short.class); 5 registerAlias("int", Integer.class); 6 registerAlias("integer", Integer.class); 7 registerAlias("double", Double.class); 8 registerAlias("float", Float.class); 9 registerAlias("boolean", Boolean.class); 10 registerAlias("byte[]", Byte[].class); 11 registerAlias("long[]", Long[].class); 12 registerAlias("short[]", Short[].class); 13 registerAlias("int[]", Integer[].class); 14 registerAlias("integer[]", Integer[].class); 15 registerAlias("double[]", Double[].class); 16 registerAlias("float[]", Float[].class); 17 registerAlias("boolean[]", Boolean[].class); 18 registerAlias("_byte", byte.class); 19 registerAlias("_long", long.class); 20 registerAlias("_short", short.class); 21 registerAlias("_int", int.class); 22 registerAlias("_integer", int.class); 23 registerAlias("_double", double.class); 24 registerAlias("_float", float.class); 25 registerAlias("_boolean", boolean.class); 26 registerAlias("_byte[]", byte[].class); 27 registerAlias("_long[]", long[].class); 28 registerAlias("_short[]", short[].class); 29 registerAlias("_int[]", int[].class); 30 registerAlias("_integer[]", int[].class); 31 registerAlias("_double[]", double[].class); 32 registerAlias("_float[]", float[].class); 33 registerAlias("_boolean[]", boolean[].class); 34 registerAlias("date", Date.class); 35 registerAlias("decimal", BigDecimal.class); 36 registerAlias("bigdecimal", BigDecimal.class); 37 registerAlias("biginteger", BigInteger.class); 38 registerAlias("object", Object.class); 39 registerAlias("date[]", Date[].class); 40 registerAlias("decimal[]", BigDecimal[].class); 41 registerAlias("bigdecimal[]", BigDecimal[].class); 42 registerAlias("biginteger[]", BigInteger[].class); 43 registerAlias("object[]", Object[].class); 44 registerAlias("map", Map.class); 45 registerAlias("hashmap", HashMap.class); 46 registerAlias("list", List.class); 47 registerAlias("arraylist", ArrayList.class); 48 registerAlias("collection", Collection.class); 49 registerAlias("iterator", Iterator.class); 50 registerAlias("ResultSet", ResultSet.class);
在 org.apache.ibatis.session.Configuration 类中初始化的别名:
1 typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); 2 typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class); 3 typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class); 4 typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class); 5 typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class); 6 typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class); 7 typeAliasRegistry.registerAlias("FIFO", FifoCache.class); 8 typeAliasRegistry.registerAlias("LRU", LruCache.class); 9 typeAliasRegistry.registerAlias("SOFT", SoftCache.class); 10 typeAliasRegistry.registerAlias("WEAK", WeakCache.class); 11 typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class); 12 typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class); 13 typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class); 14 typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class); 15 typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class); 16 typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class); 17 typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class); 18 typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class); 19 typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class); 20 typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class); 21 typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class); 22 typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
typeHandlers 类型处理器
在MyBatis在预处理语句中设置一个参数 和 从数据源获取一个值时,均会通过 类型处理器来转换 java 类型数据。
你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 具体做法为:实现 org.apache.ibatis.type.TypeHandler 接口,或继承类 org.apache.ibatis.type.BaseTypeHandler。
下面的例子,是指定一个 实现了 Serializable 的 java 类型 与 jdbc.CLOB 类型的转换处理器。
当字段jdbc类型为CLOB,实体属性类型为Map,MyBatis将数据转实体时会默认调用此转换器. 当然你也可以在 mapper 配置文件中的字段内指定其它转换器.
1 <typeHandlers> 2 <typeHandler handler="cn.xleos.mybatis.typehandlers.JsonTypeHandler" 3 javaType="java.util.Map" jdbcType="CLOB" /> 4 <package name="cn.xleos.mybatis.typehandlers"/> 5 </typeHandlers>
若希望MyBatis来扫描类型处理器,可以使用 package 子元素,但需要使用注解的方式来指定 jdbc 类型。
若转换器是需要处理一个泛型时,就需要一个接收该类型的构造方法,这样在MyBatis构造该类型处理器时, 就会转入一个具体的类型。
1 @MappedJdbcTypes(value = JdbcType.BLOB) 2 public class JsonTypeHandler<E extends Serializable> extends BaseTypeHandler<E> { 3 Class<E> type; 4 public JsonTypeHandler(Class<E> type) { 5 if (type == null) 6 throw new IllegalArgumentException("Type argument cannot be null"); 7 this.type = type; 8 } 9 }
再通过查看源码的方式,看看 MyBatis 已经内置了哪些类型处理器:
以下源码在 org.apache.ibatis.type.TypeHandlerRegistry 类。
1 register(Boolean.class, new BooleanTypeHandler()); 2 register(boolean.class, new BooleanTypeHandler()); 3 register(JdbcType.BOOLEAN, new BooleanTypeHandler()); 4 register(JdbcType.BIT, new BooleanTypeHandler()); 5 register(Byte.class, new ByteTypeHandler()); 6 register(byte.class, new ByteTypeHandler()); 7 register(JdbcType.TINYINT, new ByteTypeHandler()); 8 register(Short.class, new ShortTypeHandler()); 9 register(short.class, new ShortTypeHandler()); 10 register(JdbcType.SMALLINT, new ShortTypeHandler()); 11 register(Integer.class, new IntegerTypeHandler()); 12 register(int.class, new IntegerTypeHandler()); 13 register(JdbcType.INTEGER, new IntegerTypeHandler()); 14 register(Long.class, new LongTypeHandler()); 15 register(long.class, new LongTypeHandler()); 16 register(Float.class, new FloatTypeHandler()); 17 register(float.class, new FloatTypeHandler()); 18 register(JdbcType.FLOAT, new FloatTypeHandler()); 19 register(Double.class, new DoubleTypeHandler()); 20 register(double.class, new DoubleTypeHandler()); 21 register(JdbcType.DOUBLE, new DoubleTypeHandler()); 22 register(String.class, new StringTypeHandler()); 23 register(String.class, JdbcType.CHAR, new StringTypeHandler()); 24 register(String.class, JdbcType.CLOB, new ClobTypeHandler()); 25 register(String.class, JdbcType.VARCHAR, new StringTypeHandler()); 26 register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler()); 27 register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler()); 28 register(String.class, JdbcType.NCHAR, new NStringTypeHandler()); 29 register(String.class, JdbcType.NCLOB, new NClobTypeHandler()); 30 register(JdbcType.CHAR, new StringTypeHandler()); 31 register(JdbcType.VARCHAR, new StringTypeHandler()); 32 register(JdbcType.CLOB, new ClobTypeHandler()); 33 register(JdbcType.LONGVARCHAR, new ClobTypeHandler()); 34 register(JdbcType.NVARCHAR, new NStringTypeHandler()); 35 register(JdbcType.NCHAR, new NStringTypeHandler()); 36 register(JdbcType.NCLOB, new NClobTypeHandler()); 37 register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler()); 38 register(JdbcType.ARRAY, new ArrayTypeHandler()); 39 register(BigInteger.class, new BigIntegerTypeHandler()); 40 register(JdbcType.BIGINT, new LongTypeHandler()); 41 register(BigDecimal.class, new BigDecimalTypeHandler()); 42 register(JdbcType.REAL, new BigDecimalTypeHandler()); 43 register(JdbcType.DECIMAL, new BigDecimalTypeHandler()); 44 register(JdbcType.NUMERIC, new BigDecimalTypeHandler()); 45 register(Byte[].class, new ByteObjectArrayTypeHandler()); 46 register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler()); 47 register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler()); 48 register(byte[].class, new ByteArrayTypeHandler()); 49 register(byte[].class, JdbcType.BLOB, new BlobTypeHandler()); 50 register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler()); 51 register(JdbcType.LONGVARBINARY, new BlobTypeHandler()); 52 register(JdbcType.BLOB, new BlobTypeHandler()); 53 register(Object.class, UNKNOWN_TYPE_HANDLER); 54 register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER); 55 register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER); 56 register(Date.class, new DateTypeHandler()); 57 register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler()); 58 register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler()); 59 register(JdbcType.TIMESTAMP, new DateTypeHandler()); 60 register(JdbcType.DATE, new DateOnlyTypeHandler()); 61 register(JdbcType.TIME, new TimeOnlyTypeHandler()); 62 register(java.sql.Date.class, new SqlDateTypeHandler()); 63 register(java.sql.Time.class, new SqlTimeTypeHandler()); 64 register(java.sql.Timestamp.class, new SqlTimestampTypeHandler()); 65 // issue #273 66 register(Character.class, new CharacterTypeHandler()); 67 register(char.class, new CharacterTypeHandler());
objectFactory 对象工厂
MyBatis在运行时内部会根据类型创建的对象(包括创建预处理参数类型实体,返回参数类型实例),均会使用此对象工厂来创建。
在没有进行配置时,使用了 org.apache.ibatis.reflection.factory.DefaultObjectFactory 默认的对象工厂。 它使用 Constructor.newInstance() 于构造一个新对象。
通过观察 DefaultObjectFactory 源码, 就可以知道配置的 mapper 文件在 paramterType 中定义 map 接口时,对象工厂是怎么进行实例化它的了。
1 protected Class<?> resolveInterface(Class<?> type) { 2 Class<?> classToCreate; 3 if (type == List.class || type == Collection.class || type == Iterable.class) { 4 classToCreate = ArrayList.class; 5 } else if (type == Map.class) { 6 classToCreate = HashMap.class; 7 } else if (type == SortedSet.class) { // issue #510 Collections Support 8 classToCreate = TreeSet.class; 9 } else if (type == Set.class) { 10 classToCreate = HashSet.class; 11 } else { 12 classToCreate = type; 13 } 14 return classToCreate; 15 }
我们还可以通过自定义实现这个对象工厂,让它支持更多的自定义的接口实现。
若希望自定义实现一个对象工厂,需要实现 org.apache.ibatis.reflection.factory.ObjectFactory 接口,
也可以继承 DefaultObjectFactory 默认对象工厂,增加自定义处理代码。
1 public class ExampleObjectFactory extends DefaultObjectFactory { 2 private static final long serialVersionUID = -2441495242597031432L; 3 @Override 4 public <T> T create(Class<T> type) { 5 return super.create(type); 6 } 7 @Override 8 public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, 9 List<Object> constructorArgs) { 10 return super.create(type, constructorArgTypes, constructorArgs); 11 } 12 @Override 13 public void setProperties(Properties properties) { 14 super.setProperties(properties); 15 } 16 @Override 17 protected Class<?> resolveInterface(Class<?> type) { 18 return super.resolveInterface(type); 19 } 20 @Override 21 public <T> boolean isCollection(Class<T> type) { 22 return super.isCollection(type); 23 } 24 }
还需要在全局配置文件中添加以下节点:
1 <objectFactory type="cn.xleos.mybatis.factory.ExampleObjectFactory"> 2 <property name="someProperty" value="100" /> 3 </objectFactory>
plugins 插件
MyBatis内置的插件扩展接口,在 Configuration 类中的 newExecutor,newParameterHandler,newResultSetHandler,newStatementHandler方法获取新对象时,
会在其方法体内调用 interceptorChain.pluginAll() 方法,使得在调用其处理方法之前,就会先调用用户编写的插件处理方法。
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
以下是一个物理分页的插件示例代码,没有具体的实现。
1 @Intercepts({ @Signature(type = Executor.class, method = "query", args = { 2 MappedStatement.class, Object.class, RowBounds.class, 3 ResultHandler.class }) }) 4 public class OffsetLimitInterceptor implements Interceptor { 5 public Object intercept(final Invocation invocation) throws Throwable { 6 final Executor executor = (Executor) invocation.getTarget(); 7 final Object[] queryArgs = invocation.getArgs(); 8 final MappedStatement ms = (MappedStatement) queryArgs[0]; 9 final Object parameter = queryArgs[1]; 10 final RowBounds rowBounds = (RowBounds) queryArgs[2]; 11 12 // 具体的处理代码位置. 13 14 return invocation; 15 } 16 17 public Object plugin(Object target) { 18 return Plugin.wrap(target, this); 19 } 20 21 public void setProperties(Properties properties) { 22 // 获取全局配置文件中所定义该插件的属性配置值. 23 } 24 }
MyBatis的插件类首先需要实现一个叫 Interceptor 的接口。
该插件的注解已经描述出用于拦截 Executor 类型的 query 方法。
具体的插件实现通过 intercept 方法来执行,该方法的参数 invocation 提供了几个有用的方法:
invocation.getTarget() 用于获取该插件的拦截类型;
invocation.getArgs() 用于获取传入的参数值,它的类型在 @Signature 注解中的 args 参数中指定;
1 <plugins> 2 <!-- 这是用于拦截 executor 在执行 query 方法时用于将 sql 包装成物理分页的插件. --> 3 <plugin interceptor="cn.xleos.framework.mybatis.paginator.OffsetLimitInterceptor"> 4 <!-- OffsetLimitInterceptor 的属性设置,告知分页插件所使用的数据库支持。 --> 5 <property name="dialectClass" value="cn.xleos.framework.mybatis.paginator.dialect.MSSQL2005Dialect" /> 6 </plugin> 7 </plugins>
最后再全局配置文件中定义如上的XML节,用于MyBatis实始化时读取该插件信息。 事实上使用MyBatis的插件机制,完全可以替换掉MyBatis的原有处理逻辑。
environments 环境
MyBatis提供的与数据库之间的连接配置,并且允许能同时连接多个数据源。 但要注意每个 SqlSessionFactory 只能连接一个数据源,所以在需要多个数据源时,也需要多个 SqlSessionFactory 实例;
你可以首先在全局配置文件中配置多个 environment 节,并为其命名不同的名称。
1 <!-- 数据库环境配置节 --> 2 <environments default="dev"> 3 <environment id="dev"> 4 <!-- 配置该工程所使用的事务管理器类型,JDBC 为 JdbcTransactionFactory.class 的别名 --> 5 <transactionManager type="JDBC" /> 6 <!-- 配置数据源类型, POOLED 为 PooledDataSourceFactory.class 的别名 --> 7 <dataSource type="POOLED"> 8 <property name="driver" value="${jdbc.driver}" /> 9 <property name="url" value="${jdbc.url}" /> 10 <property name="username" value="${jdbc.username}" /> 11 <property name="password" value="${jdbc.password}" /> 12 </dataSource> 13 </environment> 14 <environment id="test"> 15 <transactionManager type="JDBC" /> 16 <dataSource type="POOLED"> 17 <property name="driver" value="${jdbc.test.driver}" /> 18 <property name="url" value="${jdbc.test.url}" /> 19 <property name="username" value="${jdbc.test.username}" /> 20 <property name="password" value="${jdbc.test.password}" /> 21 </dataSource> 22 </environment> 23 </environments>
- 默认环境ID:default="dev"
- 环境ID(environment id="dev"):可以自定义任何名称
- 事务管理器(transactionManager):type = "JDBC|MANAGED"
- 数据源类型(dataSource):type="UNPOOLED|POOLED|JNDI"
- driver :这是 JDBC 驱动的 Java 类的完全限定名,如: com.mysql.jdbc.Driver
- url :jdbc数据连接串,如: jdbc:mysql://localhost:3306/mybatis
- username :数据库连接接用户名
- password :数据库连接用户的密码
- defaultTransactionIsolationLevel :
- driver.encoding : utf-8
更多的属性设置请参考数据源类型源码(UnpooledDataSource、PooledDataSource)
在代码中使用 SqlSessionFactoryBuilder 来构造 会话工厂,通过传入 environment 参数值将使用指定的环境ID做为数据源。
1 // 创建 <environments default="dev"> 中默认 dev 的数据会话工厂 2 SqlSessionFactory factory1 = new SqlSessionFactoryBuilder().build(inputStream); 3 // 创建 environment 节名称为 test 的数据会话工厂 4 SqlSessionFactory factory2 = new SqlSessionFactoryBuilder().build(inputStream, "test");
databaseIdProvider 数据库厂商标识
MyBatis 会根据不同数据库厂商的标识,执行相应 databaseId 的映射SQL脚本。
要实现此需求,首先需要在全局配置文件中定义 databaseIdProvider 节:
1 <databaseIdProvider type="DB_VENDOR"> 2 <property name="SQL Server" value="sqlserver" /> 3 <property name="DB2" value="db2" /> 4 <property name="Oracle" value="oracle" /> 5 </databaseIdProvider>
DB_VENDOR :它是一个mybatis的别名,它代表 org.apache.ibatis.mapping.VendorDatabaseIdProvider.class
VendorDatabaseIdProvider 类实现了org.apache.ibatis.mapping.DatabaseIdProvider 接口,它提供了默认的多数据厂商标识的实现。
它提供了数据库产品名的别名映射支持,如上示例中: SQL Server 数据产品名被映射为 sqlserver 别名。
具体可参数这部分源码:
1 // org.apache.ibatis.mapping.VendorDatabaseIdProvider 2 private String getDatabaseProductName(DataSource dataSource) throws SQLException { 3 Connection con = null; 4 try { 5 con = dataSource.getConnection(); 6 DatabaseMetaData metaData = con.getMetaData(); 7 return metaData.getDatabaseProductName(); // 配置的 property.name 是由此获取,并映射为自定义别名. 8 } finally { 9 if (con != null) { 10 try { 11 con.close(); 12 } catch (SQLException e) { 13 // ignored 14 } 15 } 16 } 17 }
databaseId 在 mapper 文件中被使用,你可以为同一个preparedStatement设置多个数据库支持。
下面示例中为 getUserById 设置了2个特定数据库支持,分别为:db2, mysql
MyBatis 在执行preparedStatement时,首选会找到相应 databaseId,找不到的情况下就会找到不带 databaseId 的 preparedStatement。
1 <select id="getUserById" parameterType="int"> 2 SELECT * FROM user WHERE id = #{value} 3 </select> 4 <select id="getUserById" parameterType="int" databaseId="db2" > 5 SELECT * FROM user WHERE id = #{value} 6 </select> 7 <select id="getUserById" parameterType="int" databaseId="mysql" > 8 SELECT * FROM user WHERE id = #{value} 9 </select>
mappers 映射器
最后需要告诉 MyBatis 去哪里查找编号的 mapper 文件,通过 mappers 节来完成此工作。
你可以使用:资源路径(mapper.resource)、文件路径(mapper.url)、类名(mapper.class)、包名(package);
1 <!-- 告诉 MyBatis到何处去查询映射文件. --> 2 <mappers> 3 <mapper resource="mapper/user.xml" /> 4 <mapper url="file:///mapper/user.xml"/> 5 <mapper class="cn.xleos.mybatis.dao.UserMapper"/> 6 <package name="cn.xleos.mybatis.dao" /> 7 </mappers>
使用 mapper.class 和 package 配置方式时,所指定的均为映射接口,映射文件必需与接口名相同。
完整示例
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 3 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 4 <configuration> 5 6 <!-- 有3处可以定义属性值,以下是根据优选级(1>2>3)读取,会覆盖优先级低的同名属性。 7 1. sqlSessionFactoryBuilder.build(reader, props); 方法中转入的属性。 8 2. properties 元素内定义的属性。 9 3. resource 读取的属性文件中的属性。 --> 10 <properties resource="db.properties"> 11 <property name="username1" value="dev_user" /> 12 </properties> 13 14 <!-- MyBatis内部的参数设置,将会改变MyBatis运行时的行为。 以下是几个常用的参数示例。 --> 15 <settings> 16 <!-- 已设置启用缓存,默认为true --> 17 <setting name="cacheEnabled" value="true" /> 18 <!-- 已设置启用延时加载,默认为false --> 19 <setting name="lazyLoadingEnabled" value="true" /> 20 <!-- 已设置启用单一SQL语句多个返回集,默认为true. --> 21 <setting name="multipleResultSetsEnabled" value="true" /> 22 </settings> 23 24 <!-- 用于将一个 java 类型定义为一个简短的别名,用于减少类完全限定名的冗余. 25 比如在 environment 标签内所使用到的 JDBC, POOLED 就内置定义的别名. --> 26 <typeAliases> 27 <!-- 定义单个java类型,并为它定义一个别名. --> 28 <typeAlias alias="user" type="cn.xleos.mybatis.po.User" /> 29 <!-- 指定一个包下所有的 java 类型,并使用类名首字母小写做为其别名. 若存在多个包,只需要定义多个 package 标签. --> 30 <package name="cn.xleos.mybatis.po" /> 31 </typeAliases> 32 33 <!-- 数据库字段值到实体属性的数据类型转换处理器定义 --> 34 <typeHandlers> 35 <!-- 这里定义的是可Serializable类型与Json类型的类型转换器. 36 当字段jdbc类型为CLOB,实体属性类型为Map,MyBatis将数据转实体时会默认调用此转换器. 37 在 mapper 配置文件中也可会字段指定其它转换器. 38 --> 39 <typeHandler handler="cn.xleos.mybatis.typehandlers.JsonTypeHandler" 40 javaType="java.util.Map" jdbcType="CLOB" /> 41 <package name="cn.xleos.mybatis.typehandlers"/> 42 </typeHandlers> 43 44 <objectFactory type="cn.xleos.mybatis.factory.ExampleObjectFactory"> 45 <property name="someProperty" value="100" /> 46 </objectFactory> 47 48 <!-- 用于在执行某个映射语句时的拦截调用. --> 49 <plugins> 50 <!-- 这是用于拦截 executor 在执行 query 方法时用于将 sql 包装成物理分页的插件. --> 51 <plugin interceptor="cn.xleos.framework.mybatis.paginator.OffsetLimitInterceptor"> 52 <!-- OffsetLimitInterceptor 的属性设置,告知分页插件所使用的数据库支持。 --> 53 <property name="dialect" value="cn.xleos.framework.mybatis.paginator.dialect.MSSQL2005Dialect" /> 54 </plugin> 55 </plugins> 56 57 <!-- 数据库环境配置节 --> 58 <environments default="dev"> 59 <!-- 开发库 --> 60 <environment id="dev"> 61 <!-- 配置该工程所使用的事务管理器类型,JDBC 为 JdbcTransactionFactory.class 的别名 --> 62 <transactionManager type="JDBC" /> 63 <!-- 配置数据源类型, POOLED 为 PooledDataSourceFactory.class 的别名 --> 64 <dataSource type="POOLED"> 65 <property name="driver" value="${jdbc.driver}" /> 66 <property name="url" value="${jdbc.url}" /> 67 <property name="username" value="${jdbc.username}" /> 68 <property name="password" value="${jdbc.password}" /> 69 </dataSource> 70 </environment> 71 <!-- 测试库 --> 72 <environment id="test"> 73 <transactionManager type="JDBC" /> 74 <dataSource type="POOLED"> 75 <property name="driver" value="${jdbc.test.driver}" /> 76 <property name="url" value="${jdbc.test.url}" /> 77 <property name="username" value="${jdbc.test.username}" /> 78 <property name="password" value="${jdbc.test.password}" /> 79 </dataSource> 80 </environment> 81 </environments> 82 83 <!-- 用于多数据库的支持,基于 mapper 映射语句的 databaseId属性。 84 DB_VENDOR 是 MyBatis中内置的 org.apache.ibatis.mapping.VendorDatabaseIdProvider 的别名。 85 --> 86 <databaseIdProvider type="DB_VENDOR"> 87 <!-- 以下 property.name 是通过 DatabaseMetaData#getDatabaseProductName() 获取的名称。 88 property.value 是定义简短的别名,它将用于匹配 mapper映射语句的 databaseId 属性。 89 --> 90 <property name="SQL Server" value="sqlserver" /> 91 <property name="DB2" value="db2" /> 92 <property name="Oracle" value="oracle" /> 93 </databaseIdProvider> 94 95 <!-- 告诉 MyBatis到何处去查询映射文件. --> 96 <mappers> 97 <!-- 配置工程所使用的 User 的 Mapper 映射文件路径 --> 98 <mapper resource="mapper/user.xml" /> 99 <mapper url="file:///mapper/user.xml"/> 100 <mapper class="cn.xleos.mybatis.dao.UserMapper"/> 101 <package name="cn.xleos.mybatis.dao" /> 102 </mappers> 103 104 </configuration>