扩展ORM框架功能,实现配置方法注解的方式处理增删改查操作
resource="mapper/User_Mapper.xml"
class="com.lino.mybatis.test.dao.IUserDao"
如何通过注解方式处理SQL的配置?
XMLConfigBuilder#mapperElement
配置构建器解析 mapper 配置时,处理关于注解的解析部分。
@Select、@Insert、@Update、@Delete
。ResultMap、MappedStatement
信息。mybatis-step-12
|-src
|-main
| |-java
| |-com.lino.mybatis
| |-annotations
| | |-Delete.java
| | |-Insert.java
| | |-Select.java
| | |-Update.java
| |-binding
| | |-MapperMethod.java
| | |-MapperProxy.java
| | |-MapperProxyFactory.java
| | |-MapperRegistry.java
| |-builder
| | |-annotations
| | | |-MapperAnnotationBuilder.java
| | |-xml
| | | |-XMLConfigBuilder.java
| | | |-XMLMapperBuilder.java
| | | |-XMLStatementBuilder.java
| | |-BaseBuilder.java
| | |-MapperBuilderAssistant.java
| | |-ParameterExpression.java
| | |-SqlSourceBuilder.java
| | |-StaticSqlSource.java
| |-datasource
| | |-druid
| | | |-DruidDataSourceFacroty.java
| | |-pooled
| | | |-PooledConnection.java
| | | |-PooledDataSource.java
| | | |-PooledDataSourceFacroty.java
| | | |-PoolState.java
| | |-unpooled
| | | |-UnpooledDataSource.java
| | | |-UnpooledDataSourceFacroty.java
| | |-DataSourceFactory.java
| |-executor
| | |-parameter
| | | |-ParameterHandler.java
| | |-result
| | | |-DefaultResultContext.java
| | | |-DefaultResultHandler.java
| | |-resultset
| | | |-DefaultResultSetHandler.java
| | | |-ResultSetHandler.java
| | | |-ResultSetWrapper.java
| | |-statement
| | | |-BaseStatementHandler.java
| | | |-PreparedStatementHandler.java
| | | |-SimpleStatementHandler.java
| | | |-StatementHandler.java
| | |-BaseExecutor.java
| | |-Executor.java
| | |-SimpleExecutor.java
| |-io
| | |-Resources.java
| |-mapping
| | |-BoundSql.java
| | |-Environment.java
| | |-MappedStatement.java
| | |-ParameterMapping.java
| | |-ResultMap.java
| | |-ResultMapping.java
| | |-SqlCommandType.java
| | |-SqlSource.java
| |-parsing
| | |-GenericTokenParser.java
| | |-TokenHandler.java
| |-reflection
| | |-factory
| | | |-DefaultObjectFactory.java
| | | |-ObjectFactory.java
| | |-invoker
| | | |-GetFieldInvoker.java
| | | |-Invoker.java
| | | |-MethodInvoker.java
| | | |-SetFieldInvoker.java
| | |-property
| | | |-PropertyNamer.java
| | | |-PropertyTokenizer.java
| | |-wrapper
| | | |-BaseWrapper.java
| | | |-BeanWrapper.java
| | | |-CollectionWrapper.java
| | | |-DefaultObjectWrapperFactory.java
| | | |-MapWrapper.java
| | | |-ObjectWrapper.java
| | | |-ObjectWrapperFactory.java
| | |-MetaClass.java
| | |-MetaObject.java
| | |-Reflector.java
| | |-SystemMetaObject.java
| |-scripting
| | |-defaults
| | | |-DefaultParameterHandler.java
| | | |-RawSqlSource.java
| | |-xmltags
| | | |-DynamicContext.java
| | | |-MixedSqlNode.java
| | | |-SqlNode.java
| | | |-StaticTextSqlNode.java
| | | |-XMLLanguageDriver.java
| | | |-XMLScriptBuilder.java
| | |-LanguageDriver.java
| | |-LanguageDriverRegistry.java
| |-session
| | |-defaults
| | | |-DefaultSqlSession.java
| | | |-DefaultSqlSessionFactory.java
| | |-Configuration.java
| | |-ResultContext.java
| | |-ResultHandler.java
| | |-RowBounds.java
| | |-SqlSession.java
| | |-SqlSessionFactory.java
| | |-SqlSessionFactoryBuilder.java
| | |-TransactionIsolationLevel.java
| |-transaction
| | |-jdbc
| | | |-JdbcTransaction.java
| | | |-JdbcTransactionFactory.java
| | |-Transaction.java
| | |-TransactionFactory.java
| |-type
| | |-BaseTypeHandler.java
| | |-IntegerTypeHandler.java
| | |-JdbcType.java
| | |-LongTypeHandler.java
| | |-StringTypeHandler.java
| | |-TypeAliasRegistry.java
| | |-TypeHandler.java
| | |-TypeHandlerRegistry.java
|-test
|-java
| |-com.lino.mybatis.test
| |-dao
| | |-IUserDao.java
| |-po
| | |-User.java
| |-ApiTest.java
|-resources
|-mybatis-config-datasource.xml
mapperElement
开始,处理 XML 解析的基础上,扩展注解的解析处理。xml
读取到的 class
配置,通过 Configuration 配置项类的添加 Mapper 方法,启动解析注解类语句的操作。addMapper
时所做的注解解析操作。LanguageDriver.java
package com.lino.mybatis.scripting;
import com.lino.mybatis.executor.parameter.ParameterHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.mapping.SqlSource;
import com.lino.mybatis.session.Configuration;
import org.dom4j.Element;
/**
* @description: 脚本语言驱动
*/
public interface LanguageDriver {
/**
* 创建SQL源
*
* @param configuration 配置项
* @param script 元素
* @param parameterType 参数类型
* @return SqlSource SQL源码
*/
SqlSource createSqlSource(Configuration configuration, Element script, Class<?> parameterType);
/**
* 创建SQL源(annotation 注解方式)
*
* @param configuration 配置项
* @param script 注解名称
* @param parameterType 参数类型
* @return SqlSource SQL源码
*/
SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);
/**
* 创建参数处理器
*
* @param mappedStatement 映射器语句类
* @param parameterObject 参数对象
* @param boundSql sql语句
* @return ParameterHandler 参数处理器
*/
ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
}
createSqlSource
接口,把 script
的入参设置为 String
类型,来解析注解 SQL 的配置XMLLanguageDriver.java
package com.lino.mybatis.scripting.xmltags;
import com.lino.mybatis.executor.parameter.ParameterHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.mapping.SqlSource;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.scripting.defaults.DefaultParameterHandler;
import com.lino.mybatis.scripting.defaults.RawSqlSource;
import com.lino.mybatis.session.Configuration;
import org.dom4j.Element;
/**
* @description: XML 语言驱动器
*/
public class XMLLanguageDriver implements LanguageDriver {
@Override
public SqlSource createSqlSource(Configuration configuration, Element script, Class<?> parameterType) {
// 用XML脚本构建器解析
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
return builder.parseScriptNode();
}
@Override
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
// 暂时不解析动态 SQL
return new RawSqlSource(configuration, script, parameterType);
}
@Override
public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
}
}
createSqlSource
方法,其与 XML 解析来说,更加简单。
annotations
注解包,来提供用于配置到 DAO 方法上的注解。@Insert、@Delete、@Update、@Select
Insert.java
package com.lino.mybatis.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @description: insert 语句注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Insert {
String[] value();
}
Delete.java
package com.lino.mybatis.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @description: delete 语句注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Delete {
String[] value();
}
Update.java
package com.lino.mybatis.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @description: update 语句注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Update {
String[] value();
}
Select.java
package com.lino.mybatis.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @description: select 语句注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Select {
String[] value();
}
@Arg、@InsertProvider、@Param、@ResultMap
等注解参数配置。MapperAnnotationBuilder.java
package com.lino.mybatis.builder.annotation;
import com.lino.mybatis.annotations.Delete;
import com.lino.mybatis.annotations.Insert;
import com.lino.mybatis.annotations.Select;
import com.lino.mybatis.annotations.Update;
import com.lino.mybatis.binding.MapperMethod;
import com.lino.mybatis.builder.MapperBuilderAssistant;
import com.lino.mybatis.mapping.SqlCommandType;
import com.lino.mybatis.mapping.SqlSource;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.session.RowBounds;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
/**
* @description: 注解配置构建器 Mapper
*/
public class MapperAnnotationBuilder {
private final Set<Class<? extends Annotation>> sqlAnnotationTypes = new HashSet<>();
private Configuration configuration;
private MapperBuilderAssistant assistant;
private Class<?> 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);
}
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
assistant.setCurrentNamespace(type.getName());
Method[] methods = type.getMethods();
for (Method method : methods) {
if (!method.isBridge()) {
// 解析语句
parseStatement(method);
}
}
}
}
/**
* 解析语句
*
* @param method 方法
*/
private void parseStatement(Method method) {
Class<?> parameterTypeClass = getParameterType(method);
LanguageDriver languageDriver = getLanguageDriver(method);
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
if (sqlSource != null) {
final String mappedStatementId = type.getName() + "." + method.getName();
SqlCommandType sqlCommandType = getSqlCommandType(method);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
String resultMapId = null;
if (isSelect) {
resultMapId = parseResultMap(method);
}
// 调用助手类
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
sqlCommandType,
parameterTypeClass,
resultMapId,
getReturnType(method),
languageDriver
);
}
}
/**
* 重点:DAO方法的返回类型,如果为 List 则需要获取集合中的对象类型
*
* @param method 方法
* @return 对象类型
*/
private Class<?> getReturnType(Method method) {
Class<?> returnType = method.getReturnType();
if (Collection.class.isAssignableFrom(returnType)) {
Type returnTypeParameter = method.getGenericReturnType();
if (returnTypeParameter instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) returnTypeParameter).getActualTypeArguments();
if (actualTypeArguments != null && actualTypeArguments.length == 1) {
returnTypeParameter = actualTypeArguments[0];
if (returnTypeParameter instanceof Class) {
returnType = (Class<?>) returnTypeParameter;
} else if (returnTypeParameter instanceof ParameterizedType) {
returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
} else if (returnTypeParameter instanceof GenericArrayType) {
Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();
// (issue #525) support List
returnType = Array.newInstance(componentType, 0).getClass();
}
}
}
}
return returnType;
}
private String parseResultMap(Method method) {
// generateResultMapName
StringBuilder suffix = new StringBuilder();
for (Class<?> c : method.getParameterTypes()) {
suffix.append("-");
suffix.append(c.getSimpleName());
}
if (suffix.length() < 1) {
suffix.append("-void");
}
String resultMapId = type.getName() + "." + method.getName() + suffix;
// 添加 ResultMap
Class<?> returnType = getReturnType(method);
assistant.addResultMap(resultMapId, returnType, new ArrayList<>());
return resultMapId;
}
private SqlCommandType getSqlCommandType(Method method) {
Class<? extends Annotation> type = getSqlAnnotationType(method);
if (type == null) {
return SqlCommandType.UNKNOWN;
}
return SqlCommandType.valueOf(type.getSimpleName().toUpperCase(Locale.ENGLISH));
}
private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
try {
Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
if (sqlAnnotationType != null) {
Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
}
return null;
} catch (Exception e) {
throw new RuntimeException("Could not find value method on SQL annotation. Cause: " + e);
}
}
private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
final StringBuilder sql = new StringBuilder();
for (String fragment : strings) {
sql.append(fragment);
sql.append(" ");
}
return languageDriver.createSqlSource(configuration, sql.toString(), parameterTypeClass);
}
private Class<? extends Annotation> getSqlAnnotationType(Method method) {
for (Class<? extends Annotation> type : sqlAnnotationTypes) {
Annotation annotation = method.getAnnotation(type);
if (annotation != null) {
return type;
}
}
return null;
}
private LanguageDriver getLanguageDriver(Method method) {
Class<?> langClass = configuration.getLanguageRegistry().getDefaultDriverClass();
return configuration.getLanguageRegistry().getDriver(langClass);
}
private Class<?> getParameterType(Method method) {
Class<?> parameterType = null;
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> clazz : parameterTypes) {
if (!RowBounds.class.isAssignableFrom(clazz) && !ResultHandler.class.isAssignableFrom(clazz)) {
if (parameterType == null) {
parameterType = clazz;
} else {
parameterType = MapperMethod.ParamMap.class;
}
}
}
return parameterType;
}
}
Method#getParameterTypes
方法获取入参类型。value
值中的 SQL 来创建 SqlSource 语句。SELECT
时,创建出 ResultMap 信息。
Map resultMaps
。getReturnType(Method method)
方法的使用。它有一个非常核心的问题点,在于要拿到方法的返回类型:
Collection.class.isAssignableFrom
判断,再进行集合中参数类型的获取。
List
则需要根据 method.getGenericReturnType()
获取返回类型,并判断是否为 Class
进行返回具体的类型。xml
还是注解。MapperRegistry#addMapper
方法,并开始执行解析注解的相关操作。Configuration.java
package com.lino.mybatis.session;
import com.lino.mybatis.binding.MapperRegistry;
import com.lino.mybatis.datasource.druid.DruidDataSourceFactory;
import com.lino.mybatis.datasource.pooled.PooledDataSourceFactory;
import com.lino.mybatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.SimpleExecutor;
import com.lino.mybatis.executor.parameter.ParameterHandler;
import com.lino.mybatis.executor.resultset.DefaultResultSetHandler;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.executor.statement.PreparedStatementHandler;
import com.lino.mybatis.executor.statement.StatementHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.mapping.ResultMap;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.factory.DefaultObjectFactory;
import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.reflection.wrapper.DefaultObjectWrapperFactory;
import com.lino.mybatis.reflection.wrapper.ObjectWrapperFactory;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.scripting.LanguageDriverRegistry;
import com.lino.mybatis.scripting.xmltags.XMLLanguageDriver;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.jdbc.JdbcTransactionFactory;
import com.lino.mybatis.type.TypeAliasRegistry;
import com.lino.mybatis.type.TypeHandlerRegistry;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @description: 配置项
*/
public class Configuration {
/**
* 环境
*/
protected Environment environment;
/**
* 映射注册机
*/
protected MapperRegistry mapperRegistry = new MapperRegistry(this);
/**
* 映射的语句,存在Map里
*/
protected final Map<String, MappedStatement> mappedStatements = new HashMap<>(16);
/**
* 结果映射,存在Map里
*/
protected final Map<String, ResultMap> resultMaps = new HashMap<>(16);
...
public ResultMap getResultMap(String id) {
return resultMaps.get(id);
}
public void addResultMap(ResultMap resultMap) {
resultMaps.put(resultMap.getId(), resultMap);
}
}
Map resultMaps
结果映射的列表。MapperBuilderAssistant.java
package com.lino.mybatis.builder;
import com.lino.mybatis.mapping.*;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.session.Configuration;
import java.util.ArrayList;
import java.util.List;
/**
* @description: 映射构建器助手,建造者
*/
public class MapperBuilderAssistant extends BaseBuilder {
private String currentNamespace;
private String resource;
public MapperBuilderAssistant(Configuration configuration, String resource) {
super(configuration);
this.resource = resource;
}
public String getCurrentNamespace() {
return currentNamespace;
}
public void setCurrentNamespace(String currentNamespace) {
this.currentNamespace = currentNamespace;
}
public String applyCurrentNamespace(String base, boolean isReference) {
if (base == null) {
return null;
}
if (isReference) {
if (base.contains(".")) {
return base;
}
} else {
if (base.startsWith(currentNamespace + ".")) {
return base;
}
if (base.contains(".")) {
throw new RuntimeException("Dots are not allowed in element names, please remove it from " + base);
}
}
return currentNamespace + "." + base;
}
/**
* 添加映射器语句
*/
public MappedStatement addMappedStatement(String id, SqlSource sqlSource, SqlCommandType sqlCommandType,
Class<?> parameterType, String resultMap, Class<?> resultType,
LanguageDriver lang) {
// 给id加上namespace前缀:com.lino.mybatis.test.dao.IUserDao.queryUserInfoById
id = applyCurrentNamespace(id, false);
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlCommandType, sqlSource, resultType);
// 结果映射, 给 MappedStatement#resultMaps
setStatementResultMap(resultMap, resultType, statementBuilder);
MappedStatement statement = statementBuilder.build();
// 映射语句信息,建造完存放到配置项中
configuration.addMappedStatement(statement);
return statement;
}
private void setStatementResultMap(String resultMap, Class<?> resultType, MappedStatement.Builder statementBuilder) {
// 因为暂时还没有在 Mapper XML 中配置 Map 返回结果,所以这里返回的是 null
resultMap = applyCurrentNamespace(resultMap, true);
List<ResultMap> resultMaps = new ArrayList<>();
if (resultMap != null) {
String[] resultMapNames = resultMap.split(",");
for (String resultMapName : resultMapNames) {
resultMaps.add(configuration.getResultMap(resultMapName.trim()));
}
}
/*
* 通常使用 resultType 即可满足大部分场景
*
else if (resultType != null) {
ResultMap.Builder inlineResultMapBuilder = new ResultMap.Builder(
configuration, statementBuilder.id() + "-Inline", resultType, new ArrayList<>());
resultMaps.add(inlineResultMapBuilder.build());
}
statementBuilder.resultMaps(resultMaps);
}
public ResultMap addResultMap(String id, Class<?> type, List<ResultMapping> resultMappings) {
ResultMap.Builder inlineResultMapBuilder = new ResultMap.Builder(configuration, id, type, resultMappings);
ResultMap resultMap = inlineResultMapBuilder.build();
configuration.addResultMap(resultMap);
return resultMap;
}
}
applyCurrentNamespace
方法,添加对命名空间的全称判断。setStatementResultMap
方法, 完善 resultMap != null
时的逻辑处理。addResultMap
方法,添加返回值集合。XMLConfigBuilder
package com.lino.mybatis.builder.xml;
import com.lino.mybatis.builder.BaseBuilder;
import com.lino.mybatis.datasource.DataSourceFactory;
import com.lino.mybatis.io.Resources;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.transaction.TransactionFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;
import javax.sql.DataSource;
import java.io.InputStream;
import java.io.Reader;
import java.util.List;
import java.util.Properties;
/**
* @description: XML配置构建器,建造者模式,集成BaseBuilder
*/
public class XMLConfigBuilder extends BaseBuilder {
...
/**
*
*
*
*
*
*/
private void mapperElement(Element mappers) throws Exception {
List<Element> mapperList = mappers.elements("mapper");
for (Element e : mapperList) {
String resource = e.attributeValue("resource");
String mapperClass = e.attributeValue("class");
// XML解析
if (resource != null && mapperClass == null) {
InputStream inputStream = Resources.getResourceAsStream(resource);
// 在for循环里每个mapper都重新new一个XMLMapperBuilder,来解析
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource);
mapperParser.parse();
}
// Annontation 注解解析
else if (resource == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
}
}
}
}
resource、class
分别进行判断解析。resource
为空,mapperClass
不为空,则进行注解的解析处理。
mapperClass
获取对应的接口,并通过 Configuration#addMapper
方法,添加到配置项中。MapperRegistry.java
package com.lino.mybatis.binding;
import cn.hutool.core.lang.ClassScanner;
import com.lino.mybatis.builder.annotation.MapperAnnotationBuilder;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.SqlSession;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @description: 映射器注册机
*/
public class MapperRegistry {
private Configuration configuration;
public MapperRegistry(Configuration configuration) {
this.configuration = configuration;
}
/**
* 将已添加的映射器代理加入到HashMap
*/
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>(16);
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new RuntimeException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new RuntimeException("Error getting mapper instance. Cause: " + e, e);
}
}
public <T> void addMapper(Class<T> type) {
/* Mapper 必须是接口才会注册 */
if (type.isInterface()) {
if (hasMapper(type)) {
// 如果重复添加,报错
throw new RuntimeException("Type " + type + " is already known to the MapperRegistry.");
}
// 注册映射器代理工厂
knownMappers.put(type, new MapperProxyFactory<>(type));
// 解析注解类语句配置
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(configuration, type);
parser.parse();
}
}
public <T> boolean hasMapper(Class<T> type) {
return knownMappers.containsKey(type);
}
public void addMappers(String packageName) {
Set<Class<?>> mapperSet = ClassScanner.scanPackage(packageName);
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
}
addMapper
方法中,根据 Class
注册完映射器代理工厂后,则开始进行解析注解操作。
DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
<mappers>
<mapper class="com.lino.mybatis.test.dao.IUserDao"/>
mappers>
configuration>
IUserDao.java
package com.lino.mybatis.test.dao;
import com.lino.mybatis.annotations.Delete;
import com.lino.mybatis.annotations.Insert;
import com.lino.mybatis.annotations.Select;
import com.lino.mybatis.annotations.Update;
import com.lino.mybatis.test.po.User;
import java.util.List;
/**
* @Description: 用户持久层
*/
public interface IUserDao {
/**
* 根据ID查询用户信息
*
* @param uId ID
* @return User 用户
*/
@Select("SELECT id, userId, userName, userHead FROM user WHERE id = #{id}")
User queryUserInfoById(Long uId);
/**
* 根据用户对象查询用户信息
*
* @param user 用户
* @return User 用户
*/
@Select("SELECT id, userId, userName, userHead FROM user WHERE id = #{id} and userId = #{userId}")
User queryUserInfo(User user);
/**
* 查询用户对象列表
*
* @return List 用户列表
*/
@Select("SELECT id, userId, userName, userHead FROM user")
List<User> queryUserInfoList();
/**
* 更新用户信息
*
* @param user 用户对象
* @return 受影响的行数
*/
@Update("UPDATE user SET userName = #{userName} WHERE id = #{id}")
int updateUserInfo(User user);
/**
* 新增用户信息
*
* @param user 用户对象
* @return 受影响的行数
*/
@Insert("INSERT INTO user (userId, userName, userHead, createTime, updateTime) VALUES (#{userId}, #{userName}, #{userHead}, now(), now())")
int insertUserInfo(User user);
/**
* 根据ID删除用户信息
*
* @param uId ID
* @return 受影响的行数
*/
@Delete("DELETE FROM user WHERE userId = #{userId}")
int deleteUserInfoByUserId(String uId);
}
ApiTest.java
@Test
public void test_insertUserInfo() {
// 1.获取映射器对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
// 2.测试验证
User user = new User();
user.setUserId("10002");
user.setUserName("张三");
user.setUserHead("1_05");
userDao.insertUserInfo(user);
logger.info("测试结果:{}", "Insert OK");
// 3.提交事务
sqlSession.commit();
}
测试结果
16:32:32.510 [main] INFO c.l.mybatis.builder.SqlSourceBuilder - 构建参数映射 property:userId propertyType:class java.lang.String
16:32:32.510 [main] INFO c.l.mybatis.builder.SqlSourceBuilder - 构建参数映射 property:userName propertyType:class java.lang.String
16:32:32.510 [main] INFO c.l.mybatis.builder.SqlSourceBuilder - 构建参数映射 property:userHead propertyType:class java.lang.String
16:32:33.171 [main] INFO c.l.m.d.pooled.PooledDataSource - Created connention 597190999.
16:32:33.213 [main] INFO c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value:"10002"
16:32:33.213 [main] INFO c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value:"张三"
16:32:33.213 [main] INFO c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value:"1_05"
16:32:33.213 [main] INFO com.lino.mybatis.test.ApiTest - 测试结果:Insert OK
sqlSession.commit()
。
DefaultSqlSessionFactory#openSession
开启 Session
创建事务工厂的时候,传入给事务工厂构造函数的事务是否自动提交为 false。ApiTest.java
@Test
public void test_queryUserInfoList() {
// 1.获取映射器对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
// 2.测试验证: 对象参数
List<User> users = userDao.queryUserInfoList();
logger.info("测试结果:{}", JSON.toJSONString(users));
}
测试结果
16:40:47.699 [main] INFO c.l.m.s.defaults.DefaultSqlSession - 执行查询 statement:com.lino.mybatis.test.dao.IUserDao.queryUserInfoList parameter:null
16:40:48.361 [main] INFO c.l.m.d.pooled.PooledDataSource - Created connention 1192171522.
16:40:48.395 [main] INFO com.lino.mybatis.test.ApiTest - 测试结果:[{"id":1,"userHead":"1_04","userId":"10001","userName":"小零哥"},{"id":4,"userHead":"1_05","userId":"10002","userName":"张三"}]
MapperMethod#execute
调用 sqlSession.selectList(command.getName(), param)
是测试通过的。ApiTest.java
@Test
public void test_updateUserInfo() {
// 1.获取映射器对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
// 2.测试验证
int count = userDao.updateUserInfo(new User(1L, "10001", "小灵哥"));
logger.info("测试结果:{}", count);
// 3.提交事务
sqlSession.commit();
}
测试结果
16:44:11.979 [main] INFO c.l.m.d.pooled.PooledDataSource - Created connention 597190999.
16:44:12.027 [main] INFO c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value:"小灵哥"
16:44:12.028 [main] INFO c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value:1
16:44:12.037 [main] INFO com.lino.mybatis.test.ApiTest - 测试结果:1
ID=1
的用户,userName
修改为 小灵哥,通过测试日志和数据库截图,测试通过。ApiTest.java
@Test
public void test_deleteUserInfoByUserId() {
// 1.获取映射器对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
// 2.测试验证
int count = userDao.deleteUserInfoByUserId("10002");
logger.info("测试结果:{}", count == 1);
// 3.提交事务
sqlSession.commit();
}
测试结果
16:47:54.591 [main] INFO c.l.m.d.pooled.PooledDataSource - Created connention 597190999.
16:47:54.633 [main] INFO c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value:"10002"
16:47:54.643 [main] INFO com.lino.mybatis.test.ApiTest - 测试结果:true
userId = '10002'
的用户删除掉,通过测试日志和数据库截图,测试通过。@Insert、@Delete、@Update、@Select
四个注解。