JPA/hibernate中UUID映射问题

33

根据文档,Hibernate 3.6 应该支持 java.util.UUID 类型。但是当我像这样进行映射时:

@Id protected UUID uuid;

我得到了以下异常:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [test-applicationContext.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: persistenceUnit] Unable to build EntityManagerFactory
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1420) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findDefaultEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:529) ~[spring-orm-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:495) ~[spring-orm-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java:656) ~[spring-orm-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToInject(PersistenceAnnotationBeanPostProcessor.java:629) ~[spring-orm-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:147) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:84) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:338) ~[spring-orm-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    ... 51 common frames omitted
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: persistenceUnit] Unable to build EntityManagerFactory
    at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:911) ~[hibernate-entitymanager-3.6.0.Final.jar:3.6.0.Final]
    at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:74) ~[hibernate-entitymanager-3.6.0.Final.jar:3.6.0.Final]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:225) ~[spring-orm-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:308) ~[spring-orm-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    ... 64 common frames omitted
Caused by: org.hibernate.MappingException: No Dialect mapping for JDBC type: -2
    at org.hibernate.dialect.TypeNames.get(TypeNames.java:78) ~[hibernate-core-3.6.0.Final.jar:3.6.0.Final]
    at org.hibernate.dialect.TypeNames.get(TypeNames.java:103) ~[hibernate-core-3.6.0.Final.jar:3.6.0.Final]
    at org.hibernate.dialect.Dialect.getTypeName(Dialect.java:249) ~[hibernate-core-3.6.0.Final.jar:3.6.0.Final]
    at org.hibernate.mapping.Column.getSqlType(Column.java:208) ~[hibernate-core-3.6.0.Final.jar:3.6.0.Final]
    at org.hibernate.mapping.Table.sqlTemporaryTableCreateString(Table.java:371) ~[hibernate-core-3.6.0.Final.jar:3.6.0.Final]
    at org.hibernate.mapping.PersistentClass.prepareTemporaryTables(PersistentClass.java:765) ~[hibernate-core-3.6.0.Final.jar:3.6.0.Final]
    at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:270) ~[hibernate-core-3.6.0.Final.jar:3.6.0.Final]
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1842) ~[hibernate-core-3.6.0.Final.jar:3.6.0.Final]
    at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:902) ~[hibernate-entitymanager-3.6.0.Final.jar:3.6.0.Final]
    ... 69 common frames omitted

我知道带有堆栈跟踪的问题并不太受欢迎,但这是一个非常针对 Hibernate 的问题,我在谷歌上也无法找到任何内容 :)

谢谢


你正在使用哪个关系型数据库管理系统,以及你正在使用哪个JDBC驱动程序版本? - Mike Lively
MySQL 5.1.47和mysql-connector 5.1.13 - Piotr
15
实际上,没有堆栈跟踪的问题并不受欢迎。 - Bozho
6个回答

51

参考 Mike Lively 的答案并添加代码示例,也提到了 Oracle。

我使用 OracleDialect (Oracle10gDialect) 时遇到了这个问题。对 UUID 字段添加注解 @Type 对我有帮助。

@Id
@Type(type="uuid-char")
private UUID id;

注意:还在该字段上使用了TwoWayStringBridge,使用@FieldBridge注释。

注意:type="uuid-binary"无效,出现相同的未知类型错误。


1
有没有全局属性可以将所有java.util.UUID映射为uuid-char,这样我就不必为每个使用UUID的实体键入此注释了?(我还想避免在代码中使用特定于Hibernate的注释,而将其全部留给javax.persistence。) - L. Holanda
1
是的!请参考jpkrohling下面的答案。只需自定义您的数据库方言并在其构造函数中注册列类型即可。 - Barett
1
受这个答案的启发,我在 Kotlin 中使用 Spring JPA 与 Oracle DB 进行了工作:@Column(name = "uuid") @Type(type="uuid-char") var uuid: UUID? = null.在使用时,使用 val x = UUID.fromString("00a023b3-1357-3070-a661-72a09f8ccdb2") 将字符串转换为 UUID。在 Oracle 数据库上:ALTER TABLE ADD UUID RAW(16) default SYS_GUID() NOT NULL - Emily
谢谢 @Emily!! 很高兴这能激励到你。 - Barett
2
这真是救命稻草。谢谢。 - Shweta Valunj
显示剩余2条评论

38

UUID是在3.6中添加的基本类型。然而默认情况下,它会被转换为JDBC二进制类型,这似乎会导致mysql出现问题。您可以通过明确指定uuid-char作为类型来覆盖此行为。


好的,如果我想以二进制(16位)存储它呢? - Piotr
如果是这种情况,那么您会想要执行类似于partenon答案的操作。从您对他的回答的评论中可以看出,由于某种原因,16位不够用。这可能是mysql的jdbc驱动程序中映射的方式。我需要进行更多的研究才能提供具体信息。如果您想玩一下以查看发生了什么,请尝试将其提升到binary(32)并查看放入数据库的内容。 - Mike Lively

10

使用Hibernate 4和MySQL 5.5与InnoDB表一起,我能够将UUID列以原样存储为BINARY(16)(无需配置或自定义类型)。我没有将其用作实体ID,并手动创建该值使用UUID.randomUUID()

@Entity
@Table(name = "post")
public class PostModel implements Serializable
{
    ...
    @Column(name = "uuid", nullable = false, updatable = false)
    private UUID uuid;
    ...
}

> desc post;
+----------------+---------------+------+-----+---------------------+
| Field          | Type          | Null | Key | Default             |
+----------------+---------------+------+-----+---------------------+
| ...            |               |      |     |                     |
| uuid           | binary(16)    | YES  | UNI | NULL                |
| ...            |               |      |     |                     |
+----------------+---------------+------+-----+---------------------+

4
如何将值以UUID的形式返回? - daydreamer
1
Hibernate 自动进行映射。你的代码是否与我添加到答案中的代码相匹配? - David Harkness
这是针对 Hibernate 4 及以上版本的最佳答案! - HopeKing
2020年仍然是完美的答案。 - Hassingard

9
Caused by: org.hibernate.MappingException: No Dialect mapping for JDBC type: -2

这意味着 Hibernate 正将 UUID 映射为 BINARY[1],但是没有 MySQL 方言将 BINARY 映射到 MySQL 数据类型。请查看 MySQL 的方言层次结构:

https://github.com/hibernate/hibernate-core/blob/master/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java

https://github.com/hibernate/hibernate-core/blob/master/hibernate-core/src/main/java/org/hibernate/dialect/MySQL5Dialect.java

https://github.com/hibernate/hibernate-core/blob/master/hibernate-core/src/main/java/org/hibernate/dialect/MySQL5InnoDBDialect.java

请将以下内容翻译成中文:

将其与此进行比较(搜索二进制映射): https://github.com/hibernate/hibernate-core/blob/master/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java

这可能是Hibernate的一个错误,因为我在MySQL文档中看到了BINARY数据类型可用,但您可能需要在Hibernate的JIRA中进行一些搜索,以查看是否有任何原因导致未映射。

如果您愿意测试,只需对MySQL5InnoDBDialect进行子类化(如果您使用InnoDB),并将其用于构造函数:

registerColumnType( Types.BINARY, "binary" );

所以,这就是为什么String能够工作,但java.util.UUID不能的原因。
1 - http://download.oracle.com/javase/6/docs/api/constant-values.html#java.sql.Types.BINARY

我做了这件事,但得到了这个错误信息:com.mysql.jdbc.MysqlDataTruncation: 数据截断:列“uuid”在第1行的数据过长。 - Piotr
我会阅读MySQL文档,并尝试想出一种仅使用JDBC将UUID二进制数据存储到MySQL列中的方法。如果您能够使用纯JDBC完成此操作,则Hibernate也应该能够。在这种情况下,请在Hibernate JIRA中打开一个问题,并提供尽可能多的细节(如果附加测试用例则可获得额外积分)。我相信Hibernate的工作人员会接受并乐于接受您的贡献。 - jpkroehling

3
不要使用类型UUID,因为需要自定义类型来处理它。
使用String。参见这篇文章中的一种实现方式。
另一种方式是使用内置于Hibernate的UUID生成器。您需要使用名为hibernate-uuid的生成器和@GeneratedValue

9
使用String进行查询和存储不太高效。此外,Hibernate 3.6应该支持UUID类型的映射吗?或者我需要将其与@generatedvalue结合使用才能使其起作用? - Piotr
1
如果不是字符串,你将如何将其存储在数据库中? - Bozho
谢谢,这对我来说很有效,我正在尝试连接到一个 MS SQL 服务器。 - cbmeeks
1
使用UUID而不是字符串有一个关键优势,您永远不必担心ID的大小写问题。在我的应用程序中,ID来自不同的来源,因此大小写有时可能会有所不同,这会在比较它们时造成一些麻烦。 - Broccoli
我正在使用String,而且这里发生了https://dev59.com/KlgR5IYBdhLWcg3wJ6tw的问题,所以这不是一个解决方案。 - Al-Mothafar
答案中的链接已过时。 - Pang

1
谷歌搜索引擎将我带到了这篇文章,当时我正在寻找使用JDBC进行UUID映射的方法,所以如果您不介意,我想分享我的经验。在我的项目中,我需要在H2和MySQL之间进行切换,而在单元测试中使用H2。H2本地支持UUID类型,但是MySQL Java连接器不支持。因此,我的唯一选择是在客户端代码中将BINARY(16)转换为UUID,但我不喜欢这种做法。结果,我对官方MySQL Java连接器进行了修改,使其将UUID视为BINARY(16)。我知道这有点像黑客行为,但对我来说有效。如果您想尝试,请访问我的github链接:http://goo.gl/NIhNi

你的链接出现了404错误。 - Bugs Buggy
1
@BugsBuggy,对不起啊。我以为2020年没人用JPA了,所以删掉了那个repo。我建议使用Jdbi - expert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接