我想在不加载整个对象的情况下获取一对一关系的id。我想我可以使用延迟加载执行此操作,如下所示:
class Foo {
@OneToOne(fetch = FetchType.LAZY, optional = false)
private Bar bar;
}
Foo f = session.get(Foo.class, fooId); // Hibernate fetches Foo
f.getBar(); // Hibernate fetches full Bar object
f.getBar().getId(); // No further fetch, returns id
我希望f.getBar()到而不是触发另一次提取。我希望hibernate给我一个代理对象,允许我调用.getId()而不实际获取Bar对象。
我做错了什么?
答案 0 :(得分:38)
使用属性访问策略
而不是
@OneToOne(fetch=FetchType.LAZY, optional=false)
private Bar bar;
使用
private Bar bar;
@OneToOne(fetch=FetchType.LAZY, optional=false)
public Bar getBar() {
return this.bar;
}
现在它运作正常!
如果您调用任何不是标识符getter方法的方法,则会初始化代理。但它在使用属性访问策略时才有效。记住这一点。
答案 1 :(得分:27)
添加到Arthur Ronald F D Garcia'post:您可以@Access(AccessType.PROPERTY)
(或已弃用@AccessType("property")
)强制进行财产访问,请参阅http://256stuff.com/gray/docs/misc/hibernate_lazy_field_access_annotations.shtml
另一种解决方案可能是:
public static Integer getIdDirect(Entity entity) {
if (entity instanceof HibernateProxy) {
LazyInitializer lazyInitializer = ((HibernateProxy) entity).getHibernateLazyInitializer();
if (lazyInitializer.isUninitialized()) {
return (Integer) lazyInitializer.getIdentifier();
}
}
return entity.getId();
}
也适用于分离的实体。
答案 2 :(得分:14)
添加 @AccessType("属性")
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@AccessType("property")
protected Long id;
答案 3 :(得分:14)
不幸的是,接受的答案是错误的。其他答案也没有提供最简单或最明确的解决方案。
使用ID
类的BAR
的属性访问级别。
@Entity
public class Bar {
@Id
@Access(AccessType.PROPERTY)
private Long id;
...
}
就像那样简单:)
答案 4 :(得分:7)
Java Persistence with Hibernate Book在“13.1.3了解代理”中提到了这一点:
只要您只访问数据库标识符属性,否则 初始化代理是必要的。 (请注意,事实并非如此 如果您使用直接字段访问映射标识符属性;过冬 然后甚至不知道getId()方法存在。如果你打电话, 必须初始化代理。)
但是,根据@xmedeko在此页面中的回答,我开发了一个黑客,以避免初始化代理即使使用直接字段访问策略。只需更改getId()
方法,如下所示。
而不是:
public long getId() { return id; }
使用:
public final long getId() {
if (this instanceof HibernateProxy) {
return (long)((HibernateProxy)this).getHibernateLazyInitializer().getIdentifier();
}
else { return id; }
}
这里的想法是将getId()
方法标记为final
,以便代理不能覆盖它。然后,调用该方法不能运行任何代理代码,因此无法初始化代理。该方法本身检查其实例是否是代理,并在这种情况下从代理返回id。如果实例是真实对象,则返回id。
答案 5 :(得分:3)
在org.hibernate.Session中你有一个函数可以在不延迟加载实体的情况下完成工作:
public Serializable getIdentifier(Object object)抛出HibernateException;
在hibernate 3.3.2.GA中找到:
public Serializable getIdentifier(Object object) throws HibernateException {
errorIfClosed();
checkTransactionSynchStatus();
if ( object instanceof HibernateProxy ) {
LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
if ( li.getSession() != this ) {
throw new TransientObjectException( "The proxy was not associated with this session" );
}
return li.getIdentifier();
}
else {
EntityEntry entry = persistenceContext.getEntry(object);
if ( entry == null ) {
throw new TransientObjectException( "The instance was not associated with this session" );
}
return entry.getId();
}
}
答案 6 :(得分:2)
现在有一个jackson hibernate数据类型库:
https://github.com/FasterXML/jackson-datatype-hibernate
您可以配置功能:
Hibernate4Module hibernate4Module = new Hibernate4Module();
hibernate4Module.configure(Hibernate4Module.Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS, true);
这将包括延迟加载关系的id -
答案 7 :(得分:0)
您可以使用HQL查询。 getBar()方法将真正返回一个代理,在您调用某些数据绑定方法之前不会获取该代理。我不确定你究竟是什么问题。你能给我们一些更多的背景吗?
答案 8 :(得分:0)
改变你的getter方法:
public Bar getBar() {
if (bar instanceof HibernateProxy) {
HibernateProxy hibernateProxy = (HibernateProxy) this.bar;
LazyInitializer lazyInitializer = hibernateProxy.getHibernateLazyInitializer();
if (lazyInitializer.getSession() == null)
bar = new Bar((long) lazyInitializer.getIdentifier());
}
return bar;
}