目前Mybatis除了可以通过XML配置SQL外还可以通过注解的形式配置SQL,本文中主要介绍了Mybatis是如何处理注解SQL映射的,通过源码分析处理过程
XML配置
<configuration>
<settings>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="useGeneratedKeys" value="true"/>
</settings>
<typeAliases>
<typeAlias type="org.apache.ibatis.submitted.blocking_cache.Person" alias="Person" />
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="" value="" />
</transactionManager>
<dataSource type="UNPOOLED">
<property name="driver" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:cache" />
<property name="username" value="sa" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="org.apache.ibatis.submitted.blocking_cache.PersonMapper"/>
</mappers>
</configuration>
注解
@Insert("insert into table2 (name) values(#{name})")
@SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class)
int insertTable2(Name name);
@Insert("insert into table2 (name) values(#{name})")
@Options(useGeneratedKeys=true, keyProperty="nameId,generatedName", keyColumn="ID,NAME_FRED")
int insertTable2WithGeneratedKey(Name name);
解析过程时序图
源码分析
解析Mapper节点
private void mapperElement(XNode parent) throws Exception {
//如果configuration中配置了mapper节点
if (parent != null) {
for (XNode child : parent.getChildren()) {
//如果配置对是包路径
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
//获取mapper元素中的resource属性
String resource = child.getStringAttribute("resource");
//获取mapper元素中的url属性
String url = child.getStringAttribute("url");
//获取mapper元素中的class属性,如果基于注解的配置的mapper 配置的就是class
String mapperClass = child.getStringAttribute("class");
//对于resource,url,mapperClass 优先使用resource,其次是url最后是class
if (resource != null && url == null && mapperClass == null) {
//创建异常上下文
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建XMLMapperBuilder
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
//解析XML
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
//获取mapper的接口
Class<?> mapperInterface = Resources.classForName(mapperClass);
//将该接口注册到已知mapper
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
注册Mapper
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
//注册Mapper
public <T> void addMapper(Class<T> type) {
//如果type是接口
if (type.isInterface()) {
//判断该接口是否已经注册过相应的Mapper了,如果是则抛出异常,因为knownMappers的key为type
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//对该接口创建MapperProxyFactory,并保注册到knownMappers
knownMappers.put(type, new MapperProxyFactory<T>(type));
//创建MapperAnnotationBuilder
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
//解析Annotation
parser.parse();
//解析成功则表示加载完成
loadCompleted = true;
} finally {
//如果加载没有完成则将其从knownMappers中删除
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
注解解析器
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
String resource = type.getName().replace('.', '/') + ".java (best guess)";
this.assistant = new MapperBuilderAssistant(configuration, resource);
this.configuration = configuration;
this.type = type;
//初始化注解类型,分为两组
sqlAnnotationTypes.add(Select.class);
sqlAnnotationTypes.add(Insert.class);
sqlAnnotationTypes.add(Update.class);
sqlAnnotationTypes.add(Delete.class);
sqlProviderAnnotationTypes.add(SelectProvider.class);
sqlProviderAnnotationTypes.add(InsertProvider.class);
sqlProviderAnnotationTypes.add(UpdateProvider.class);
sqlProviderAnnotationTypes.add(DeleteProvider.class);
}
public void parse() {
String resource = type.toString();
//判断该资源是否已经注册
if (!configuration.isResourceLoaded(resource)) {
//没有注册过则需要加载XML资源
loadXmlResource();
//将该资源名称添加到已经注册集合中
configuration.addLoadedResource(resource);
//设置nameSpace
assistant.setCurrentNamespace(type.getName());
//解析cache
parseCache();
//解析cacheRef
parseCacheRef();
//获取
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
//如果不是bridge方法则解析statement
if (!method.isBridge()) {
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
//解析待定方法
parsePendingMethods();
}
加载XML资源
private void loadXmlResource() {
//判断资源是否有加载过
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
//获取资源的path
String xmlResource = type.getName().replace('.', '/') + ".xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
} catch (IOException e) {
// ignore, resource is not required
}
//如果资源存在
if (inputStream != null) {
//构建XMLMapperBuilder
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
//解析XML
xmlParser.parse();
}
}
}
下图是同时存在XML和annotation的情况
private void parseCache() {
//获取接口上 @CacheNamespace注解
CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);
//如果注解存在
if (cacheDomain != null) {
//获取注解配置的缓存大小
Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size();
//获取缓存刷新频率
Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval();
//解析CacheNamespace配置的属性
Properties props = convertToProperties(cacheDomain.properties());
//使用注解配置的数据创建缓存
assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size, cacheDomain.readWrite(), cacheDomain.blocking(), props);
}
}
private void parseCacheRef() {
//获取接口上 @CacheNamespaceRef 注解
CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class);
//如果配置了缓存引用
if (cacheDomainRef != null) {
//获取引用的类型
Class<?> refType = cacheDomainRef.value();
String refName = cacheDomainRef.name();
//如果引用类型和引用名称都为空则抛出异常
if (refType == void.class && refName.isEmpty()) {
throw new BuilderException("Should be specified either value() or name() attribute in the @CacheNamespaceRef");
}
//如果引用类型和引用名称同时配置了有效数据则抛出异常,这两个是互斥数据
if (refType != void.class && !refName.isEmpty()) {
throw new BuilderException("Cannot use both value() and name() attribute in the @CacheNamespaceRef");
}
//获取namespace
String namespace = (refType != void.class) ? refType.getName() : refName;
//使用缓存
assistant.useCacheRef(namespace);
}
}
void parseStatement(Method method) {
//获取参数类型
Class<?> parameterTypeClass = getParameterType(method);
//获取LanguageDriver
LanguageDriver languageDriver = getLanguageDriver(method);
//从注解中获取Sqlsource
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
//如果sqlSource不为null
if (sqlSource != null) {
//获取 @Options注解
Options options = method.getAnnotation(Options.class);
//创建statementId
final String mappedStatementId = type.getName() + "." + method.getName();
Integer fetchSize = null;
Integer timeout = null;
StatementType statementType = StatementType.PREPARED;
ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
//获取sql类型
SqlCommandType sqlCommandType = getSqlCommandType(method);
//是否是查询
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
//是否需要刷新缓存,如果不是查询默认值为true,查询默认值为false
boolean flushCache = !isSelect;
//是否使用缓存,查询默认为true,不是查询默认为false
boolean useCache = isSelect;
KeyGenerator keyGenerator;
String keyProperty = "id";
String keyColumn = null;
//如果是插入或更新
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
//获取SelectKey注解
SelectKey selectKey = method.getAnnotation(SelectKey.class);
if (selectKey != null) {
keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
keyProperty = selectKey.keyProperty();
} else if (options == null) {
keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
} else {
keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
keyProperty = options.keyProperty();
keyColumn = options.keyColumn();
}
} else {//不是插入或更新 即查询和删除则keyGenerator为NoKeyGenerator实例
keyGenerator = NoKeyGenerator.INSTANCE;
}
//如果@Options注解不为null
if (options != null) {
//根据配置的值设置是否刷新缓存
if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
flushCache = true;
} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
flushCache = false;
}
//是否使用缓存
useCache = options.useCache();
//fetchSize
fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
timeout = options.timeout() > -1 ? options.timeout() : null;
statementType = options.statementType();
resultSetType = options.resultSetType();
}
String resultMapId = null;
//获取ResultMap注解
ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
if (resultMapAnnotation != null) {
String[] resultMaps = resultMapAnnotation.value();
StringBuilder sb = new StringBuilder();
for (String resultMap : resultMaps) {
if (sb.length() > 0) {
sb.append(",");
}
sb.append(resultMap);
}
resultMapId = sb.toString();
} else if (isSelect) {
resultMapId = parseResultMap(method);
}
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
// ParameterMapID
null,
parameterTypeClass,
resultMapId,
getReturnType(method),
resultSetType,
flushCache,
useCache,
// TODO gcode issue #577
false,
keyGenerator,
keyProperty,
keyColumn,
// DatabaseID
null,
languageDriver,
// ResultSets
options != null ? nullOrEmpty(options.resultSets()) : null);
}
}
private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
try {
//获取@Select, @Insert, @Update, @Delete类型的注解
Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
//获取 @SelectProvider, @InsertProvider, @UpdateProvider @DeleteProvider注解
Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
//如果SQL注解不为null
if (sqlAnnotationType != null) {
//同时sqlProvider注解也不为空则抛出异常,两者互斥
if (sqlProviderAnnotationType != null) {
throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());
}
//获取注解
Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
//获取注解配置的值
final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
//根据配置的数据创建SqlSource
return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
} else if (sqlProviderAnnotationType != null) {//如果SqlProvider注解不为空
Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
//创建一个ProviderSqlSource
return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method);
}
//如果没有配置Sql注解也没有配置SqlProvider注解则返回null
return null;
} catch (Exception e) {
throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e);
}
}