最近更新为 spring boot 2.1.7
后,遇到了一系列小的问题。本文阐述下 spring boot
对mysql
引擎的支持。
解决方法
spring:
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL55Dialect
问题描述
当我们配置 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.MySQL55Dialect
来设置。
如果我们还需要设置其它的 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;}