public Builder(
     Configuration configuration,
     String id,
     SqlSource sqlSource,
     SqlCommandType sqlCommandType) {
   mappedStatement.configuration = configuration;
   mappedStatement.id = id;
   mappedStatement.sqlSource = sqlSource;
   mappedStatement.statementType = StatementType.PREPARED;
   mappedStatement.parameterMap =
       new ParameterMap.Builder(
               configuration, "defaultParameterMap", null, new ArrayList<ParameterMapping>())
           .build();
   mappedStatement.resultMaps = new ArrayList<ResultMap>();
   mappedStatement.timeout = configuration.getDefaultStatementTimeout();
   mappedStatement.sqlCommandType = sqlCommandType;
   mappedStatement.keyGenerator =
       configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)
           ? new Jdbc3KeyGenerator()
           : new NoKeyGenerator();
   String logId = id;
   if (configuration.getLogPrefix() != null) logId = configuration.getLogPrefix() + id;
   mappedStatement.statementLog = LogFactory.getLog(logId);
   mappedStatement.lang = configuration.getDefaultScriptingLanuageInstance();
 }
 @Override
 public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
   if (!isStart) {
     /** 导入操作记录数据 */
     //            fileContentParser.initOperate();
     isStart = true;
     org.apache.ibatis.logging.LogFactory.useSlf4jLogging();
   }
 }
/**
 * @author LeeWonHee
 * @since 2015.09.22
 */
public class MemberDAOImpl implements MemberDAO {

  protected Log log = LogFactory.getLog(this.getClass());
  private SqlSessionTemplate sqlMap;

  public MemberDAOImpl(SqlSessionTemplate sqlMap) {
    super();
    this.sqlMap = sqlMap;
  }

  protected void printQueryId(String queryId) {
    if (log.isDebugEnabled()) {
      log.debug("\t QueryId	\t :	" + queryId);
    }
  }

  @Override
  public int join(String queryId, MemberDTO dto) throws DataAccessException {
    printQueryId(queryId);
    return sqlMap.insert("insertMember", dto);
  }

  @Override
  public MemberDTO getMemberInfo(String queryId, String id) throws DataAccessException {
    printQueryId(queryId);
    return sqlMap.selectOne("selectMember", id);
  }

  @Override
  public int updateMemberInfo(String queryId, String id) throws DataAccessException {
    printQueryId(queryId);
    return sqlMap.update("updateMember", id);
  }

  @Override
  public int deleteMember(String queryId, String id) throws DataAccessException {
    printQueryId(queryId);
    return sqlMap.update("deleteMember", id);
  }
}
/**
 * created by tom on 15/10/08 通过拦截<code>StatementHandler</code>的 <code>prepare</code>
 * 方法,重写sql语句实现物理分页。 老规矩,签名里要拦截的类型只能是接口。 专门为bootstrap设计 mybatis 分页拦截器
 */
@Intercepts({
  @Signature(
      type = StatementHandler.class,
      method = "prepare",
      args = {Connection.class})
})
public class PaginationInterceptor implements Interceptor {
  private static final Log logger = LogFactory.getLog(PaginationInterceptor.class);
  private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
  private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY =
      new DefaultObjectWrapperFactory();
  private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();
  private static final String defaultDialect = "mysql";
  private static final String defaultPageSqlId = ".*Page$";
  private String dialect;
  private String pageSqlId;

  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
    MetaObject metaStatementHandler =
        MetaObject.forObject(
            statementHandler,
            DEFAULT_OBJECT_FACTORY,
            DEFAULT_OBJECT_WRAPPER_FACTORY,
            DEFAULT_REFLECTOR_FACTORY);
    // 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过下面的两次循环
    // 可以分离出最原始的的目标类)
    while (metaStatementHandler.hasGetter("h")) {
      Object object = metaStatementHandler.getValue("h");
      metaStatementHandler =
          MetaObject.forObject(
              object,
              DEFAULT_OBJECT_FACTORY,
              DEFAULT_OBJECT_WRAPPER_FACTORY,
              DEFAULT_REFLECTOR_FACTORY);
    }
    // 分离最后一个代理对象的目标类
    while (metaStatementHandler.hasGetter("target")) {
      Object object = metaStatementHandler.getValue("target");
      metaStatementHandler =
          MetaObject.forObject(
              object,
              DEFAULT_OBJECT_FACTORY,
              DEFAULT_OBJECT_WRAPPER_FACTORY,
              DEFAULT_REFLECTOR_FACTORY);
    }
    if (null == dialect || "".equals(dialect)) {
      logger.warn("Property dialect is not setted,use default 'mysql' ");
      dialect = defaultDialect;
    }
    if (null == pageSqlId || "".equals(pageSqlId)) {
      logger.warn("Property pageSqlId is not setted,use default '.*Page$' ");
      pageSqlId = defaultPageSqlId;
    }
    MappedStatement mappedStatement =
        (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
    PageParameter page = null;
    Object type = metaStatementHandler.getValue("delegate.boundSql.parameterObject");
    if (PageParameter.class.isInstance(type)) {
      page = (PageParameter) type;
    }
    // 只重写需要分页的sql语句。通过MappedStatement的ID匹配,默认重写以Page结尾的
    // MappedStatement的sql
    if (mappedStatement.getId().matches(pageSqlId) && page != null && page.getLimit() > 0) {
      BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
      Object parameterObject = boundSql.getParameterObject();
      if (parameterObject == null) {
        throw new NullPointerException("parameterObject is null!");
      } else {
        // 分页参数作为参数对象parameterObject的一个属性
        String sql = boundSql.getSql();
        // 重写sql
        String pageSql = buildPageSql(sql, page, dialect);
        metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);
        // 采用物理分页后,就不需要mybatis的内存分页了,所以重置下面的两个参数
        metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
        metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
        Connection connection = (Connection) invocation.getArgs()[0];
        // 重设分页参数里的总页数等
        setPageParameter(sql, connection, mappedStatement, boundSql, page);
      }
    }
    // 将执行权交给下一个拦截器
    return invocation.proceed();
  }

  private String buildPageSql(String sql, PageParameter page, String dialect) {
    if (page != null) {
      StringBuilder pageSql = new StringBuilder();
      if ("mysql".equals(dialect)) {
        pageSql = buildPageSqlForMysql(sql, page);
      } else if ("oracle".equals(dialect)) {
        pageSql = buildPageSqlForOracle(sql, page);
      } else {
        return sql;
      }
      return pageSql.toString();
    } else {
      return sql;
    }
  }

  @Override
  public Object plugin(Object target) {
    // 当目标类是StatementHandler类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的次数
    if (target instanceof StatementHandler) {
      return Plugin.wrap(target, this);
    } else {
      return target;
    }
  }

  @Override
  public void setProperties(Properties properties) {
    // To change body of implemented methods use File | Settings | File
    // Templates.
  }

  public StringBuilder buildPageSqlForMysql(String sql, PageParameter page) {
    StringBuilder pageSql = new StringBuilder(100);

    pageSql.append(sql);
    pageSql.append(" limit " + page.getOffset() + "," + page.getLimit());
    return pageSql;
  }

  public StringBuilder buildPageSqlForOracle(String sql, PageParameter page) {
    StringBuilder pageSql = new StringBuilder(100);
    String beginrow = String.valueOf((page.getCurrentPage() - 1) * page.getPageSize());
    String endrow = String.valueOf(page.getCurrentPage() * page.getPageSize());
    pageSql.append("select * from ( select temp.*, rownum row_id from ( ");
    pageSql.append(sql);
    pageSql.append(" ) temp where rownum <= ").append(endrow);
    pageSql.append(") where row_id > ").append(beginrow);
    return pageSql;
  }

  /**
   * 从数据库里查询总的记录数并计算总页数,回写进分页参数<code>PageParameter</code>,这样调用 者就可用通过 分页参数 <code>PageParameter
   * </code>获得相关信息。
   *
   * @param sql
   * @param connection
   * @param mappedStatement
   * @param boundSql
   * @param page
   */
  private void setPageParameter(
      String sql,
      Connection connection,
      MappedStatement mappedStatement,
      BoundSql boundSql,
      PageParameter page) {
    // 记录总记录数
    String countSql = "select count(0) as total from (" + sql + ")  tt";
    PreparedStatement countStmt = null;
    ResultSet rs = null;
    try {
      countStmt = connection.prepareStatement(countSql);
      setParameters(countStmt, mappedStatement, boundSql);
      rs = countStmt.executeQuery();
      int totalCount = 0;
      if (rs.next()) {
        totalCount = rs.getInt(1);
      }
      page.setTotalElements(totalCount);
      //			int totalPage = totalCount / page.getPageSize() + ((totalCount % page.getPageSize() == 0)
      // ? 0 : 1);
      //			page.setTotalPage(totalPage);
    } catch (SQLException e) {
      logger.error("Ignore this exception", e);
    } finally {
      try {
        rs.close();
      } catch (SQLException e) {
        logger.error("Ignore this exception", e);
      }
      try {
        countStmt.close();
      } catch (SQLException e) {
        logger.error("Ignore this exception", e);
      }
    }
  }

  /**
   * 对SQL参数(?)设值
   *
   * @param ps
   * @param mappedStatement
   * @param boundSql
   * @param parameterObject
   * @throws SQLException
   */
  private void setParameters(
      PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql)
      throws SQLException {

    ParameterHandler parameterHandler =
        mappedStatement
            .getConfiguration()
            .newParameterHandler(mappedStatement, boundSql.getParameterObject(), boundSql);
    parameterHandler.setParameters(ps);
  }

  public void setDialect(String dialect) {
    this.dialect = dialect;
  }

  public void setPageSqlId(String pageSqlId) {
    this.pageSqlId = pageSqlId;
  }
}
Exemple #5
0
  public MyBatis start() {
    LogFactory.useSlf4jLogging();

    MyBatisConfBuilder confBuilder = new MyBatisConfBuilder(database);

    // DTO aliases, keep them sorted alphabetically
    confBuilder.loadAlias("ActiveDashboard", ActiveDashboardDto.class);
    confBuilder.loadAlias("ActiveRule", ActiveRuleDto.class);
    confBuilder.loadAlias("ActiveRuleParam", ActiveRuleParamDto.class);
    confBuilder.loadAlias("Author", AuthorDto.class);
    confBuilder.loadAlias("Component", ComponentDto.class);
    confBuilder.loadAlias("ComponentLink", ComponentLinkDto.class);
    confBuilder.loadAlias("ComponentWithSnapshot", ComponentDtoWithSnapshotId.class);
    confBuilder.loadAlias("CustomMeasure", CustomMeasureDto.class);
    confBuilder.loadAlias("Dashboard", DashboardDto.class);
    confBuilder.loadAlias("DuplicationUnit", DuplicationUnitDto.class);
    confBuilder.loadAlias("Event", EventDto.class);
    confBuilder.loadAlias("FilePathWithHash", FilePathWithHashDto.class);
    confBuilder.loadAlias("Group", GroupDto.class);
    confBuilder.loadAlias("GroupMembership", GroupMembershipDto.class);
    confBuilder.loadAlias("GroupPermission", GroupPermissionDto.class);
    confBuilder.loadAlias("IdUuidPair", IdUuidPair.class);
    confBuilder.loadAlias("InternalProperty", InternalPropertyDto.class);
    confBuilder.loadAlias("IssueChange", IssueChangeDto.class);
    confBuilder.loadAlias("IssueFilterFavourite", IssueFilterFavouriteDto.class);
    confBuilder.loadAlias("IssueFilter", IssueFilterDto.class);
    confBuilder.loadAlias("Issue", IssueDto.class);
    confBuilder.loadAlias("LoadedTemplate", LoadedTemplateDto.class);
    confBuilder.loadAlias("MeasureFilterFavourite", MeasureFilterFavouriteDto.class);
    confBuilder.loadAlias("MeasureFilter", MeasureFilterDto.class);
    confBuilder.loadAlias("Measure", MeasureDto.class);
    confBuilder.loadAlias("NotificationQueue", NotificationQueueDto.class);
    confBuilder.loadAlias("Organization", OrganizationDto.class);
    confBuilder.loadAlias(
        "PermissionTemplateCharacteristic", PermissionTemplateCharacteristicDto.class);
    confBuilder.loadAlias("PermissionTemplateGroup", PermissionTemplateGroupDto.class);
    confBuilder.loadAlias("PermissionTemplate", PermissionTemplateDto.class);
    confBuilder.loadAlias("PermissionTemplateUser", PermissionTemplateUserDto.class);
    confBuilder.loadAlias("ProjectQgateAssociation", ProjectQgateAssociationDto.class);
    confBuilder.loadAlias("PurgeableAnalysis", PurgeableAnalysisDto.class);
    confBuilder.loadAlias("QualityGateCondition", QualityGateConditionDto.class);
    confBuilder.loadAlias("QualityGate", QualityGateDto.class);
    confBuilder.loadAlias("QualityProfile", QualityProfileDto.class);
    confBuilder.loadAlias("RequirementMigration", RequirementMigrationDto.class);
    confBuilder.loadAlias("ResourceIndex", ResourceIndexDto.class);
    confBuilder.loadAlias("Resource", ResourceDto.class);
    confBuilder.loadAlias("RuleParam", RuleParamDto.class);
    confBuilder.loadAlias("Rule", RuleDto.class);
    confBuilder.loadAlias("SchemaMigration", SchemaMigrationDto.class);
    confBuilder.loadAlias("ScrapProperty", ScrapPropertyDto.class);
    confBuilder.loadAlias("Snapshot", SnapshotDto.class);
    confBuilder.loadAlias("UserGroup", UserGroupDto.class);
    confBuilder.loadAlias("UserPermission", UserPermissionDto.class);
    confBuilder.loadAlias("UserTokenCount", UserTokenCount.class);
    confBuilder.loadAlias("UserToken", UserTokenDto.class);
    confBuilder.loadAlias("User", UserDto.class);
    confBuilder.loadAlias("UuidWithProjectUuid", UuidWithProjectUuidDto.class);
    confBuilder.loadAlias("ViewsSnapshot", ViewsSnapshotDto.class);
    confBuilder.loadAlias("WidgetProperty", WidgetPropertyDto.class);
    confBuilder.loadAlias("Widget", WidgetDto.class);

    // ResourceMapper has to be loaded before IssueMapper because this last one used it
    confBuilder.loadMapper(ResourceMapper.class);

    // keep them sorted alphabetically
    Class<?>[] mappers = {
      ActiveDashboardMapper.class,
      ActiveRuleMapper.class,
      AuthorMapper.class,
      CeActivityMapper.class,
      CeQueueMapper.class,
      CeScannerContextMapper.class,
      CeTaskInputMapper.class,
      ComponentKeyUpdaterMapper.class,
      ComponentLinkMapper.class,
      ComponentMapper.class,
      CustomMeasureMapper.class,
      DashboardMapper.class,
      DuplicationMapper.class,
      EventMapper.class,
      FileSourceMapper.class,
      GroupMapper.class,
      GroupMembershipMapper.class,
      GroupPermissionMapper.class,
      InternalPropertiesMapper.class,
      IsAliveMapper.class,
      IssueChangeMapper.class,
      IssueFilterFavouriteMapper.class,
      IssueFilterMapper.class,
      IssueMapper.class,
      LoadedTemplateMapper.class,
      MeasureFilterFavouriteMapper.class,
      MeasureFilterMapper.class,
      MeasureMapper.class,
      MetricMapper.class,
      Migration45Mapper.class,
      Migration50Mapper.class,
      Migration53Mapper.class,
      NotificationQueueMapper.class,
      OrganizationMapper.class,
      PermissionMapper.class,
      PermissionTemplateCharacteristicMapper.class,
      PermissionTemplateMapper.class,
      ProjectQgateAssociationMapper.class,
      PropertiesMapper.class,
      PurgeMapper.class,
      QProfileChangeMapper.class,
      QualityGateConditionMapper.class,
      QualityGateMapper.class,
      QualityProfileMapper.class,
      ResourceIndexMapper.class,
      RoleMapper.class,
      RuleMapper.class,
      RuleRepositoryMapper.class,
      SchemaMigrationMapper.class,
      SnapshotMapper.class,
      UserGroupMapper.class,
      UserMapper.class,
      UserPermissionMapper.class,
      UserTokenMapper.class,
      WidgetMapper.class,
      WidgetPropertyMapper.class
    };
    confBuilder.loadMappers(mappers);

    sessionFactory = new SqlSessionFactoryBuilder().build(confBuilder.build());
    return this;
  }
/**
 * {@code FactoryBean} that creates an MyBatis {@code SqlSessionFactory}. This is the usual way to
 * set up a shared MyBatis {@code SqlSessionFactory} in a Spring application context; the
 * SqlSessionFactory can then be passed to MyBatis-based DAOs via dependency injection.
 *
 * <p>Either {@code DataSourceTransactionManager} or {@code JtaTransactionManager} can be used for
 * transaction demarcation in combination with a {@code SqlSessionFactory}. JTA should be used for
 * transactions which span multiple databases or when container managed transactions (CMT) are being
 * used.
 *
 * @see #setConfigLocation
 * @see #setDataSource
 * @version $Id$
 */
public class SqlSessionFactoryBeanMultiple
    implements FactoryBean<SqlSessionFactory>,
        InitializingBean,
        ApplicationListener<ApplicationEvent> {

  //	private static Logger log = Logger.getLogger(SqlSessionFactoryBeanMultiple.class);

  private final Log logger = LogFactory.getLog(getClass());

  private Resource configLocation;

  private Resource[] mapperLocations;
  private Resource[] mapperLocations1;
  private Resource[] mapperLocations2;
  private Resource[] mapperLocations3;
  private Resource[] mapperLocations4;

  private DataSource dataSource;

  private TransactionFactory transactionFactory;

  private Properties configurationProperties;

  private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

  private SqlSessionFactory sqlSessionFactory;

  private String environment =
      SqlSessionFactoryBeanMultiple.class.getSimpleName(); // EnvironmentAware requires spring 3.1

  private boolean failFast;

  private Interceptor[] plugins;

  private TypeHandler<?>[] typeHandlers;

  private String typeHandlersPackage;

  private Class<?>[] typeAliases;

  private String typeAliasesPackage;

  private Class<?> typeAliasesSuperType;

  private DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();

  private ObjectFactory objectFactory;

  private ObjectWrapperFactory objectWrapperFactory;

  /**
   * Sets the ObjectFactory.
   *
   * @since 1.1.2
   * @param objectFactory
   */
  public void setObjectFactory(ObjectFactory objectFactory) {
    this.objectFactory = objectFactory;
  }

  /**
   * Sets the ObjectWrapperFactory.
   *
   * @since 1.1.2
   * @param objectWrapperFactory
   */
  public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
    this.objectWrapperFactory = objectWrapperFactory;
  }

  /**
   * Sets the DatabaseIdProvider.
   *
   * @since 1.1.0
   * @return
   */
  public DatabaseIdProvider getDatabaseIdProvider() {
    return databaseIdProvider;
  }

  /**
   * Gets the DatabaseIdProvider
   *
   * @since 1.1.0
   * @param databaseIdProvider
   */
  public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {
    this.databaseIdProvider = databaseIdProvider;
  }

  /**
   * Mybatis plugin list.
   *
   * @since 1.0.1
   * @param plugins list of plugins
   */
  public void setPlugins(Interceptor[] plugins) {
    this.plugins = plugins;
  }

  /**
   * Packages to search for type aliases.
   *
   * @since 1.0.1
   * @param typeAliasesPackage package to scan for domain objects
   */
  public void setTypeAliasesPackage(String typeAliasesPackage) {
    this.typeAliasesPackage = typeAliasesPackage;
  }

  /**
   * Super class which domain objects have to extend to have a type alias created. No effect if
   * there is no package to scan configured.
   *
   * @since 1.1.2
   * @param typeAliasesSuperType super class for domain objects
   */
  public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {
    this.typeAliasesSuperType = typeAliasesSuperType;
  }

  /**
   * Packages to search for type handlers.
   *
   * @since 1.0.1
   * @param typeHandlersPackage package to scan for type handlers
   */
  public void setTypeHandlersPackage(String typeHandlersPackage) {
    this.typeHandlersPackage = typeHandlersPackage;
  }

  /**
   * Set type handlers. They must be annotated with {@code MappedTypes} and optionally with {@code
   * MappedJdbcTypes}
   *
   * @since 1.0.1
   * @param typeHandlers Type handler list
   */
  public void setTypeHandlers(TypeHandler<?>[] typeHandlers) {
    this.typeHandlers = typeHandlers;
  }

  /**
   * List of type aliases to register. They can be annotated with {@code Alias}
   *
   * @since 1.0.1
   * @param typeAliases Type aliases list
   */
  public void setTypeAliases(Class<?>[] typeAliases) {
    this.typeAliases = typeAliases;
  }

  /**
   * If true, a final check is done on Configuration to assure that all mapped statements are fully
   * loaded and there is no one still pending to resolve includes. Defaults to false.
   *
   * @since 1.0.1
   * @param failFast enable failFast
   */
  public void setFailFast(boolean failFast) {
    this.failFast = failFast;
  }

  /**
   * Set the location of the MyBatis {@code SqlSessionFactory} config file. A typical value is
   * "WEB-INF/mybatis-configuration.xml".
   */
  public void setConfigLocation(Resource configLocation) {
    this.configLocation = configLocation;
  }

  /**
   * Set locations of MyBatis mapper files that are going to be merged into the {@code
   * SqlSessionFactory} configuration at runtime.
   *
   * <p>This is an alternative to specifying "&lt;sqlmapper&gt;" entries in an MyBatis config file.
   * This property being based on Spring's resource abstraction also allows for specifying resource
   * patterns here: e.g. "classpath*:sqlmap/*-mapper.xml".
   */
  public void setMapperLocations1(Resource[] mapperLocations) {
    this.mapperLocations1 = mapperLocations;
  }

  /**
   * Set locations of MyBatis mapper files that are going to be merged into the {@code
   * SqlSessionFactory} configuration at runtime.
   *
   * <p>This is an alternative to specifying "&lt;sqlmapper&gt;" entries in an MyBatis config file.
   * This property being based on Spring's resource abstraction also allows for specifying resource
   * patterns here: e.g. "classpath*:sqlmap/*-mapper.xml".
   */
  public void setMapperLocations2(Resource[] mapperLocations) {
    this.mapperLocations2 = mapperLocations;
  }

  /**
   * Set locations of MyBatis mapper files that are going to be merged into the {@code
   * SqlSessionFactory} configuration at runtime.
   *
   * <p>This is an alternative to specifying "&lt;sqlmapper&gt;" entries in an MyBatis config file.
   * This property being based on Spring's resource abstraction also allows for specifying resource
   * patterns here: e.g. "classpath*:sqlmap/*-mapper.xml".
   */
  public void setMapperLocations3(Resource[] mapperLocations) {
    this.mapperLocations3 = mapperLocations;
  }

  public void setMapperLocations4(Resource[] mapperLocations) {
    this.mapperLocations4 = mapperLocations;
  }

  /**
   * Set optional properties to be passed into the SqlSession configuration, as alternative to a
   * {@code &lt;properties&gt;} tag in the configuration xml file. This will be used to resolve
   * placeholders in the config file.
   */
  public void setConfigurationProperties(Properties sqlSessionFactoryProperties) {
    this.configurationProperties = sqlSessionFactoryProperties;
  }

  /**
   * Set the JDBC {@code DataSource} that this instance should manage transactions for. The {@code
   * DataSource} should match the one used by the {@code SqlSessionFactory}: for example, you could
   * specify the same JNDI DataSource for both.
   *
   * <p>A transactional JDBC {@code Connection} for this {@code DataSource} will be provided to
   * application code accessing this {@code DataSource} directly via {@code DataSourceUtils} or
   * {@code DataSourceTransactionManager}.
   *
   * <p>The {@code DataSource} specified here should be the target {@code DataSource} to manage
   * transactions for, not a {@code TransactionAwareDataSourceProxy}. Only data access code may work
   * with {@code TransactionAwareDataSourceProxy}, while the transaction manager needs to work on
   * the underlying target {@code DataSource}. If there's nevertheless a {@code
   * TransactionAwareDataSourceProxy} passed in, it will be unwrapped to extract its target {@code
   * DataSource}.
   */
  public void setDataSource(DataSource dataSource) {
    if (dataSource instanceof TransactionAwareDataSourceProxy) {
      // If we got a TransactionAwareDataSourceProxy, we need to perform
      // transactions for its underlying target DataSource, else data
      // access code won't see properly exposed transactions (i.e.
      // transactions for the target DataSource).
      this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
    } else {
      this.dataSource = dataSource;
    }
  }

  /**
   * Sets the {@code SqlSessionFactoryBuilder} to use when creating the {@code SqlSessionFactory}.
   *
   * <p>This is mainly meant for testing so that mock SqlSessionFactory classes can be injected. By
   * default, {@code SqlSessionFactoryBuilder} creates {@code DefaultSqlSessionFactory} instances.
   */
  public void setSqlSessionFactoryBuilder(SqlSessionFactoryBuilder sqlSessionFactoryBuilder) {
    this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;
  }

  /**
   * Set the MyBatis TransactionFactory to use. Default is {@code SpringManagedTransactionFactory}
   *
   * <p>The default {@code SpringManagedTransactionFactory} should be appropriate for all cases: be
   * it Spring transaction management, EJB CMT or plain JTA. If there is no active transaction,
   * SqlSession operations will execute SQL statements non-transactionally.
   *
   * <p><b>It is strongly recommended to use the default {@code TransactionFactory}.</b> If not
   * used, any attempt at getting an SqlSession through Spring's MyBatis framework will throw an
   * exception if a transaction is active.
   *
   * @see SpringManagedTransactionFactory
   * @param transactionFactory the MyBatis TransactionFactory
   */
  public void setTransactionFactory(TransactionFactory transactionFactory) {
    this.transactionFactory = transactionFactory;
  }

  /**
   * <b>NOTE:</b> This class <em>overrides</em> any {@code Environment} you have set in the MyBatis
   * config file. This is used only as a placeholder name. The default value is {@code
   * SqlSessionFactoryBean.class.getSimpleName()}.
   *
   * @param environment the environment name
   */
  public void setEnvironment(String environment) {
    this.environment = environment;
  }

  /** {@inheritDoc} */
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

  /**
   * Build a {@code SqlSessionFactory} instance.
   *
   * <p>The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
   * {@code SqlSessionFactory} instance based on an Reader.
   *
   * @return SqlSessionFactory
   * @throws IOException if loading the config file failed
   */
  protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    Configuration configuration;

    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configLocation != null) {
      xmlConfigBuilder =
          new XMLConfigBuilder(
              this.configLocation.getInputStream(), null, this.configurationProperties);
      configuration = xmlConfigBuilder.getConfiguration();
    } else {
      if (this.logger.isDebugEnabled()) {
        this.logger.debug(
            "Property 'configLocation' not specified, using default MyBatis Configuration");
      }
      configuration = new Configuration();
      configuration.setVariables(this.configurationProperties);
    }

    if (this.objectFactory != null) {
      configuration.setObjectFactory(this.objectFactory);
    }

    if (this.objectWrapperFactory != null) {
      configuration.setObjectWrapperFactory(this.objectWrapperFactory);
    }

    if (hasLength(this.typeAliasesPackage)) {
      String[] typeAliasPackageArray =
          tokenizeToStringArray(
              this.typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      for (String packageToScan : typeAliasPackageArray) {
        configuration
            .getTypeAliasRegistry()
            .registerAliases(
                packageToScan, typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
        if (this.logger.isDebugEnabled()) {
          this.logger.debug("Scanned package: '" + packageToScan + "' for aliases");
        }
      }
    }

    if (!isEmpty(this.typeAliases)) {
      for (Class<?> typeAlias : this.typeAliases) {
        configuration.getTypeAliasRegistry().registerAlias(typeAlias);
        if (this.logger.isDebugEnabled()) {
          this.logger.debug("Registered type alias: '" + typeAlias + "'");
        }
      }
    }

    if (!isEmpty(this.plugins)) {
      for (Interceptor plugin : this.plugins) {
        configuration.addInterceptor(plugin);
        if (this.logger.isDebugEnabled()) {
          this.logger.debug("Registered plugin: '" + plugin + "'");
        }
      }
    }

    if (hasLength(this.typeHandlersPackage)) {
      String[] typeHandlersPackageArray =
          tokenizeToStringArray(
              this.typeHandlersPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      for (String packageToScan : typeHandlersPackageArray) {
        configuration.getTypeHandlerRegistry().register(packageToScan);
        if (this.logger.isDebugEnabled()) {
          this.logger.debug("Scanned package: '" + packageToScan + "' for type handlers");
        }
      }
    }

    if (!isEmpty(this.typeHandlers)) {
      for (TypeHandler<?> typeHandler : this.typeHandlers) {
        configuration.getTypeHandlerRegistry().register(typeHandler);
        if (this.logger.isDebugEnabled()) {
          this.logger.debug("Registered type handler: '" + typeHandler + "'");
        }
      }
    }

    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();

        if (this.logger.isDebugEnabled()) {
          this.logger.debug("Parsed configuration file: '" + this.configLocation + "'");
        }
      } catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }

    if (this.transactionFactory == null) {
      this.transactionFactory = new SpringManagedTransactionFactory();
    }

    Environment environment =
        new Environment(this.environment, this.transactionFactory, this.dataSource);
    configuration.setEnvironment(environment);

    if (this.databaseIdProvider != null) {
      try {
        configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new NestedIOException("Failed getting a databaseId", e);
      }
    }

    ArrayList<Resource> listado_resources = new ArrayList<Resource>();

    if (!isEmpty(this.mapperLocations1)) {
      for (Resource resource : this.mapperLocations1) {
        listado_resources.add(resource);
      }
    }

    if (!isEmpty(this.mapperLocations2)) {
      for (Resource resource : this.mapperLocations2) {
        listado_resources.add(resource);
      }
    }

    if (!isEmpty(this.mapperLocations3)) {
      for (Resource resource : this.mapperLocations3) {
        listado_resources.add(resource);
      }
    }

    if (!isEmpty(this.mapperLocations4)) {
      for (Resource resource : this.mapperLocations4) {
        listado_resources.add(resource);
      }
    }

    this.mapperLocations = new Resource[listado_resources.size()];
    int index = 0;
    for (Resource resource : listado_resources) {
      this.mapperLocations[index] = resource;
      index++;
    }

    if (!isEmpty(this.mapperLocations)) {
      for (Resource mapperLocation : this.mapperLocations) {
        if (mapperLocation == null) {
          continue;
        }

        //				//log.info("Mapa cargado: " + mapperLocation);

        try {
          XMLMapperBuilder xmlMapperBuilder =
              new XMLMapperBuilder(
                  mapperLocation.getInputStream(),
                  configuration,
                  mapperLocation.toString(),
                  configuration.getSqlFragments());
          xmlMapperBuilder.parse();
        } catch (Exception e) {
          throw new NestedIOException(
              "Failed to parse mapping resource: '" + mapperLocation + "'", e);
        } finally {
          ErrorContext.instance().reset();
        }

        if (this.logger.isDebugEnabled()) {
          this.logger.debug("Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      if (this.logger.isDebugEnabled()) {
        this.logger.debug(
            "Property 'mapperLocations' was not specified or no matching resources found");
      }
    }

    return this.sqlSessionFactoryBuilder.build(configuration);
  }

  /** {@inheritDoc} */
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }

  /** {@inheritDoc} */
  public Class<? extends SqlSessionFactory> getObjectType() {
    return this.sqlSessionFactory == null
        ? SqlSessionFactory.class
        : this.sqlSessionFactory.getClass();
  }

  /** {@inheritDoc} */
  public boolean isSingleton() {
    return true;
  }

  /** {@inheritDoc} */
  public void onApplicationEvent(ApplicationEvent event) {
    if (failFast && event instanceof ContextRefreshedEvent) {
      // fail-fast -> check all statements are completed
      this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
    }
  }
}
/**
 * Mybatis分页拦截器基类
 *
 * @author poplar.yfyang / ssmas
 * @version 2013-8-28
 */
public abstract class BaseInterceptor implements Interceptor, Serializable {

  private static final long serialVersionUID = 1L;

  protected static final String PAGE = "page";

  protected static final String DELEGATE = "delegate";

  protected static final String MAPPED_STATEMENT = "mappedStatement";

  protected Log log = LogFactory.getLog(this.getClass());

  protected Dialect DIALECT;

  //    /**
  //     * 拦截的ID,在mapper中的id,可以匹配正则
  //     */
  //    protected String _SQL_PATTERN = "";

  /**
   * 对参数进行转换和检查
   *
   * @param parameterObject 参数对象
   * @param page 分页对象
   * @return 分页对象
   * @throws NoSuchFieldException 无法找到参数
   */
  @SuppressWarnings("unchecked")
  protected static Page<Object> convertParameter(Object parameterObject, Page<Object> page) {
    try {
      if (parameterObject instanceof Page) {
        return (Page<Object>) parameterObject;
      } else {
        return (Page<Object>) Reflections.getFieldValue(parameterObject, PAGE);
      }
    } catch (Exception e) {
      return null;
    }
  }

  /**
   * 设置属性,支持自定义方言类和制定数据库的方式 <code>dialectClass</code>,自定义方言类。可以不配置这项 <ode>dbms</ode> 数据库类型,插件支持的数据库
   * <code>sqlPattern</code> 需要拦截的SQL ID
   *
   * @param p 属性
   */
  protected void initProperties(Properties p) {
    Dialect dialect = null;
    String dbType = Global.getConfig("jdbc.type");
    if ("db2".equals(dbType)) {
      dialect = new DB2Dialect();
    } else if ("derby".equals(dbType)) {
      dialect = new DerbyDialect();
    } else if ("h2".equals(dbType)) {
      dialect = new H2Dialect();
    } else if ("hsql".equals(dbType)) {
      dialect = new HSQLDialect();
    } else if ("mysql".equals(dbType)) {
      dialect = new MySQLDialect();
    } else if ("oracle".equals(dbType)) {
      dialect = new OracleDialect();
    } else if ("postgre".equals(dbType)) {
      dialect = new PostgreSQLDialect();
    } else if ("mssql".equals(dbType) || "sqlserver".equals(dbType)) {
      dialect = new SQLServer2005Dialect();
    } else if ("sybase".equals(dbType)) {
      dialect = new SybaseDialect();
    }
    if (dialect == null) {
      throw new RuntimeException("mybatis dialect error.");
    }
    DIALECT = dialect;
    //        _SQL_PATTERN = p.getProperty("sqlPattern");
    //        _SQL_PATTERN = Global.getConfig("mybatis.pagePattern");
    //        if (StringUtils.isEmpty(_SQL_PATTERN)) {
    //            throw new RuntimeException("sqlPattern property is not found!");
    //        }
  }
}
/**
 * This is a simple, synchronous, thread-safe database connection pool.
 *
 * @author Clinton Begin
 */
public class PooledDataSource implements DataSource {

  private static final Log log = LogFactory.getLog(PooledDataSource.class);

  private final PoolState state = new PoolState(this); // state保存了空闲连接list和活动连接list

  private final UnpooledDataSource dataSource; // 组合一个 没pool的数据源

  // OPTIONAL CONFIGURATION FIELDS
  protected int poolMaximumActiveConnections = 10;
  protected int poolMaximumIdleConnections = 5;
  protected int poolMaximumCheckoutTime = 20000; // hama
  protected int poolTimeToWait = 20000;
  protected String poolPingQuery = "NO PING QUERY SET";
  protected boolean poolPingEnabled = false; // hama
  protected int poolPingConnectionsNotUsedFor = 0; // hama

  private int expectedConnectionTypeCode; // hama

  public PooledDataSource() {
    dataSource = new UnpooledDataSource();
  }

  public PooledDataSource(UnpooledDataSource dataSource) {
    this.dataSource = dataSource;
  }

  public PooledDataSource(String driver, String url, String username, String password) {
    dataSource = new UnpooledDataSource(driver, url, username, password);
    expectedConnectionTypeCode =
        assembleConnectionTypeCode(
            dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  public PooledDataSource(String driver, String url, Properties driverProperties) {
    dataSource = new UnpooledDataSource(driver, url, driverProperties);
    expectedConnectionTypeCode =
        assembleConnectionTypeCode(
            dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  public PooledDataSource(
      ClassLoader driverClassLoader, String driver, String url, String username, String password) {
    dataSource = new UnpooledDataSource(driverClassLoader, driver, url, username, password);
    expectedConnectionTypeCode =
        assembleConnectionTypeCode(
            dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  public PooledDataSource(
      ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
    dataSource = new UnpooledDataSource(driverClassLoader, driver, url, driverProperties);
    expectedConnectionTypeCode =
        assembleConnectionTypeCode(
            dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  @Override
  public Connection getConnection()
      throws SQLException { // 获得连接,即popConnection(...).getProxyConnection()获得代理连接
    return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
  }

  @Override
  public Connection getConnection(String username, String password) throws SQLException {
    return popConnection(username, password).getProxyConnection();
  }

  @Override
  public void setLoginTimeout(int loginTimeout) throws SQLException {
    DriverManager.setLoginTimeout(loginTimeout);
  }

  @Override
  public int getLoginTimeout() throws SQLException {
    return DriverManager.getLoginTimeout();
  }

  @Override
  public void setLogWriter(PrintWriter logWriter) throws SQLException {
    DriverManager.setLogWriter(logWriter);
  }

  @Override
  public PrintWriter getLogWriter() throws SQLException {
    return DriverManager.getLogWriter();
  }

  public void setDriver(String driver) {
    dataSource.setDriver(driver);
    forceCloseAll();
  }

  public void setUrl(String url) {
    dataSource.setUrl(url);
    forceCloseAll();
  }

  public void setUsername(String username) {
    dataSource.setUsername(username);
    forceCloseAll();
  }

  public void setPassword(String password) {
    dataSource.setPassword(password);
    forceCloseAll();
  }

  public void setDefaultAutoCommit(boolean defaultAutoCommit) {
    dataSource.setAutoCommit(defaultAutoCommit);
    forceCloseAll();
  }

  public void setDefaultTransactionIsolationLevel(Integer defaultTransactionIsolationLevel) {
    dataSource.setDefaultTransactionIsolationLevel(defaultTransactionIsolationLevel);
    forceCloseAll();
  }

  public void setDriverProperties(Properties driverProps) {
    dataSource.setDriverProperties(driverProps);
    forceCloseAll();
  }

  /*
   * The maximum number of active connections
   *
   * @param poolMaximumActiveConnections The maximum number of active connections
   */
  public void setPoolMaximumActiveConnections(int poolMaximumActiveConnections) {
    this.poolMaximumActiveConnections = poolMaximumActiveConnections;
    forceCloseAll();
  }

  /*
   * The maximum number of idle connections
   *
   * @param poolMaximumIdleConnections The maximum number of idle connections
   */
  public void setPoolMaximumIdleConnections(int poolMaximumIdleConnections) {
    this.poolMaximumIdleConnections = poolMaximumIdleConnections;
    forceCloseAll();
  }

  /*
   * The maximum time a connection can be used before it *may* be
   * given away again.
   *
   * @param poolMaximumCheckoutTime The maximum time
   */
  public void setPoolMaximumCheckoutTime(int poolMaximumCheckoutTime) { // 最长的可用时间,check out前能用多久
    this.poolMaximumCheckoutTime = poolMaximumCheckoutTime;
    forceCloseAll();
  }

  /*
   * The time to wait before retrying to get a connection
   *
   * @param poolTimeToWait The time to wait
   */
  public void setPoolTimeToWait(int poolTimeToWait) { // 重试获得连接前的等待时间
    this.poolTimeToWait = poolTimeToWait;
    forceCloseAll();
  }

  /*
   * The query to be used to check a connection
   *
   * @param poolPingQuery The query
   */
  public void setPoolPingQuery(String poolPingQuery) { // hama
    this.poolPingQuery = poolPingQuery;
    forceCloseAll();
  }

  /*
   * Determines if the ping query should be used.
   *
   * @param poolPingEnabled True if we need to check a connection before using it
   */
  public void setPoolPingEnabled(boolean poolPingEnabled) {
    this.poolPingEnabled = poolPingEnabled;
    forceCloseAll();
  }

  /*
   * If a connection has not been used in this many milliseconds, ping the
   * database to make sure the connection is still good.
   *
   * @param milliseconds the number of milliseconds of inactivity that will trigger a ping
   */
  public void setPoolPingConnectionsNotUsedFor(int milliseconds) {
    this.poolPingConnectionsNotUsedFor = milliseconds;
    forceCloseAll();
  }

  public String getDriver() {
    return dataSource.getDriver();
  }

  public String getUrl() {
    return dataSource.getUrl();
  }

  public String getUsername() {
    return dataSource.getUsername();
  }

  public String getPassword() {
    return dataSource.getPassword();
  }

  public boolean isAutoCommit() {
    return dataSource.isAutoCommit();
  }

  public Integer getDefaultTransactionIsolationLevel() {
    return dataSource.getDefaultTransactionIsolationLevel();
  }

  public Properties getDriverProperties() {
    return dataSource.getDriverProperties();
  }

  public int getPoolMaximumActiveConnections() {
    return poolMaximumActiveConnections;
  }

  public int getPoolMaximumIdleConnections() {
    return poolMaximumIdleConnections;
  }

  public int getPoolMaximumCheckoutTime() {
    return poolMaximumCheckoutTime;
  }

  public int getPoolTimeToWait() {
    return poolTimeToWait;
  }

  public String getPoolPingQuery() {
    return poolPingQuery;
  }

  public boolean isPoolPingEnabled() {
    return poolPingEnabled;
  }

  public int getPoolPingConnectionsNotUsedFor() {
    return poolPingConnectionsNotUsedFor;
  }

  /*
   * Closes all active and idle connections in the pool
   */
  public void forceCloseAll() { // 关闭pool里的所有连接,两个list中的连接都移除且关闭
    synchronized (state) {
      expectedConnectionTypeCode =
          assembleConnectionTypeCode(
              dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
      for (int i = state.activeConnections.size(); i > 0; i--) { // 遍历活动连接list
        try {
          PooledConnection conn = state.activeConnections.remove(i - 1); // 从活动连接list移出
          conn.invalidate(); // PooledConnection的valid设为false

          Connection realConn = conn.getRealConnection(); // 获取真conn (java.sql.Connection)
          if (!realConn.getAutoCommit()) { // 不是自动提交的话
            realConn.rollback(); // 回滚
          }
          realConn.close();
        } catch (Exception e) {
          // ignore
        }
      }
      for (int i = state.idleConnections.size(); i > 0; i--) { // 简历空闲连接list
        try {
          PooledConnection conn = state.idleConnections.remove(i - 1);
          conn.invalidate();

          Connection realConn = conn.getRealConnection();
          if (!realConn.getAutoCommit()) {
            realConn.rollback();
          }
          realConn.close();
        } catch (Exception e) {
          // ignore
        }
      }
    }
    if (log.isDebugEnabled()) {
      log.debug("PooledDataSource forcefully closed/removed all connections.");
    }
  }

  public PoolState getPoolState() {
    return state;
  }

  private int assembleConnectionTypeCode(String url, String username, String password) {
    return ("" + url + username + password).hashCode();
  }

  protected void pushConnection(PooledConnection conn) throws SQLException { // 关闭一个连接,其实是放回pool

    synchronized (state) { // 加锁
      state.activeConnections.remove(conn); // 从活动list移出
      if (conn.isValid()) { // conn是有效的
        if (state.idleConnections.size() < poolMaximumIdleConnections
            && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) { // 不是自动提交的话
            conn.getRealConnection().rollback(); // 回滚
          }
          PooledConnection newConn =
              new PooledConnection(conn.getRealConnection(), this); // 新建一个连接,用原来的真连接
          state.idleConnections.add(newConn); // 加入空闲list
          newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
          newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
          conn.invalidate(); // 老连接标记为无效
          if (log.isDebugEnabled()) {
            log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
          }
          state.notifyAll(); // 唤醒popConnection中等待连接的线程可以来抢state的锁
        } else {
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback(); // 回滚
          }
          conn.getRealConnection().close(); // 关闭
          if (log.isDebugEnabled()) {
            log.debug("Closed connection " + conn.getRealHashCode() + ".");
          }
          conn.invalidate();
        }
      } else {
        if (log.isDebugEnabled()) {
          log.debug(
              "A bad connection ("
                  + conn.getRealHashCode()
                  + ") attempted to return to the pool, discarding connection.");
        }
        state.badConnectionCount++;
      }
    }
  }

  private PooledConnection popConnection(String username, String password)
      throws SQLException { // 获取一个连接
    boolean countedWait = false;
    PooledConnection conn = null;
    long t = System.currentTimeMillis();
    int localBadConnectionCount = 0;

    while (conn == null) {
      synchronized (state) {
        if (!state.idleConnections.isEmpty()) { // pool里还有空闲连接
          // Pool has available connection
          conn = state.idleConnections.remove(0); // 从空闲list移出一个conn,用做新连接
          if (log.isDebugEnabled()) {
            log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
          }
        } else { // pool里没有空闲连接了
          // Pool does not have available connection
          if (state.activeConnections.size() < poolMaximumActiveConnections) { // 活动list没有满
            // Can create new connection
            conn = new PooledConnection(dataSource.getConnection(), this); // 新建一个PooledConnection
            if (log.isDebugEnabled()) {
              log.debug("Created connection " + conn.getRealHashCode() + ".");
            }
          } else { // 活动list已经满了,无法创建新连接
            // Cannot create new connection
            PooledConnection oldestActiveConnection = state.activeConnections.get(0); // 获取最早开始活动的连接
            long longestCheckoutTime =
                oldestActiveConnection.getCheckoutTime(); // 获得最老的活动连接的checkout时间
            if (longestCheckoutTime > poolMaximumCheckoutTime) { // 如果checkout时间大于最大值
              // Can claim overdue connection
              state.claimedOverdueConnectionCount++; // 过期连接计数++
              state.accumulatedCheckoutTimeOfOverdueConnections +=
                  longestCheckoutTime; // 累加过期连接的checkout时间
              state.accumulatedCheckoutTime += longestCheckoutTime; // 累加checkout时间
              state.activeConnections.remove(oldestActiveConnection); // 从活动list移出最老的活动连接,留出空位来啦
              if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
                oldestActiveConnection.getRealConnection().rollback(); // 回滚
              }
              conn =
                  new PooledConnection(
                      oldestActiveConnection.getRealConnection(), this); // 新建连接,用最老连接的真连接
              oldestActiveConnection.invalidate(); // 最老连接标记无效
              if (log.isDebugEnabled()) {
                log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
              }
            } else { // 如果checkout时间并没有超过最大值,必须等待
              // Must wait
              try {
                if (!countedWait) { // countedWait原来是false
                  state.hadToWaitCount++; // 必须等待的计数++
                  countedWait = true; // countedWait变为true
                }
                if (log.isDebugEnabled()) {
                  log.debug(
                      "Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
                }
                long wt = System.currentTimeMillis();
                state.wait(
                    poolTimeToWait); // 等poolTimeToWait微秒,pushConnection移出一个连接时会notifyAll来唤醒wait的线程
                state.accumulatedWaitTime += System.currentTimeMillis() - wt;
              } catch (InterruptedException e) {
                break;
              }
            }
          }
        }
        if (conn != null) { // conn非空,确实建出来了
          if (conn.isValid()) { // conn有效
            if (!conn.getRealConnection().getAutoCommit()) {
              conn.getRealConnection().rollback(); // 这里回滚是干啥? hama
            }
            conn.setConnectionTypeCode(
                assembleConnectionTypeCode(dataSource.getUrl(), username, password));
            conn.setCheckoutTimestamp(System.currentTimeMillis());
            conn.setLastUsedTimestamp(System.currentTimeMillis());
            state.activeConnections.add(conn); // 加入活动list
            state.requestCount++;
            state.accumulatedRequestTime += System.currentTimeMillis() - t;
          } else { // conn无效
            if (log.isDebugEnabled()) {
              log.debug(
                  "A bad connection ("
                      + conn.getRealHashCode()
                      + ") was returned from the pool, getting another connection.");
            }
            state.badConnectionCount++;
            localBadConnectionCount++;
            conn = null;
            if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) {
              if (log.isDebugEnabled()) {
                log.debug("PooledDataSource: Could not get a good connection to the database.");
              }
              throw new SQLException(
                  "PooledDataSource: Could not get a good connection to the database.");
            }
          }
        }
      }
    } // 到这里while结束

    if (conn == null) { // 出现了奇葩的Unknown错吧
      if (log.isDebugEnabled()) {
        log.debug(
            "PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
      }
      throw new SQLException(
          "PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
    }

    return conn;
  }

  /*
   * Method to check to see if a connection is still usable
   *
   * @param conn - the connection to check
   * @return True if the connection is still usable
   */
  protected boolean pingConnection(PooledConnection conn) { // 用连接做个查询,测试一个
    boolean result = true;

    try {
      result = !conn.getRealConnection().isClosed();
    } catch (SQLException e) {
      if (log.isDebugEnabled()) {
        log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
      }
      result = false;
    }

    if (result) {
      if (poolPingEnabled) {
        if (poolPingConnectionsNotUsedFor >= 0
            && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {
          try {
            if (log.isDebugEnabled()) {
              log.debug("Testing connection " + conn.getRealHashCode() + " ...");
            }
            Connection realConn = conn.getRealConnection(); // 取出真连接
            Statement statement = realConn.createStatement();
            ResultSet rs = statement.executeQuery(poolPingQuery); // 测试一个
            rs.close();
            statement.close();
            if (!realConn.getAutoCommit()) {
              realConn.rollback();
            }
            result = true;
            if (log.isDebugEnabled()) {
              log.debug("Connection " + conn.getRealHashCode() + " is GOOD!");
            }
          } catch (Exception e) {
            log.warn("Execution of ping query '" + poolPingQuery + "' failed: " + e.getMessage());
            try {
              conn.getRealConnection().close();
            } catch (Exception e2) {
              // ignore
            }
            result = false;
            if (log.isDebugEnabled()) {
              log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
            }
          }
        }
      }
    }
    return result;
  }

  /*
   * Unwraps a pooled connection to get to the 'real' connection
   *
   * @param conn - the pooled connection to unwrap
   * @return The 'real' connection
   */
  public static Connection unwrapConnection(Connection conn) {
    if (Proxy.isProxyClass(conn.getClass())) {
      InvocationHandler handler = Proxy.getInvocationHandler(conn);
      if (handler instanceof PooledConnection) {
        return ((PooledConnection) handler).getRealConnection();
      }
    }
    return conn;
  }

  protected void finalize() throws Throwable { // 被GC前挣扎一记的finalize
    forceCloseAll(); // 全关了
    super.finalize();
  }

  public <T> T unwrap(Class<T> iface) throws SQLException {
    throw new SQLException(getClass().getName() + " is not a wrapper.");
  }

  public boolean isWrapperFor(Class<?> iface) throws SQLException {
    return false;
  }

  public Logger getParentLogger() {
    return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); // requires JDK version 1.6
  }
}
Exemple #9
0
  public MyBatis start() {
    LogFactory.useSlf4jLogging();

    Configuration conf = new Configuration();
    conf.setEnvironment(
        new Environment("production", createTransactionFactory(), database.getDataSource()));
    conf.setUseGeneratedKeys(true);
    conf.setLazyLoadingEnabled(false);
    conf.setJdbcTypeForNull(JdbcType.NULL);
    Dialect dialect = database.getDialect();
    conf.setDatabaseId(dialect.getId());
    conf.getVariables().setProperty("_true", dialect.getTrueSqlValue());
    conf.getVariables().setProperty("_false", dialect.getFalseSqlValue());
    conf.getVariables()
        .setProperty("_scrollFetchSize", String.valueOf(dialect.getScrollDefaultFetchSize()));

    loadAlias(conf, "ActiveDashboard", ActiveDashboardDto.class);
    loadAlias(conf, "Author", AuthorDto.class);
    loadAlias(conf, "Component", ComponentDto.class);
    loadAlias(conf, "Dashboard", DashboardDto.class);
    loadAlias(conf, "Dependency", DependencyDto.class);
    loadAlias(conf, "DuplicationUnit", DuplicationUnitDto.class);
    loadAlias(conf, "Graph", GraphDto.class);
    loadAlias(conf, "Group", GroupDto.class);
    loadAlias(conf, "GroupRole", GroupRoleDto.class);
    loadAlias(conf, "GroupMembership", GroupMembershipDto.class);
    loadAlias(conf, "LoadedTemplate", LoadedTemplateDto.class);
    loadAlias(conf, "MeasureFilter", MeasureFilterDto.class);
    loadAlias(conf, "NotificationQueue", NotificationQueueDto.class);
    loadAlias(conf, "Property", PropertyDto.class);
    loadAlias(conf, "PurgeableSnapshot", PurgeableSnapshotDto.class);
    loadAlias(conf, "QualityGate", QualityGateDto.class);
    loadAlias(conf, "QualityGateCondition", QualityGateConditionDto.class);
    loadAlias(conf, "ProjectQgateAssociation", ProjectQgateAssociationDto.class);
    loadAlias(conf, "Resource", ResourceDto.class);
    loadAlias(conf, "ResourceIndex", ResourceIndexDto.class);
    loadAlias(conf, "ResourceSnapshot", ResourceSnapshotDto.class);
    loadAlias(conf, "Rule", RuleDto.class);
    loadAlias(conf, "RuleParam", RuleParamDto.class);
    loadAlias(conf, "Snapshot", SnapshotDto.class);
    loadAlias(conf, "Semaphore", SemaphoreDto.class);
    loadAlias(conf, "SchemaMigration", SchemaMigrationDto.class);
    loadAlias(conf, "User", UserDto.class);
    loadAlias(conf, "UserRole", UserRoleDto.class);
    loadAlias(conf, "UserGroup", UserGroupDto.class);
    loadAlias(conf, "Widget", WidgetDto.class);
    loadAlias(conf, "WidgetProperty", WidgetPropertyDto.class);
    loadAlias(conf, "MeasureModel", MeasureModel.class);
    loadAlias(conf, "Measure", MeasureDto.class);
    loadAlias(conf, "Metric", MetricDto.class);
    loadAlias(conf, "Issue", IssueDto.class);
    loadAlias(conf, "IssueChange", IssueChangeDto.class);
    loadAlias(conf, "IssueFilter", IssueFilterDto.class);
    loadAlias(conf, "IssueFilterFavourite", IssueFilterFavouriteDto.class);
    loadAlias(conf, "ActionPlanIssue", ActionPlanDto.class);
    loadAlias(conf, "ActionPlanStats", ActionPlanStatsDto.class);
    loadAlias(conf, "PermissionTemplate", PermissionTemplateDto.class);
    loadAlias(conf, "PermissionTemplateUser", PermissionTemplateUserDto.class);
    loadAlias(conf, "PermissionTemplateGroup", PermissionTemplateGroupDto.class);
    loadAlias(conf, "Characteristic", CharacteristicDto.class);
    loadAlias(conf, "UserWithPermission", UserWithPermissionDto.class);
    loadAlias(conf, "GroupWithPermission", GroupWithPermissionDto.class);
    loadAlias(conf, "QualityProfile", QualityProfileDto.class);
    loadAlias(conf, "ActiveRule", ActiveRuleDto.class);
    loadAlias(conf, "ActiveRuleParam", ActiveRuleParamDto.class);
    loadAlias(conf, "RequirementMigration", RequirementMigrationDto.class);
    loadAlias(conf, "Activity", ActivityDto.class);
    loadAlias(conf, "AnalysisReport", AnalysisReportDto.class);
    loadAlias(conf, "IdUuidPair", IdUuidPair.class);
    loadAlias(conf, "FilePathWithHash", FilePathWithHashDto.class);
    loadAlias(conf, "UuidWithProjectUuid", UuidWithProjectUuidDto.class);

    // AuthorizationMapper has to be loaded before IssueMapper because this last one used it
    loadMapper(conf, "org.sonar.core.user.AuthorizationMapper");
    // ResourceMapper has to be loaded before IssueMapper because this last one used it
    loadMapper(conf, ResourceMapper.class);

    loadMapper(conf, "org.sonar.core.permission.PermissionMapper");
    Class<?>[] mappers = {
      ActivityMapper.class,
      ActiveDashboardMapper.class,
      AuthorMapper.class,
      DashboardMapper.class,
      DependencyMapper.class,
      DuplicationMapper.class,
      GraphDtoMapper.class,
      IssueMapper.class,
      IssueChangeMapper.class,
      IssueFilterMapper.class,
      IssueFilterFavouriteMapper.class,
      LoadedTemplateMapper.class,
      MeasureFilterMapper.class,
      Migration44Mapper.class,
      PermissionTemplateMapper.class,
      PropertiesMapper.class,
      PurgeMapper.class,
      ResourceKeyUpdaterMapper.class,
      ResourceIndexerMapper.class,
      ResourceSnapshotMapper.class,
      RoleMapper.class,
      RuleMapper.class,
      SchemaMigrationMapper.class,
      SemaphoreMapper.class,
      UserMapper.class,
      GroupMapper.class,
      UserGroupMapper.class,
      WidgetMapper.class,
      WidgetPropertyMapper.class,
      org.sonar.api.database.model.MeasureMapper.class,
      FileSourceMapper.class,
      ActionPlanMapper.class,
      ActionPlanStatsMapper.class,
      NotificationQueueMapper.class,
      CharacteristicMapper.class,
      GroupMembershipMapper.class,
      QualityProfileMapper.class,
      ActiveRuleMapper.class,
      MeasureMapper.class,
      MetricMapper.class,
      QualityGateMapper.class,
      QualityGateConditionMapper.class,
      ComponentMapper.class,
      SnapshotMapper.class,
      ProjectQgateAssociationMapper.class,
      AnalysisReportMapper.class,
      ComponentIndexMapper.class,
      Migration45Mapper.class,
      Migration50Mapper.class
    };
    loadMappers(conf, mappers);

    sessionFactory = new SqlSessionFactoryBuilder().build(conf);
    return this;
  }
Exemple #10
0
public abstract class BaseExecutor implements Executor {

  private static final Log log = LogFactory.getLog(BaseExecutor.class);

  // for backward compatibility with 3.0 style logging
  private static final Log connectionLog = LogFactory.getLog(Connection.class);

  protected Transaction transaction;

  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
  protected PerpetualCache localCache;
  protected PerpetualCache localOutputParameterCache;
  protected Configuration configuration;

  protected int queryStack = 0;

  protected List<BatchResult> batchResults = new ArrayList<BatchResult>();
  private boolean closed;

  protected BaseExecutor(Configuration configuration, Transaction transaction) {
    this.transaction = transaction;
    this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>();
    this.localCache = new PerpetualCache("LocalCache");
    this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
    this.closed = false;
    this.configuration = configuration;
  }

  public Transaction getTransaction() {
    if (closed) throw new ExecutorException("Executor was closed.");
    return transaction;
  }

  public void close(boolean forceRollback) {
    try {
      try {
        rollback(forceRollback);
      } finally {
        if (transaction != null) transaction.close();
      }
    } catch (SQLException e) {
      // Ignore.  There's nothing that can be done at this point.
      log.debug("Unexpected exception on closing transaction.  Cause: " + e);
    } finally {
      transaction = null;
      deferredLoads = null;
      localCache = null;
      localOutputParameterCache = null;
      batchResults = null;
      closed = true;
    }
  }

  public boolean isClosed() {
    return closed;
  }

  public int update(MappedStatement ms, Object parameter) throws SQLException {
    ErrorContext.instance()
        .resource(ms.getResource())
        .activity("executing an update")
        .object(ms.getId());
    if (closed) throw new ExecutorException("Executor was closed.");
    clearLocalCache();
    return doUpdate(ms, parameter);
  }

  public List<BatchResult> flushStatements() throws SQLException {
    return flushStatements(false);
  }

  public List<BatchResult> flushStatements(boolean isRollBack) throws SQLException {
    if (closed) throw new ExecutorException("Executor was closed.");
    batchResults.addAll(doFlushStatements(isRollBack));
    return batchResults;
  }

  public <E> List<E> query(
      MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)
      throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }

  @SuppressWarnings("unchecked")
  public <E> List<E> query(
      MappedStatement ms,
      Object parameter,
      RowBounds rowBounds,
      ResultHandler resultHandler,
      CacheKey key,
      BoundSql boundSql)
      throws SQLException {
    ErrorContext.instance()
        .resource(ms.getResource())
        .activity("executing a query")
        .object(ms.getId());
    if (closed) throw new ExecutorException("Executor was closed.");
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        clearLocalCache(); // issue #482
      }
    }
    return list;
  }

  public void deferLoad(
      MappedStatement ms, MetaObject resultObject, String property, CacheKey key) {
    if (closed) throw new ExecutorException("Executor was closed.");
    DeferredLoad deferredLoad =
        new DeferredLoad(ms, resultObject, property, key, localCache, configuration);
    if (deferredLoad.canLoad()) {
      deferredLoad.load();
    } else {
      deferredLoads.add(
          new DeferredLoad(ms, resultObject, property, key, localCache, configuration));
    }
  }

  public CacheKey createCacheKey(
      MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (closed) throw new ExecutorException("Executor was closed.");
    CacheKey cacheKey = new CacheKey();
    cacheKey.update(ms.getId());
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());
    cacheKey.update(boundSql.getSql());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings.size() > 0 && parameterObject != null) {
      TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
      if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
        cacheKey.update(parameterObject);
      } else {
        MetaObject metaObject = configuration.newMetaObject(parameterObject);
        for (ParameterMapping parameterMapping : parameterMappings) {
          String propertyName = parameterMapping.getProperty();
          if (metaObject.hasGetter(propertyName)) {
            cacheKey.update(metaObject.getValue(propertyName));
          } else if (boundSql.hasAdditionalParameter(propertyName)) {
            cacheKey.update(boundSql.getAdditionalParameter(propertyName));
          }
        }
      }
    }
    return cacheKey;
  }

  public boolean isCached(MappedStatement ms, CacheKey key) {
    return localCache.getObject(key) != null;
  }

  public void commit(boolean required) throws SQLException {
    if (closed) throw new ExecutorException("Cannot commit, transaction is already closed");
    clearLocalCache();
    flushStatements();
    if (required) {
      transaction.commit();
    }
  }

  public void rollback(boolean required) throws SQLException {
    if (!closed) {
      try {
        clearLocalCache();
        flushStatements(true);
      } finally {
        if (required) {
          transaction.rollback();
        }
      }
    }
  }

  public void clearLocalCache() {
    if (!closed) {
      localCache.clear();
      localOutputParameterCache.clear();
    }
  }

  protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;

  protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;

  protected abstract <E> List<E> doQuery(
      MappedStatement ms,
      Object parameter,
      RowBounds rowBounds,
      ResultHandler resultHandler,
      BoundSql boundSql)
      throws SQLException;

  protected void closeStatement(Statement statement) {
    if (statement != null) {
      try {
        statement.close();
      } catch (SQLException e) {
        // ignore
      }
    }
  }

  private void handleLocallyCachedOutputParameters(
      MappedStatement ms, CacheKey key, Object parameter, BoundSql boundSql) {
    if (ms.getStatementType() == StatementType.CALLABLE) {
      final Object cachedParameter = localOutputParameterCache.getObject(key);
      if (cachedParameter != null && parameter != null) {
        final MetaObject metaCachedParameter = MetaObject.forObject(cachedParameter);
        final MetaObject metaParameter = MetaObject.forObject(parameter);
        for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
          if (parameterMapping.getMode() != ParameterMode.IN) {
            final String parameterName = parameterMapping.getProperty();
            final Object cachedValue = metaCachedParameter.getValue(parameterName);
            metaParameter.setValue(parameterName, cachedValue);
          }
        }
      }
    }
  }

  private <E> List<E> queryFromDatabase(
      MappedStatement ms,
      Object parameter,
      RowBounds rowBounds,
      ResultHandler resultHandler,
      CacheKey key,
      BoundSql boundSql)
      throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

  protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled() || connectionLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog);
    } else {
      return connection;
    }
  }

  private static class DeferredLoad {

    private final MetaObject resultObject;
    private final String property;
    private final CacheKey key;
    private final PerpetualCache localCache;
    private final ObjectFactory objectFactory;
    private final Configuration configuration;

    public DeferredLoad(
        MappedStatement mappedStatement,
        MetaObject resultObject,
        String property,
        CacheKey key,
        PerpetualCache localCache,
        Configuration configuration) {
      this.resultObject = resultObject;
      this.property = property;
      this.key = key;
      this.localCache = localCache;
      this.objectFactory = configuration.getObjectFactory();
      this.configuration = configuration;
    }

    public boolean canLoad() {
      return localCache.getObject(key) != null
          && localCache.getObject(key) != EXECUTION_PLACEHOLDER;
    }

    public void load() {
      Object value = null;
      @SuppressWarnings("unchecked") // we suppose we get back a List
      List<Object> list = (List<Object>) localCache.getObject(key);
      Class<?> targetType = resultObject.getSetterType(property);
      if (targetType.isAssignableFrom(list.getClass())) {
        value = list;
      } else if (objectFactory.isCollection(targetType)) {
        value = objectFactory.create(targetType);
        MetaObject metaObject = configuration.newMetaObject(value);
        metaObject.addAll(list);
      } else if (targetType.isArray()) {
        Object[] array = (Object[]) Array.newInstance(targetType.getComponentType(), list.size());
        value = list.toArray(array);
      } else {
        if (list != null && list.size() > 1) {
          throw new ExecutorException(
              "Statement returned more than one row, where no more than one was expected.");
        } else if (list != null && list.size() == 1) {
          value = list.get(0);
        }
      }
      resultObject.setValue(property, value);
    }
  }
}
/*
 * This is a simple, synchronous, thread-safe database connection pool.
 */
public class PooledDataSource implements DataSource {

  private static final Log log = LogFactory.getLog(PooledDataSource.class);

  private final PoolState state = new PoolState(this);

  private final UnpooledDataSource dataSource;

  // OPTIONAL CONFIGURATION FIELDS
  protected int poolMaximumActiveConnections = 10;
  protected int poolMaximumIdleConnections = 5;
  protected int poolMaximumCheckoutTime = 20000;
  protected int poolTimeToWait = 20000;
  protected String poolPingQuery = "NO PING QUERY SET";
  protected boolean poolPingEnabled = false;
  protected int poolPingConnectionsNotUsedFor = 0;

  private int expectedConnectionTypeCode;

  public PooledDataSource() {
    dataSource = new UnpooledDataSource();
  }

  public PooledDataSource(String driver, String url, String username, String password) {
    dataSource = new UnpooledDataSource(driver, url, username, password);
    expectedConnectionTypeCode =
        assembleConnectionTypeCode(
            dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  public PooledDataSource(String driver, String url, Properties driverProperties) {
    dataSource = new UnpooledDataSource(driver, url, driverProperties);
    expectedConnectionTypeCode =
        assembleConnectionTypeCode(
            dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  public PooledDataSource(
      ClassLoader driverClassLoader, String driver, String url, String username, String password) {
    dataSource = new UnpooledDataSource(driverClassLoader, driver, url, username, password);
    expectedConnectionTypeCode =
        assembleConnectionTypeCode(
            dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  public PooledDataSource(
      ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
    dataSource = new UnpooledDataSource(driverClassLoader, driver, url, driverProperties);
    expectedConnectionTypeCode =
        assembleConnectionTypeCode(
            dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  public Connection getConnection() throws SQLException {
    return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
  }

  public Connection getConnection(String username, String password) throws SQLException {
    return popConnection(username, password).getProxyConnection();
  }

  public void setLoginTimeout(int loginTimeout) throws SQLException {
    DriverManager.setLoginTimeout(loginTimeout);
  }

  public int getLoginTimeout() throws SQLException {
    return DriverManager.getLoginTimeout();
  }

  public void setLogWriter(PrintWriter logWriter) throws SQLException {
    DriverManager.setLogWriter(logWriter);
  }

  public PrintWriter getLogWriter() throws SQLException {
    return DriverManager.getLogWriter();
  }

  public void setDriver(String driver) {
    dataSource.setDriver(driver);
    forceCloseAll();
  }

  public void setUrl(String url) {
    dataSource.setUrl(url);
    forceCloseAll();
  }

  public void setUsername(String username) {
    dataSource.setUsername(username);
    forceCloseAll();
  }

  public void setPassword(String password) {
    dataSource.setPassword(password);
    forceCloseAll();
  }

  public void setDefaultAutoCommit(boolean defaultAutoCommit) {
    dataSource.setAutoCommit(defaultAutoCommit);
    forceCloseAll();
  }

  public void setDefaultTransactionIsolationLevel(Integer defaultTransactionIsolationLevel) {
    dataSource.setDefaultTransactionIsolationLevel(defaultTransactionIsolationLevel);
    forceCloseAll();
  }

  public void setDriverProperties(Properties driverProps) {
    dataSource.setDriverProperties(driverProps);
    forceCloseAll();
  }

  /*
   * The maximum number of active connections
   *
   * @param poolMaximumActiveConnections The maximum number of active connections
   */
  public void setPoolMaximumActiveConnections(int poolMaximumActiveConnections) {
    this.poolMaximumActiveConnections = poolMaximumActiveConnections;
    forceCloseAll();
  }

  /*
   * The maximum number of idle connections
   *
   * @param poolMaximumIdleConnections The maximum number of idle connections
   */
  public void setPoolMaximumIdleConnections(int poolMaximumIdleConnections) {
    this.poolMaximumIdleConnections = poolMaximumIdleConnections;
    forceCloseAll();
  }

  /*
   * The maximum time a connection can be used before it *may* be
   * given away again.
   *
   * @param poolMaximumCheckoutTime The maximum time
   */
  public void setPoolMaximumCheckoutTime(int poolMaximumCheckoutTime) {
    this.poolMaximumCheckoutTime = poolMaximumCheckoutTime;
    forceCloseAll();
  }

  /*
   * The time to wait before retrying to get a connection
   *
   * @param poolTimeToWait The time to wait
   */
  public void setPoolTimeToWait(int poolTimeToWait) {
    this.poolTimeToWait = poolTimeToWait;
    forceCloseAll();
  }

  /*
   * The query to be used to check a connection
   *
   * @param poolPingQuery The query
   */
  public void setPoolPingQuery(String poolPingQuery) {
    this.poolPingQuery = poolPingQuery;
    forceCloseAll();
  }

  /*
   * Determines if the ping query should be used.
   *
   * @param poolPingEnabled True if we need to check a connection before using it
   */
  public void setPoolPingEnabled(boolean poolPingEnabled) {
    this.poolPingEnabled = poolPingEnabled;
    forceCloseAll();
  }

  /*
   * If a connection has not been used in this many milliseconds, ping the
   * database to make sure the connection is still good.
   *
   * @param milliseconds the number of milliseconds of inactivity that will trigger a ping
   */
  public void setPoolPingConnectionsNotUsedFor(int milliseconds) {
    this.poolPingConnectionsNotUsedFor = milliseconds;
    forceCloseAll();
  }

  public String getDriver() {
    return dataSource.getDriver();
  }

  public String getUrl() {
    return dataSource.getUrl();
  }

  public String getUsername() {
    return dataSource.getUsername();
  }

  public String getPassword() {
    return dataSource.getPassword();
  }

  public boolean isAutoCommit() {
    return dataSource.isAutoCommit();
  }

  public Integer getDefaultTransactionIsolationLevel() {
    return dataSource.getDefaultTransactionIsolationLevel();
  }

  public Properties getDriverProperties() {
    return dataSource.getDriverProperties();
  }

  public int getPoolMaximumActiveConnections() {
    return poolMaximumActiveConnections;
  }

  public int getPoolMaximumIdleConnections() {
    return poolMaximumIdleConnections;
  }

  public int getPoolMaximumCheckoutTime() {
    return poolMaximumCheckoutTime;
  }

  public int getPoolTimeToWait() {
    return poolTimeToWait;
  }

  public String getPoolPingQuery() {
    return poolPingQuery;
  }

  public boolean isPoolPingEnabled() {
    return poolPingEnabled;
  }

  public int getPoolPingConnectionsNotUsedFor() {
    return poolPingConnectionsNotUsedFor;
  }

  /*
   * Closes all active and idle connections in the pool
   */
  public void forceCloseAll() {
    synchronized (state) {
      expectedConnectionTypeCode =
          assembleConnectionTypeCode(
              dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
      for (int i = state.activeConnections.size(); i > 0; i--) {
        try {
          PooledConnection conn = (PooledConnection) state.activeConnections.remove(i - 1);
          conn.invalidate();

          Connection realConn = conn.getRealConnection();
          if (!realConn.getAutoCommit()) {
            realConn.rollback();
          }
          realConn.close();
        } catch (Exception e) {
          // ignore
        }
      }
      for (int i = state.idleConnections.size(); i > 0; i--) {
        try {
          PooledConnection conn = (PooledConnection) state.idleConnections.remove(i - 1);
          conn.invalidate();

          Connection realConn = conn.getRealConnection();
          if (!realConn.getAutoCommit()) {
            realConn.rollback();
          }
          realConn.close();
        } catch (Exception e) {
          // ignore
        }
      }
    }
    if (log.isDebugEnabled()) {
      log.debug("PooledDataSource forcefully closed/removed all connections.");
    }
  }

  public PoolState getPoolState() {
    return state;
  }

  private int assembleConnectionTypeCode(String url, String username, String password) {
    return ("" + url + username + password).hashCode();
  }

  protected void pushConnection(PooledConnection conn) throws SQLException {

    synchronized (state) {
      state.activeConnections.remove(conn);
      if (conn.isValid()) {
        if (state.idleConnections.size() < poolMaximumIdleConnections
            && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
          state.idleConnections.add(newConn);
          newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
          newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
          conn.invalidate();
          if (log.isDebugEnabled()) {
            log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
          }
          state.notifyAll();
        } else {
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          conn.getRealConnection().close();
          if (log.isDebugEnabled()) {
            log.debug("Closed connection " + conn.getRealHashCode() + ".");
          }
          conn.invalidate();
        }
      } else {
        if (log.isDebugEnabled()) {
          log.debug(
              "A bad connection ("
                  + conn.getRealHashCode()
                  + ") attempted to return to the pool, discarding connection.");
        }
        state.badConnectionCount++;
      }
    }
  }

  private PooledConnection popConnection(String username, String password) throws SQLException {
    boolean countedWait = false;
    PooledConnection conn = null;
    long t = System.currentTimeMillis();
    int localBadConnectionCount = 0;

    while (conn == null) {
      synchronized (state) {
        if (state.idleConnections.size() > 0) {
          // Pool has available connection
          conn = (PooledConnection) state.idleConnections.remove(0);
          if (log.isDebugEnabled()) {
            log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
          }
        } else {
          // Pool does not have available connection
          if (state.activeConnections.size() < poolMaximumActiveConnections) {
            // Can create new connection
            conn = new PooledConnection(dataSource.getConnection(), this);
            @SuppressWarnings("unused")
            // used in logging, if enabled
            Connection realConn = conn.getRealConnection();
            if (log.isDebugEnabled()) {
              log.debug("Created connection " + conn.getRealHashCode() + ".");
            }
          } else {
            // Cannot create new connection
            PooledConnection oldestActiveConnection =
                (PooledConnection) state.activeConnections.get(0);
            long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
            if (longestCheckoutTime > poolMaximumCheckoutTime) {
              // Can claim overdue connection
              state.claimedOverdueConnectionCount++;
              state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
              state.accumulatedCheckoutTime += longestCheckoutTime;
              state.activeConnections.remove(oldestActiveConnection);
              if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
                oldestActiveConnection.getRealConnection().rollback();
              }
              conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
              oldestActiveConnection.invalidate();
              if (log.isDebugEnabled()) {
                log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
              }
            } else {
              // Must wait
              try {
                if (!countedWait) {
                  state.hadToWaitCount++;
                  countedWait = true;
                }
                if (log.isDebugEnabled()) {
                  log.debug(
                      "Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
                }
                long wt = System.currentTimeMillis();
                state.wait(poolTimeToWait);
                state.accumulatedWaitTime += System.currentTimeMillis() - wt;
              } catch (InterruptedException e) {
                break;
              }
            }
          }
        }
        if (conn != null) {
          if (conn.isValid()) {
            if (!conn.getRealConnection().getAutoCommit()) {
              conn.getRealConnection().rollback();
            }
            conn.setConnectionTypeCode(
                assembleConnectionTypeCode(dataSource.getUrl(), username, password));
            conn.setCheckoutTimestamp(System.currentTimeMillis());
            conn.setLastUsedTimestamp(System.currentTimeMillis());
            state.activeConnections.add(conn);
            state.requestCount++;
            state.accumulatedRequestTime += System.currentTimeMillis() - t;
          } else {
            if (log.isDebugEnabled()) {
              log.debug(
                  "A bad connection ("
                      + conn.getRealHashCode()
                      + ") was returned from the pool, getting another connection.");
            }
            state.badConnectionCount++;
            localBadConnectionCount++;
            conn = null;
            if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) {
              if (log.isDebugEnabled()) {
                log.debug("PooledDataSource: Could not get a good connection to the database.");
              }
              throw new SQLException(
                  "PooledDataSource: Could not get a good connection to the database.");
            }
          }
        }
      }
    }

    if (conn == null) {
      if (log.isDebugEnabled()) {
        log.debug(
            "PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
      }
      throw new SQLException(
          "PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
    }

    return conn;
  }

  /*
   * Method to check to see if a connection is still usable
   *
   * @param conn - the connection to check
   * @return True if the connection is still usable
   */
  protected boolean pingConnection(PooledConnection conn) {
    boolean result = true;

    try {
      result = !conn.getRealConnection().isClosed();
    } catch (SQLException e) {
      if (log.isDebugEnabled()) {
        log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
      }
      result = false;
    }

    if (result) {
      if (poolPingEnabled) {
        if (poolPingConnectionsNotUsedFor >= 0
            && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {
          try {
            if (log.isDebugEnabled()) {
              log.debug("Testing connection " + conn.getRealHashCode() + " ...");
            }
            Connection realConn = conn.getRealConnection();
            Statement statement = realConn.createStatement();
            ResultSet rs = statement.executeQuery(poolPingQuery);
            rs.close();
            statement.close();
            if (!realConn.getAutoCommit()) {
              realConn.rollback();
            }
            result = true;
            if (log.isDebugEnabled()) {
              log.debug("Connection " + conn.getRealHashCode() + " is GOOD!");
            }
          } catch (Exception e) {
            log.warn("Execution of ping query '" + poolPingQuery + "' failed: " + e.getMessage());
            try {
              conn.getRealConnection().close();
            } catch (Exception e2) {
              // ignore
            }
            result = false;
            if (log.isDebugEnabled()) {
              log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
            }
          }
        }
      }
    }
    return result;
  }

  /*
   * Unwraps a pooled connection to get to the 'real' connection
   *
   * @param conn - the pooled connection to unwrap
   * @return The 'real' connection
   */
  public static Connection unwrapConnection(Connection conn) {
    if (Proxy.isProxyClass(conn.getClass())) {
      InvocationHandler handler = Proxy.getInvocationHandler(conn);
      if (handler instanceof PooledConnection) {
        return ((PooledConnection) handler).getRealConnection();
      }
    }
    return conn;
  }

  protected void finalize() throws Throwable {
    forceCloseAll();
  }

  public <T> T unwrap(Class<T> iface) throws SQLException {
    throw new SQLException(getClass().getName() + " is not a wrapper.");
  }

  public boolean isWrapperFor(Class<?> iface) throws SQLException {
    return false;
  }

  public Logger getParentLogger() {
    return Logger.getLogger(LogFactory.GLOBAL_LOGGER_NAME);
  }
}
/** @author Clinton Begin */
public class CglibProxyFactory implements ProxyFactory {

  private static final Log log = LogFactory.getLog(CglibProxyFactory.class);
  private static final String FINALIZE_METHOD = "finalize";
  private static final String WRITE_REPLACE_METHOD = "writeReplace";

  public CglibProxyFactory() {
    try {
      Resources.classForName("net.sf.cglib.proxy.Enhancer");
    } catch (Throwable e) {
      throw new IllegalStateException(
          "Cannot enable lazy loading because CGLIB is not available. Add CGLIB to your classpath.",
          e);
    }
  }

  public Object createProxy(
      Object target,
      ResultLoaderMap lazyLoader,
      Configuration configuration,
      ObjectFactory objectFactory,
      List<Class<?>> constructorArgTypes,
      List<Object> constructorArgs) {
    return EnhancedResultObjectProxyImpl.createProxy(
        target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
  }

  public Object createDeserializationProxy(
      Object target,
      Map<String, ResultLoaderMap.LoadPair> unloadedProperties,
      ObjectFactory objectFactory,
      List<Class<?>> constructorArgTypes,
      List<Object> constructorArgs) {
    return EnhancedDeserializationProxyImpl.createProxy(
        target, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
  }

  public void setProperties(Properties properties) {}

  private static Object crateProxy(
      Class<?> type,
      Callback callback,
      List<Class<?>> constructorArgTypes,
      List<Object> constructorArgs) {
    Enhancer enhancer = new Enhancer();
    enhancer.setCallback(callback);
    enhancer.setSuperclass(type);
    try {
      type.getDeclaredMethod(WRITE_REPLACE_METHOD);
      // ObjectOutputStream will call writeReplace of objects returned by writeReplace
      log.debug(
          WRITE_REPLACE_METHOD
              + " method was found on bean "
              + type
              + ", make sure it returns this");
    } catch (NoSuchMethodException e) {
      enhancer.setInterfaces(new Class[] {WriteReplaceInterface.class});
    } catch (SecurityException e) {
      // nothing to do here
    }
    Object enhanced = null;
    if (constructorArgTypes.isEmpty()) {
      enhanced = enhancer.create();
    } else {
      Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
      Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
      enhanced = enhancer.create(typesArray, valuesArray);
    }
    return enhanced;
  }

  private static class EnhancedResultObjectProxyImpl implements MethodInterceptor {

    private Class<?> type;
    private ResultLoaderMap lazyLoader;
    private boolean aggressive;
    private Set<String> lazyLoadTriggerMethods;
    private ObjectFactory objectFactory;
    private List<Class<?>> constructorArgTypes;
    private List<Object> constructorArgs;

    private EnhancedResultObjectProxyImpl(
        Class<?> type,
        ResultLoaderMap lazyLoader,
        Configuration configuration,
        ObjectFactory objectFactory,
        List<Class<?>> constructorArgTypes,
        List<Object> constructorArgs) {
      this.type = type;
      this.lazyLoader = lazyLoader;
      this.aggressive = configuration.isAggressiveLazyLoading();
      this.lazyLoadTriggerMethods = configuration.getLazyLoadTriggerMethods();
      this.objectFactory = objectFactory;
      this.constructorArgTypes = constructorArgTypes;
      this.constructorArgs = constructorArgs;
    }

    public static Object createProxy(
        Object target,
        ResultLoaderMap lazyLoader,
        Configuration configuration,
        ObjectFactory objectFactory,
        List<Class<?>> constructorArgTypes,
        List<Object> constructorArgs) {
      final Class<?> type = target.getClass();
      EnhancedResultObjectProxyImpl callback =
          new EnhancedResultObjectProxyImpl(
              type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
      Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
      PropertyCopier.copyBeanProperties(type, target, enhanced);
      return enhanced;
    }

    public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy)
        throws Throwable {
      final String methodName = method.getName();
      try {
        synchronized (lazyLoader) {
          if (WRITE_REPLACE_METHOD.equals(methodName)) {
            Object original = null;
            if (constructorArgTypes.isEmpty()) {
              original = objectFactory.create(type);
            } else {
              original = objectFactory.create(type, constructorArgTypes, constructorArgs);
            }
            PropertyCopier.copyBeanProperties(type, enhanced, original);
            if (lazyLoader.size() > 0) {
              return new CglibSerialStateHolder(
                  original,
                  lazyLoader.getProperties(),
                  objectFactory,
                  constructorArgTypes,
                  constructorArgs);
            } else {
              return original;
            }
          } else {
            if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
              if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
                lazyLoader.loadAll();
              } else if (PropertyNamer.isProperty(methodName)) {
                final String property = PropertyNamer.methodToProperty(methodName);
                if (lazyLoader.hasLoader(property)) {
                  lazyLoader.load(property);
                }
              }
            }
          }
        }
        return methodProxy.invokeSuper(enhanced, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
  }

  private static class EnhancedDeserializationProxyImpl extends AbstractEnhancedDeserializationProxy
      implements MethodInterceptor {

    private EnhancedDeserializationProxyImpl(
        Class<?> type,
        Map<String, ResultLoaderMap.LoadPair> unloadedProperties,
        ObjectFactory objectFactory,
        List<Class<?>> constructorArgTypes,
        List<Object> constructorArgs) {
      super(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
    }

    public static Object createProxy(
        Object target,
        Map<String, ResultLoaderMap.LoadPair> unloadedProperties,
        ObjectFactory objectFactory,
        List<Class<?>> constructorArgTypes,
        List<Object> constructorArgs) {
      final Class<?> type = target.getClass();
      EnhancedDeserializationProxyImpl callback =
          new EnhancedDeserializationProxyImpl(
              type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
      Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
      PropertyCopier.copyBeanProperties(type, target, enhanced);
      return enhanced;
    }

    @Override
    public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy)
        throws Throwable {
      final Object o = super.invoke(enhanced, method, args);
      return (o instanceof AbstractSerialStateHolder) ? o : methodProxy.invokeSuper(o, args);
    }

    @Override
    protected AbstractSerialStateHolder newSerialStateHolder(
        Object userBean,
        Map<String, ResultLoaderMap.LoadPair> unloadedProperties,
        ObjectFactory objectFactory,
        List<Class<?>> constructorArgTypes,
        List<Object> constructorArgs) {
      return new CglibSerialStateHolder(
          userBean, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
    }
  }
}