java - 如何解决 honeSTLY 没有意义的 TransientObjectException

标签 java hibernate exception tags transient

我正在使用 Hibernate 4.1,每当我尝试存储已添加到对象集合中的 transient 标记列表时,都会遇到以下异常。

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.myapplication.domain.Tag
    at org.hibernate.engine.internal.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:249)
    at org.hibernate.type.EntityType.getIdentifier(EntityType.java:459)
    at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:132)
    at org.hibernate.persister.collection.AbstractCollectionPersister.writeElement(AbstractCollectionPersister.java:811)

现在,我需要解释一下。我的 Tag 类有一个同义词集合。在添加这种关系之前,我从未遇到过此异常,并且一切正常:

<class name="com.myapplication.domain.Tag">
    <id name="id" column="tag_id" type="long">
        <generator class="native"/>
    </id>
    <property name="term" index="termIndex" unique="true" not-null="true" />
    <property name="description" />
    <property name="approved" />

    <many-to-one name="masterTag" column="master_tag_id"
                 class="com.myapplication.domain.Tag" />

    <bag name="synonyms">
        <key column="master_tag_id" />
        <one-to-many class="com.myapplication.domain.Tag" />
    </bag>
</class>

以下是对象如何映射标签。 Tags class 只是一个组件类。 Tags.list 是 Hibernate 持久保存的实际集合。

    <component name="tags">
        <bag name="list" table="user_to_tag">
            <key column="user_id" />
            <many-to-many class="com.myapplication.domain.Tag" column="tag_id"/>
        </bag>
    </component>

我试图保存在数据库中的标签实际上根本不包含任何同义词,所以我不认为它与 transient 同义词标签有任何关系。我不希望它们级联,这就是为什么我没有在 <bag> 上给它级联属性。标签。

用户通常会通过字符串列表将标签插入系统。我这样做是因为我想在保存包含标签的对象时同时动态创建标签或链接到现有标签。也许这比它的值(value)更麻烦?如果我只让它们链接到存在的标签,那么我知道我可以解决我的问题。也许在 Hibernate 中处理动态标签创建的复杂性有点超出了它的处理能力?

这是我用来一次性保存新/现有标签的方法。每当一个对象被保存到数据库时,它首先调用此方法来保存标签集合:

public void saveAll(Tags tags) {
    List<Tag> tagsToOverwrite = new ArrayList<Tag>();

    Iterator<Tag> i = tags.getList().iterator();
    while(i.hasNext()) {
        Tag tag = i.next();

        if(stopListService.hasWord(tag.getTerm())) {
            i.remove();
        } else {
            if(tag.isTransient()) {
                Tag dbTag = findByTerm(tag.getTerm());

                if(dbTag == null) {
                    save(tag);
                } else {
                    tagsToOverwrite.add(dbTag);
                }
            }
        }
    }

    tags.overwriteAll(tagsToOverwrite);
}

由于某种原因,它在 Tag dbTag = findByTerm(tag.getTerm()); 上失败这时它会抛出 TransientObjectException 。这个方法只是一个简单的查询:

public Tag findByTerm(final String term) {
    Query query = getCurrentSession().createQuery(
        "from Tag tag " +
        "where tag.term = :term "
    );

    return (Tag) query
        .setParameter("term", term.toLowerCase())
        .setCacheable(true)
        .uniqueResult(); // This is where the exception gets thrown specifically
}

我不确定为什么这个方法会抛出这个异常,因为我想做的只是在保存之前检查它是否在数据库中。当我所做的只是查询对象时,为什么会提示对象没有被保存?

如果标签列表仅包含单个标签,则该方法有效。仅当要保存的 transient 标签数量为 2 个或更多时才会失败。例如,这会失败:

@Test
public void saveShouldPersistTransientTags() {
    User user = userRepository.find(1);
            // makes list of 3 tags that are transient (id = 0)
    user.getTags().setTagsAsString("training learning business");

    userRepository.save(user);
    flush();

    user = userRepository.find(1);
    assertEquals(3, user.getTags().getList().size());
}

这是 UserRepository.save() 方法:

@Override
public void save(User user) {
    tagRepository.saveAll(user.getTags());

    super.save(user);
}

如您所见,我试图在保存用户之前先保存所有 transient 标签(或在数据库中查找它们)。然而,Hibernate 似乎想要保留 user.tags.list通话期间领取tagRepository.saveAll(user.getTags());无论如何,在抛出异常的位置,即使我从未要求它这样做。

当然,如果我删除 synonyms从 Hibernate 映射收集,此测试通过,没有任何问题。仅当我启用 synonym 时才会失败集合,只有当它到达learning时才会失败。标签(training 标签在数据库中找不到,并且保存得很好。)

知道为什么同义词集合会导致这个问题吗?

如果我不能很快解决这个问题,为了节省时间,我很想只使用 JDBC 来实现这一点,因为在这种情况下 Hibernate 的复杂性并不是一件好事。我认为考虑到这种行为,这甚至可能是一个错误。这对我来说仍然没有任何意义。

最佳答案

当您准备查询 Hibernate 时,可以查看其托管对象。那些脏的它将尝试刷新到数据库,以便查询命中最新的数据存储。在代码中的某个位置,您向集合添加非托管(可能是新的?)同义词,然后当 Hibernate 尝试通过刷新脏对象来“帮助”您时,您会得到该异常。

关于java - 如何解决 honeSTLY 没有意义的 TransientObjectException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12922003/

相关文章:

java - 为什么我们总是要抛出新的异常?我们可以不将该异常存储为实例变量并每次都抛出相同的实例吗?

java - 运行 GNU 并行 Java 作业

java - Java中有线程组局部变量吗?

java - 如何强制 Hibernate 对开始分页的同一数据集进行分页?

java - 无法解决错误 "save is not valid without active transaction"

java - 分页 - 使用 Spring Data JPA 访问第二页时出现 UnsupportedOperationException

python - 从回溯中获取最后一个函数的调用参数?

java - Android:如何知道运行时小部件应用了哪种样式

java - 使用线程处理请求

java - BufferedReader可以在Java中自动关闭吗