【问题标题】:Saving entities with Hibernate 5.1 and Spring 5.2.9 fails to save entity (spring-boot 2.3.4, spring-boot-devtools) [duplicate]使用 Hibernate 5.1 和 Spring 5.2.9 保存实体无法保存实体(spring-boot 2.3.4,spring-boot-devtools)[重复]
【发布时间】:2020-10-07 00:37:53
【问题描述】:

[编辑:将 q 中的弹簧版本固定到 5.2.9。 v2.3.4 是聚合的 maven dep。]

我浏览了大量帖子试图弄清楚这一点。似乎存在巨大的兼容性问题,但我无法弄清楚要更改为哪些版本。我通常不做Java,但在教学时必须这样做,所以希望有人会忽略我不存在的Java技能并给我一些建议。 ????‍♂️????

如果我单步执行反汇编代码,我可以看到我的单元测试(集成)在尝试反映我的 ID 属性时会做其他事情,而不是使用 Spring 运行时所做的事情。

[编辑] 这是 SQL 日志:

Hibernate: create table ChatMessages (Id varchar(255) not null, UserName varchar(50), Message varchar(512), primary key (Id))
2020-10-07 10:01:15.142  INFO 43304 --- [nio-8081-exec-8] o.h.h.i.QueryTranslatorFactoryInitiator  : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select chatmessag0_.Id as Id1_0_, chatmessag0_.UserName as UserName2_0_, chatmessag0_.Message as Message3_0_ from ChatMessages chatmessag0_

这是异常和跟踪的顶部:

[nio-8081-exec-6] o.h.p.access.spi.GetterMethodImpl        : HHH000122: IllegalArgumentException in class: org.hiof.chatroom.core.ChatMessage, getter method of property: id
2020-10-07 02:28:59.438 ERROR 47504 --- [nio-8081-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of org.hiof.chatroom.core.ChatMessage.id] with root cause

java.lang.IllegalArgumentException: object is not an instance of declaring class
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_172]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_172]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_172]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_172]
    at org.hibernate.property.access.spi.GetterMethodImpl.get(GetterMethodImpl.java:41) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final]
    at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:223) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final]
    at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:4633) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final]
    at org.hibernate.id.Assigned.generate(Assigned.java:32) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final]
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:105) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final]
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:192) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final]
    at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:38) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final]
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:177) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final]
    at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:32) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final]
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final]
    at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:682) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final]
    at org.hibernate.internal.SessionImpl.save(SessionImpl.java:674) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final]
    at org.hibernate.internal.SessionImpl.save(SessionImpl.java:669) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final]
    at org.hiof.chatroom.database.ChatMessageRepository.add(ChatMessageRepository.java:21) ~[classes/:na]

这是一个使用 SQL Lite 的超级简单的数据库设置。

唯一的实体是这样映射的:

<class name="org.hiof.chatroom.core.ChatMessage" table="ChatMessages">
    <id name="id" column="Id">
        <generator class="assigned"/>
    </id>
    <property name="user" column="UserName" length="50"/>
    <property name="message" column="Message" length="512"/>
</class>

还有实体:

public class ChatMessage {
    private String id;
    private String user;
    private String message;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

这是一个测试,它通过的基础设施和配置与(不)有 Spring 运行应用程序不同:

public class When_persisting_chat_messages {
    @Test
    public void stores_message() throws Exception {
        DatabaseManager.ensureDatabase("./db/chat-test.db");
        UnitOfWork uow = new UnitOfWork();
        ChatMessageRepository repo = new ChatMessageRepository(uow);
        ChatMessage msg = new ChatMessage();
        String id = UUID.randomUUID().toString();
        msg.setId(id);
        repo.add(msg);
        uow.saveChanges();
        uow.close();
        uow = new UnitOfWork();
        repo = new ChatMessageRepository(uow);
        msg = repo.get(id);
        Assertions.assertNotNull(msg);
    }
}

我很确定这与在我的UOW 中设置会话工厂所涉及的StandardServiceRegistryBuilder 有关。不知道该怎么办。

太晚了,我希望明天为一些学生演示这个,所以如果我离开这篇文章有点偏离 SO 的首选标准,我深表歉意。

[编辑]
回购发布在这里:https://github.com/lars-erik/hiof-sweat2020-chatroom-demo

[2 天后编辑]
我设法确定了它在 Web 项目中运行时抛出的位置。对于像我这样的 dotnet 开发人员,这闻起来就像我的核心“程序集”(模块)的两个版本被加载到“域”中,并且 ID 属性是从“错误模块的元数据”中读取的。不知道,但看起来就是这样。在getIdentifier 方法中,owner 和idGetter 都指向同一个类org.hiof.chatroom.core.ChatMessage。然而invoke 坚持认为该实例不是 id getter 的声明类。这是跟踪:

get:42, GetterMethodImpl (org.hibernate.property.access.spi)
getIdentifier:230, AbstractEntityTuplizer (org.hibernate.tuple.entity)
getIdentifier:5155, AbstractEntityPersister (org.hibernate.persister.entity)
generate:31, Assigned (org.hibernate.id)
saveWithGeneratedId:115, AbstractSaveEventListener (org.hibernate.event.internal)
saveWithGeneratedOrRequestedId:194, DefaultSaveOrUpdateEventListener (org.hibernate.event.internal)
saveWithGeneratedOrRequestedId:38, DefaultSaveEventListener (org.hibernate.event.internal)
entityIsTransient:179, DefaultSaveOrUpdateEventListener (org.hibernate.event.internal)
performSaveOrUpdate:32, DefaultSaveEventListener (org.hibernate.event.internal)
onSaveOrUpdate:75, DefaultSaveOrUpdateEventListener (org.hibernate.event.internal)
accept:-1, 1659093435 (org.hibernate.internal.SessionImpl$$Lambda$541)
fireEventOnEachListener:102, EventListenerGroupImpl (org.hibernate.event.service.internal)
fireSave:636, SessionImpl (org.hibernate.internal)
save:629, SessionImpl (org.hibernate.internal)
save:624, SessionImpl (org.hibernate.internal)

[编辑八月。 11.] 使用 spring 运行时,调试当前显示 ChatMessage 类的两个版本。 :/

owner.getClass() = {Class@9587} "class org.hiof.chatroom.core.ChatMessage"
owner.getClass() == getterMethod.getDeclaringClass() = false
org.hiof.chatroom.core.ChatMessage.class = {Class@8446} "class org.hiof.chatroom.core.ChatMessage"
getterMethod.getDeclaringClass() = {Class@8446} "class org.hiof.chatroom.core.ChatMessage"

所有者类的类加载器是“RestartClassLoader”,而声明的类是“Launcher$AppClassLoader”。

【问题讨论】:

  • 我能看到的唯一区别是,在你的web 模块中,有一个丢失的文件chat.hbm.xml,你在database 模块中有它但没有web,也许你可以放入并重试。
  • 不知何故,您没有在web 中重新定义hibernate-core,而是在webdatabase 中重新定义了sqlite-dialectsqlite-jdbc,有什么原因吗?您可能需要在 web 上运行 maven 依赖树检查以查看任何库冲突。
  • @Lars-Erik 你能在你的web 模块中运行这个mvn dependency:tree -Dverbose 并发布结果吗?这样可以找出什么是冲突。
  • 天哪,终于有机会看看那棵树了。看来我可能已经离开并再次“假设”了。我确信 Spring something 依赖于 Hibernate。根据树,它是我提取的 SQLite 方言包。那应该很容易用裸代码替换。希望明天晚上试一试。谢谢你,@SamYC。
  • @Lars-Erik 您必须使用mvn clean install 再次进行全新安装。我检查了你的代码,并运行 mvn 依赖检查,我得到了不同的结果。您的依赖关系树显示您在数据库模块中使用org.hibernate:hibernate-core:jar:5.4.22,这已经是错误的,与您在数据库模块中定义的5.1.0.Final 相比,它不一样,这意味着您有mvn install 5.4.22 之前的版本。我建议您仔细检查在您的数据库模块中实际运行的休眠版本并进行全新的全新安装。

标签: java spring spring-boot hibernate spring-boot-devtools


【解决方案1】:

这是由spring-boot-devtools 引起的。它创建了一个名为RestartClassLoader 的类加载器来加速重启。但是,当我们进行 id 反射时,它会与 Hibernate 的元数据反射产生相同类型的两个实例。

所以这最终有点像这个和其他一些人的欺骗:
Classes loaded by different Classloaders in Spring Boot and Logback

【讨论】:

    猜你喜欢
    • 2018-01-13
    • 2017-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-04
    • 1970-01-01
    • 2018-12-02
    相关资源
    最近更新 更多