对象引用一个未保存的临时实例:如何刷新或返回已保存的对象。

6

我使用Spring 3.2.3,Hibernate 4.2.3和JDK 7。

我有一个简单的实体:

@Entity
public class Language {
    @Id
    @GeneratedValue
    private long id;

    @Column(nullable = false, length = 3, unique = true)
    private String code;
}

我使用一个带有@Transactional注解的方法,通过一个带有@Service注解的类保存了这个实体。该方法使用一个DAO来保存该实体。

sessionFactory.getCurrentSession().save(object);

然后我使用了保存的Language 语言 实体来创建EntityX,它在一个ManyToOne关系中使用了它...

lang=new Language();
// ...
languageService.saveLanguage(lang);
e=new EntityX();
// ...
e.setLanguage(lang);
otherService.saveEntity(e);

并且EntityX被定义为...

@Entity
public class EntityX {
    @ManyToOne
    @JoinColumn(nullable = false)
    private Language language;
        // ...
}

我总是遇到异常

Exception in thread "main" org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: somepackage.Language

我尝试使用一些级联定义在EntityX与Language的关系中,正如其他帖子中建议的那样,但是它没有产生任何效果。
如果我通过某些HQL查询通过其code查找并重新加载保存的Language实体,则一切都正常工作,但这远非“好”的解决方案。
不幸的是,org.hibernate.Session的save(...)方法不会返回保存的对象。
有人有任何想法如何解决吗?
2个回答

5

你的代码是否在单个@Transactional方法中?如果不是,问题可能是,在调用任何服务方法后,事务将被提交并清除会话。当您尝试保存实体时,语言对象未在会话中检测到,被视为瞬态实例并产生错误。

如果您的代码处于单个事务下,您尝试在保存实体之前使用flush()强制Hibernate将Language存储到数据库并分配一个有效的@Id标识符了吗?

最后 - 在我看来 - 如果您从实体和语言存在依赖关系,则最好的选择是:

@ManyToOne(cascade = CascadeType.ALL)
private Language language;

并将您的代码更改为:

e=new EntityX();
Language lang = new Language();
// ...
e.setLanguage(lang);
otherService.saveEntity(e);

你不需要将实体在两个步骤(语言 + 实体)中持久化;将语言和实体作为单个项目管理。

PS: org.hibernate.Session的save(...)方法不返回保存的对象,因为对象保持不变(引用不变),仅对象属性更改(例如标记为@Id的属性)!

编辑:
使对象持久化(我的意思是session.save())不会立即进行插入/更新;没有级联提示Hibernate查找EntityX和Language之间的依赖关系,并在保存EntityX之前执行Language的sql插入。
languageService.save(language)调用不执行session.flush(),因为您在同一个@Transactional下,而没有session.commit(),不会执行session.flush(),最好的选择是Language对象仍然标记为瞬态。
您可以进行检查:提取服务保存代码(语言entityX)并放置在单个@Transactional中,并检查Hibernate是否仍会给您错误。
我最好的选择仍然是在中间执行flush()或更改映射,没有其他方法。


代码在一个单独的@Transactional方法中。服务方法也是@Transactional,但它们不需要新的事务,所以我认为会使用现有的事务。我将Language保存为自己的实体,因为我有一个Language实体和许多EntityX,它们每个都使用相同的Language实体。 - t777
1
请检查我的编辑,评论太长了。我希望能清楚表达,英语不是我的母语,抱歉。 - Luca Basso Ricci
嗨@LucaBassoRicci,我遇到了同样的问题,但我的技术不同,你能看一下这个吗..http://stackoverflow.com/questions/28279304/error-when-saving-a-object-which-has-foreign-key-object-references-an-unsaved - The Coder

2

首先,如果code字段是主键(或者您至少将其用作Hibernate的主ID),请指定@Id:

@Id 
@GeneratedValue(generator="assigned")
@Column(length = 3)
private String code;

没有指定ID列可能会让Hibernate有些困惑;不过这只是我的猜测。

如果在此之后save()仍然无法工作,那么可以使用merge()

Language lang = new Language("XYZ");
lang = session.merge(lange);

对不起,我把代码缩短了太多。有一个ID字段。我已经将其添加到我的帖子代码中。当我使用合并而不是保存时,我遇到了同样的问题。 - t777

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