java - 在 Spring Boot 中保存 Map 属性时出现 TransientObjectException
问题描述
查找具有Map
属性的持久对象时出现以下错误:
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
实体与地图:
@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);
}
}
地图键:
@Entity
public class MapKey {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
}
地图值:
@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());
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);
}
解决方案
在您的示例中,有几件事似乎不正确。
- 您的测试
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
andMapValue
映射并且它仅用于内存中使用,请尝试将@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;
}
- 您不需要
HashMap<>
在 entity 的地图声明中创建新实例EntityWithMap
。JPA 应该为您做到这一点。这也可能是您获得异常的原因。
查看这篇文章了解更多信息: https ://www.baeldung.com/hibernate-persisting-maps