2015-08-03 73 views
15

应该读取表Person(具有name,firstnameage)的每一行。如何使用休眠标准懒惰地获取字段

EntityManager em = emf.createEntityManager(); 
Session s = (Session) em.getDelegate(); 
Criteria criteria = s.createCriteria(Person.class); 
criteria.setFetchMode("age", FetchMode.SELECT); 

但SQL显示

Hibernate: 
    select 
     person0_.name, 
     person0_.firstname, 
     person0_.age 
    from 
     SCOPE.PERSON person0_ 

如何让年龄偷懒ONLY为准则?

+0

我认为这是不可能的,但相反的是,让它懒惰触发它在需要时初始化。 –

+0

在你的'Person'实体中添加'age'注解'@Basic(fetch = FetchType.LAZY)'。至少给一个镜头。 –

+0

@PawełGłowacz我知道这种风格,我不喜欢它。它应该是标准的一部分,而不是实体的实施。 –

回答

10

我认为懒惰模式只对关联有意义。如果你正在访问一个普通表,它将加载所有的字段。

如果你想age场没有出现在SQL,因此没有被加载到内存中,然后使用预测:

Criteria crit = session.createCriteria(Person.class); 
ProjectionList projList = Projections.projectionList(); 
projList.add(Projections.property("name")); 
projList.add(Projections.property("firstname")); 
crit.setProjection(projList); 
+1

Btw:这个标准将产生一个列表,每个Object []包含'name'和'firstname'的2个字符串。 –

+0

有了这种方法,你应该看看ResultTransformer。 – gabrielgiussi

8

设置一个标准的“年龄”属性的FetchMode有,因为没有影响此时的抓取策略仅适用于关联的对象,但不适用于属性。请参阅hibernate文档的20.1. Fetching strategies部分。

的Hibernate使用抓取策略检索关联的对象 如果应用程序需要导航的关联。可以在O/R映射元数据中声明提取策略 ,或者通过特定的HQL或Criteria查询来覆盖提取策略 。

延迟加载属性的唯一方法是将@Basic注释设置为FetchType.LAZY。请参阅here,或者如果使用.hbm.xml文件进行映射,请使用lazy=true,请参阅hibernate文档的this部分。

的@Basic注解允许你来声明抓取策略 属性。如果设置为LAZY,则指定在第一次访问实例变量时该属性应该为 。它 需要编译时字节码检测,如果你的类不是 检测,属性级别的延迟加载会被忽略。性质的

延迟加载也使用编译时字节码instumentation(休眠被编译之后改变实体类,以允许性能的延迟加载)。阅读20.1.8. Using lazy property fetching

的其他可能的解决方案(除了所有其他解决方案),以您的问题做出简单的Person类,并使用constructor query,如:

public class PersonDTO { 
    private String name; 
    private String firstname; 

    private Person(String name, String firstname) { 
     this.name = name; 
     this.firstname = firstname; 
    } 
    // getters & setters 
} 

Query q = session.createQuery("select new your.package.name.PersonDTO(" 
    + "p.name, p.firstname) from Person p"); 
q.list(); 

你甚至可以使用现有的Person类,只是用适当的构造函数来扩展它,但我更愿意显式。

但是,这里介绍的所有解决方案都没有实现age属性的延迟加载。要做到这一点的唯一方法是注释@Basic,或者您必须实现自己的延迟加载。

4

您的推理是有效的(一般来说,我们可以争论age字段的具体示例),但不幸的是没有直接的解决方案。实际上,Hibernate的概念是fetch profiles,但它目前非常有限(只能使用连接样式获取配置文件覆盖默认获取计划/策略)。

因此,您的问题可能的解决方法如下。

1)移动age到一个单独的实体和Person实体与其关联有懒惰一对一的关系:

@Entity 
class PersonAge { 
    private Integer age; 
} 

@Entity 
class Person { 
    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true, optional = false) 
    @JoinColumn(name = "PERSON_AGE_ID") 
    private PersonAge personAge; 

    public Integer getAge() { 
     return personAge.getAge(); 
    } 

    public void setAge(Integer age) { 
     personAge.setAge(age); 
    } 
} 

2)定义它覆盖默认的一个取配置文件:

@FetchProfile(name = "person-with-age", fetchOverrides = { 
    @FetchProfile.FetchOverride(entity = Person.class, association = "personAge", mode = FetchMode.JOIN) 
}) 

3)启用此配置文件为应用中的每个会话:

session.enableFetchProfile("person-with-age"); 

根据您使用的框架,应该有一个简单的钩子/拦截器,您可以使用该钩子/拦截器为启动的每个会话(事务)启用配置文件。例如,Spring中的方法可能是覆盖正在使用的事务管理器的AbstractPlatformTransactionManager.doBegin

这样personAge将被急切地加载到应用程序的所有会话中,除非显式禁用获取配置文件。

4)禁用在会话中使用所需的条件查询获取配置文件:

session.disableFetchProfile("person-with-age"); 

这样的默认获取计划/使用策略(在实体映射指定),其是PersonAge的延迟加载。

4

如果你的年龄是@Dragan PersonAge这样的对象,你可以将fecth模式与标准联系起来,而不是像你这样的实体。

所以,我觉得你有三种选择:

  1. 年龄为原始和投影像@Paco说(Person.age将是无效和不代理,你失去了你想要的lazyness)
  2. 年龄作为原始无投影(在电线更多的字节)
  3. 年龄人士+ criteria.setFetchMode(你会得到你想要在一个额外的对象/表/映射成本的lazyness)

投影,你可以使用使用ResultTransformer到

Criteria crit = session.createCriteria(Person.class); 
ProjectionList projList = Projections.projectionList(); 
projList.add(Projections.property("name")); 
projList.add(Projections.property("firstname")); 
crit.setProjection(projList); 
crit.setResultTransformer(new ResultTransformer() { 

     @Override 
     public Object transformTuple(Object[] tuple, String[] aliases) { 
     String name = (Long) tuple[0]; 
     String firstName = (String) tuple[1]; 
     return new Person(name , firstName); 
     } 

     @Override 
     public List<Reference> transformList(List collection) { 
     return collection; 
     } 
    }); 

我想你可以自己创建一个PersonProxy触发为检索年龄查询,但是这是一种可怕的。

@Override 
    public Object transformTuple(Object[] tuple, String[] aliases) { 
    String name = (Long) tuple[0]; 
    String firstName = (String) tuple[1]; 
    return new PersonProxy(name , firstName); 
    } 

    class PersonProxy { 
    Person realPerson; 

    public getAge(){ 
     // create a query with realPerson.id for retrieve the age. 
    } 
    } 
4

您可以简单地定义映射到只包含以下属性相同persons数据库表的新实体SimplePerson

  • ID
  • 的firstName

这样,当选择一个SimplePerson与两个标准和HQL,年龄列将不会被检索。

另一种替代方法是使用lazy loading for basic attributes,但将多个子实体映射到同一个数据库表更灵活。