解決環境搭建中的問題(過程記錄)
1、數據源的創建
@Configuration
public class AuditDatasourceConfig extends AbstractProcessEngineAutoConfiguration {
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource.act")
@Qualifier("activitiDataSource")
public DataSource activitiDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.ihrm")
@Qualifier("ihrmDataSource")
public DataSource ihrmDataSource() {
return DataSourceBuilder.create().build();
}
}
2、多數據源的持久化配置
@Configuration
@EnableJpaRepositories(
basePackages = "com.ihrm.audit.dao",
entityManagerFactoryRef = "ihrmEntityManager",
transactionManagerRef = "ihrmTransactionManager"
)
public class JpaRepositoriesConfig {
@Autowired
private Environment env;
@Autowired
@Qualifier("ihrmDataSource")
private DataSource ihrmDataSource;
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean ihrmEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(ihrmDataSource);
em.setPackagesToScan(new String[] { "com.ihrm.audit.entity" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
/************** 第一個問題的出處*************/
properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
/********************************************/
em.setJpaPropertyMap(properties);
return em;
}
@Primary
@Bean
public PlatformTransactionManager ihrmTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory( ihrmEntityManager().getObject());
return transactionManager;
}
}
Q1
問題大致是這樣
仔細一看問題主要如下
org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when ‘hibernate.dialect’ not set
原來是
@Autowired
private Environment env;
properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
的問題,經過測試輸出結果查看,果不其然
其中以下代碼
@Autowired
private Environment env;
env.getProperty("hibernate.hbm2ddl.auto")
env.getProperty("hibernate.dialect")
在環境變量中完全吧獲取不到任何屬性,於是鎖定了錯誤所在。
A1
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean ihrmEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(ihrmDataSource);
em.setPackagesToScan(new String[] { "com.ihrm.audit.entity" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
/*********修改後的代碼********************/
properties.put("hibernate.hbm2ddl.auto", "update");
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
em.setJpaPropertyMap(properties);
/*************************************/
/*System.out.println("*********************************************************************");
System.out.println(env.getProperty("hibernate.hbm2ddl.auto"));
System.out.println(env.getProperty("hibernate.dialect"));
System.out.println("*********************************************************************");*/
return em;
}
其中hibernate.hbm2ddl.auto
和hibernate.dialect
需要手動設置屬性值。
也可以把屬性值都設置在配置文件中,之後再通過env去獲取。
注:
hibernate.hbm2ddl.auto
屬性值說明:
自動創建|更新|驗證數據庫表結構。如果不是此方面的需求建議填寫屬性值爲"none"
。
create
:
每次加載hibernate時都會刪除上一次的生成的表,然後根據你的model類再重新來生成新表,哪怕兩次沒有任何改變也要這樣執行,這就是導致數據庫表數據丟失的一個重要原因。
create-drop
:
每次加載hibernate時根據model類生成表,但是sessionFactory一關閉,表就自動刪除。
update
:
最常用的屬性,第一次加載hibernate時根據model類會自動建立起表的結構(前提是先建立好數據庫),以後加載hibernate時根據 model類自動更新表結構,即使表結構改變了但表中的行仍然存在不會刪除以前的行。要注意的是當部署到服務器後,表結構是不會被馬上建立起來的,是要等 應用第一次運行起來後纔會。
validate
:
每次加載hibernate時,驗證創建數據庫表結構,只會和數據庫中的表進行比較,不會創建新表,但是會插入新值。
Q2
在搭建過程中還會出現這樣的錯誤
Invalid syntax error “type= MyISAM” in DDL generated by Hibernate
A2
原因是開始的時候hibernate.dialect
使用的屬性值是org.hibernate.dialect.MySQLDialect
,在通過Hiberbate自動創建表的時候SQL語句中會使用TYPE
,然而在MySQL5.0以後就廢棄了這樣的語法,統一使用ENGINE
。故而,必須把屬性值設置爲org.hibernate.dialect.MySQL5Dialect
Q3
Unable to build Hibernate SessionFactory; nested exception is java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.
一看這個錯誤有些懵,明明加入了JDBC驅動了爲啥還來報錯!
A3
原因是這樣的,在單數據源的時候,在配置文件中直接使用url
是可以的,然而現在是都數據源的情況,那麼就必須使用jdbc-url
作爲屬性的key
datasource:
act:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/act?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
jdbc-url: jdbc:mysql://localhost:3306/act?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: 123456
ihrm:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ihrm?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
jdbc-url: jdbc:mysql://localhost:3306/ihrm?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: 123456
加上jdbc-url
就可以解決這個Bug了!
Q4
多數據源的問題解決了,主要矛盾開始向Activiti7轉移
java.sql.SQLSyntaxErrorException: Unknown column ‘VERSION_’ in ‘field list’
A4
原因是這樣的,一開始使用的是目前最新的Activiti的插件版本
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M6</version>
</dependency>
這個版本目前如果要自己創建activiti的數據表的話只支持MySQL5.0的版本,也就是需要給數據庫降級,這顯然不能滿足需求,因此,首先需要版本回退,先不使用這個版本插件,我這裏直接使用了<version>7.1.0.M2</version>
因爲合格版本比較老,似乎沒有這個Bug。但是如果要Activiti7自動創建數據庫也還是不行的,需要通過查閱資料把Activiti7要用的數據表(附件下載SQL)使用MySQL8.0手動創建好,之後填寫好配置文件。
activiti:
# database-schema-update: true
history-level: full
db-history-used: true
其中database-schema-update
參數會涉及到表的創建與刪除,特別說明一下:
flase
: 默認值。activiti在啓動時,會對比數據庫表中保存的版本,如果沒有表或者版本不匹配,將拋出異常。
true
: activiti會對數據庫中所有表進行更新操作。如果表不存在,則自動創建。
create_drop
: 在activiti啓動時創建表,在關閉時刪除表(必須手動關閉引擎,才能刪除表)。
drop-create
: 在activiti啓動時刪除原來的舊錶,然後在創建新表(不需要手動關閉引擎)
還有history-level
:
none
:不保存任何的歷史數據,因此,在流程執行過程中,這是最高效的。
activity
:級別高於none,保存流程實例與流程行爲,其他數據不保存。
audit
:除activity級別會保存的數據外,還會保存全部的流程任務及其屬性。audit爲history的默認值。
full
:保存歷史數據的最高級別,除了會保存audit級別的數據外,還會保存其他全部流程相關的細節數據,包括一些流程參數等。
db-history-used: true
:表示使用歷史表,如果不配置,則工程啓動後可以檢查數據庫,只建立了17張表,歷史表沒有建立,則流程圖及運行節點無法展示
Q5
在整合Activiti7的過程中,還出現了java.lang.NullPointerException
的錯誤
A5
原因是Activiti7所需要使用的數據表
裏面沒有數據,一般情況下,Activiti7在自動創建這張表之後會加入一些配置信息,那麼可以插入如下數據
INSERT INTO `act_ge_property` VALUES ('cfg.execution-related-entities-count', 'false', 1);
INSERT INTO `act_ge_property` VALUES ('next.dbid', '1', 1);
INSERT INTO `act_ge_property` VALUES ('schema.history', 'create(7.0.0.0)', 1);
INSERT INTO `act_ge_property` VALUES ('schema.version', '7.0.0.0', 1);
Q6
在以上有關數據庫問題解決後,還會出現一些細枝末節的問題
比如
org.apache.commons.io.IOUtils.toString(Ljava/io/InputStream;Ljava/nio/charset/Charset;)Ljava/lang/String;
java.lang.NoClassDefFoundError: org/w3c/dom/ElementTraversal
A6
這些問題都可以同意通過引入相應依賴進行解決
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>1.4.01</version>
</dependency>
對待這些問題的解決辦法就是知道了他們是缺少方法或者類,那就進行引入。
總結
這次花了挺長時間來整合Activiti7與SpringBoot2,其中的坑還是真不少,在面對這種新事物整合的時候,需要不怕困難敢於亮劍的精神,面對IDE報出的成堆錯誤信息,要學會快速鎖定錯誤的方法,那就是快速找到IDE錯誤日誌的最後,鎖定錯誤原因,之後通過Google進行解決,在解決錯誤的過程中也要靈活機變,畢竟錯誤不一定是相同的,要相互啓發,勇於實踐,敢於實驗,一步一個腳印的解決錯誤。
補充
Q1
1、在進行如上配置以後JPA查詢不到結果或無法插入數據,一個原因是hibernate.hbm2ddl.auto
使用了create
或create-drop
屬性,這樣會新建一張空表或在代碼執行結束以後刪除數據庫表。
2、當hibernate.hbm2ddl.auto
使用了none
或update
屬性後可能會出現,SQLSyntaxErrorException:Unknown column 'xxxxxxx' in 'field list'
的情況。
A1
解決辦法是,JPA查詢不到數據是因爲hibernate.hbm2ddl.auto
使用了create
或 create-drop
屬性,因爲這樣會覆蓋原來的數據表,在實體類的屬性名稱上加@Column
註解,就能解決第二個問題。