Java 休眠一对一:getId(),而不获取整个对象
我想在不加载整个对象的情况下获取一对一关系的id。我想我可以通过如下方式使用惰性加载来实现这一点:Java 休眠一对一:getId(),而不获取整个对象,java,hibernate,jpa,Java,Hibernate,Jpa,我想在不加载整个对象的情况下获取一对一关系的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
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对象
我做错了什么 您可以使用HQL查询。getBar()方法将真正返回一个代理,在调用某个数据绑定方法之前不会获取该代理。我不确定你到底有什么问题。你能给我们提供更多的背景资料吗?使用财产访问策略
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@AccessType("property")
protected Long id;
而不是
@OneToOne(fetch=FetchType.LAZY, optional=false)
private Bar bar;
使用
现在它工作得很好
如果调用的任何方法不是标识符getter方法,则会初始化代理。但它只在使用财产访问策略时起作用。记住这一点
请参阅:仅添加到Arthur Ronald F D Garcia的帖子中:您可以通过
@access(AccessType.property)
(或不推荐的@AccessType(“property”)
)强制访问属性,请参阅
另一种解决方案可能是:
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();
}
也适用于分离的实体。在org.hibernate.Session中,您有一个不用延迟加载实体的函数: 公共可序列化getIdentifier(对象对象)抛出HibernateeException 可在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();
}
}
Java Persistence with Hibernate一书在“13.1.3理解代理”中提到了这一点: 只要您只访问数据库标识符属性,就不会 代理的初始化是必要的。(请注意,事实并非如此 如果将标识符属性映射为直接字段访问,请休眠 然后甚至不知道getId()方法存在。如果调用它, 必须初始化代理。) 然而,基于本页中的@xmedeko answer,我开发了一个黑客程序,以避免在使用直接字段访问策略时初始化代理。只需修改
getId()
方法,如下所示
而不是:
public long getId() { return id; }
使用:
这里的想法是将
getId()
方法标记为final
,以便代理无法覆盖它。然后,调用该方法无法运行任何代理代码,因此无法初始化代理。该方法本身检查其实例是否为代理,在本例中,将从代理返回id。如果实例是真实对象,它将返回id。更改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;
}
添加@AccessType(“属性”)
现在这里有一个jackson hibernate数据类型库: 您可以配置以下功能:
Hibernate4Module hibernate4Module = new Hibernate4Module();
hibernate4Module.configure(Hibernate4Module.Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS, true);
这将包括延迟加载关系的id-不幸的是,接受的答案是错误的。其他答案也不能提供最简单或明确的解决方案 使用
条
类的ID
的属性访问级别
@Entity
public class Bar {
@Id
@Access(AccessType.PROPERTY)
private Long id;
...
}
就这么简单:)谢谢您的回复。你所描述的并不是发生了什么。getBar()正在导致进行提取。正如您所描述的,我希望返回一个代理对象,而不执行获取。是否有其他配置可能丢失?实际上,getBar()后面的getId()导致实体被提取。您没有丢失任何配置。可能像“从Foo f中选择f.bar.id,其中f.id=?”这样的查询会帮您解决问题。代理对象不应该在bar.getId()上获取完整的bar。它已经知道id,因为这是Foo的一部分。无论如何,它在不调用.getId()的情况下执行fetch。使用@ManyToOne(fetch=FetchType.LAZY,optional=false)单值关联的相同行为对我来说并不顺利。这是一个Hibernate错误:另请参阅比较字段或属性访问:+1。如果没有会话,它就不能工作,例如对于分离的实体。这是否意味着我必须更改我的实体,使其在属性级别具有所有注释才能工作?如果我保持原样,将一对一注释移动到属性级别,并将访问类型设置为属性,则它不会work@shane谢谢你的回复。我不信任在我的项目中执行的查询,所以我添加了自己的db集成测试来验证。我现在可以用了。唯一的更改是在目标实体的id上添加访问类型。这是唯一需要的改变@Id@GeneratedValue(strategy=GenerationType.SEQUENCE,generator=“FILECONTENT\u Id\u SEQ”)@SequenceGenerator(name=“FILECONTENT\u Id\u SEQ”,sequenceName=“FILECONTENT\u Id\u SEQ”)@Column(name=“Id”,nullable=false)@Access(AccessType.PROPERTY)私有长Id;除了identity/identifier getter方法之外,在没有完全对象检索/代理初始化的情况下,在字段中是否有其他解决方法?PatrickM,不使用此功能。您可以创建映射到同一个表的不同简化实体。我使用了您的想法,加上代理无法覆盖最终方法的事实,来更改
getId()
方法本身以避免初始化。如果可以的话,请看我在这一页的答案,告诉我你的想法。我也不明白你为什么要检查lazyInitializer.isUninitialized()
。当实体是HibernateProxy时,是否总是返回lazyInitializer.getIdentifier()
?我不记得为什么使用if(lazyInitializer.isUninitialized())
。也许只有在真正必要的时候才使用肮脏的伎俩。我认为它可能会被省略。Hibernate注释“AccessType”已被弃用。改用JPA2注释@Access(AccessType.PROPERTY)
不推荐使用Hibernate注释“AccessType”。改用JPA2注释:@Access(AccessType.PROPERTY)
哈哈,这真是一个可怕的黑客:)“不要在家里做”@OndraŽižka你错了。这段代码工作得很好。而且,它没有违反任何规则,有
Hibernate4Module hibernate4Module = new Hibernate4Module();
hibernate4Module.configure(Hibernate4Module.Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS, true);
@Entity
public class Bar {
@Id
@Access(AccessType.PROPERTY)
private Long id;
...
}