如何从 Hibernate 会话中删除不需要的实体?

Posted

技术标签:

【中文标题】如何从 Hibernate 会话中删除不需要的实体?【英文标题】:How to remove unwanted entity from Hibernate session? 【发布时间】:2019-04-03 02:27:13 【问题描述】:

我正在尝试通过查询映射到它的实体来获取Entity1。我正在使用CriteriaBuilder 来执行此操作,如下所示

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Entity1> createQuery = criteriaBuilder.createQuery(Entity1.class);
Root<Entity1> root = createQuery.from(Entity1.class);
Join<Entity1, MappedEntity2> mappedEntity2Join = root.join("mappedEntity2");
createQuery.select(root);

predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get(COL_USER_ID), userId));

// where clause to filter by query params
createQuery.where(predicate).distinct(true);
createQuery.getRestriction();

TypedQuery<Entity1> query = entityManager.createQuery(createQuery);

但在随机情况下,我发现查询是在“Entity2.entities1”上执行的,而没有在 join 中指定 Entity2。我的猜测是 Entity2 已经在会话中可用,并且它是用 entity1 懒惰地初始化的。因为这个条件生成了一个 Entity2 而不是 Entity1 的查询。

有什么方法可以限制查询 Entity1 的条件吗?或如何在执行此特定条件之前从会话中删除 Entity2。

预期查询,

select * 
from Entity1 obj1_ 
inner join mappedEntity1 mObj_ on obj1_.obj_id=mObj_.id 
where obj1_.id=?

但查询生成为,

select * 
from entities1_entities2 obj0_ 
inner join Entity1 obj1_ on obj0_.obj_id=obj1_.id 
where obj0_.entity2_id=?

实体结构:

public class Entity1 

    @ManyToOne
    MappedEntity1 mappedEntity1;

    @OneToMany
    MappedEntity2 mappedEntity2;

    @OneToMany
    MappedEntity3 mappedEntity3;


public class Entity2 

    @OneToMany
    List<Entity1> entities1;

    @OneToOne
    MappedEntity2 mappedEntity2;


Entity1 和 Entity2 的参考表

表名:entities1_entities2

entity1_id INTEGER NOT NULL,
entity2_id INTEGER NOT NULL,
CONSTRAINT entities1_entities2_entity1_id_fkey FOREIGN KEY (entity1_id)
REFERENCES entity1 (id),
CONSTRAINT entities1_entities2_entity2_id_fkey FOREIGN KEY (entity2_id)
    REFERENCES entity2 (id)

【问题讨论】:

发布的标准将(应该)始终使用 Entity1 的候选者进行查询。这就是所有要说的。如果您的 JPA 提供程序正在查询其他内容,那么这是您的 JPA 提供程序中的一个错误。 L1缓存内容与它无关。 您确定您发布的查询是唯一发出的查询吗?该查询可能是在您期望的查询已执行之后急切获取其中一个关联实体的一部分,但很难说,因为您发布的实体不完整 您使用的是哪个 Hibernate 版本? 【参考方案1】:

首先,您需要在查询新实体之前检查旧实体是否存在。您可以直接尝试将实体传递给 session.delete(),以删除该对象。如果在数据库中没有找到需要处理的记录,则应该有一个例外。事实上,我们通常不会真正得到这种情况。我们总是删除一个现有的实体,我的意思是通常的逻辑是这样的;因此,如果已经完成,则无需这样做。你可以这样做,

Entity1 ent = session.load(Entity1.class, '1234');
session.delete(ent);

或者你可以这样做,

Entity1 ent = new Entity1('1234'); // used constructor for brevity
session.delete(ent);

对了,这个版本也可以用session.delete(String query),

session.delete("from Entity1 e where e.id = '1234'"); // Just found it is deprecated

【讨论】:

【参考方案2】:

我对此不是 100% 确定的。在执行搜索之前尝试关闭当前会话并打开另一个会话。

session.close();
session = sessionFactory.openSession();

这应该清除之前创建的(延迟初始化的)实体。

【讨论】:

【参考方案3】:

尝试加载您要删除的实例并将其删除。

private boolean deleteById(Class<?> type, Serializable id) 
        Object persistentInstance = session.load(type, id);
        if (persistentInstance != null) 
            session.delete(persistentInstance);
            return true;
        
        return false;
    


boolean result = deleteById(Product.class, new Long(41));

【讨论】:

【参考方案4】:

如果您想从 Hibernate 会话中删除任何实体,您可以分两步完成: 1-确保 Hibernate 持久化数据库中所有未决的更改 2-从持久化上下文中移除实体

em.flush(); //Write all pending changes to the DB
em.detach(Entity2);// Remove Entity2 from the persistence context

【讨论】:

【参考方案5】:

大多数 IDE 都可以为您处理实体。您或许可以找到一个工具来显示所有实体并让您在您选择的 IDE 中修改它们。

【讨论】:

以上是关于如何从 Hibernate 会话中删除不需要的实体?的主要内容,如果未能解决你的问题,请参考以下文章

JPA/Hibernate - 不需要的部分回滚和会话处理

删除服务内的实体时引发异常:org.hibernate.LazyInitializationException:无法初始化代理 - 没有会话 [重复]

2.Hibernate 会话

Hibernate 在从相关实体中删除时加载 LOBS(不应该是懒惰的)吗?

在 JPA/Hibernate 中使用 @OnetoMany 的实体中不存在时从数据库中删除子记录(Spring 引导应用程序)

如何使用 Hibernate 从 Sql 数据库中更新、删除对象?