java Hibernate OneToOne 延迟加载和级联

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6290202/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 15:13:22  来源:igfitidea点击:

Hibernate OneToOne lazy loading and cascading

javamysqlhibernatecascade

提问by Holm

Here's what I'm trying to do.

这就是我想要做的。

  1. Create a parent with a OneToOne relation to a child
  2. The parent has to fetch the children using lazy loading
  3. If parent is removed, so is the child
  4. If the child is removed, the parent should not be affected
  5. The cascade update and delete has to be translated into DDL
  1. 创建与子级具有一对一关系的父级
  2. 父级必须使用延迟加载来获取子级
  3. 如果父母被移除,那么孩子也会被移除
  4. 如果孩子被移除,父母不应该受到影响
  5. 级联更新和删除必须转换为 DDL

class Parent

班级家长

@OneToOne(mappedBy = "parent", cascade = CascadeType.ALL)
public Child getChild()

class Child

班级儿童

@OneToOne(fetch = FetchType.LAZY)
@OnDelete(action = OnDeleteAction.CASCADE)
@JoinColumn(name="parent_id")
public Parent getParent()

I've got point 1, 3, 4fully working and Point 5partially working, still need to solve how to translate the update part indo DDL.

我有第 1、3、4完全工作,第 5 点部分工作,仍然需要解决如何翻译更新部分 indo DDL。

Point 2is the big issue here, with my current solution the parent does not load the child lazily. The child however does load the parent lazily, but inverting the annotations would mess upp the cascading (points 3, 4 and 5).

第 2 点是这里的大问题,在我目前的解决方案中,父母不会懒惰地加载孩子。然而,孩子确实懒惰地加载父母,但反转注释会弄乱级联(第 3、4 和 5 点)。

I'm very confused right now, hoping I've missed something obvious, so any help would be greatly appreciated.

我现在很困惑,希望我错过了一些明显的东西,所以任何帮助将不胜感激。

EDIT:Code requested by Adeel Ansari

编辑:Adeel Ansari 请求的代码

'fetch=FetchType.LAZY' has been added to class Parent, otherwise the same as above.

'fetch=FetchType.LAZY' 已添加到 Parent 类中,其他同上。

IParentDAO parentDAO = DAOFactory.getFactory().getParentDAO();

parentDAO.beginTransaction();
//findByPrimaryKey uses 'org.hibernate.Session.get(Class clazz, Serializable id)'
parentDAO.findByPrimaryKey(1l);
parentDAO.commitTransaction();

The resulting hibernate queries, one fetching Parent and one fetching Child:

由此产生的休眠查询,一个获取父项和一个获取子项:

Hibernate: select parent0_.id as id0_0_ from parents parent0_ where parent0_.id=?
Hibernate: select child0_.id as id1_0_, child0_.parent_id as parent2_1_0_ from childs child0_ where child0_.parent_id=?

Here's the code for findByPrimaryKey:

这是 findByPrimaryKey 的代码:

public class HibernateParentDAO extends HibernateDAO<Parent, Long> implements IParentDAO {

    public HibernateParentDAO() {
        super(Parent.class);
    }
}

public abstract class HibernateDAO<T, ID extends Serializable> implements IGenericDAO<T, ID> {
    private Class<T> persistentClass;

    public HibernateDAO(Class<T> c) {
        persistentClass = c;
    }

    @SuppressWarnings("unchecked")
    public T findByPrimaryKey(ID id) {
        return (T) HibernateUtil.getSession().get(persistentClass, id);
    }
}

回答by iliaden

I've been having a similar issue. There are a few different solutions, but all of them are workarounds.

我一直有类似的问题。有几种不同的解决方案,但所有这些都是解决方法。

The short answer is: Hibernate does NOT support lazy one-to-one relationships.

简短的回答是:Hibernate 不支持惰性一对一关系。

The long answer (workaround) is:

长答案(解决方法)是:

  1. Declare the relationship to be one-to-one on one side (child), and one-to-many on the other side (parent). Thus a parent.getchild()returns a set, yet it will be able to use lazy loading.

  2. You can try to have the parent and the children to share the primary key, but this would require you to alter the schema.

  3. You can try to configure a view in your database reflecting this one-to-one relationship.

  1. 将关系声明为一侧(子)一对一,另一侧(父)一对多。因此 aparent.getchild()返回一个集合,但它可以使用延迟加载。

  2. 您可以尝试让父项和子项共享主键,但这需要您更改架构。

  3. 您可以尝试在数据库中配置一个反映这种一对一关系的视图。

回答by Adeel Ansari

[This part doesn't hold anymore]

[这部分不再成立]

Modify this in your Parentlike below,

Parent像下面这样修改它,

@OneToOne(mappedBy = "parent", cascade = CascadeType.ALL, fetch=FetchType.LAZY)
public Child getChild()

Should work.

应该管用。



[Edited to explain why it will not work]

[编辑以解释为什么它不起作用]

Right after loading B, you may call getCee() to obtain C. But look, getCee() is a method of YOUR class and Hibernate has no control over it. Hibernate does not know when someone is going to call getCee(). That means Hibernate must put an appropriate value into "cee" property at the moment it loads B from database.

If proxy is enabled for C, Hibernate can put a C-proxy object which is not loaded yet, but will be loaded when someone uses it. This gives lazy loading for one-to-one.

But now imagine your B object may or may not have associated C (constrained="false"). What should getCee() return when specific B does not have C? Null. But remember, Hibernate must set correct value of "cee" at the moment it set B (because it does no know when someone will call getCee()). Proxy does not help here because proxy itself in already non-null object.

If your B->C mapping is mandatory (constrained=true), Hibernate will use proxy for C resulting in lazy initialization. But if you allow B without C, Hibernate just HAS TO check presence of C at the moment it loads B. But a SELECT to check presence is just inefficient because the same SELECT may not just check presence, but load entire object. So lazy loading goes away.

在加载 B 之后,您可以立即调用 getCee() 来获取 C。但是请注意,getCee() 是您的类的一个方法,而 Hibernate 无法控制它。Hibernate 不知道什么时候有人会调用 getCee()。这意味着 Hibernate 必须在从数据库加载 B 时将适当的值放入“cee”属性中。

如果为 C 启用了代理,则 Hibernate 可以放置一个尚未加载的 C-proxy 对象,但是当有人使用它时会加载它。这为一对一提供了延迟加载。

但是现在想象一下你的 B 对象可能有也可能没有关联的 C(受约束的 =“假”)。当特定的 B 没有 C 时,getCee() 应该返回什么?空值。但请记住,Hibernate 必须在设置 B 的那一刻设置正确的“cee”值(因为它不知道何时有人会调用 getCee())。代理在这里没有帮助,因为代理本身已经是非空对象。

如果您的 B->C 映射是强制性的(受约束的=真),Hibernate 将使用 C 的代理,从而导致延迟初始化。但是如果你允许 B 而没有 C,Hibernate 只需要在它加载 B 的那一刻检查 C 的存在。但是一个 SELECT 来检查存在是低效的,因为同一个 SELECT 可能不仅检查存在,而且加载整个对象。所以懒加载消失了。

Reference: http://community.jboss.org/wiki/Someexplanationsonlazyloadingone-to-one

参考:http: //community.jboss.org/wiki/Someexplanationsonlazyloadingone-to-one



[Edited to include a workaround]

[编辑以包含解决方法]

You can use optional=falseand @LazyToOnefor the relationship that is not optional. Don't forget to include cascade={CascadeType.PERSIST,CascadeType.REMOVE}. Since, it is obvious for a not-optional relationship. Below is an example,

您可以将optional=falseand@LazyToOne用于非可选的关系。不要忘记包括cascade={CascadeType.PERSIST,CascadeType.REMOVE}. 因为,对于非可选关系,这是显而易见的。下面是一个例子,

@OneToOne(mappedBy="parent", optional=false, fetch=FetchType.LAZY, cascade={CascadeType.PERSIST,CascadeType.REMOVE})
@LazyToOne(LazyToOneOption.PROXY)
public Child getChild(){...}

This should work for you, as I can see you are using cascade=CascadeType.ALL, which means not-optional. Isn't it? But for optional relationship, you might like to consider workaround given by iliaden, here.

这应该对您有用,因为我可以看到您正在使用cascade=CascadeType.ALL,这意味着不是可选的。不是吗?但是对于可选关系,您可能需要考虑iliaden提供的解决方法,这里

回答by abalogh

Have you tried @OneToOne(fetch = FetchType.LAZY, optional=false)? Also check thisblog and thisthread.

你试过@OneToOne(fetch = FetchType.LAZY, optional=false)吗?另请查看博客和线程。

回答by borchvm

@One-to-one relationship not support lazy initialization. To get object you don't put FetchType.LAZYin child class and obtain all child object.

@一对一关系不支持延迟初始化。要获取对象,您不要将 FetchType.LAZY放在子类中并获取所有子对象。

class Parent

@OneToOne(mappedBy = "parent", cascade = CascadeType.REMOVE)
public Child getChild()

class Child

@OneToOne(cascade = CascadeType.REMOVE)
public Parent getParent()