postgresql - SpringBoot+Kotlin+Postgres 和 JSONB:“org.hibernate.MappingException:没有 JDBC 类型的方言映射”
问题描述
为了在运行 Kotlin/SpringBoot 应用程序时处理以下错误(完整堆栈跟踪),我一直在咨询一些方法/帖子/stackoverflow 问题:
2020-04-22 18:33:56.823 ERROR 46345 --- [ restartedMain] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: No Dialect mapping for JDBC type: 2118910070
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1803)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:595)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1108)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:868)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
at app.ApplicationKt.main(Application.kt:13)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: No Dialect mapping for JDBC type: 2118910070
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:403)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:378)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1862)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1799)
... 21 common frames omitted
Caused by: org.hibernate.MappingException: No Dialect mapping for JDBC type: 2118910070
at org.hibernate.dialect.TypeNames.get(TypeNames.java:71)
at org.hibernate.dialect.TypeNames.get(TypeNames.java:103)
at org.hibernate.dialect.Dialect.getTypeName(Dialect.java:369)
at org.hibernate.mapping.Column.getSqlType(Column.java:238)
at org.hibernate.tool.schema.internal.AbstractSchemaValidator.validateColumnType(AbstractSchemaValidator.java:156)
at org.hibernate.tool.schema.internal.AbstractSchemaValidator.validateTable(AbstractSchemaValidator.java:143)
at org.hibernate.tool.schema.internal.GroupedSchemaValidatorImpl.validateTables(GroupedSchemaValidatorImpl.java:42)
at org.hibernate.tool.schema.internal.AbstractSchemaValidator.performValidation(AbstractSchemaValidator.java:89)
at org.hibernate.tool.schema.internal.AbstractSchemaValidator.doValidation(AbstractSchemaValidator.java:68)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:192)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:73)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:320)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:462)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1249)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:391)
... 25 common frames omitted
问题在于将 PostgreSQL 的 JSONB 数据类型与 Hibernate 映射。
我广泛尝试和调试的两种方法如下:
我在这两个方面都进行了大量尝试,但没有任何运气,我很想知道我哪里出错了,我错过了什么。
方法一
我的实体:
@Entity
@TypeDef(name = "JsonUserType", typeClass = JsonUserType::class)
@Table(name = "entity")
data class MyEntity(
@Column(nullable = false)
val id: UUID,
@Column(nullable = false)
@Enumerated(value = EnumType.STRING)
@Column(nullable = false)
val type: Type,
@Type(type = "JsonUserType")
@Column(columnDefinition = "jsonb")
@Basic(fetch = FetchType.LAZY)
var event_data: Event
) : SomeEntity<UUID>(), SomeOtherStuff {
override fun getName(): String {
return id
}
}
enum class Type(val value: String) {
TYPE1("Type1"),
TYPE2("Type2")
}
我的 PoJO:
data class Event(
val someContent: String,
val someBoolean: Boolean
) : Serializable { //equals, hashcode etc are omitted }
我的自定义休眠方言:
class CustomPostgreSQLDialect : PostgreSQL95Dialect {
constructor() : super() {
this.registerColumnType(Types.JAVA_OBJECT, "jsonb")
}
}
我的自定义类型(抽象类)
abstract class JsonDataUserType : UserType {
override fun sqlTypes(): IntArray? {
return intArrayOf(Types.JAVA_OBJECT)
}
override fun equals(value1: Any?, value2: Any?): Boolean {
return value1 == value2
}
override fun hashCode(value1: Any?): Int {
return value1!!.hashCode()
}
override fun assemble(value1: Serializable?, value2: Any?): Any {
return deepCopy(value1)
}
override fun disassemble(value1: Any?): Serializable {
return deepCopy(value1) as Serializable
}
override fun deepCopy(p0: Any?): Any {
return try {
val bos = ByteArrayOutputStream()
val oos = ObjectOutputStream(bos)
oos.writeObject(p0)
oos.flush()
oos.close()
bos.close()
val bais = ByteArrayInputStream(bos.toByteArray())
ObjectInputStream(bais).readObject()
} catch (ex: ClassNotFoundException) {
throw HibernateException(ex)
} catch (ex: IOException) {
throw HibernateException(ex)
}
}
override fun replace(p0: Any?, p1: Any?, p2: Any?): Any {
return deepCopy(p0)
}
override fun nullSafeSet(p0: PreparedStatement?, p1: Any?, p2: Int, p3: SharedSessionContractImplementor?) {
if (p1 == null) {
p0?.setNull(p2, Types.OTHER)
return
}
try {
val mapper = ObjectMapper()
val w = StringWriter()
mapper.writeValue(w, p1)
w.flush()
p0?.setObject(p2, w.toString(), Types.OTHER)
} catch (ex: java.lang.Exception) {
throw RuntimeException("Failed to convert Jsonb to String: " + ex.message, ex)
}
}
override fun nullSafeGet(p0: ResultSet?, p1: Array<out String>?, p2: SharedSessionContractImplementor?, p3: Any?): Any {
val cellContent = p0?.getString(p1?.get(0))
return try {
val mapper = ObjectMapper()
mapper.readValue(cellContent?.toByteArray(charset("UTF-8")), returnedClass())
} catch (ex: Exception) {
throw RuntimeException("Failed to convert String to Jsonb: " + ex.message, ex)
}
}
override fun isMutable(): Boolean {
return true
}
}
这样的课程取自这个Stackoverflow 问题
我的具体课程:
class JsonType : JsonDataUserType() {
override fun returnedClass(): Class<Event> {
return Event::class.java
}
}
我的 application.yml jpa 休眠属性
jpa.properties.database.database-platform: org.hibernate.dialect.PostgreSQL95Dialect
jpa.properties.hibernate.dialect: org.myapp.util.CustomPostgreSQLDialect
方法二
Hibernate 属性与 PoJo 类完全相同,不包含自定义映射器。
实体
@Entity
@TypeDef(
name = "jsonb",
typeClass = JsonBinaryType::class
)
@Table(name = "entity")
data class MyEntity(
@Column(nullable = false)
val id: UUID,
@Column(nullable = false)
@Enumerated(value = EnumType.STRING)
@Column(nullable = false)
val type: Type,
@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")
@Basic(fetch = FetchType.LAZY)
var event_data: Event
) : SomeEntity<UUID>(), SomeOtherStuff {
override fun getName(): String {
return id
}
}
enum class Type(val value: String) {
TYPE1("Type1"),
TYPE2("Type2")
}
自定义方言(使用休眠类型):
class CustomPostgreSQLDialect : PostgreSQL95Dialect {
constructor() : super() {
this.registerHibernateType(Types.OTHER, JsonNodeBinaryType::class.java.name)
this.registerHibernateType(Types.OTHER, JsonStringType::class.java.name)
this.registerHibernateType(Types.OTHER, JsonBinaryType::class.java.name)
this.registerHibernateType(Types.OTHER, JsonNodeBinaryType::class.java.name)
this.registerHibernateType(Types.OTHER, JsonNodeStringType::class.java.name)
}
}
请注意,我也尝试仅使用:
this.registerHibernateType(Types.OTHER, "jsonb")
以及在我的实体或它扩展的基础实体中拥有所有这些(没有改变):
@TypeDefs({
@TypeDef(name = "string-array", typeClass = StringArrayType.class),
@TypeDef(name = "int-array", typeClass = IntArrayType.class),
@TypeDef(name = "json", typeClass = JsonStringType.class),
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class),
@TypeDef(name = "jsonb-node", typeClass = JsonNodeBinaryType.class),
@TypeDef(name = "json-node", typeClass = JsonNodeStringType.class),
})
我在这两种方法中做的有什么明显的错误吗?我无法让它工作,并且不确定是否以任何方式相关,JDBC 类型的 No Dialect 映射后的数值:总是不同的。我添加了这个,因为我已经看到一些 id 与某些类别的错误相关。
你能帮我吗?
谢谢
编辑: 我想提供有关 jpa、postgres 和 hibernate 版本的更多信息。我目前正在使用以下内容:
postgres:10-高山
PostgreSQL JDBC 驱动程序 JDBC 4.2 » 42.2.8
org.springframework.boot:spring-boot-starter-data-jpa:2.2.1.RELEASE
org.hibernate:hibernate-core:5.4.8.Final
它们之间是否存在任何特定的版本问题?
编辑 2 我一直在尝试成功使用休眠类型(方法 2,如上所述)。我根据 Postgres 版本(10)做了如下改动:
class CustomPostgreSQLDialect : PostgreSQL10Dialect {
constructor() : super() {
this.registerHibernateType(Types.OTHER, StringArrayType::class.java.name)
this.registerHibernateType(Types.OTHER, IntArrayType::class.java.name)
this.registerHibernateType(Types.OTHER, JsonStringType::class.java.name)
this.registerHibernateType(Types.OTHER, JsonBinaryType::class.java.name)
this.registerHibernateType(Types.OTHER, JsonNodeBinaryType::class.java.name)
this.registerHibernateType(Types.OTHER, JsonNodeStringType::class.java.name)
}
}
然后在我的实体中我有
@TypeDefs({
@TypeDef(name = "string-array", typeClass = StringArrayType.class),
@TypeDef(name = "int-array", typeClass = IntArrayType.class),
@TypeDef(name = "json", typeClass = JsonStringType.class),
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
和
@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")
@Basic(fetch = FetchType.LAZY)
var event_data: Event
然后我在错误来自的TypeNames中调试了get方法:
public String get(final int typeCode) throws MappingException {
final Integer integer = Integer.valueOf( typeCode );
final String result = defaults.get( integer );
if ( result == null ) {
throw new MappingException( "No Dialect mapping for JDBC type: " + typeCode );
}
return result;
}
这就是我得到的:
defaults = {HashMap@12093} size = 27
{Integer@12124} -1 -> "text"
{Integer@12126} 1 -> "char(1)"
{Integer@12128} -2 -> "bytea"
{Integer@12130} 2 -> "numeric($p, $s)"
{Integer@12132} -3 -> "bytea"
{Integer@12133} -4 -> "bytea"
{Integer@12134} 4 -> "int4"
{Integer@12136} -5 -> "int8"
{Integer@12138} -6 -> "int2"
{Integer@12140} 5 -> "int2"
{Integer@12141} -7 -> "bool"
{Integer@12143} 6 -> "float4"
{Integer@12145} 7 -> "real"
{Integer@12147} 8 -> "float8"
{Integer@12149} -9 -> "nvarchar($l)"
{Integer@12151} 12 -> "varchar($l)"
{Integer@12153} -15 -> "nchar($l)"
{Integer@12155} -16 -> "nvarchar($l)"
{Integer@12156} 16 -> "boolean"
{Integer@12158} 2000 -> "json"
{Integer@12160} 2004 -> "oid"
{Integer@12162} 2005 -> "text"
{Integer@12163} 1111 -> "uuid"
{Integer@12165} 91 -> "date"
{Integer@12167} 2011 -> "nclob"
{Integer@12169} 92 -> "time"
{Integer@12171} 93 -> "timestamp"
找不到jsonb,当我调试自定义方言时,我得到以下信息:
{Integer@10846} 1111 -> "com.vladmihalcea.hibernate.type.json.JsonStringType"
key = {Integer@10846} 1111
value = "com.vladmihalcea.hibernate.type.json.JsonStringType"
这是为什么?为什么我没有得到 jsonb 类型?
解决方案
举个例子,对不起,我知道我来晚了
在你的 pom.xml 中:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.4.3</version>
</dependency>
然后我有一个名为 Day 的实体:
import com.vladmihalcea.hibernate.type.json.JsonBinaryType;
@TypeDefs({
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
@Data
@Entity
public class Day {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "DayId")
private Integer id;
private Integer day;
private Integer month;
private Integer year;
@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")
private List<Activity> activities;
@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")
private Notification notification;
}
活动和通知 JSONB 的类:
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Activity implements Serializable {
private String name;
private String emoji;
private Integer durationInSeconds;
private Boolean highPriority;
public Activity (){}
}
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Notification implements Serializable {
private String email;
private String mobile;
public Notification (){}
}
我们的存储库:
@Repository
public interface DayRepository extends CrudRepository<Day, Integer> {
}
我们的服务:
public interface DayService{
Day saveArbitraryDay();
}
@Service
@Transactional
public DayServiceImpl implements DayService{
private DayRepository repository;
public DayServiceImpl(DayRepository repository){
this.repository = repository;
}
@Override
public Day saveArbitraryDay(){
Day day = new Day();
day.setDay(16);
day.setMonth(04);
day.setYear(1991);
//Set the jsonb objects
//You can use custom constructors whatever
Notification notification = new Notification();
notification.setEmail("contoso@hotmail.com");
day.setNotification(notification);
//Now putting activities
List<Activity> activities = new ArrayList<>();
Activity actOne = new Activity();
actOne.setName("Breakfast");
actOne.setEmoji("");
actOne.setDurationInSeconds(9000);
actOne.setHighPriority(true);
Activity actTwo = new Activity();
actTwo.setName("Shopping");
actTwo.setEmoji("");
activities.add(actOne);
activities.add(actTwo);
day.setActivities(activities)
return repository.save(day);
}
}
我认为这差不多,如果你想用休眠更多地挖掘类型,请查看这个链接
推荐阅读
- javascript - Heroku 错误部署:找不到模块'/app/index.js'
- c++ - 如何将容器元素的指针分配给自身?
- python - 用户警告:[Errno 13] 权限被拒绝。joblib 将以串行模式运行 warnings.warn('%s.joblib 将以串行模式运行' % (e,))
- ruby-on-rails - Ruby On Rails PG 数据库问题使用嵌套表单和具有无表模型关系的表实体“有很多”
- python - curl: (56) Recv failure: Connection reset by peer for Docker Container running on Cpanel
- c# - 使用 Selenium C# 测试具有基本身份验证的登录页面
- visual-studio - 使用 Visual Studio 2019 部署我的应用程序后,我应该如何在 Linux 中打开它?
- python-3.x - 如何让 Django 休息框架尊重我的序列化程序中的“required=false”指定?
- python - 我可以将变量分配给分配给 Class 实例的变量吗?- Python问题
- joomla - 通过 Joomla API 创建文章