Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Introduction to SqlSession of MyBatis

2025-03-04 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Database >

Share

Shulou(Shulou.com)06/01 Report--

Lu Chunli's work notes are not as good as bad notes.

Reproduced from: deep and simple MyBatis-Sqlsession

The creation of SqlSession

Sqlsession corresponds to a database session. Since the database reply is not permanent, the life cycle of Sqlsession should not be permanent, on the contrary, you need to create it every time you access the database (of course, it does not mean that you can only execute sql once in Sqlsession, you can execute it multiple times, once you close Sqlsession, you need to recreate it). There is only one place to create a Sqlsession, and that is the openSession method of SqlsessionFactory:

Package org.apache.ibatis.session;import java.sql.Connection;public interface SqlSessionFactory {SqlSession openSession (); SqlSession openSession (boolean autoCommit); SqlSession openSession (Connection connection); SqlSession openSession (TransactionIsolationLevel level); SqlSession openSession (ExecutorType execType); SqlSession openSession (ExecutorType execType, boolean autoCommit); SqlSession openSession (ExecutorType execType, TransactionIsolationLevel level); SqlSession openSession (ExecutorType execType, Connection connection); Configuration getConfiguration ();}

The place where the SqlSession is actually created is openSessionFromDataSource, as follows:

Package org.apache.ibatis.session.defaults;import java.sql.Connection;import java.sql.SQLException;import org.apache.ibatis.exceptions.ExceptionFactory;import org.apache.ibatis.executor.ErrorContext;import org.apache.ibatis.executor.Executor;import org.apache.ibatis.mapping.Environment;import org.apache.ibatis.session.Configuration;import org.apache.ibatis.session.ExecutorType;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.TransactionIsolationLevel Import org.apache.ibatis.transaction.Transaction;import org.apache.ibatis.transaction.TransactionFactory;import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;public class DefaultSqlSessionFactory implements SqlSessionFactory {private final Configuration configuration; public DefaultSqlSessionFactory (Configuration configuration) {this.configuration = configuration;} public SqlSession openSession () {return openSessionFromDataSource (configuration.getDefaultExecutorType (), null, false);} private SqlSession openSessionFromDataSource (ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null Try {final Environment environment = configuration.getEnvironment (); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment (environment); tx = transactionFactory.newTransaction (environment.getDataSource (), level, autoCommit); final Executor executor = configuration.newExecutor (tx, execType, autoCommit); return new DefaultSqlSession (configuration, executor);} catch (Exception) {closeTransaction (tx); / / may have fetched a connection so lets call close () throw ExceptionFactory.wrapException ("Error opening session. Cause: "+ e, e);} finally {ErrorContext.instance (). Reset ();}

As you can see, the creation of sqlsession goes through the following main steps:

1) obtain Environment from configuration

2) get DataSource from Environment

3) get TransactionFactory from Environment

4) get the database connection object Connection from DataSource

5) create a transaction object Transaction on the acquired database connection

6) create an Executor object (this object is very important, in fact, all operations of sqlsession are done through it)

7) create a sqlsession object.

The creation of Executor

Sqlsession is just a facade, what really works is that all the operations of Executor,Sqlsession to the database are done through Executor. Like Sqlsession, Executor is created dynamically:

Final Executor executor = configuration.newExecutor (tx, execType, autoCommit)

Implementation of the Configuration class:

Public class Configuration {protected Environment environment; public Executor newExecutor (Transaction transaction, ExecutorType executorType, boolean autoCommit) {executorType = executorType = = null? DefaultExecutorType: executorType; 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, autoCommit);} executor = (Executor) interceptorChain.pluginAll (executor); return executor;}}

Definition of Executor:

Package org.apache.ibatis.executor;import java.sql.SQLException;import java.util.List;import org.apache.ibatis.cache.CacheKey;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.reflection.MetaObject;import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds;import org.apache.ibatis.transaction.Transaction;public interface Executor {ResultHandler NO_RESULT_HANDLER = null; int update (MappedStatement ms, Object parameter) throws SQLException List query (MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException; List query (MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException; List flushStatements () throws SQLException; void commit (boolean required) throws SQLException; void rollback (boolean required) throws SQLException; CacheKey createCacheKey (MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql); boolean isCached (MappedStatement ms, CacheKey key); void clearLocalCache (); void deferLoad (MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class targetType); Transaction getTransaction () Void close (boolean forceRollback); boolean isClosed ();}

As you can see, the Executor created is just one of the 3 basic types:

BatchExecutor is designed to perform batch sql operations

ReuseExecutor reuses statement to perform sql operations

There is nothing special about SimpleExecutor simply executing sql.

If cache is turned on (it is on by default and there is no reason to turn it off), CachingExecutor is created, which takes the Executor created earlier as the only parameter. CachingExecutor looks up the cache before querying the database, and if it cannot find it, it calls delegate (that is, the Executor object passed in during construction) to query from the database and stores the query results in the cache.

Package org.apache.ibatis.executor;import java.sql.SQLException;import java.util.List;import org.apache.ibatis.cache.Cache;import org.apache.ibatis.cache.CacheKey;import org.apache.ibatis.cache.TransactionalCacheManager;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.mapping.ParameterMapping;import org.apache.ibatis.mapping.ParameterMode;import org.apache.ibatis.mapping.StatementType;import org.apache.ibatis.reflection.MetaObject Import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds;import org.apache.ibatis.transaction.Transaction;public class CachingExecutor implements Executor {private Executor delegate; private boolean autoCommit; / / issue # 573. No need to call commit () on autoCommit sessions private TransactionalCacheManager tcm = new TransactionalCacheManager (); private boolean dirty; public CachingExecutor (Executor delegate) {this (delegate, false);} public CachingExecutor (Executor delegate, boolean autoCommit) {this.delegate = delegate; this.autoCommit = autoCommit;} public Transaction getTransaction () {return delegate.getTransaction ();} public void close (boolean forceRollback) {try {/ / issue # 499. Unresolved session handling / / issue # 573. Autocommit sessions should commit if (dirty & &! autoCommit) {tcm.rollback ();} else {tcm.commit ();}} finally {delegate.close (forceRollback);}} / / other code slightly}

Executor objects can be intercepted by plug-ins (interceptor Interceptor, such as:

@ Intercepts ({@ Signature (type = Executor.class, method = "query")

Args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})

If a plug-in for the Executor type is defined, the resulting Executor object is the proxy object inserted by each plug-in.

Mapper introduction

The official Mybatis manual recommends accessing mybatis through the mapper object, because using mapper looks more elegant, like this:

Session = sqlSessionFactory.openSession (); UserMapper userMapper= session.getMapper (UserMapper.class); UserBo user = new UserBo (); user.setUsername ("iMbatis"); user.setPassword ("iMbatis"); userMapper.insertUser (user)

So what exactly is this mapper, how is it created, and how does it relate to sqlsession and so on? Here are some answers for you.

On the face of it, mapper is created in sqlsession, but where it is actually created is MapperRegistry:

Package org.apache.ibatis.session;import java.io.Closeable;import java.sql.Connection;import java.util.List;import java.util.Map;import org.apache.ibatis.executor.BatchResult;/** * The primary Java interface for working with MyBatis. * Through this interface you can execute commands, get mappers and manage transactions. * / public interface SqlSession extends Closeable {T selectOne (String statement); T selectOne (String statement, Object parameter); List selectList (String statement); List selectList (String statement, Object parameter); List selectList (String statement, Object parameter, RowBounds rowBounds); Map selectMap (String statement, String mapKey); Map selectMap (String statement, Object parameter, String mapKey); Map selectMap (String statement, Object parameter, String mapKey, RowBounds rowBounds); void select (String statement, Object parameter, ResultHandler handler); void select (String statement, ResultHandler handler) Void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler handler); int insert (String statement); int insert (String statement, Object parameter); int update (String statement); int update (String statement, Object parameter); int delete (String statement); int delete (String statement, Object parameter); void commit (); void commit (boolean force); void rollback (); void rollback (boolean force); List flushStatements (); void close (); void clearCache (); Configuration getConfiguration () T getMapper (Class type); Connection getConnection ();}

SqlSeesion is the interface, and getMapper must be done by implementing the class. Check its class structure:

Package org.apache.ibatis.session.defaults;import org.apache.ibatis.session.Configuration;import org.apache.ibatis.session.RowBounds;import org.apache.ibatis.session.SqlSession;public class DefaultSqlSession implements SqlSession {private Configuration configuration; private Executor executor; private boolean dirty; public T getMapper (Class type) {return configuration.getMapper (type, this);}}-where the implementation of configuration is public T getMapper (Class type, SqlSession sqlSession) {return mapperRegistry.getMapper (type, sqlSession) }-- MapperRegistry is implemented as package org.apache.ibatis.binding;public class MapperRegistry {private Configuration config; private final Map > knownMappers = new HashMap > (); public MapperRegistry (Configuration config) {this.config = config;} @ SuppressWarnings ("unchecked") public T getMapper (Class type, SqlSession sqlSession) {final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) 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);}} public boolean hasMapper (Class type) {return knownMappers.containsKey (type);} public void addMapper (Class type) {if (type.isInterface ()) {if (hasMapper (type)) {throw new BindingException (" Type "+ type +" is already known to the MapperRegistry. ");} boolean loadCompleted = false Try {knownMappers.put (type, new MapperProxyFactory (type)); / / It's important that the type is added before the parser is run / / otherwise the binding may automatically be attempted by the / / mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder (config, type); parser.parse (); loadCompleted = true;} finally {if (! loadCompleted) {knownMappers.remove (type);}

As you can see, mapper is a proxy object, and the interface it implements is the incoming type, which is why the mapper object can be accessed directly through the interface. You can also see that the sqlsession object was passed in when the mapper proxy object was created, thus associating the sqlsession. Let's take a closer look at what's going on behind mapperProxyFactory.newInstance (sqlSession):

Package org.apache.ibatis.binding;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import org.apache.ibatis.session.SqlSession;public class MapperProxyFactory {private final Class mapperInterface; private Map methodCache = new ConcurrentHashMap (); public MapperProxyFactory (Class mapperInterface) {this.mapperInterface = mapperInterface;} public Class getMapperInterface () {return mapperInterface;} public Map getMethodCache () {return methodCache } / * newProxyInstance (ClassLoader loader, Class [] interfaces, InvocationHandler h) * ClassLoader loader: classloader Generally use the same class loader as the proxied object * Class [] interfaces: interface array of the proxied object (target object) * InvocationHandler h: set the callback object. When the method of the proxy object is called, the invoke method * / @ SuppressWarnings ("unchecked") protected T newInstance (MapperProxy mapperProxy) {return (T) Proxy.newProxyInstance (mapperInterface.getClassLoader ()) of the specified object is delegated to the parameter. New Class [] {mapperInterface}, mapperProxy) } public T newInstance (SqlSession sqlSession) {final MapperProxy mapperProxy = new MapperProxy (sqlSession, mapperInterface, methodCache); return newInstance (mapperProxy);}}

It seems nothing special, and as with the creation of other proxy classes, let's focus on the invoke method of MapperProxy.

We know that the access to the method of the proxied object will be implemented on the invoke of the agent. The invoke of MapperProxy is as follows:

Package org.apache.ibatis.binding;import java.io.Serializable;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.util.Map;import org.apache.ibatis.reflection.ExceptionUtil;import org.apache.ibatis.session.SqlSession;/** * @ author Clinton Begin * @ author Eduardo Macarron * / public class MapperProxy implements InvocationHandler, Serializable {private static final long serialVersionUID =-6424540398559729838L; private final SqlSession sqlSession; private final Class mapperInterface; private final Map methodCache Public MapperProxy (SqlSession sqlSession, Class mapperInterface, Map methodCache) {this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache;} @ Override public Object invoke (Object proxy, Method method, Object [] args) throws Throwable {if (Object.class.equals (method.getDeclaringClass () {try {/ / the method of transferring a specific target object, where the target object is this return method.invoke (this, args) } catch (Throwable t) {throw ExceptionUtil.unwrapThrowable (t);}} final MapperMethod mapperMethod = cachedMapperMethod (method); return mapperMethod.execute (sqlSession, args);} private MapperMethod cachedMapperMethod (Method method) {MapperMethod mapperMethod = methodCache.get (method); if (mapperMethod = = null) {mapperMethod = new MapperMethod (mapperInterface, method, sqlSession.getConfiguration (); methodCache.put (method, mapperMethod);} return mapperMethod;}}

You can see that invoke has transferred the power of execution to MapperMethod. Let's take a look at how MapperMethod works:

Package org.apache.ibatis.binding;import org.apache.ibatis.annotations.Flush;import org.apache.ibatis.annotations.MapKey;import org.apache.ibatis.annotations.Param;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.mapping.SqlCommandType;import org.apache.ibatis.reflection.MetaObject;import org.apache.ibatis.session.Configuration;import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds;import org.apache.ibatis.session.SqlSession;import java.lang.reflect.Array Import java.lang.reflect.Method;import java.util.*;/** * @ author Clinton Begin * @ author Eduardo Macarron * @ author Lasse Voss * / public class MapperMethod {private final SqlCommand command; private final MethodSignature method; public MapperMethod (Class mapperInterface, Method method, Configuration config) {this.command = new SqlCommand (config, mapperInterface, method); this.method = new MethodSignature (config, method);} public Object execute (SqlSession sqlSession, Object [] args) {Object result If (SqlCommandType.INSERT = = command.getType ()) {Object param = method.convertArgsToSqlCommandParam (args); result = rowCountResult (sqlSession.insert (command.getName (), param));} else if (SqlCommandType.UPDATE = = command.getType ()) {Object param = method.convertArgsToSqlCommandParam (args); result = rowCountResult (sqlSession.update (command.getName (), param)) } else if (SqlCommandType.DELETE = = command.getType ()) {Object param = method.convertArgsToSqlCommandParam (args); result = rowCountResult (sqlSession.delete (command.getName (), param));} else if (SqlCommandType.SELECT = = command.getType ()) {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 {Object param = method.convertArgsToSqlCommandParam (args); result = sqlSession.selectOne (command.getName (), param);}} else if (SqlCommandType.FLUSH = = command.getType ()) {result = sqlSession.flushStatements () } else {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;}

As you can see, MapperMethod is like a distributor who chooses different sqlsession methods to execute depending on the parameter and return value type. In this way, the mapper object is really associated with sqlsession.

Executor introduction

Sqlsession is just a facade, and it is executor that really works, and access to sqlsession methods will eventually fall on the corresponding methods of executor.

Executor is divided into two categories: one is CacheExecutor, the other is ordinary Executor.

CacheExecutor

CacheExecutor has an important attribute, delegate, which holds some kind of ordinary Executor, and the value is passed in when the image is constructed. When executing the database update operation, it directly calls the update method of delegate. When executing the query method, it first tries to take the value from cache, then invokes the query method of delegate, and stores the query results in cache.

@ Override public List query (MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {Cache cache = ms.getCache (); if (cache! = null) {flushCacheIfRequired (ms); if (ms.isUseCache () & resultHandler = = null) {ensureNoOutParams (ms, parameterObject, boundSql); @ SuppressWarnings ("unchecked") List list = (List) tcm.getObject (cache, key) If (list = = null) {list = delegate. Query (ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject (cache, key, list); / / issue # 578 and # 116} return list;}} / / delegate.query is actually org.apache.ibatis.executor.BaseExecutor.query / / will eventually call BaseExecutor return delegate. Query (ms, parameterObject, rowBounds, resultHandler, key, boundSql);} normal Executor

There are three types of ordinary Executor, all of which are inherited from BaseExecutor,BatchExecutor for performing batch sql operations, ReuseExecutor reuses statement to perform sql operations, and SimpleExecutor simply executes sql. Take SimpleExecutor as an example:

Override public List doQuery (MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null; try {Configuration configuration = ms.getConfiguration (); StatementHandler handler = configuration.newStatementHandler (wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement (handler, ms.getStatementLog ()); return handler.query (stmt, resultHandler);} finally {closeStatement (stmt);}}

It can be seen that Executor is essentially a shopkeeper, and the specific things are originally done by StatementHandler.

StatementHandler introduction

When Executor gives the baton to StatementHandler, the next job is StatementHandler. Let's first look at how StatementHandler is created.

StatementHandler handler = configuration.newStatementHandler (wrapper, ms, parameter, rowBounds, resultHandler, boundSql)

The implementation of configuration.newStatementHandler is:

Public StatementHandler newStatementHandler (Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {StatementHandler statementHandler = new RoutingStatementHandler (executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); statementHandler = (StatementHandler) interceptorChain.pluginAll (statementHandler); return statementHandler;}

You can see that the StatementHandler created each time is RoutingStatementHandler, which is just a distributor, whose attribute delegate is used to specify which specific StatementHandler to use.

At the same time, note that StatementHandler can be intercepted by an interceptor, and like Executor, the intercepted object is a proxy object. Because mybatis does not implement physical paging of the database, many physical paging implementations are implemented in this place using interceptors.

There are three StatementHandler options: SimpleStatementHandler, PreparedStatementHandler, and CallableStatementHandler. Which one is specified in each statement in the mapper configuration file, the default is PreparedStatementHandler.

The value of statementType is constrained by the DTD file:

For details, see: http://mybatis.org/dtd/mybatis-3-mapper.dtd

After the creation of StatementHandler, you need to perform some initial operations, such as opening and setting parameters of statement, setting parameters for PreparedStatement, and so on.

The code is as follows: org.apache.ibatis.executor.SimpleExecutor

Private Statement prepareStatement (StatementHandler handler, Log statementLog) throws SQLException {Statement stmt; Connection connection = getConnection (statementLog); stmt = handler.prepare (connection); handler.parameterize (stmt); return stmt;}

There is nothing special about the enabling and parameter setting of statement:

Package org.apache.ibatis.executor.statement;import java.sql.Connection;import java.sql.SQLException;import java.sql.Statement;import java.util.List;import org.apache.ibatis.executor.Executor;import org.apache.ibatis.executor.ExecutorException;import org.apache.ibatis.executor.parameter.ParameterHandler;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds / * * @ author Clinton Begin * / public class RoutingStatementHandler implements StatementHandler {private final StatementHandler delegate; public RoutingStatementHandler (Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {switch (ms.getStatementType ()) {case STATEMENT: delegate = new SimpleStatementHandler (executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED: delegate = new PreparedStatementHandler (executor, ms, parameter, rowBounds, resultHandler, boundSql); break Case CALLABLE: delegate = new CallableStatementHandler (executor, ms, parameter, rowBounds, resultHandler, boundSql); break; default: throw new ExecutorException ("Unknown statement type:" + ms.getStatementType ());}} @ Override public Statement prepare (Connection connection) throws SQLException {return delegate.prepare (connection);}}

Org.apache.ibatis.executor.statement.PreparedStatementHandler does not have a specific implementation of prepare, but calls the implementation of its parent class, org.apache.ibatis.executor.statement.BaseStatementHandler:

@ Override public Statement prepare (Connection connection) throws SQLException {ErrorContext.instance (). Sql (boundSql.getSql ()); Statement statement = null; try {statement = instantiateStatement (connection); setStatementTimeout (statement); setFetchSize (statement); return statement;} catch (SQLException e) {closeStatement (statement); throw e;} catch (Exception e) {closeStatement (statement); throw new ExecutorException ("Error preparing statement. Cause: "+ e, e);}}

Handler.parameterize can see what's going on:

Handler.parameterize sets the parameters by calling setParameters of ParameterHandler. ParameterHandler is created with the creation of StatementHandler. The default implementation is DefaultParameterHandler:

/ / = package org.apache.ibatis.executor.statement;/** * @ author Clinton Begin * / public class PreparedStatementHandler extends BaseStatementHandler {public PreparedStatementHandler (Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {super (executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);} / Code slightly @ Override public void parameterize (Statement statement) throws SQLException {parameterHandler.setParameters ((PreparedStatement) statement);}} / = package org.apache.ibatis.executor.statement;import java.sql.Connection Import java.sql.SQLException;import java.sql.Statement;import org.apache.ibatis.executor.ErrorContext;import org.apache.ibatis.executor.Executor;import org.apache.ibatis.executor.ExecutorException;import org.apache.ibatis.executor.keygen.KeyGenerator;import org.apache.ibatis.executor.parameter.ParameterHandler;import org.apache.ibatis.executor.resultset.ResultSetHandler;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.reflection.factory.ObjectFactory;import org.apache.ibatis.session.Configuration Import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds;import org.apache.ibatis.type.TypeHandlerRegistry;/** * @ author Clinton Begin * / public abstract class BaseStatementHandler implements StatementHandler {protected final Configuration configuration; protected final ObjectFactory objectFactory; protected final TypeHandlerRegistry typeHandlerRegistry; protected final ResultSetHandler resultSetHandler; protected final ParameterHandler parameterHandler; protected final Executor executor; protected final MappedStatement mappedStatement; protected final RowBounds rowBounds; protected BoundSql boundSql Protected BaseStatementHandler (Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {this.configuration = mappedStatement.getConfiguration (); this.executor = executor; this.mappedStatement = mappedStatement; this.rowBounds = rowBounds; this.typeHandlerRegistry = configuration.getTypeHandlerRegistry (); this.objectFactory = configuration.getObjectFactory (); if (boundSql = = null) {/ / issue # 435, get the key before calculating the statement generateKeys (parameterObject); boundSql = mappedStatement.getBoundSql (parameterObject) } this.boundSql = boundSql; / / defaults to org.apache.ibatis.scripting.defaults.DefaultParameterHandler this.parameterHandler = configuration.newParameterHandler (mappedStatement, parameterObject, boundSql); this.resultSetHandler = configuration.newResultSetHandler (executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);}}

Like Executor and StatementHandler, ParameterHandler can be intercepted.

DefaultParameterHandler

The code for setting parameters in DefaultParameterHandler is as follows:

Package org.apache.ibatis.scripting.defaults;import java.sql.PreparedStatement;import java.sql.SQLException;import java.util.List;import org.apache.ibatis.executor.ErrorContext;import org.apache.ibatis.executor.parameter.ParameterHandler;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.mapping.ParameterMapping;import org.apache.ibatis.mapping.ParameterMode;import org.apache.ibatis.reflection.MetaObject;import org.apache.ibatis.session.Configuration Import org.apache.ibatis.type.JdbcType;import org.apache.ibatis.type.TypeException;import org.apache.ibatis.type.TypeHandler;import org.apache.ibatis.type.TypeHandlerRegistry;/** * @ author Clinton Begin * @ author Eduardo Macarron * / public class DefaultParameterHandler implements ParameterHandler {private final TypeHandlerRegistry typeHandlerRegistry; private final MappedStatement mappedStatement; private final Object parameterObject; private BoundSql boundSql; private Configuration configuration; public DefaultParameterHandler (MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {this.mappedStatement = mappedStatement; this.configuration = mappedStatement.getConfiguration () This.typeHandlerRegistry = mappedStatement.getConfiguration () .getTypeHandlerRegistry (); this.parameterObject = parameterObject; this.boundSql = boundSql;} @ Override public Object getParameterObject () {return parameterObject;} @ Override public void setParameters (PreparedStatement ps) {ErrorContext.instance () .activity ("setting parameters") .object (mappedStatement.getParameterMap () .getId ()); List parameterMappings = boundSql.getParameterMappings (); if (parameterMappings! = null) {for (int I = 0; I)

< parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } catch (SQLException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } } } } }} 这里面最重要的一句其实就是最后一句代码,它的作用是用合适的TypeHandler完成参数的设置。那么什么是合适的TypeHandler呢,它又是如何决断出来的呢?BaseStatementHandler的构造方法里有这么一句: if (boundSql == null) { // issue #435, get the key before calculating the statement generateKeys(parameterObject); boundSql = mappedStatement.getBoundSql(parameterObject); } 它触发了sql 的解析,在解析sql的过程中,TypeHandler也被决断出来了,决断的原则就是根据参数的类型和参数对应的JDBC类型决定使用哪个TypeHandler。比如:参数类型是String的话就用StringTypeHandler,参数类型是整数的话就用IntegerTypeHandler等。 参数设置完毕后,执行数据库操作(update或query)。如果是query最后还有个查询结果的处理过程。 ResultSetHandler 结果处理使用ResultSetHandler来完成,默认的ResultSetHandler是FastResultSetHandler,它在创建StatementHandler时一起创建,代码如下: org.apache.ibatis.executor.statement.BaseStatementHandler this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql); this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql); org.apache.ibatis.session.Configuration public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); return resultSetHandler; } org.apache.ibatis.executor.statement.PreparedStatementHandler @Override public List query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler. handleResultSets(ps); } 可以看出ResultSetHandler也是可以被拦截的,可以编写自己的拦截器改变ResultSetHandler的默认行为。 private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException { List autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix); boolean foundValues = false; if (autoMapping.size() >

0) {for (UnMappedColumAutoMapping mapping: autoMapping) {final Object value = mapping.typeHandler.getResult (rsw.getResultSet (), mapping.column); / / issue # 377, call setter on nulls if (value! = null | | configuration.isCallSettersOnNulls ()) {if (value! = null | |! mapping.primitive) {metaObject.setValue (mapping.property, value);} foundValues = true } return foundValues;}

As you can see from the code, the decision TypeHandler uses the attribute type of the result parameter. Therefore, we must consider compatibility with the database field type when defining the properties of the resulting object.

Attachment: http://down.51cto.com/data/2366409

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Database

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report