All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.viiyue.plugins.mybatis.scripting.parser.MappedStatementRegistry Maven / Gradle / Ivy

Go to download

Mybatis generic mapper plugin for solving most basic operations, simplifying sql syntax and improving dynamic execution efficiency

There is a newer version: 1.3.7
Show newest version
/**
 * Copyright (C) 2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.viiyue.plugins.mybatis.scripting.parser;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;

import com.viiyue.plugins.mybatis.annotation.mark.EnableResultMap;
import com.viiyue.plugins.mybatis.metadata.Entity;
import com.viiyue.plugins.mybatis.metadata.EntityParser;
import com.viiyue.plugins.mybatis.metadata.info.GeneratedKeyInfo;
import com.viiyue.plugins.mybatis.utils.LoggerUtil;
import com.viiyue.plugins.mybatis.utils.StatementUtil;

/**
 * Provide some methods for refactoring MappedStatement
 *
 * @author tangxbai
 * @since 1.1.0
 * @see DynamicProviderRegistry
 */
public final class MappedStatementRegistry {
	
	private MappedStatementRegistry() {}

	private static boolean isRecycled = false;
	private DynamicProviderRegistry providerRegistry = new DynamicProviderRegistry();
	private Set> registered = new HashSet>();
	private Map, Map> interfaceMethodMappings = new HashMap, Map>();
	
	/**
	 * MappedStatementRegistry singleton mode
	 */
	private static class Holder {
		private static final MappedStatementRegistry registry = new MappedStatementRegistry();
	}
	
	/**
	 * Return a single instance object of {@link MappedStatementRegistry}
	 * 
	 * @return the MappedStatementRegistry instance
	 */
	public static final MappedStatementRegistry instance() {
		return Holder.registry;
	}
	
	/**
	 * Recycling resources and properly assisting the GC in garbage collection
	 */
	public void recycle() {
		statusCheck();
		isRecycled = true;
		providerRegistry.recycle();
		interfaceMethodMappings.clear();
		providerRegistry = null;
		registered = null;
		interfaceMethodMappings = null;
	}
	
	/**
	 * Register the mapper interface and scan it for initialization
	 * 
	 * @param configuration the mybatis configuration
	 * @param mapperInterface the mapper interface type
	 * @param modelBeanType model bean type in mapper generics
	 * @param returnBeanType mapper query method returns bean type
	 */
	public void registerMapper( 
		Configuration configuration, 
		Class mapperInterface, 
		Class modelBeanType, 
		Class returnBeanType ) {
		statusCheck();
		if ( registered.contains( mapperInterface ) ) {
			return;
		}
		
		// Map the mapper method to the provider method
		// and cache it for direct query later
		providerRegistry.scanning( mapperInterface );
		
		// Register all methods of the mapper interface to quickly and directly
		// locate a method
		registerMapperMethod( mapperInterface );
		
		// Initialize the parsing object of the model bean
		// The first time the data is loaded will be cached, 
		// and the result will be obtained directly after the call.
		EntityParser.getEntity( modelBeanType );
		
		// If the result map is not loaded, 
		// the result map will be initialized automatically.
		ResultMapRegistry.getResultMap( configuration, mapperInterface, modelBeanType, returnBeanType );
		
		// Record the scanned mapper, which will be printed out by the log after
		// the program is started.
		LoggerUtil.addMapper( mapperInterface );
		registered.add( mapperInterface );
	}
	
	/**
	 * Bind the specific sql source for the annotation sql provider
	 * 
	 * @param ms the {@code MappedStatement} instance
	 * @param msObject the {@code MappedStatement} metadata information
	 * @param mapperInterface the mapper interface type
	 * @param modelBeanType model bean type in mapper generics
	 * @param namespace the mapper namespace
	 * @param methodName the mapper interface method
	 */
	public void bindSqlSource( 
		MappedStatement ms, 
		MetaObject msObject, 
		Class mapperInterface, 
		Class modelBeanType, 
		String namespace, 
		String methodName ) {
		statusCheck();
		SqlSource source = providerRegistry.getSqlSource( ms, mapperInterface, modelBeanType, namespace, methodName );
		if ( source != null ) {
			msObject.setValue( "sqlSource", source );
		}
	}
	
	/**
	 * Binding result mapping for sql source
	 * 
	 * @param ms the {@code MappedStatement} instance
	 * @param msObject the {@code MappedStatement} metadata information
	 * @param mapperInterface the mapper interface type
	 * @param modelBeanType model bean type in mapper generics
	 * @param returnBeanType mapper query method returns bean type
	 * @param namespace the mapper namespace
	 * @param methodName the mapper interface method
	 */
	public void bindResultMap( 
		MappedStatement ms, 
		MetaObject msObject, 
		Class mapperInterface, 
		Class modelBeanType, 
		Class returnBeanType, 
		String namespace, 
		String methodName ) {
		statusCheck();
		if ( StatementUtil.isSelect( ms ) ) {
			Method method = getIntefaceMethod( mapperInterface, methodName );
			if ( method.isAnnotationPresent( EnableResultMap.class ) ) {
				ResultMap resultMap = ResultMapRegistry.getResultMap( ms.getConfiguration(), mapperInterface, modelBeanType, returnBeanType );
				if ( resultMap != null ) {
					msObject.setValue( "resultMaps", Arrays.asList( resultMap ) );
				}
			}
		}
	}
	
	/**
	 * Binding the primary key generator for the sql source
	 * 
	 * @param ms the {@code MappedStatement} instance
	 * @param msObject the {@code MappedStatement} metadata information
	 * @param mapperInterface the mapper interface type
	 * @param modelBeanType model bean type in mapper generics
	 * @param namespace the mapper namespace
	 * @param methodName the mapper interface method
	 */
	public void bindKeyGenerator( 
		MappedStatement ms, 
		MetaObject msObject, 
		Class mapperInterface, 
		Class modelBeanType, 
		String namespace, 
		String methodName ) {
		statusCheck();
		String selectKeyId = ms.getId() + SelectKeyGenerator.SELECT_KEY_SUFFIX;
		Configuration configuration = ms.getConfiguration();
		if ( !configuration.hasKeyGenerator( selectKeyId ) ) {
			Entity entity = EntityParser.getEntity( modelBeanType );
			if ( StatementUtil.isInsert( ms ) && entity.hasGeneratedKeyInfo() ) {
				// Find the mapper method object by method name
				Method method = getIntefaceMethod( mapperInterface, methodName );
				// Generate and register a key generator
				GeneratedKeyInfo generatedKeyInfo = entity.getGeneratedKeyInfo();
				KeyGeneratorRegistry registry = new KeyGeneratorRegistry( ms, entity, selectKeyId );
				KeyGenerator keyGenerator = registry.getKeyGenerator( method );
				configuration.addKeyGenerator( selectKeyId, keyGenerator );
				// Change key generator
				msObject.setValue( "keyGenerator", keyGenerator );
				msObject.setValue( "keyColumns", generatedKeyInfo.getKeyColumns() );
				msObject.setValue( "keyProperties", generatedKeyInfo.getKeyProperties() );
			}
		}
	}
	
	/**
	 * Instance status check
	 */
	public void statusCheck() {
		if ( isRecycled ) {
			throw new IllegalStateException( "The mybatis mapper registry has been recycled and cannot be accessed." );
		}
	}
	
	/**
	 * Get the cached interface method by specified method name
	 * 
	 * @param mapperInterface the mapper interface type
	 * @param methodName mapper method name
	 * @return the found mapper method
	 */
	public Method getIntefaceMethod( Class mapperInterface, String methodName ) {
		statusCheck();
		Map methods = interfaceMethodMappings.get( mapperInterface );
		if ( methods != null ) {
			return methods.get( methodName );
		}
		return null;
	}
	
	/**
	 * Cache all visible methods of mapper, convenient to find specific methods
	 * directly through the interface class and method name
	 * 
	 * @param mapperInterface the mapper interface type
	 */
	private void registerMapperMethod( Class mapperInterface ) {
		if ( interfaceMethodMappings.get( mapperInterface ) == null ) {
			Map methods = new HashMap();
			for ( Method method : mapperInterface.getMethods() ) {
				methods.put( method.getName(), method );
			}
			interfaceMethodMappings.put( mapperInterface, methods );
		}
	}
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy