情景复现
这周在写issue遇到一个需求,培训通知和大纲是@ManyToOne的关系,需要在创建培训通知的时候,大纲是选填项,即大纲可以为空。
在实施过程中,就遇到了这个问题。
object references an unsaved transient instance - save the transient instance before flushing
问题分析
该问题是在说:对象引用了一个未保存还是瞬时态的实例,它希望我们在事务flushing之前先保存这个瞬时态的实例。
用简单的实体描述问题就是:
@Entity
public class A {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
@ManyToOne
private B b;
}
A a = new A();
B b = new B();
aRepository.save(a); // 由于a中有b 关联字段,但是在保存a的时候,a.b这个字段的值需要在B实体表能够找到一条数据。
解决问题
我们可以采用在保存a之前先保存b实体。
A a = new A();
B b = new B();
aRepository.save(b);
a.b = b;
aRepository.save(a);我们可以在注解@ManyToOne里添加cascade = CascadeType.ALL设置。该设置了级联的自动操作,也就是在A保存的之前会自动保存B。
@Entity public class A { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String name; @ManyToOne(cascade = CascadeType.ALL) private B b; }
拓展
JPA/Hibernate 中实体的状态
- Transient(瞬时):刚new出来的对象,未持久化到数据库,未分配到ID,。在此状态下,对实体所做的更改不会持久保存到数据库中。
- Persistent (持久状态):实体已经存入到数据库中,已有ID,能通过find()函数在数据库中检索到实体,它处于持久状态。在这种状态下对实体所做的任何更改都将在持久上下文刷新时自动与数据库同步。
- Detached(分离状态):当实体之前与持久性上下文关联但当前未与任何持久性上下文关联时,该实体处于分离状态。在此状态下,必须通过将实体重新附加到持久性上下文或将更改合并回新的持久性上下文来显式管理对实体所做的更改。
- Removed (删除状态):该实体被持久的状态remove()删除。
实体状态之间的转换
// todo代码示例
下面我们看一下spring框架中的save方法和delete方法的源代码:
@Transactional
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null.");
if (this.entityInformation.isNew(entity)) {
this.em.persist(entity);
return entity;
} else {
return this.em.merge(entity);
}
}
数据的save 操作就是将瞬时态的对象变更为持久态。
@Transactional
public void delete(T entity) {
Assert.notNull(entity, "Entity must not be null!");
if (!this.entityInformation.isNew(entity)) {
Class<?> type = ProxyUtils.getUserClass(entity);
T existing = this.em.find(type, this.entityInformation.getId(entity));
if (existing != null) {
this.em.remove(this.em.contains(entity) ? entity : this.em.merge(entity));
}
}
}
delete操作就是把持久态的数据变更为删除态。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。