在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

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);

}

解决方法

在您的示例中有几处地方似乎不对。

  1. 您的测试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; 试图通过引用childNodesPersistMappingTest的实例来持久保存EntityWithMap的记录,而不先保持它们的持久性。您需要先保存MapKeyMapValue记录,然后才能将它们用作MapKey记录中的引用。这可能是您获得MapValue的主要原因。

示例(伪代码):

EntityWithMap

注意:如果有意不将TransientObjectExceptionMapKey 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中。

  1. 您的@Transient实体根本没有引用EntityWithMap。如果MapValue不知道,MapKey怎么能成为MapKey的密钥。

示例(伪代码):

MapValue
  1. 您无需在实体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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...