目录
话引
xml 配置标签
概览
properties
子标签 property
resource
程序注入
settings
别名
TypeHandler
自定义 TypeHandler
- EnumTypeHandler
- EnumOrdinalTypeHandler
- SexTypeHandler
typeHandler 注意点
# 加入战队
微信公众号
话引
前两张我们分别介绍了 Mybatis 环境搭建及其组件的生命周期. 这些都是我们 Mybatis 入门必备技能. 有了前两篇的铺垫我们今天就来深入下 Mybatis, 也为了填下之前埋下的坑.
xml 配置标签
概览
<?xml version="1.0" encoding="UTF-8" ?>
- <!DOCTYPE configuration
- PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-config.dtd">
- <configuration>
- <!-- 引入外部配置文件 -->
- <properties resource=""/>
- <!-- 设置 -->
- <settings/>
- <!-- 定义别名 -->
- <typeAliases>
- <package name=""/>
- </typeAliases>
- <!-- 类型处理器 -->
- <typeHandlers/>
- <!-- 对象工厂 -->
- <objectFactory/>
- <!-- 插件 -->
- <plugins/>
- <!-- 定义数据库信息, 默认使用 development 数据库构建环境 -->
- <environments default="development">
- <environment id="development">
- <!--jdbc 事物管理 -->
- <transactionManager type="JDBC"/>
- <!-- 配置数据库连接信息 -->
- <dataSource type="POOLED"/>
- </environment>
- </environments>
- <!-- 数据库厂商标识 -->
- <databaseIdProvider/>
- <mappers/>
- </configuration>
上面模板列出了所有 xml 可以配置的属性. 这里 plugins 是一个让人哭笑不得的东西. 用的好是利器, 用的不好就是埋坑. 接下来我们来看看各个属性的作用
properties
该标签的作用就是引入变量. 和 maven 的 properties 一样. 在这里定义的变量或者引入的变量, 在下面我们是可以童工 ${} 使用的.
子标签 property
- <properties>
- <property name="zxhtom" value="jdbc:mysql://localhost:3306/mybatis"/>
- </properties>
- <dataSource type="POOLED">
- <property name="driver" value="${zxhtom}"/>
- <dataSource>
上述的配置就可以直接使用 zxhtom 这个变量.
resource
除了上述方法我们还可以通过引入其他 properties 文件, 就可以使用文件里的配置变量了.
<properties resource="mybatis.properties"/>
程序注入
最后还有一种我们在构建 SqlSessionFactory 的时候重新载入我们的 Properties 对象就可以了. 另外三者的优先级是从低到高
- settings
- configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
- configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
- configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
- configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
- configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
- configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
- configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
- configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
- configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
- configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
- configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
- configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
- configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
- configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
- configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
- configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
- configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
- configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
- configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
- configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
- configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
- configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
- configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
- configuration.setLogPrefix(props.getProperty("logPrefix"));
- configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
上面代码是我们在 XMLConfigBuilder 解析 settings 标签的代码. 从这段代码中我们了解到 settings 子标签.
参数 | 功能 | 可选值 | 默认值 |
---|---|---|---|
autoMappingBehavior | 指定 Mybatis 应如何自动映射列到字段上。 NONE : 表示取消自动映射 < br ztid="122" ow="0" oh="0">PARTIAL: 只会自动映射没有定义嵌套结果集映射的结果集 < br ztid="123" ow="0" oh="0">FULL : 自动映射任意复杂的结果集 | NONE、PARTIAL、FULL | PARTIAL |
autoMappingUnknownColumnBehavior | 指定识别到位置列或属性的时间 < br ztid="130" ow="0" oh="0">NONE : 什么都不做 < br ztid="131" ow="0" oh="0">WARNING: 日志会报警(前提是日志设置了显示权限) FAILING : 抛出异常。 | NONE, WARNING, FAILING | NONE |
cacheEnabled | 该配置影响的所有映射器中配置的缓存的全局开关 | true|false | true |
proxyFactory | 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具未指定时将自动查找 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | not set |
lazyLoadingEnabled | 延时加载全局开关 < br ztid="148" ow="0" oh="0">开启时:级联对象会延时加载;级联标签中可以通过 fetchType 来定制覆盖此选项 | true|false | false |
aggressiveLazyLoading | 启用时:对任意延迟属性的调用会使带有延迟加载属性的对象分层性质完整加载,反之按需加载 | true|false | true |
multipleResultSetsEnabled | 是否允许单一语句返回多结果集 | true|false | true |
useColumnLabel | 确切的说当映射找不到参数时会使用列标签 (数据库列名) 代替别名去映射 | true|false | true |
useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby) | true|false | false |
defaultExecutorType | 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,决定驱动等待数据库响应的秒数 | 整数 | null |
defaultFetchSize | 设置数据库 resultSet 读取数据方式,默认全部加载进内存,设置该属性可以设置一次性读多少条数据进内存 | 整数 | null |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 | true|false | false |
safeRowBoundsEnabled | - 允许在嵌套语句中使用分页 | true|false | false |
localCacheScope | 一级缓存。mybatis 默认对同一个 sqlsession 中数据是共享的。一个 sqlsession 调用两次相同查询实际只会查询一次。就是因为该属性为 SESSION , STATEMENT 则针对的是每一条 sql | SESSION|STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数提供特定的 jdbc 类型时,为空值则指定 JDBC 类型。在新增时我们没有设置参数,这个时候就会根据此参数天长。加入设置 VARCHAR,那么我们新增的数据没传参数则为空字符 | NULL|VARCHAR|OTHER | OTHER |
lazyLoadTriggerMethods | 指定具体方法延时加载 | 方法 | equals,clone,hashCode,toString |
safeResultHandlerEnabled | 允许在嵌套语句中使用分页 | true|false | true |
defaultScriptingLanguage | 动态 SQL 生成的默认语言 | org.apache.ibatis.scripting.xmltags.XMLDynamicLanguageDriver | |
defaultEnumTypeHandler | mybatis 默认的枚举处理类 | ||
callSettersOnNulls | 指定当结果集中值为 null 的时候是否调用映射对象的 setter(put)方法。 | ||
useActualParamName | 允许使用他们的编译后名称来映射,3.4.2 后默认 true. 在 xml 中 #{0}则报错。设置为 false,则 #{0}代表第一个参数 #{n}第 n 个 | true|false | true |
returnInstanceForEmptyRow | 当返回行的所有列都是空时,MyBatis 默认返回 null。 当开启这个设置时,MyBatis 会返回一个空实例。 请注意,它也适用于嵌套的结果集 (如集合或关联)。(新增于 3.4.2) | true|false | false |
logPrefix | 指定 MyBatis 增加到日志名称的前缀。 |
别名
别名是 mybatis 为我们项目中类起的一个名字, 类名往往会很长所以别名就方便我们平时的开发. Mybatis 为我们内置了一些类的别名: byte,short,int,long,float,double,boolean,char 等基础类型的别名. 还有其的封装类型, String,Object,Map,List 等等常用的类.
org.apache.ibatis.type.TypeAliasRegistry
这个类中帮我们内置了别名. 可以看下. 自定义别名也是通过这个类进行注册的. 我们可以通过 settings 中 typeAliases 配置的方式结合 @Alias. 或者扫描包也可以的.
TypeHandler
这个接口就四个方法
- public interface TypeHandler<T> {
- /**
- * 设置参数是用到的方法
- */
- void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
- T getResult(ResultSet rs, String columnName) throws SQLException;
- T getResult(ResultSet rs, int columnIndex) throws SQLException;
- T getResult(CallableStatement cs, int columnIndex) throws SQLException;
- }
可以理解成拦截器. 它主要拦截的是设置参数和获取结果的两个节点. 这个类的作用就是将 Java 对象和 jdbcType 进行相互转换的一个功能. 同样的在
org.apache.ibatis.type.TypeHandlerRegistry
这个类中 mybatis 为我们提供了内置的 TypeHandler. 基本上是对于基本数据和分装对象的转换.
下面我们随便看一个 TypeHandler 处理细节
- public class BooleanTypeHandler extends BaseTypeHandler<Boolean> {
- @Override
- public void setNonNullParameter(PreparedStatement ps, int i, Boolean parameter, JdbcType jdbcType)
- throws SQLException {
- ps.setBoolean(i, parameter);
- }
- @Override
- public Boolean getNullableResult(ResultSet rs, String columnName)
- throws SQLException {
- boolean result = rs.getBoolean(columnName);
- return !result && rs.wasNull() ? null : result;
- }
- @Override
- public Boolean getNullableResult(ResultSet rs, int columnIndex)
- throws SQLException {
- boolean result = rs.getBoolean(columnIndex);
- return !result && rs.wasNull() ? null : result;
- }
- @Override
- public Boolean getNullableResult(CallableStatement cs, int columnIndex)
- throws SQLException {
- boolean result = cs.getBoolean(columnIndex);
- return !result && cs.wasNull() ? null : result;
- }
- }
setParameter 是 PreparedStatement 进行设置成 boolean 类型. getResult 分别通过三种不同方式获取. 在这些方法里我们可以根据自己也无需求进行控制. 常见的控制是枚举的转换. 传递参数过程可能是枚举的 name, 但是传递到数据库中要枚举的 index. 这种需求我们就可以在 TypeHandler 中实现. 我们书写的 typeHandler 之后并不能被识别, 还需要我们在 resultMap 中的 result 标签中通过 typeHandler 指定我们的自定义 Handler.
自定义 TypeHandler
承接上文我们说道枚举的转换. 下面我们还是已学生类为例. 学生中性别之前是 boolean 类型. 现在我们采用枚举类型. 但是数据库中存的还是数据, 01.
EnumTypeHandler
在 TypeHandlerRegister 类中申明了默认的枚举类处理器是
- private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
- .
- @Override
- public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
- String s = rs.getString(columnName);
- return s == null ? null : Enum.valueOf(type, s);
- }
我们通过这个方法可以看出, 这个枚举处理器适合已枚举名称存储的方式
EnumOrdinalTypeHandler
在 Enum 中还有一个属性 oridinal. 这个表示枚举中的索引. 然后我们通过查看 Mybatis 提供的处理器发现有个叫 EnumOrdinalTypeHandler. 我们很容易联想到的就是这个处理器是通过枚举的所以作为数据库内容的. 在 SexEnum 中 MALE 存储到数据库中则为 0. 注意这个 0 不是我们的 index. 而是 MALE 的索引. 如果将 MALE 和 FEMAEL 调换. 那么 MALE 索引则为 1.
因为默认的是 EnumTypeHandler. 所以想用 EnumOrdinalTypeHandler 的话我们要么在 resultMap 中 sex 字段指定该处理器. 要不就通过配置文件 typeHandlers 注册进来.(将处理器与 Java 类进行绑定. mybatis 遇到这个 Java 对象的时候就知道用什么处理器处理)
- <typeHandlers>
- <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.github.zxhtom.enums.SexEnum"/>
- </typeHandlers>
SexTypeHandler
上面的不管是通过名称存储还是通过索引存储都不太满足我们的需求. 我们想通过我们的 index 存储. 那么这时候我们就得自定义处理逻辑了. Mybatis 处理器都是继承 BaseTypeHandler. 因为 BaseTypeHandler 实现了 TypeHandler. 所以我们这里也就继承 BaseTypeHandler.
- public class SexTypeHandler extends BaseTypeHandler<SexEnum> {
- @Override
- public void setNonNullParameter(PreparedStatement ps, int i, SexEnum parameter, JdbcType jdbcType) throws SQLException {
- ps.setInt(i,parameter.getIndex());
- }
- @Override
- public SexEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
- int i = rs.getInt(columnName);
- return SexEnum.getSexEnum(i);
- }
- @Override
- public SexEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
- int i = rs.getInt(columnIndex);
- return SexEnum.getSexEnum(i);
- }
- @Override
- public SexEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
- int i = cs.getInt(columnIndex);
- return SexEnum.getSexEnum(i);
- }
- }
typeHandler 注意点
在编写自定义处理器的时候我们得之处 Javatype,jdbctype. 两者不是必填. 但至少得有一个. 正常我们默认 javatype 是必填的.
填写的方式有三种
通过 MappedTypes,MappedJdbcTypes 分别指定 javatype,jdbctype
通过在 mybatis-config.xml 中配置 typeHandlers 进行注解. 里面也有这两个属性的配置.
通过在 mapper.xml 的 resultmap 中再次指定某个字段的 typehandler.
TypeHandler 为我们提供了 Java 到 jdbc 数据的转换桥梁. 极大的方便了我们平时的开发. 让我们开发期间忽略数据的转换这么糟心的事情.
加入战队
# 加入战队
微信公众号
来源: https://www.cnblogs.com/zhangxinhua/p/11933227.html