package com.hotent.base.transaction;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.mybatis.spring.transaction.SpringManagedTransaction;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import com.hotent.base.util.AppUtil;
import com.hotent.base.util.TransactionThreadLocal;

/**
 * 自定义spring 管理事务工厂
 * 
 * @author jason
 *
 */
@Component
public class CustomSpringManagedTransactionFactory extends SpringManagedTransactionFactory {

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
		// 使用代理类 用于修改connection
		Transaction transaction = new SpringManagedTransaction(dataSource);
		return (Transaction) Proxy.newProxyInstance(transaction.getClass().getClassLoader(),
				SpringManagedTransaction.class.getInterfaces(), new SpringManagedTransactionProxy(transaction));

	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Transaction newTransaction(Connection conn) {
		throw new UnsupportedOperationException("New Spring transactions require a DataSource");
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setProperties(Properties props) {
		// not needed in this version
	}

}

/**
 * 增强getConnection 方法 切换数据源后从当前线程TransactionThreadLocal中获取connection,没有获取到还是按照原来的逻辑获取
 * 
 * @author jason
 *
 */
class SpringManagedTransactionProxy implements InvocationHandler {
	private static final String GET_CONNECTION = "getConnection";
	
	Transaction transaction;

	public SpringManagedTransactionProxy(Transaction transaction) {
		this.transaction = transaction;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		String methodName = method.getName();
		if (methodName.equals(GET_CONNECTION)) {
			// 如果当前线程中有切换数据源需要使用的connection对象则使用该对象进行数据库操作
			Connection connection = null;
			TransactionThreadLocal transactionThreadLocal = getTransactionThreadLocal();
			if (transactionThreadLocal != null) {
				connection = transactionThreadLocal.getCurrentConnection();
			}
			if (connection != null) {
				return connection;
			}
		}
		Object invoke = method.invoke(transaction, args);
		return invoke;
	}

	private TransactionThreadLocal getTransactionThreadLocal() {
		ApplicationContext applicaitonContext = AppUtil.getApplicaitonContext();
		if (applicaitonContext != null && applicaitonContext.containsBean("transactionThreadLocal")) {
			return applicaitonContext.getBean(TransactionThreadLocal.class);
		}
		return null;
	}

}