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.xml | ACT_EVT_LOG | EventLogEntryEntity |
ByteArray.xml | ACT_GE_BYTEARRAY | ByteArrayEntity |
Resource.xml | ACT_GE_BYTEARRAY | ResourceEntity |
Property.xml | ACT_GE_PROPERTY | PropertyEntity |
HistoricActivityInstance.xml | ACT_HI_ACTINST | HistoricActivityInstanceEntity |
Attachment.xml | ACT_HI_ATTACHMENT | AttachmentEntity |
Comment.xml | ACT_HI_COMMENT | CommentEntity |
HistoricDetail.xml | ACT_HI_DETAIL | HistoricFormPropertyEntity |
HistoricIdentityLink.xml | ACT_HI_IDENTITYLINK | HistoricIdentityEntity |
HistoricProcessInstance.xml | ACT_HI_PROCINST | HistoricProcessInstanceEntity |
HistoricTaskInstance.xml | ACT_HI_TASKINST | HistoricTaskInstanceEntity |
HistoricVariableInstance.xml | ACT_HI_VARINST | HistoricVariableInstanceEntity |
Group.xml | ACT_ID_GROUP | GroupEntity |
IdentityInfo.xml | ACT_ID_INFO | IdentityInfoEntity |
Membership.xml | ACT_ID_MEMBERSHIP | MembershipEntity |
User.xml | ACT_ID_USER | UserEntity |
ProcessDefinitionInfo.xml | ACT_PROCDEF_INFO | ProcessDefinitionInfoEntity |
Deployment.xml | ACT_RE_DEPLOYMENT | DeploymentEntity |
Model.xml | ACT_RE_MODEL | ModelEntity |
ProcessDefinition.xml | ACT_RE_PROCDEF | ProcessDefinitionEntity |
EventSubscription.xml | ACT_RU_EVENT_SUBSCR | EventSubscriptionEntity |
Execution.xml | ACT_RU_EXECUTION | ExecutionEntity |
IdentityLink.xml | ACT_RU_IDENTITYLINK | IdentityLinkEntity |
Job.xml | ACT_RU_JOB | JobEntity |
Task.xml | ACT_RU_TASK | TaskEntity |
VariableInstance.xml | ACT_RU_VARIABLE | VariableInstanceEntity |
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,因此使用实体管理类必须通过命令类。