如何使用Hibernate Criteria懒洋洋地获取字段

时间:2015-08-03 11:04:25

标签: java hibernate orm lazy-loading fetch

String urlString = new Uri(HttpContext.Current.Request.Url.AbsoluteUri).OriginalString; var queryParameters = HttpUtility.ParseQueryString(new Uri(urlString).Query); var numberOfParameter = queryParameters.Count; if (numberOfParameter == 10) { int OrranizationID = 0, MachineID = 0, empId = 0, customerId = 0; string PhoneNo = Request.QueryString["CPHNO"] == null ? "0" : Request.QueryString["CPHNO"].ToString(); string RecieptNo = Request.QueryString["RCPT"] == null ? "0" : Request.QueryString["RCPT"].ToString(); string ChequeNo = Request.QueryString["CHQNUM"] == null ? "0" : Request.QueryString["CHQNUM"].ToString(); string PaidAmnt = Request.QueryString["PAMT"] == null ? "0" : Request.QueryString["PAMT"].ToString(); string TransactionTime = Request.QueryString["TOT"] == null ? "0" : Request.QueryString["TOT"].ToString(); OrranizationID = Convert.ToInt32(Request.QueryString["OID"] == null ? "0" : Request.QueryString["OID"].ToString()); MachineID = Convert.ToInt32(Request.QueryString["MID"] == null ? "0" : Request.QueryString["MID"].ToString()); empId = Convert.ToInt32(Request.QueryString["UID"] == null ? "0" : Request.QueryString["UID"].ToString()); customerId = Convert.ToInt32(Request.QueryString["CID"] == null ? "0" : Request.QueryString["CID"].ToString()); } else { ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "alert", "alert('Please provide PaymentInfo');", true); } (包含Personnamefirstname)的每一行都应被阅读。

age

但SQL显示

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

如何让年龄懒惰作为标准?

4 个答案:

答案 0 :(得分:11)

我认为懒惰模式只对协会有意义。如果您正在访问普通表,它将加载所有字段。

如果您希望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 :(得分:9)

设置" age"的FetchMode条件上的属性无效,因为此时的提取策略仅适用于关联对象,但不适用于属性。请参阅hibernate docs的20.1. Fetching strategies部分。

  

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

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

  

@Basic注释允许您声明获取策略   财产。如果设置为LAZY,则指定此属性应为   首次访问实例变量时,懒惰地获取。它   如果您的类不是,则需要构建时字节码检测   检测,属性级别的延迟加载被默默忽略。

延迟加载属性也使用构建时字节码设置(hibernate在编译后更改实体类以允许延迟加载属性)。阅读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注释,或者你必须实现自己的延迟加载。

答案 2 :(得分: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的延迟加载。

答案 3 :(得分:4)

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

所以,我认为你有三个选择:

  1. 年龄如原始和投影一样,@ Paco说(Person.age将为null而不是代理,你会失去你想要的懒惰)
  2. 没有投影的原始年龄(电线中的字节数更多)
  3. 年龄为PersonAge + criteria.setFetchMode(您将以额外的对象/表/映射为代价获得所需的延迟)
  4. 对于Projection,您可以使用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. 
        }
      }