activiti学习(十二)——activiti数据库存储(一)——数据连接初始化与mybatis封装原理

activiti对持久化层的初始化主要有几个步骤:1、初始化数据源。这个很好理解,就是配置好要连接的数据库;2、初始化mybatis的SqlSessionFactory。因为activiti使用mybatis作为持久化层,所以需要对mybatis进行相应的配置和初始化;3、封装增删查改操作,不让用户直接使用mybatis进行操作。主要通过sessionFactory和XXXEntityManagement来进行封装。本文主要讲讲这三个步骤。

初始化数据源

在流程引擎配置的时候,我们通常要指定对应的数据库信息,例如我们配置在activiti.cfg.xml中的数据库连接信息。在流程引擎初始化的时候,进行对应的设置。查看ProcessEngineConfigurationImpl.java

protected void initDataSource() {
  if (dataSource==null) {
    if (dataSourceJndiName!=null) {
      try {
        dataSource = (DataSource) new InitialContext().lookup(dataSourceJndiName);
      } catch (Exception e) {
        throw new ActivitiException("couldn't lookup datasource from "+dataSourceJndiName+": "+e.getMessage(), e);
      }
      
    } else if (jdbcUrl!=null) {
      if ( (jdbcDriver==null) || (jdbcUrl==null) || (jdbcUsername==null) ) {
        throw new ActivitiException("DataSource or JDBC properties have to be specified in a process engine configuration");
      }
      
      log.debug("initializing datasource to db: {}", jdbcUrl);
      
      PooledDataSource pooledDataSource = 
        new PooledDataSource(ReflectUtil.getClassLoader(), jdbcDriver, jdbcUrl, jdbcUsername, jdbcPassword );
      
      ......//省略设置pooledDataSource属性
  }

  if (databaseType == null) {
    initDatabaseType();
  }
}

3-10行判断dataSourceJndiName是否已设置,非空则通过JNDI方式配置数据源。10-21行判断jdbcUrl是否为空,非空则实例化PooledDataSource作为数据源,并对其属性进行设置。24行初始化数据库的类型。

public void initDatabaseType() {
  Connection connection = null;
  try {
    connection = dataSource.getConnection();
    DatabaseMetaData databaseMetaData = connection.getMetaData();
    String databaseProductName = databaseMetaData.getDatabaseProductName();
    databaseType = databaseTypeMappings.getProperty(databaseProductName);
    if (databaseType==null) {
      throw new ActivitiException("couldn't deduct database type from database product name '"+databaseProductName+"'");
    }

  } catch (SQLException e) {
    log.error("Exception while initializing Database connection", e);
  } finally {
    try {
      if (connection!=null) {
        connection.close();
      }
    } catch (SQLException e) {
        log.error("Exception while closing the Database connection", e);
    }
  }
}

第4行打开数据库连接,第7行通过databaseTypeMappings获取对应的数据库类型,并赋值到databaseType上。在ProcessEngineConfigurationImpl.java初始化时,已设置了多种默认的数据库类型

protected static Properties databaseTypeMappings = getDefaultDatabaseTypeMappings();

public static final String DATABASE_TYPE_H2 = "h2";
public static final String DATABASE_TYPE_HSQL = "hsql";
public static final String DATABASE_TYPE_MYSQL = "mysql";
public static final String DATABASE_TYPE_ORACLE = "oracle";
public static final String DATABASE_TYPE_POSTGRES = "postgres";
public static final String DATABASE_TYPE_MSSQL = "mssql";
public static final String DATABASE_TYPE_DB2 = "db2";

protected static Properties getDefaultDatabaseTypeMappings() {
  Properties databaseTypeMappings = new Properties();
  databaseTypeMappings.setProperty("H2", DATABASE_TYPE_H2);
  databaseTypeMappings.setProperty("HSQL Database Engine", DATABASE_TYPE_HSQL);
  databaseTypeMappings.setProperty("MySQL", DATABASE_TYPE_MYSQL);
  databaseTypeMappings.setProperty("Oracle", DATABASE_TYPE_ORACLE);
  databaseTypeMappings.setProperty("PostgreSQL", DATABASE_TYPE_POSTGRES);
  databaseTypeMappings.setProperty("Microsoft SQL Server", DATABASE_TYPE_MSSQL);
  ......//省略

  return databaseTypeMappings;
}

初始化SqlSessionFactory

mybatis操作数据库是基于SqlSessionFactory实现的,接下来看看activiti如何初始化SqlSessionFactory。在ProcessEngineConfigurationImpl.java:

public static final String DEFAULT_MYBATIS_MAPPING_FILE = "org/activiti/db/mapping/mappings.xml";
protected void initSqlSessionFactory() {
  if (sqlSessionFactory==null) {
    InputStream inputStream = null;
    try {
      inputStream = getMyBatisXmlConfigurationSteam();
      Environment environment = new Environment("default", transactionFactory, dataSource);
      Reader reader = new InputStreamReader(inputStream);
      Properties properties = new Properties();
      properties.put("prefix", databaseTablePrefix);
      String wildcardEscapeClause = "";
      if ((databaseWildcardEscapeCharacter != null) && (databaseWildcardEscapeCharacter.length() != 0)) {
        wildcardEscapeClause = " escape '" + databaseWildcardEscapeCharacter + "'";
      }
      properties.put("wildcardEscapeClause", wildcardEscapeClause);
      if(databaseType != null) {
        properties.put("limitBefore" , DbSqlSessionFactory.databaseSpecificLimitBeforeStatements.get(databaseType));
        properties.put("limitAfter" , DbSqlSessionFactory.databaseSpecificLimitAfterStatements.get(databaseType));
        properties.put("limitBetween" , DbSqlSessionFactory.databaseSpecificLimitBetweenStatements.get(databaseType));
        properties.put("limitOuterJoinBetween" , DbSqlSessionFactory.databaseOuterJoinLimitBetweenStatements.get(databaseType));
        properties.put("orderBy" , DbSqlSessionFactory.databaseSpecificOrderByStatements.get(databaseType));
        properties.put("limitBeforeNativeQuery" , ObjectUtils.toString(DbSqlSessionFactory.databaseSpecificLimitBeforeNativeQueryStatements.get(databaseType)));
      }
      
      Configuration configuration = initMybatisConfiguration(environment, reader, properties);
      sqlSessionFactory = new DefaultSqlSessionFactory(configuration);

    } catch (Exception e) {
      throw new ActivitiException("Error while building ibatis SqlSessionFactory: " + e.getMessage(), e);
    } finally {
      IoUtil.closeSilently(inputStream);
    }
  }
}

protected InputStream getMyBatisXmlConfigurationSteam() {
  return getResourceAsStream(DEFAULT_MYBATIS_MAPPING_FILE);
}

protected Configuration initMybatisConfiguration(Environment environment, Reader reader, Properties properties) {
  XMLConfigBuilder parser = new XMLConfigBuilder(reader,"", properties);
  Configuration configuration = parser.getConfiguration();
  configuration.setEnvironment(environment);
  
  initMybatisTypeHandlers(configuration);
  initCustomMybatisMappers(configuration);
  
  configuration = parseMybatisConfiguration(configuration, parser);
  return configuration;
}

protected void initMybatisTypeHandlers(Configuration configuration) {
  configuration.getTypeHandlerRegistry().register(VariableType.class, JdbcType.VARCHAR, new IbatisVariableTypeHandler());
}

protected void initCustomMybatisMappers(Configuration configuration) {
  if (getCustomMybatisMappers() != null) {
	for (Class<?> clazz : getCustomMybatisMappers()) {
      configuration.addMapper(clazz);
    }
  }
}

第6行调用36-38行代码,获取mybatis的Mapper映射文件的数据流,可知默认在org/activiti/db/mapping/mappings.xml处。第7行创建mybatis的Environment,15-23行设置属性,用于构造Configuration。25行调用40-50行构造Configuration。26行创建DefaultSqlSessionFactory。45行调用52-54行,主要负责注册IbatisVariableTypeHandler类,用于把activiti中VariableType类与VARCHAR类型进行转换。46行调用56-62行注册用户自定义Mapper到Configuration中。

关于实体类和数据库表的映射

在SqlSessionFactory初始化时,我们读取org/activiti/db/mapping/mappings.xml文件,它是mybatis的Mapper映射文件。我们看看其内容:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
  <settings>
    <setting name="lazyLoadingEnabled" value="false" />
  </settings>
  <typeAliases>
    <typeAlias type="org.activiti.engine.impl.persistence.ByteArrayRefTypeHandler" alias="ByteArrayRefTypeHandler"/>
  </typeAliases>
  <typeHandlers>
    <typeHandler handler="ByteArrayRefTypeHandler" 
                 javaType="org.activiti.engine.impl.persistence.entity.ByteArrayRef"
                 jdbcType="VARCHAR"/>
  </typeHandlers>
  <mappers>
    <mapper resource="org/activiti/db/mapping/entity/Attachment.xml" />
    <mapper resource="org/activiti/db/mapping/entity/ByteArray.xml" />
    <mapper resource="org/activiti/db/mapping/entity/Comment.xml" />
    
......//省略映射文件
  </mappers>
</configuration>

mapper里面,我们可以找到实体类与数据库表的对应关系,整理关系如下:

映射文件名数据库表名实体类名
EventLogEntry.xmlACT_EVT_LOGEventLogEntryEntity
ByteArray.xmlACT_GE_BYTEARRAYByteArrayEntity
Resource.xmlACT_GE_BYTEARRAYResourceEntity
Property.xmlACT_GE_PROPERTYPropertyEntity
HistoricActivityInstance.xmlACT_HI_ACTINSTHistoricActivityInstanceEntity
Attachment.xmlACT_HI_ATTACHMENTAttachmentEntity
Comment.xmlACT_HI_COMMENTCommentEntity
HistoricDetail.xmlACT_HI_DETAILHistoricFormPropertyEntity
HistoricIdentityLink.xmlACT_HI_IDENTITYLINKHistoricIdentityEntity
HistoricProcessInstance.xmlACT_HI_PROCINSTHistoricProcessInstanceEntity
HistoricTaskInstance.xmlACT_HI_TASKINSTHistoricTaskInstanceEntity
HistoricVariableInstance.xmlACT_HI_VARINSTHistoricVariableInstanceEntity
Group.xmlACT_ID_GROUPGroupEntity
IdentityInfo.xmlACT_ID_INFOIdentityInfoEntity
Membership.xmlACT_ID_MEMBERSHIPMembershipEntity
User.xmlACT_ID_USERUserEntity
ProcessDefinitionInfo.xmlACT_PROCDEF_INFOProcessDefinitionInfoEntity
Deployment.xmlACT_RE_DEPLOYMENTDeploymentEntity
Model.xmlACT_RE_MODELModelEntity
ProcessDefinition.xmlACT_RE_PROCDEFProcessDefinitionEntity
EventSubscription.xmlACT_RU_EVENT_SUBSCREventSubscriptionEntity
Execution.xmlACT_RU_EXECUTIONExecutionEntity
IdentityLink.xmlACT_RU_IDENTITYLINKIdentityLinkEntity
Job.xmlACT_RU_JOBJobEntity
Task.xmlACT_RU_TASKTaskEntity
VariableInstance.xmlACT_RU_VARIABLEVariableInstanceEntity
TableData.xml引擎所在数据库的所有表 

初始化SessionFactory

activiti在初始化SqlSessionFactory后,并没有直接提供给外部使用,而是通过DbSqlSessionFactory类进行封装,最后通过activiti的DbSqlSession类调用mybatis的SqlSession进行增删查改操作。另外activiti通过抽象工厂的设计模式为上表中XXXEntity构造对应的XXXEntityManagement类封装插入、删除等操作供外部使用。其架构比较复杂,本文不进行展示,留待后面再讲。看看ProcessEngineConfigurationImpl.java中对SessionFactory初始化:

protected void initSessionFactories() {
  if (sessionFactories==null) {
    sessionFactories = new HashMap<Class<?>, SessionFactory>();

    if (dbSqlSessionFactory == null) {
      dbSqlSessionFactory = new DbSqlSessionFactory();
    }
    dbSqlSessionFactory.setDatabaseType(databaseType);
    dbSqlSessionFactory.setIdGenerator(idGenerator);
    dbSqlSessionFactory.setSqlSessionFactory(sqlSessionFactory);
    dbSqlSessionFactory.setDbIdentityUsed(isDbIdentityUsed);
    dbSqlSessionFactory.setDbHistoryUsed(isDbHistoryUsed);
    dbSqlSessionFactory.setDatabaseTablePrefix(databaseTablePrefix);
    dbSqlSessionFactory.setTablePrefixIsSchema(tablePrefixIsSchema);
    dbSqlSessionFactory.setDatabaseCatalog(databaseCatalog);
    dbSqlSessionFactory.setDatabaseSchema(databaseSchema);
    dbSqlSessionFactory.setBulkInsertEnabled(isBulkInsertEnabled, databaseType);
    dbSqlSessionFactory.setMaxNrOfStatementsInBulkInsert(maxNrOfStatementsInBulkInsert);
    addSessionFactory(dbSqlSessionFactory);
    
    addSessionFactory(new GenericManagerFactory(AttachmentEntityManager.class));
    addSessionFactory(new GenericManagerFactory(CommentEntityManager.class));
    addSessionFactory(new GenericManagerFactory(DeploymentEntityManager.class));
	......//省略
  }
  
  if (customSessionFactories!=null) {
    for (SessionFactory sessionFactory: customSessionFactories) {
      addSessionFactory(sessionFactory);
    }
  }
}

protected void addSessionFactory(SessionFactory sessionFactory) {
  sessionFactories.put(sessionFactory.getSessionType(), sessionFactory);
}

sessionFactories是一个map,key值是sessionFactory.getSessionType()的值,即XXXEntityManagement.class。在流程引擎对实体类进行增删改查时,可以根据XXXEntityManagement.class的类型,新建并返回对应实体管理类。实体管理类里面封装一些实体的增删查改方法供用户使用。注意由于这些实体管理类操作都需要通过CommandContext获取DbSqlSession,因此使用实体管理类必须通过命令类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值