package cn.hmsoft.application.config;

import java.util.Properties;

import javax.sql.DataSource;

import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.support.RegexpMethodPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.springframework.util.StringUtils;

import com.alibaba.druid.pool.DruidDataSource;

import cn.hmsoft.jdbc.core.JdbcProperty;
import cn.hmsoft.jdbc.entity.DatabaseType;
import cn.hmsoft.jdbc.generator.ISQLGenerator;
import cn.hmsoft.jdbc.generator.MySqlSQLGenerator;
import cn.hmsoft.jdbc.generator.OracleSQLGenerator;
import cn.hmsoft.jdbc.generator.SQLServerSQLGenerator;
import cn.hmsoft.jdbc.generator.SqliteSQLGenerator;

/************************
 * 数据库连接
 * 
 * @author zxq
 * @create 2019-05-04 23:33:36
 * @version 2.0.0
 * @email: revisit@126.com
 * @Company: www.hmsoft.cn
 */
@Configuration // 标记为spring配置文件
@EnableAspectJAutoProxy // 标记为允许自动注入
@EnableTransactionManagement // 标记为允许事务自动管理
public class JdbcConfig {

	@Bean(destroyMethod = "close", initMethod = "init", name = "dataSource")
	public DataSource getDataSource() {
		DruidDataSource ds = new DruidDataSource();
		// 连接数据库的url，不同数据库不一样。例如：
		// mysql : jdbc:mysql://10.20.153.104:3306/druid2
		// oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
		ds.setUrl(JdbcProperty.JdbcUrl);
		// 连接数据库的用户名
		ds.setUsername(JdbcProperty.JdbcUser);
		ds.setPassword(JdbcProperty.JdbcPassword);
		// 驱动名称，应该能自动判断，为了安全还是需要设置
		if (!StringUtils.isEmpty(JdbcProperty.JdbcDriverClass))
			ds.setDriverClassName(JdbcProperty.JdbcDriverClass);
		// 初始化时建立物理连接的个数。初始化发生在显示调用init方法，或者第一次getConnection时
		ds.setInitialSize(JdbcProperty.JdbcInitialpoolsize);
		// 最大连接池数量
		ds.setMaxActive(JdbcProperty.JdbcMaxpoolsize);
		// 最小连接池数量
		ds.setMinIdle(JdbcProperty.JdbcMinpoolsize);
		// 获取连接时最大等待时间，单位毫秒。配置了maxWait之后，缺省启用公平锁，并发效率会有所下降，如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
		ds.setMaxWait(JdbcProperty.JdbcAcquireertydelay);
		ds.setUseUnfairLock(false);
		// 是否缓存preparedStatement，也就是PSCache。PSCache对支持游标的数据库性能提升巨大，比如说oracle。在mysql下建议关闭。
		if (DatabaseType.Oracle == JdbcProperty.DATABAETYPE)
			ds.setPoolPreparedStatements(true);
		else
			ds.setPoolPreparedStatements(false);
		// 要启用PSCache，必须配置大于0，当大于0时，poolPreparedStatements自动触发修改为true。在Druid中，不会存在Oracle下PSCache占用内存过多的问题，可以把这个数值配置大一些，比如说100
		ds.setMaxOpenPreparedStatements(JdbcProperty.JdbcMaxstatments);
		// 用来检测连接是否有效的sql，要求是一个查询语句。如果validationQuery为null，testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
		ds.setValidationQuery(null);
		// 单位：秒，检测连接是否有效的超时时间。底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法
		ds.setValidationQueryTimeout(JdbcProperty.JdbcCheckouttimeout);
		// 申请连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。
		ds.setTestOnBorrow(false);
		// 归还连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能
		ds.setTestOnReturn(false);
		// 建议配置为true，不影响性能，并且保证安全性。申请连接的时候检测，如果空闲时间大于timeBetweenEvictionRunsMillis，执行validationQuery检测连接是否有效。
		ds.setTestWhileIdle(true);

		return ds;
	}

	@Bean(name = "jdbcTemplate")
	public JdbcTemplate getJdbcTemplate() {
		JdbcTemplate jdbc = new JdbcTemplate();
		jdbc.setDataSource(this.getDataSource());
		return jdbc;
	}

	/******************************
	 * 定义事务
	 */
	@Bean(name = "transactionManager")
	public DataSourceTransactionManager getDataSourceTransactionManager() {
		DataSourceTransactionManager ds = new DataSourceTransactionManager();
		ds.setDataSource(this.getDataSource());
		return ds;
	}

	/******************************
	 * 定义sql生成类
	 */
	@Bean(name = "sqlGenerator")
	public ISQLGenerator getSqlGenerator() {
		ISQLGenerator sqlGenerator = null;
		if (JdbcProperty.DATABAETYPE == DatabaseType.Oracle)
			sqlGenerator = new OracleSQLGenerator();
		else if (JdbcProperty.DATABAETYPE == DatabaseType.MySql)
			sqlGenerator = new MySqlSQLGenerator();
		else if (JdbcProperty.DATABAETYPE == DatabaseType.Sqlite)
			sqlGenerator = new SqliteSQLGenerator();
		else if (JdbcProperty.DATABAETYPE == DatabaseType.SQLServer)
			sqlGenerator = new SQLServerSQLGenerator();
		else
			sqlGenerator = new OracleSQLGenerator();
		return sqlGenerator;
	}

	/*********************************
	 * Oracle Blob定义
	 */
	@Bean
	public DefaultLobHandler getDefaultLobHandler() {
		return new DefaultLobHandler();
	}

	/******************************
	 * 定义事务切片
	 */
	@Bean
	public TransactionInterceptor getTransactionInterceptor() {
		TransactionInterceptor tv = new TransactionInterceptor();
		tv.setTransactionManager(this.getDataSourceTransactionManager());
		Properties prop = new Properties();
		prop.put("find*", "PROPAGATION_REQUIRED,readOnly");
		prop.put("get*", "PROPAGATION_REQUIRED,readOnly");
		prop.put("query*", "PROPAGATION_REQUIRED,readOnly");
		prop.put("*", "PROPAGATION_REQUIRED,-Exception");
		tv.setTransactionAttributes(prop);
		return tv;
	}

	/******************************
	 * 将以Service的类全部设置为事务模式
	 */
	@Bean
	public PointcutAdvisor getPointcutAdvisor() {
		PointcutAdvisor advisor = new RegexpMethodPointcutAdvisor("cn.hmsoft..*Service.*(..)",
				this.getTransactionInterceptor());
		return advisor;
	}
}
