问题描述
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: [package].MapKey; nested exception is java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: [package].MapKey
我发现大多数说明都是关于添加CascadeType.ALL
的。
仅当我执行自定义查询时才显示错误,而不是使用findById
方法:
EntityWithMap saved = service.save(entity);
assertEquals(entity.getMap(),service.findById(saved.getId()).get().getMap()); //No error
assertEquals(entity.getMap(),service.findByName("test entity").get(0).getMap()); //InvalidDataAccessApiUsageException
EntityWithMap:
@Entity
public class EntityWithMap {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@OnetoMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@JoinTable(name = "mapping_mapkey_mapvalue",joinColumns = {@JoinColumn(name = "value_id",referencedColumnName = "id")},inverseJoinColumns = {@JoinColumn(name = "entity_id",referencedColumnName = "id")})
@MapKeyJoinColumn(name = "key_id",referencedColumnName = "id")
private Map<MapKey,MapValue> map = new HashMap<>();
private String name;
public EntityWithMap(String name) {
this.name = name;
}
public Map<MapKey,MapValue> getMap() {
return map;
}
public Long getId() {
return id;
}
public void addToMap(MapKey key,MapValue value) {
map.put(key,value);
}
}
MapKey:
@Entity
public class MapKey {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
}
MapValue:
@Entity
public class MapValue {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
}
测试类:
@DataJpaTest
@Import(EntityWithMapService.class)
public class PersistMappingTest {
@Autowired private EntityWithMapService service;
@Test
public void testPersistence() {
EntityWithMap entity = new EntityWithMap("test entity");
entity.addToMap(new MapKey(),new MapValue());
entity.addToMap(new MapKey(),new MapValue());
EntityWithMap saved = service.save(entity);
assertEquals(entity.getMap(),service.findById(saved.getId()).get().getMap()); //No error
assertEquals(entity.getMap(),service.findByName("test entity").get(0).getMap()); //InvalidDataAccessApiUsageException
}
}
EntityWithMapService:
@Service
public class EntityWithMapService {
private EntityWithMapRepository repository;
public EntityWithMapService(EntityWithMapRepository repository) {
this.repository = repository;
}
public EntityWithMap save(EntityWithMap entity) {
return repository.save(entity);
}
public Optional<EntityWithMap> findById(Long id) {
return repository.findById(id);
}
public List<EntityWithMap> findByName(String name) {
return repository.findByName(name);
}
}
EntityWithMapRepository:
@Repository
public interface EntityWithMapRepository extends JpaRepository<EntityWithMap,Long> {
@Query("FROM EntityWithMap e WHERE e.name = :name")
public List<EntityWithMap> findByName(@Param("name") String name);
}
解决方法
在您的示例中有几处地方似乎不对。
- 您的测试
Node* &rightNode = getNodeToBeReplaced(); //returns y Node* assignNode = rightNode->parentNode; //returns assign node with x = y auto &parentChild = std::find(assignNode->childNodes.begin(),assignNode->childNodes.end(),rightNode); Var* replacementNode = new Var("z"); *replacementNode->parentNode = *assignNode; **toBeReplacedParentChild = *replacementNode;
试图通过引用childNodes
和PersistMappingTest
的实例来持久保存EntityWithMap
的记录,而不先保持它们的持久性。您需要先保存MapKey
和MapValue
记录,然后才能将它们用作MapKey
记录中的引用。这可能是您获得MapValue
的主要原因。
示例(伪代码):
EntityWithMap
注意:如果有意不将TransientObjectException
和MapKey mapKey1 = mapKeyService.save(new MapKey());
MapKey mapKey2 = mapKeyService.save(new MapKey());
MapKey mapKey3 = mapKeyService.save(new MapKey());
MapValue mapValue1 = mapValueService.save(new MapValue());
MapValue mapValue2 = mapValueService.save(new MapValue());
MapValue mapValue3 = mapValueService.save(new MapValue());
EntityWithMap entity = new EntityWithMap("test entity");
entity.addToMap(mapKey1,mapValue1);
entity.addToMap(mapKey2,mapValue2);
entity.addToMap(mapKey3,mapValue3);
映射保留在数据库中并且仅用于内存中,请尝试将MapKey
批注添加到映射字段中在MapValue
中。
- 您的
@Transient
实体根本没有引用EntityWithMap
。如果MapValue
不知道,MapKey
怎么能成为MapKey
的密钥。
示例(伪代码):
MapValue
- 您无需在实体
MapValue
中的地图声明中创建@Entity public class MapValue { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; @ManyToOne(cascade = CascadeType.ALL) @JoinColumn(name = "mapkey_id") private MapKey mapKey; }
的新实例。 JPA应该为您做到这一点。这也可能是您得到例外的原因。
有关更多信息,请参见本文: https://www.baeldung.com/hibernate-persisting-maps