adresse GitHub
détermination de la source l'adresse
https://github.com/erlieStar/mybatis-3
débogage du code source avec le projet
https://github.com/erlieStar/mybatis-examples
A propos de l'étape mybatis dynamique de proxy, principalement lié aux quatre composantes
- Exécuteur: cache et cache secondaire mis en œuvre dans le Exécuteur
- StatementHandler: utilisation Déclaration ou d'effectuer des opérations PrepareStatement JDBC fournies, jouer le rôle de connexion
- ParameterHandler: compilé pré-réglages des paramètres SQL
- ResultSetHandler: jeu de résultats est retourné à la base de données (ResultSet) encapsulé, le type d'entités de retour spécifiées par l'utilisateur
Cette dynamique de processus étapes proxy d'exécution sont inclus dans ce chiffre, afin de récupérer le code peut obtenir
Renvoie l'objet proxy dynamique
Les exemples passent un proxy dynamique pour déboguer le processus, étape par étape chase
sqlSession = sqlSessionFactory.openSession(true);
BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
int num = bookMapper.deleteById(1);
Article précédent est sqlSessionFactory classe d'implémentation est DefaultSqlSessionFactory, donc retourne openSession DefaultSqlSession, chassant méthode getMapper
Rattrapage classe MapperRegistry, pas lors de l'initialisation enregistrée objets d'interface Mapper et le mappage correspondant MapperProxyFactory faire?
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
Vous l'avez, il semble de ce retour dans l'objet proxy interface mappeur
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
J'ai trouvé pour générer le proxy du travail à la classe MapperProxyFactory, tout droit, puis chasse
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
// 实现了mapper接口的动态代理对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
Cette chasse enfin terminée, cette fonction proxy dynamique de Proxy.newProxyInstance voir les articles suivants ne comprennent pas
Mybatis l'interface écriture seule, pourquoi courir?
méthode Rechercher Proxy.newProxyInstance () dans le dernier paramètre est valide? MapperProxy, l'interface mappeur de classe proxy d'origine est MapperProxy ah.
Exécution d'une approche proxy dynamique
Lorsque vous effectuez l'opération suivante sautera Invoke fonction classe MapperProxy ()
int num = bookMapper.deleteById(1);
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 代理以后,所有Mapper方法调用时,都会执行这个invoke方法
try {
// 如果是Object本身的方法不增强
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
// 针对java7以上版本对动态类型语言的支持,暂不分析
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 从缓存中获取mapperMethod对象,如果缓存中没有,则创建一个,并添加到缓存中
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
Effectuer des opérations à la classe MapperMethod, continuer à chasser
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
// 处理返回为单一对象的情况
// 通过参数解析器解析参数
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
En fonction du type d'instructions SQL, la mise en œuvre du droit à un Sqlsession
DefaultSqlSession à l'exécuteur (actionneurs)
@Override
public int update(String statement, Object parameter) {
try {
dirty = true;
// statement为命名空间+方法名
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
Puis , dans les appels à son tour par CachingExecutor et SimpleExecutor
parce que nous avons trouvé DefaultSqlSession Exécuteur entrant retourné par la méthode suivante
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
// 防止粗心大意的人将defaultExecutorType设为null
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
// 开启二级缓存,用装饰器模式装饰一下
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 创建代理对象,插件在这里起作用
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
Le procédé est transmis executorType defaultExecutorType
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
Et cacheEnabled (contrôle du cache secondaire) La valeur par défaut est vrai, sera utilisé avec décoratif SimpleExecutor CachingExecutor
Le procédé passe ensuite SimpleExecutor de doUpate
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
Nous avons créé un PreparedStatementHandler, comme on peut le voir de la classe de type MappedStatement statementType comme PREPARE
mappedStatement.statementType = StatementType.PREPARED;
Et obtenu à partir d'un PreparedStatement PreparedStatementHandler
handler.parameterize(stmt);
Suivant les réglages des paramètres PreparedStatementHandler dans SQL ParameterHandler
Pour la méthode mise à jour SimpleStatementHandler
@Override
public int update(Statement statement) throws SQLException {
String sql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
int rows;
if (keyGenerator instanceof Jdbc3KeyGenerator) {
statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else if (keyGenerator instanceof SelectKeyGenerator) {
statement.execute(sql);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else {
statement.execute(sql);
rows = statement.getUpdateCount();
}
return rows;
}
Enfin voir libellé natif JDBC
statement.execute()
Ainsi, le processus de suppression est terminé
Le tableau d'ensemble de flux exécuté par la suite
exécuteur détaillée
SimpleExecutor: la configuration par défaut, utilisez l'objet PreparedStatement pour accéder à la base de données, chaque visite doit créer un nouvel objet PreparedStatement
ReuseExecutor: en utilisant l' objet PreparedStatement pour accéder à la base de données, réutilise l'accès objet déclaration
BatchExecutor: exécuter plusieurs instructions SQL pour obtenir la capacité de
résumé
lien ci-dessous appel
MapperProxy: interception méthode Mapper
MapperMethod: En fonction du type de déclaration DefaultSqlSession d'appel
DefaultSqlSession: la mise en œuvre du droit à l' Executor
Executor: générer StatementHandler
ParameterHandler: Le StatementHandler généré pré-compilateur SQL
ResultSetHandler: la base de données renvoie un jeu de résultats (ResultSet) ont été paquet, le type d'entités de retour spécifiées par l' utilisateur
Blog de référence
[1] https://www.jianshu.com/p/46c6e56d9774