最近更新为spring boot 2.1.7
后,遇到了一系列小的问题。本文阐述下spring boot
对mysql
引擎的支持。
解决方法
spring:
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
问题描述
当我们配置spring.jap.hibernate.ddl-auto: create
或是update
等属性后,hibernate
为我们自己动生成了数据表。但系统启动时在控制台中有报错,报错内容指明hibernate
在给字段添加外键时产生了错误。经排查,错误产生的原因在于hibernate
为我们自己动生成的表的引擎为MyISAM
,而MyISAM
并不支持外键。其实我们想要的引擎是Innodb
。
尝试解决
由于spring 1.x
版本中,是不需要配置此项的,所以我首先来到了spring boot
官方参考文档:
https://docs.spring.io/spring-boot/docs/2.x.x.RELEASE/reference/html/common-application-properties.html
具体使用时,请将2.x.x换成自己使用的版本,比如2.1.7。
很遗憾,自2.0.0开始至2.1.8结束,我们以关键字dialect
查询,并没有找到关于dialect
的选项。而且我们查看jpa
的配置项,也没有找到相关的记录:
spring.data.jpa.repositories.bootstrap-mode=default # Bootstrap mode for JPA repositories.
spring.data.jpa.repositories.enabled=true # Whether to enable JPA repositories.
spring.jpa.database= # Target database to operate on, auto-detected by default. Can be alternatively set using the "databasePlatform" property.
spring.jpa.database-platform= # Name of the target database to operate on, auto-detected by default. Can be alternatively set using the "Database" enum.
spring.jpa.generate-ddl=false # Whether to initialize the schema on startup.
spring.jpa.hibernate.ddl-auto= # DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto" property. Defaults to "create-drop" when using an embedded database and no schema manager was detected. Otherwise, defaults to "none".
spring.jpa.hibernate.naming.implicit-strategy= # Fully qualified name of the implicit naming strategy.
spring.jpa.hibernate.naming.physical-strategy= # Fully qualified name of the physical naming strategy.
spring.jpa.hibernate.use-new-id-generator-mappings= # Whether to use Hibernate's newer IdentifierGenerator for AUTO, TABLE and SEQUENCE.
spring.jpa.mapping-resources= # Mapping resources (equivalent to "mapping-file" entries in persistence.xml).
spring.jpa.open-in-view=true # Register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the thread for the entire processing of the request.
spring.jpa.properties.*= # Additional native properties to set on the JPA provider.
spring.jpa.show-sql=false # Whether to enable logging of SQL statements.
除此以外,spring.DATASOURCE
中的配置项目也未找到相关的配置信息。莫非,官方将其取消了?
官方资料
几经查找后,在官方的github
中,我们发现了以下链接https://github.com/spring-projects/spring-boot/issues/15342
。
If we detect MySQL to be in use or it is configured by the user, our JPA setup will use Spring Frameworks default dialect to be configured for Hibernate, which is the MySQL5Dialect. This – in contrast to the MySQL55… and MySQL57… flavors – unfortunately still uses the MyISAM storage engine, which doesn't take part in transactions.
大体是说:
当系统诊断或是用户配置MYSQL数据库时,JPA则会使用Spring框架的默认方言来配置Hibernate。这个默认方言是:MySQL5Dialect。这个方言在MySQL55,MySQL57等版本中都工作的很好。但不幸的是:它仍然使用了MyISAM做为了默认引擎,而MyISAM是不支持事务的。
官方已经解决了这个问题,但我们需要在2.2.X版本中看到它。
另辟蹊径
既然spirng
官方阐述了此问题,则说明该问题直接使用官方的文档是解决不了的,那么只好从源码入手碰碰运气了。
开启debug
模式后。找呀找呀,关键点发生在以下文件的如下位置:
org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl
@Override
public Dialect buildDialect(Map configValues, DialectResolutionInfoSource resolutionInfoSource) throws HibernateException {
final Object dialectReference = configValues.get( AvailableSettings.DIALECT );
if ( !isEmpty( dialectReference ) ) {
return constructDialect( dialectReference );
}
else {
return determineDialect( resolutionInfoSource );
}
}
该方法中,由configValues这个配置文件中,获取AvailableSettings.DIALECT
这个key
对应的值,而这个AvailableSettings.DIALECT
的值是:
/**
* Names the Hibernate {@literal SQL} {@link org.hibernate.dialect.Dialect} class
*/
String DIALECT ="hibernate.dialect";
该值可通过:
spring:
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
来设置。
如果我们还需要设置其它的spring boot
官方文档中未指名的信息,也可以通过spring.jpa.properties
来设置。该properties
对应的是一个Map<String, String>
,我们可以传入任意信息。配置信息值参考:org.hibernate.cfg.AvailableSettings
即可。
总结:
spring boot
的官方文档给出了常用的、他们认为必要的配置信息。一些非必要的信息,我们可以通过查看其配置文件来设置。如果配置文件中的set方法出现了Map<String, String>
,则说明此项我们可以按需求进行配置。
比如:
package org.springframework.boot.autoconfigure.orm.jpa;
@ConfigurationProperties(
prefix = "spring.jpa"
)
public class JpaProperties {
private Map<String, String> properties = new HashMap();
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}