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

Example Analysis of mybatis Core process

2025-03-01 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

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

This article will explain in detail the example analysis of the core process of mybatis. The editor thinks it is very practical, so I share it for you as a reference. I hope you can get something after reading this article.

1. Examples

Let's write an example first. The first step is to configure a resource file, app.properties, and configure some properties, such as environment variables.

# Environment configuration env=local

Then configure mybatis-config.xml, which is the configuration file of mybatis and various configuration information for configuring mybatis, including: attribute properties, global setting settings, alias typeAliases, environment environments, mapping mappers:

Then configure the mapping file AutoConstructorMapper.xml, which is where the SQL is written:

SELECT * FROM subject WHERE id = # {id}

Then the basic POJO and mapper interfaces are given:

Public class PrimitiveSubject implements Serializable {private final int id; private final String name; private final int age; private final int height; private final int weight; private final boolean active; private final Date dt; public PrimitiveSubject (final int id, final String name, final int age, final int height, final int weight, final boolean active, final Date dt) {this.id = id; this.name = name; this.age = age; this.height = height; this.weight = weight; this.active = active This.dt = dt;} @ Override public String toString () {return "PrimitiveSubject {hashcode=" + this.hashCode () + ", id=" + id + ", name='" + name +'\'+ ", age=" + age + ", height=" + height + ", weight=" + weight + ", active=" + active + ", dt=" + dt +'}';}} / * * mapper interface * / public interface AutoConstructorMapper {PrimitiveSubject selectOneById (int id);}

Initialize SQL data CreateDB.sql

DROP TABLE subjectIF EXISTS;DROP TABLE extensive_subjectIF EXISTS;CREATE TABLE subject (id INT NOT NULL, name VARCHAR (20), age INT NOT NULL, height INT, weight INT, active BIT, dt TIMESTAMP); INSERT INTO subject VALUES (1,100,45,1, CURRENT_TIMESTAMP), (2, 'baked, 10, NULL, 45,1, CURRENT_TIMESTAMP), (2,' cations, 10, NULL, NULL, 0, CURRENT_TIMESTAMP)

Finally, write a test class, which initializes SqlSessionFactory and installs an in-memory database; it opens a SqlSession through sqlSessionFactory, then gets the AutoConstructorMapper object, and executes its selectOneById method:

Class AutoConstructorTest {private static SqlSessionFactory sqlSessionFactory; @ BeforeAll static void setUp () throws Exception {/ / create a SqlSessionFactory try (Reader reader = Resources .getResourceAsReader ("org/apache/ibatis/autoconstructor/mybatis-config.xml")) {sqlSessionFactory = new SqlSessionFactoryBuilder () .build (reader) } / / populate in-memory database BaseDataTest.runScript (sqlSessionFactory.getConfiguration (). GetEnvironment (). GetDataSource (), "org/apache/ibatis/autoconstructor/CreateDB.sql");} @ Test void selectOneById () {try (SqlSession sqlSession = sqlSessionFactory.openSession ()) {/ / Test Environment Environment environment = sqlSessionFactory.getConfiguration () .getEnvironment (); System.out.println ("environment =" + environment.getId ()) Final AutoConstructorMapper mapper = sqlSession.getMapper (AutoConstructorMapper.class); PrimitiveSubject ps1 = mapper.selectOneById (1); System.out.println ("ps1 =" + ps1);}

In this way, a simple example is written. Let's go into the source code of mybatis and explore its internal process mechanism.

two。 Source code analysis

We divide its source code analysis into the following processes:

Parse the mybatis-config.xml file and build the information flow of Configuration configuration class

Parsing mapper.xml for building cache, mapping declaration and other processes

Create a SqlSession process

Get the mapper interface through SqlSession to execute the target method flow

Let's officially start parsing the source code.

2.1Parse the process of building Configuration configuration class with mybatis-config.xml

This process is reflected in the unit test class code in the above example, and the specific code is as follows:

SqlSessionFactory sqlSessionFactory;//... Omit. Try (Reader reader = Resources. GetResourceAsReader ("org/apache/ibatis/autoconstructor/mybatis-config.xml")) {sqlSessionFactory = new SqlSessionFactoryBuilder (). Build (reader);} / /. Omit.

The above logic is to load the mybatis-config.xml file into an input stream, then create a SqlSessionFactoryBuilder object to build a SqlSessionFactory instance that has a very long life cycle and shuts down as the application shuts down.

Let's take a look at its source code:

Public class SqlSessionFactoryBuilder {public SqlSessionFactory build (Reader reader) {return build (reader, null, null);} /. Omit extraneous methods. Public SqlSessionFactory build (Reader reader, String environment, Properties properties) {try {/ / create a XMLConfigBuilder to parse into a Configuration instance XMLConfigBuilder parser = new XMLConfigBuilder (reader, environment, properties); return build (parser.parse ());} catch (Exception e) {throw ExceptionFactory.wrapException ("Error building SqlSession.", e);} finally {ErrorContext.instance (). Reset () Try {reader.close ();} catch (IOException e) {/ / Intentionally ignore. Prefer previous error. } / /... Omit the extraneous method. / * build a SQLsession factory * @ param config * @ return * / public SqlSessionFactory build (Configuration config) {/ / create a default SQLsession factory return new DefaultSqlSessionFactory (config);}}

As you can see, the above code logic is mainly about creating an object of type XMLConfigBuilder. Let's take a look at its constructor.

Public XMLConfigBuilder (Reader reader, String environment, Properties props) {this (new XPathParser (reader, true, props, new XMLMapperEntityResolver ()), environment, props);} private XMLConfigBuilder (XPathParser parser, String environment, Properties props) {super (new Configuration ()); ErrorContext.instance (). Resource ("SQL Mapper Configuration"); this.configuration.setVariables (props); this.parsed = false; this.environment = environment; this.parser = parser;}

It is found that it creates a Configuration object associated with the parent class. Take a look at Configuration's constructor:

Public Configuration () {/ / configure aliases for various basic classes / / transaction manager typeAliasRegistry.registerAlias ("JDBC", JdbcTransactionFactory.class); typeAliasRegistry.registerAlias ("MANAGED", ManagedTransactionFactory.class); / / data source factory typeAliasRegistry.registerAlias ("JNDI", JndiDataSourceFactory.class); typeAliasRegistry.registerAlias ("POOLED", PooledDataSourceFactory.class); typeAliasRegistry.registerAlias ("UNPOOLED", UnpooledDataSourceFactory.class) / cache class aliases typeAliasRegistry.registerAlias ("PERPETUAL", PerpetualCache.class); typeAliasRegistry.registerAlias ("FIFO", FifoCache.class); typeAliasRegistry.registerAlias ("LRU", LruCache.class); typeAliasRegistry.registerAlias ("SOFT", SoftCache.class); typeAliasRegistry.registerAlias ("WEAK", WeakCache.class); typeAliasRegistry.registerAlias ("DB_VENDOR", VendorDatabaseIdProvider.class); typeAliasRegistry.registerAlias ("XML", XMLLanguageDriver.class); typeAliasRegistry.registerAlias ("RAW", RawLanguageDriver.class) / / log category name typeAliasRegistry.registerAlias ("SLF4J", Slf4jImpl.class); typeAliasRegistry.registerAlias ("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class); typeAliasRegistry.registerAlias ("LOG4J", Log4jImpl.class); typeAliasRegistry.registerAlias ("LOG4J2", Log4j2Impl.class); typeAliasRegistry.registerAlias ("JDK_LOGGING", Jdk14LoggingImpl.class); typeAliasRegistry.registerAlias ("STDOUT_LOGGING", StdOutImpl.class); typeAliasRegistry.registerAlias ("NO_LOGGING", NoLoggingImpl.class) / / dynamic proxy alias typeAliasRegistry.registerAlias ("CGLIB", CglibProxyFactory.class); typeAliasRegistry.registerAlias ("JAVASSIST", JavassistProxyFactory.class); / / xml script parser languageRegistry.setDefaultDriverClass (XMLLanguageDriver.class); languageRegistry.register (RawLanguageDriver.class);}

As you can see, some aliases of the underlying configured classes are registered in its constructor methods, which are typically property values used in the xml configuration file, and the corresponding actual types are parsed later based on the aliases.

Go back to XMLConfigBuilder's parsing method parse () method, which parses the xml file of mybatis into a Configuration type, and finally creates a DefaultSqlSessionFactory type to return. Org.apache.ibatis.builder.xml.XMLConfigBuilder#parse:

Public Configuration parse () {if (parsed) {throw new BuilderException ("Each XMLConfigBuilder can only be used once.");} parsed = true; / / parse parseConfiguration (parser.evalNode ("/ configuration")); return configuration;} private void parseConfiguration (XNode root) {try {/ / parse properties attribute / / issue # 117 read properties first propertiesElement (root.evalNode ("properties")) / / parsing settings setting Properties settings = settingsAsProperties (root.evalNode ("settings")); loadCustomVfs (settings); / / parsing custom log loadCustomLogImpl (settings); / / parsing type aliases typeAliasesElement (root.evalNode ("typeAliases")); / / parsing plug-in pluginElement (root.evalNode ("plugins")) / / parse object factory objectFactoryElement (root.evalNode ("objectFactory")); / / parse object wrapper factory objectWrapperFactoryElement (root.evalNode ("objectWrapperFactory")); / / parse reflector factory reflectorFactoryElement (root.evalNode ("reflectorFactory")); / / set configuration element settingsElement (settings) / / read it after objectFactory and objectWrapperFactory issue # 631 / / parsing environment environmentsElement (root.evalNode ("environments")); / / parsing database ID provider databaseIdProviderElement (root.evalNode ("databaseIdProvider")); / / parsing type processor typeHandlerElement (root.evalNode ("typeHandlers")); / / parsing mapping file mapperElement (root.evalNode ("mappers")) } catch (Exception e) {throw new BuilderException ("Error parsing SQL Mapper Configuration. Cause: "+ e, e);}}

The above code is also easy to understand, mainly for parsing each tag element in the mybatis-config.xml file:

Parsing properties attribute configuration

Parsing setting attribute configuration

Parsing typeAliases type alias configuration

Parsing plug-in plugins configuration

Parsing objectFactory object factory configuration

Parsing objectWrapperFactory object wrapper factory configuration

Parsing reflectorFactory reflection factory configuration

Parsing environments environment configuration

Parsing databaseIdProvider database ID provider configuration

Parsing typeHandlers type processor configuration

Parse the mappers mapping file configuration.

Let's take a look at its implementation, org.apache.ibatis.session.Configuration#newStatementHandler:

Public StatementHandler newStatementHandler (Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {/ / create a declaration processor for RoutingStatementHandler routing StatementHandler statementHandler = new RoutingStatementHandler (executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); / / a pair of StatementHandler application plug-ins statementHandler = (StatementHandler) interceptorChain.pluginAll (statementHandler); return statementHandler;}

Its logic:

Create a declaration processor for RoutingStatementHandler routing

Apply plug-ins to StatementHandler

Return to statementHandler.

Take a look at the RoutingStatementHandler class:

Public class RoutingStatementHandler implements StatementHandler {/ * associate a real RoutingStatementHandler * / 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, Integer transactionTimeout) throws SQLException {return delegate.prepare (connection, transactionTimeout);} @ Override public void parameterize (Statement statement) throws SQLException {delegate.parameterize (statement) } @ Override public void batch (Statement statement) throws SQLException {delegate.batch (statement);} @ Override public int update (Statement statement) throws SQLException {return delegate.update (statement);} @ Override public List query (Statement statement, ResultHandler resultHandler) throws SQLException {return delegate.query (statement, resultHandler);} @ Override public Cursor queryCursor (Statement statement) throws SQLException {return delegate.queryCursor (statement);} @ Override public BoundSql getBoundSql () {return delegate.getBoundSql () @ Override public ParameterHandler getParameterHandler () {return delegate.getParameterHandler ();}}

As you can see, this class implements the StatementHandler interface, and obtains the StatementType according to MappedStatement, and creates the corresponding StatementHandler, such as SimpleStatementHandler, PreparedStatementHandler, and CallableStatementHandler. By default, an instance of PreparedStatementHandler is created.

Its other methods are executed using delegated StatementHandler instances, such as the prepare (), parameterize (), batch (), update (), query (), queryCursor (), getBoundSql (), getParameterHandler () methods.

2.3.3.2.2 PreparedStatementHandler

Let's take a look at the actual PreparedStatementHandler class:

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);} @ Override public int update (Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement; / / execute update ps.execute (); / / get the number of rows int rows = ps.getUpdateCount () / / get the parameter object Object parameterObject = boundSql.getParameterObject (); / / get the key generator KeyGenerator keyGenerator = mappedStatement.getKeyGenerator (); / / post the processor key. For example, for the insert statement, the primary key after insertion is set to the parameter object. KeyGenerator.processAfter (executor, mappedStatement, ps, parameterObject); return rows;} @ Override public void batch (Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement; / / batch query ps.addBatch ();} @ Override public List query (Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement; / / execute query ps.execute (); / / process the result return resultSetHandler.handleResultSets (ps) through the result set processor } @ Override public Cursor queryCursor (Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement; / / execute query ps.execute (); / / result set processor processes data return resultSetHandler.handleCursorResultSets (ps);} / * initialize a Statement * * @ param connection * @ return * @ throws SQLException * / @ Override protected Statement instantiateStatement (Connection connection) throws SQLException {String sql = boundSql.getSql () If (mappedStatement.getKeyGenerator () instanceof Jdbc3KeyGenerator) {String [] keyColumnNames = mappedStatement.getKeyColumns (); if (keyColumnNames = = null) {return connection.prepareStatement (sql, PreparedStatement.RETURN_GENERATED_KEYS);} else {return connection.prepareStatement (sql, keyColumnNames);}} else if (mappedStatement.getResultSetType () = = ResultSetType.DEFAULT) {/ / return connection.prepareStatement (sql) } else {return connection.prepareStatement (sql, mappedStatement.getResultSetType (). GetValue (), ResultSet.CONCUR_READ_ONLY);} @ Override public void parameterize (Statement statement) throws SQLException {/ / use parameterized objects to set parameters parameterHandler.setParameters ((PreparedStatement) statement);}}

This class is the class that actually executes the target SQL logic, and some of its method logic:

In the update () method, SQL is executed through PreparedStatement, and then the parameter object and key generator are obtained, and the parameters are postprocessed.

In the query () and queryCursor () methods, the SQL is executed through PreparedStatement, and then the result is processed by the result set processor

2.3.4 CachingExecutor cache executor

Then it's time to look at the CachingExecutor class:

/ * cache executor, decorator mode, declaration period is a session * * @ author Clinton Begin * @ author Eduardo Macarron * / public class CachingExecutor implements Executor {/ * delegated executor * / private final Executor delegate; / * transaction cache manager * / private final TransactionalCacheManager tcm = new TransactionalCacheManager (); public CachingExecutor (Executor delegate) {this.delegate = delegate; delegate.setExecutorWrapper (this) } @ Override public Transaction getTransaction () {return delegate.getTransaction ();} @ Override public void close (boolean forceRollback) {try {/ / issues # 499,# 524 and # 573if (forceRollback) {tcm.rollback ();} else {tcm.commit ();}} finally {delegate.close (forceRollback);}} @ Override public boolean isClosed () {return delegate.isClosed () } @ Override public int update (MappedStatement ms, Object parameterObject) throws SQLException {flushCacheIfRequired (ms); return delegate.update (ms, parameterObject);} @ Override public Cursor queryCursor (MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {flushCacheIfRequired (ms); return delegate.queryCursor (ms, parameter, rowBounds);} @ Override public List query (MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {/ / bind SQL BoundSql boundSql = ms.getBoundSql (parameterObject) / / build cache key CacheKey key = createCacheKey (ms, parameterObject, rowBounds, boundSql); return query (ms, parameterObject, rowBounds, resultHandler, key, boundSql);} @ Override public List query (MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {/ / get the secondary cache configuration, which is Cache cache = ms.getCache () derived from parsing the @ CacheNamespace annotations of mapper.xml and mapper interfaces If (cache! = null) {/ / need to refresh cache flushCacheIfRequired (ms); if (ms.isUseCache () & & resultHandler = = null) {ensureNoOutParams (ms, boundSql); @ SuppressWarnings ("unchecked") List list = (List) tcm.getObject (cache, key); if (list = = null) {list = delegate.query (ms, parameterObject, rowBounds, resultHandler, key, boundSql) / / cache manager, put the cache tcm.putObject (cache, key, list); / / issue # 578 and # 116} return list;}} / / delegate the actual BaseExecutor type query return delegate.query (ms, parameterObject, rowBounds, resultHandler, key, boundSql);} @ Override public List flushStatements () throws SQLException {return delegate.flushStatements () } @ Override public void commit (boolean required) throws SQLException {/ / commit transaction delegate.commit (required); / / transaction cache manager commit tcm.commit ();} @ Override public void rollback (boolean required) throws SQLException {try {delegate.rollback (required);} finally {if (required) {tcm.rollback () } private void ensureNoOutParams (MappedStatement ms, BoundSql boundSql) {if (ms.getStatementType () = = StatementType.CALLABLE) {for (ParameterMapping parameterMapping: boundSql.getParameterMappings ()) {if (parameterMapping.getMode ()! = ParameterMode.IN) {throw new ExecutorException ("Caching stored procedures with OUT params is not supported. Please configure useCache=false in "+ ms.getId () +" statement. ");} @ Override public CacheKey createCacheKey (MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {return delegate.createCacheKey (ms, parameterObject, rowBounds, boundSql);} @ Override public boolean isCached (MappedStatement ms, CacheKey key) {return delegate.isCached (ms, key) } @ Override public void deferLoad (MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class targetType) {delegate.deferLoad (ms, resultObject, property, key, targetType);} @ Override public void clearLocalCache () {delegate.clearLocalCache ();} private void flushCacheIfRequired (MappedStatement ms) {Cache cache = ms.getCache (); if (cache! = null & & ms.isFlushCacheRequired ()) {/ / clear the secondary cache tcm.clear (cache) before query } @ Override public void setExecutorWrapper (Executor executor) {throw new UnsupportedOperationException ("This method should not be called");}}

This class is an Executor decorator class that mainly provides secondary caching capabilities. It processes the secondary cache when querying data, updating data, committing, and rolling back.

Its query data logic:

Build a cache key of type CacheKey

Get second-level cache Cache from MappedStatement

If the cache is empty, the actual delegated executor executes the query data

If the cache is not empty, first determine whether the cache needs to be refreshed, and if so, clear the cache through TransactionalCacheManager; then get the secondary cache data corresponding to key from the TransactionalCacheManager object, and return the cache data directly if the cache data is not empty, otherwise, continue to execute the actual delegated executor query data, and then cache the data to the secondary cache.

Finally, the data is returned.

2.3.4.1 TransactionalCacheManager transaction Cache Manager

Take a look at the implementation of TransactionalCacheManager:

Public class TransactionalCacheManager {private final Map transactionalCaches = new HashMap (); public void clear (Cache cache) {getTransactionalCache (cache). Clear ();} public Object getObject (Cache cache, CacheKey key) {return getTransactionalCache (cache) .getObject (key);} public void putObject (Cache cache, CacheKey key, Object value) {/ / get the TransactionalCache corresponding to cache, and then store key and value in getTransactionalCache (cache) .putObject (key, value) } public void commit () {/ / traverse transaction cache for (TransactionalCache txCache: transactionalCaches.values ()) {/ / commit transaction txCache.commit ();}} public void rollback () {for (TransactionalCache txCache: transactionalCaches.values ()) {txCache.rollback () } private TransactionalCache getTransactionalCache (Cache cache) {/ / if the cache key in transactionalCaches does not have corresponding data, create a TransactionalCache object / / pass the cache object as a parameter of the TransactionalCache constructor to return transactionalCaches.computeIfAbsent (cache, TransactionalCache::new);}}

This class holds a HashMap type attribute of type key of type Cache and value of type TransactionalCache to hold the transaction cache data.

In its getTransactionalCache () method, the parameter cache is an externally passed secondary cache. When transactionalCaches does not have a corresponding value for this cache, a TransactionalCache class is created and cache is passed as an argument to its constructor to save it.

Its structure is as follows:

TransactionalCacheManager calls the putObject () method of TransactionalCache when saving the cached data, and the commit () and rollback () methods of TransactionalCache when committing and rolling back the transaction.

2.3.4.2 TransactionalCache transaction cache

Let's take a closer look at this category. Remember the cache decorator we talked about in 2.2.2 above? Yes, here is another cache decorator TransactionalCache, which is implemented as follows:

/ * The 2nd level cache transactional buffer. *

* This class holds all cache entries that are to be added to the 2nd level cache during a Session. * Entries are sent to the cache when commit is called or discarded if the Session is rolled back. * Blocking cache support has been added. Therefore any get () that returns a cache miss * will be followed by a put () so any lock associated with the key can be released. * * @ author Clinton Begin * @ author Eduardo Macarron * / public class TransactionalCache implements Cache {private static final Log log = LogFactory.getLog (TransactionalCache.class); private final Cache delegate; private boolean clearOnCommit; / * * cached data saved before the transaction is committed * / private final Map entriesToAddOnCommit; / * cached data missed before the transaction is committed * / private final Set entriesMissedInCache; public TransactionalCache (Cache delegate) {this.delegate = delegate This.clearOnCommit = false; this.entriesToAddOnCommit = new HashMap (); this.entriesMissedInCache = new HashSet ();} @ Override public String getId () {return delegate.getId ();} @ Override public int getSize () {return delegate.getSize ();} @ Override public Object getObject (Object key) {/ / issue # 116 Object object = delegate.getObject (key); if (object = = null) {entriesMissedInCache.add (key) } / / issue # 146if (clearOnCommit) {return null;} else {return object;}} @ Override public void putObject (Object key, Object object) {/ / temporarily store the data first entriesToAddOnCommit.put (key, object);} @ Override public Object removeObject (Object key) {return null;} @ Override public void clear () {clearOnCommit = true; entriesToAddOnCommit.clear () } public void commit () {if (clearOnCommit) {/ / Clean the secondary cache delegate.clear ();} / / refresh the queried data to save to the secondary cache flushPendingEntries (); reset ();} public void rollback () {/ / resolve missed data on rollback unlockMissedEntries (); reset () } private void reset () {clearOnCommit = false; entriesToAddOnCommit.clear (); entriesMissedInCache.clear ();} private void flushPendingEntries () {/ / when submitting, the temporarily saved data is actually put into the secondary cache for (Map.Entry entry: entriesToAddOnCommit.entrySet ()) {delegate.putObject (entry.getKey (), entry.getValue ()) } for (Object entry: entriesMissedInCache) {if (! entriesToAddOnCommit.containsKey (entry)) {delegate.putObject (entry, null);} private void unlockMissedEntries () {/ / remove missed data for (Object entry: entriesMissedInCache) {try {delegate.removeObject (entry);} catch (Exception e) {log.warn ("Unexpected exception while notifiying a rollback to the cache adapter. "+" Consider upgrading your cache adapter to the latest version. Cause: "+ e);}

This class also has a cache that holds an actual delegate, which by default is the secondary cache decorated by SynchronizedCache as we talked about in Section 2.2.2.

This class also has two properties: Map entriesToAddOnCommit and Set entriesMissedInCache, which temporarily store cached data before the session transaction is committed, synchronize the cache to the secondary cache when the real transaction commits commit (), and clear the missed cache when rolling back rollback (), and so on.

By breaking the point in its getObject () method, we can get the conclusion shown below. It is a cache decorator, layer by layer of packaging.

Note that the declaration cycle of TransactionalCache is not the same as the delegated secondary cache, it is the same as the declaration of a SqlSession. The delegated secondary cache is the same as the life cycle of the application.

2.3.5 Application plug-in interceptorChain.pluginAll ()

Let's take a look at the logical interceptorChain.pluginAll (executor) that applies the plug-in to the actuator:

Public class InterceptorChain {private final List interceptors = new ArrayList (); public Object pluginAll (Object target) {for (Interceptor interceptor: interceptors) {target = interceptor.plugin (target);} return target;} public void addInterceptor (Interceptor interceptor) {interceptors.add (interceptor);} public List getInterceptors () {return Collections.unmodifiableList (interceptors);}}

Here we will iterate through all the interceptor classes that implement the Interceptor interface and call their plugin () method to intercept the target class. In fact, there are four places for interceptor calls:

They are:

Interception when creating ParameterHandler parameter handlers

Create the interception of the ResultSetHandler result set processor

Create an intercept for StatementHandler

Create an intercept for Executor.

We can implement our own interceptor and make intercept calls for these four types according to our own needs. For example, it can intercept the ParameterHandler type, realize automatic query and add paging SQL, and so on.

2.3.5 create DefaultSqlSession

The final step is to create an instance of DefaultSqlSession based on the Executor and Configuration that have been created.

Public class DefaultSqlSession implements SqlSession {private final Configuration configuration; private final Executor executor; private final boolean autoCommit; private boolean dirty; private List

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

Internet Technology

Wechat

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

12
Report