-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 18 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: DetachedCriteria error
PostPosted: Wed Jan 30, 2008 6:04 pm 
Beginner
Beginner

Joined: Sun Aug 12, 2007 11:22 am
Posts: 44
Location: Sweden
Hi!

I can't get my DetachedCriteria to work. I'm using maven2 entity-manager 3.2.1.ga with spring 2.5.1

If I use entity-manager 3.3.1.ga I get 1000 error when starting up my spring so I need if possible use 3.2.1.ga entity-manager

I have an Item class annotated like @ManyToOne collection and @ManyToOne SubCategory

In my dao I need to get all SubCategories that has Items that fill certain criteria

Code:
DetachedCriteria itemCrit = DetachedCriteria.forClass(Item.class);
itemCrit.setProjection(Projections.projectionList().add( Projections.property( "subCategory" )) );
DetachedCriteria outletCritria = criteria.createCriteria("collection");
outletCritria.add( Expression.ge( "endDate", new Date() ));
outletCritria.add( Expression.le( "startDate", new Date() ));
Criteria crit = hibernateSession.createCriteria( SubCategory.class  );
crit.add( Property.forName("id").in(  itemCrit ));
crit.addOrder( Order.desc("sortOrder") );




If I don't use the collection crit everything works. But the collection crit makes the following error

Code:
Hibernate: select this_.CategoryId as CategoryId8_1_, this_.SortOrder as SortOrder8_1_, this_.Identifier as Identifier8_1_, this_.CachedBalance as CachedBa5_8_1_, this_.ApproximateWeight as Approxim6_8_1_, this_.ParentId as ParentId8_1_, maincatego2_.CategoryId as CategoryId8_0_, maincatego2_.SortOrder as SortOrder8_0_, maincatego2_.Identifier as Identifier8_0_, maincatego2_.CachedBalance as CachedBa5_8_0_ from Categories this_ left outer join Categories maincatego2_ on this_.ParentId=maincatego2_.CategoryId where this_.Type='SUB' and this_.CategoryId in (select this0__.CategoryId as y0_ from Items this0__ where this0__.BrandId=? and collection1_.EndDate>=? and collection1_.StartDate<=?) order by this_.SortOrder desc
WARN  - JDBCExceptionReporter      - SQL Error: 1054, SQLState: 42S22
ERROR - JDBCExceptionReporter      - Unknown column 'collection1_.EndDate' in 'where clause'


How come the sql is Collection1_startDate when there is no join? Can anyone point me to the right direction


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 31, 2008 5:43 am 
Regular
Regular

Joined: Mon Aug 20, 2007 6:47 am
Posts: 74
Location: UK
Can you post your Item.java as well as the source for classes in "collection" and "subcategory" please?

Also please describe, without code, what you are trying to restrict by?

-Paul


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 31, 2008 1:52 pm 
Beginner
Beginner

Joined: Sun Aug 12, 2007 11:22 am
Posts: 44
Location: Sweden
Code:
package se.boardstore.entities.item;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import se.boardstore.entities.brand.Brand;
import se.boardstore.entities.category.SubCategory;
import se.boardstore.entities.collection.Collection;

@Entity
@Table( name = "Items" )
public class Item {
   
   @Id
   @GeneratedValue
   @Column( name = "ItemId" )
   private Long id;
   @ManyToOne( fetch = FetchType.LAZY, cascade = CascadeType.REFRESH )
   @JoinColumn( nullable=false, name = "CategoryId" )      
   private SubCategory subCategory;
   @ManyToOne( fetch = FetchType.LAZY, cascade = CascadeType.REFRESH )
   @JoinColumn( nullable=false, name = "BrandId" )      
   private Brand brand;
   @ManyToOne( fetch = FetchType.LAZY, cascade = CascadeType.REFRESH )
   @JoinColumn( nullable=false, name = "CollectionId" )      
   private Collection collection;
   /** TODO: Add colour story here */
   @Column( nullable=false, name = "Identifier" )
   private String identifier;
   @Column( nullable=false, name = "Price" )
   private float price;
   @Column( nullable=false, name = "MemberPrice" )
   private float memberPrice;
   @Column( nullable=false, name = "Value" )
   private float value;
   @Column( nullable=false, name = "Discount")
   private float discount = 0;
   @Column( name = "CachedBalance" )
   private Long cachedBalance;
   @Column( name= "IsInPromotion" )
   private boolean inPromotion;
   @Column( nullable=false, name = "EarliestDeliveryDate" )
   @Temporal(TemporalType.TIMESTAMP)
   private java.util.Date earliestDeliveryDate;
   @Column( name = "RefilledDate" )
   @Temporal(TemporalType.TIMESTAMP)
   private java.util.Date refilledDate;
   @Column( name = "IsMale", nullable = false )
   private boolean male;
   @Column( name = "IsFemale", nullable = false )
   private boolean female;
   @Column( name = "IsJunior", nullable = false )
   private boolean junior;
   public SubCategory getSubCategory() {
      return subCategory;
   }
   public void setSubCategory(SubCategory subCategory) {
      this.subCategory = subCategory;
   }
   public Brand getBrand() {
      return brand;
   }
   public void setBrand(Brand brand) {
      this.brand = brand;
   }
   public Collection getCollection() {
      return collection;
   }
   public void setCollection(Collection collection) {
      this.collection = collection;
   }
   public String getIdentifier() {
      return identifier;
   }
   public void setIdentifier(String identifier) {
      this.identifier = identifier;
   }
   public float getPrice() {
      return price;
   }
   public void setPrice(float price) {
      this.price = price;
   }
   public float getMemberPrice() {
      return memberPrice;
   }
   public void setMemberPrice(float memberPrice) {
      this.memberPrice = memberPrice;
   }
   public float getValue() {
      return value;
   }
   public void setValue(float value) {
      this.value = value;
   }
   public float getDiscount() {
      return discount;
   }
   public void setDiscount(float discount) {
      this.discount = discount;
   }
   public Long getCachedBalance() {
      return cachedBalance;
   }
   public void setCachedBalance(Long cachedBalance) {
      this.cachedBalance = cachedBalance;
   }
   public boolean isInPromotion() {
      return inPromotion;
   }
   public void setInPromotion(boolean inPromotion) {
      this.inPromotion = inPromotion;
   }
   public java.util.Date getEarliestDeliveryDate() {
      return earliestDeliveryDate;
   }
   public void setEarliestDeliveryDate(java.util.Date earliestDeliveryDate) {
      this.earliestDeliveryDate = earliestDeliveryDate;
   }
   public java.util.Date getRefilledDate() {
      return refilledDate;
   }
   public void setRefilledDate(java.util.Date refilledDate) {
      this.refilledDate = refilledDate;
   }
   public boolean isMale() {
      return male;
   }
   public void setMale(boolean male) {
      this.male = male;
   }
   public boolean isFemale() {
      return female;
   }
   public void setFemale(boolean female) {
      this.female = female;
   }
   public boolean isJunior() {
      return junior;
   }
   public void setJunior(boolean junior) {
      this.junior = junior;
   }
   public Long getId() {
      return id;
   }

}



Code:
package se.boardstore.entities.collection;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table( name = "Collections" )
public class Collection {
   
   @Id
   @GeneratedValue
   @Column( name = "CollectionId")
   private Long id;
   @Column( name = "StartDate" )
   @Temporal(TemporalType.TIMESTAMP)
   private java.util.Date startDate;
   @Temporal(TemporalType.TIMESTAMP)
   @Column( name = "EndDate" )
   private java.util.Date endDate;
   @Column( name = "Discount" )
   private float discount;
   @Column( name = "Identifier" )
   private String identifier;

   public String getIdentifier() {
      return identifier;
   }
   public void setIdentifier(String identifier) {
      this.identifier = identifier;
   }
   public java.util.Date getStartDate() {
      return startDate;
   }
   public void setStartDate(java.util.Date startDate) {
      this.startDate = startDate;
   }
   public java.util.Date getEndDate() {
      return endDate;
   }
   public void setEndDate(java.util.Date endDate) {
      this.endDate = endDate;
   }
   public float getDiscount() {
      return discount;
   }
   public void setDiscount(float discount) {
      this.discount = discount;
   }
   public Long getId() {
      return id;
   }
}


Code:
package se.boardstore.entities.category;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;


@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn( name = "Type" )
@DiscriminatorValue( "CATEGORY" )
@Table( name = "Categories" )
public class Category {
    @Id
    @GeneratedValue
    @Column( name="CategoryId")
    private Long id;
    @Column(nullable=false,name="SortOrder")
    private Long sortOrder;
    @Column( nullable=false, name="Identifier")
    private  String identifier;
    @Column( name = "CachedBalance" )
    private Long cachedBalance;

    public Long getCachedBalance() {
      return cachedBalance;
   }

   public void setCachedBalance(Long cachedBalance) {
      this.cachedBalance = cachedBalance;
   }

   public String getIdentifier() {
      return identifier;
    }

    public void setIdentifier(String identifier) {
      this.identifier = identifier;
    }

    public Long getId() {
      return id;
    }

   public Long getSortOrder() {
      return sortOrder;
   }

   public void setSortOrder(Long sortOrder) {
      this.sortOrder = sortOrder;
   }
}


Code:
package se.boardstore.entities.category;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import se.boardstore.entities.item.Item;

@Entity
@DiscriminatorValue( "SUB" )
public class SubCategory extends Category{
   @Column( name = "ApproximateWeight" )
   private Long approximateWeight;
   
   @ManyToOne(targetEntity=MainCategory.class)
   @JoinColumn( name = "ParentId")
   private MainCategory mainCategory;
   
   @OneToMany( mappedBy = "subCategory", fetch = FetchType.LAZY, cascade = CascadeType.REFRESH )//fetch = FetchType.EAGER ) //A brand can have 0 - * Items
   @JoinColumn( name = "CategoryId" )
   private List<Item> items = new ArrayList<Item>();

   public MainCategory getMainCategory() {
      return mainCategory;
   }

   public void setMainCategory(MainCategory mainCategory) {
      this.mainCategory = mainCategory;
   }

   public Long getApproximateWeight() {
      return approximateWeight;
   }

   public void setApproximateWeight(Long approximateWeight) {
      this.approximateWeight = approximateWeight;
   }

   public List<Item> getItems() {
      return items;
   }

   public void setItems(List<Item> items) {
      this.items = items;
   }
}



I want to restrict by the collections start and stop date. I only want subcategories that has items that is connected to a collection ( between two dates ). The items must have cached balace over 1 and it may also include critera on brand.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 31, 2008 5:17 pm 
Regular
Regular

Joined: Mon Aug 20, 2007 6:47 am
Posts: 74
Location: UK
I'm not an expert, but I've written some fairly complex queries using the criteria API. I think it's much easier (and I would guess faster to execute, although again I'm not a DB expert) if you use aliases for your requirements instead of using Projections on id + Property.in, which translate into SQL as sub-selects.

Code:

DetachedCriteria.forClass(SubCategory.class)
    .createAlias("items","i")
    .createAlias("i.collection","c")
    .add(Restrictions.ge("c.startDate",yourStartDate))
    .add(Restrictions.le("c.endDate",yourEndDate))
    .add(Restrictions.gt("cachedBalance",1));



If you need to add some criteria on brand, you can create yet another alias, following the same pattern.

Please let me know if that criteria query works =)


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 31, 2008 5:45 pm 
Beginner
Beginner

Joined: Sun Aug 12, 2007 11:22 am
Posts: 44
Location: Sweden
Hi!

This is not going to work because I will get all 1000 SubCategories with the same name. I only want unique and to use Distinct is to slow when dealing with 200.000 rows.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 01, 2008 2:25 am 
Regular
Regular

Joined: Mon Aug 20, 2007 6:47 am
Posts: 74
Location: UK
Mathias wrote:
Hi!

This is not going to work because I will get all 1000 SubCategories with the same name. I only want unique and to use Distinct is to slow when dealing with 200.000 rows.


That's interesting to hear. I use queries like this with "distinct" quite frequently, but normally I'm searching for id via Projections. On a table with up to 100,000 rows and queries with more joins than the above example the query runs in ~0.25 seconds (that's measuring the time to execute .list()). I then hit the second level cache for the real instances, accepting a performance hit if there are some misses.

In combination with front end pagination (typically showing 50-100 results per page), this provides what had been so far a pretty reasonable performance with good scalability.

I guess it depends on how much data you need to display at a time, and how likely an individual record is to be in the 2nd level cache as to whether the above technique is any use. I'm keen to learn new and better ways to run these kind of queries so i'll watch this thread in case someone posts a better solution.


Last edited by Irons UK on Fri Feb 01, 2008 6:08 am, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 01, 2008 5:55 am 
Beginner
Beginner

Joined: Sun Aug 12, 2007 11:22 am
Posts: 44
Location: Sweden
Do you use

Code:
setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)


If you do this means that it will be filtered in my case in this 1uery 182.134 subCategories. Is this really how you do it? It is not filtered in the sql query but when you do list() on the criteria.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 01, 2008 6:01 am 
Regular
Regular

Joined: Mon Aug 20, 2007 6:47 am
Posts: 74
Location: UK
No I use:

Code:
setProjection(Projections.distinct(Property.forName("id")))


Which generates SQL

Code:
select distinct....


Top
 Profile  
 
 Post subject:
PostPosted: Sat Feb 02, 2008 8:27 am 
Beginner
Beginner

Joined: Sun Aug 12, 2007 11:22 am
Posts: 44
Location: Sweden
Hi!

If I make this query I will only get the id of the subcategory and not whole class. Is there some way to get the whole class using this?


Top
 Profile  
 
 Post subject:
PostPosted: Sat Feb 02, 2008 8:47 am 
Regular
Regular

Joined: Mon Aug 20, 2007 6:47 am
Posts: 74
Location: UK
Mathias wrote:
Hi!

If I make this query I will only get the id of the subcategory and not whole class. Is there some way to get the whole class using this?


Well you would have to wrap the distinct id query in another query, i.e

Code:
DetachedCriteria.forClass(SubCategory.class)
   .add(Restrictions.in("id",distinctIdCriteria));



I don't use this technique myself because I want the data to come from the 2nd level cache. For this query, the data is always loaded from the database.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Feb 02, 2008 8:53 am 
Beginner
Beginner

Joined: Sun Aug 12, 2007 11:22 am
Posts: 44
Location: Sweden
Thanks but this is exactly what I have done in my first post and it doesn't work. How do you get this to work with the second level cache?


Top
 Profile  
 
 Post subject:
PostPosted: Sat Feb 02, 2008 9:07 am 
Regular
Regular

Joined: Mon Aug 20, 2007 6:47 am
Posts: 74
Location: UK
Mathias wrote:
Thanks but this is exactly what I have done in my first post and it doesn't work. How do you get this to work with the second level cache?


It's not the same as what you were doing in your first post, and I can assure you it works.

As I've already said, it won't get your data from the second level cache. See my post further up the thread for how to do that.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Feb 02, 2008 9:35 am 
Beginner
Beginner

Joined: Sun Aug 12, 2007 11:22 am
Posts: 44
Location: Sweden
OK!

I can't get the Criteria question to work but the second level cache query is working. It takes longer to return and I will get a n + 1 for every category that I iterate over.


How would you create the criteria question to not get the n + 1 for every category and fast answer. When doing the query in pure SQL it is returned fast

This query returns very fast
Code:
SELECT s.* FROM Categories s WHERE s.CategoryId IN( SELECT i.CategoryId FROM Items i, Collections c
WHERE   c.CollectionId = i.CollectionId AND
i.CachedBalance > 0 AND c.StartDate <= now() AND c.EndDate >= now() )


This does not
Code:
SELECT s.* FROM Categories s, Items i, Collections c  WHERE s.Type = 'SUB'
AND s.CategoryId = i.CategoryId AND c.CollectionId = i.CollectionId AND
i.CachedBalance > 0 AND c.StartDate <= now() AND c.EndDate >= now()
GROUP BY s.CategoryId


I would like to have a Criteria question like the first SQL query.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Feb 03, 2008 9:35 am 
Beginner
Beginner

Joined: Sun Aug 12, 2007 11:22 am
Posts: 44
Location: Sweden
I still don't get it!

I can't get it to work with the second level cache

Code:
DetachedCriteria crit = DetachedCriteria.forClass(SubCategory.class)
.createAlias("items","i")
.createAlias("i.collection","c")
.add(Restrictions.ge("i.cachedBalance", new Long( 1 )))
.setProjection(Projections.distinct(Property.forName("id")));

      
List<SubCategory> subs = crit.getExecutableCriteria(hibernateSession).list();


if I iterate over this I'll get can not cast from Long to SubCategory.
The select returns select distinct categoryid?


Top
 Profile  
 
 Post subject:
PostPosted: Sun Feb 03, 2008 10:32 am 
Regular
Regular

Joined: Mon Aug 20, 2007 6:47 am
Posts: 74
Location: UK
Your query will return a List<Long>. Iterate over this and call session.load(SubCategory.class,nextId) - this will use the second level cache rather than go to the database.

Remember, this is really only going to be useful if the chances of finding the objects in the cache is high. Otherwise it's going to go to the database N times!


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 18 posts ]  Go to page 1, 2  Next

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.