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.  [ 7 posts ] 
Author Message
 Post subject: many-to-many usage and self-referenced table
PostPosted: Wed Jan 18, 2006 2:40 pm 
Newbie

Joined: Wed Jan 18, 2006 2:19 pm
Posts: 14
Hi, I'm newbie in hibernate, so excuse me if the questions are too trivial.

1) i have got many-to-many association between entities Book and Keyword. I don't know how to write a query to find books containing any keyword.
I know that i can use a collection of keywords from a Book entity, but I down know, how to use it in a complex query with many other criteria. Is it possible to use any notation within hql query?

2) i have got mapping like this:
<hibernate-mapping>
<class name="Cathegory" table="cathegory">

<id name="id">
<generator class="sequence"/>
</id>

<many-to-one name="parentId" class="Cathegory"/>

</class>
</hibernate-mapping>

now, i have a table with values
id | parentid
1 | null
and following query works:
List c=ses.createQuery("from Cathegory").list;

but if i add a new row and now i have the table
id | parentid
1 | null
2 | 1
previous query throws exception
Exception in thread "main" java.lang.NullPointerException
at org.hibernate.tuple.AbstractEntityTuplizer.createProxy(AbstractEntityTuplizer.java:372)
at org.hibernate.persister.entity.AbstractEntityPersister.createProxy(AbstractEntityPersister.java:3121)
at org.hibernate.event.def.DefaultLoadEventListener.createProxyIfNecessary(DefaultLoadEventListener.java:232)
at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:173)
at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:87)
at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:889)
at org.hibernate.impl.SessionImpl.internalLoad(SessionImpl.java:857)
at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:266)
at org.hibernate.type.EntityType.resolve(EntityType.java:303)
at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:113)
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:842)
at org.hibernate.loader.Loader.doQuery(Loader.java:717)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:224)
at org.hibernate.loader.Loader.doList(Loader.java:2150)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2029)
at org.hibernate.loader.Loader.list(Loader.java:2024)
at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:369)
at org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:300)
at org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:153)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1127)
at org.hibernate.impl.QueryImpl.list(QueryImpl.java:79)

thanks for any help..
libor


Top
 Profile  
 
 Post subject: Re: many-to-many usage and self-referenced table
PostPosted: Wed Jan 18, 2006 6:10 pm 
Newbie

Joined: Thu Jul 14, 2005 5:39 pm
Posts: 12
Location: Ann Arbor, MI
Hi Libor,

Can you post your Cathegory Java class? I'm wondering if you have getters and setters for each property and whether your id and parentId fields are Integers and not ints -- the Java primitive-wrapper classes should be used for all properties you intend to persist via Hibernate.

-- Jim


Top
 Profile  
 
 Post subject: Re: many-to-many usage and self-referenced table
PostPosted: Wed Jan 18, 2006 6:32 pm 
Newbie

Joined: Thu Jul 14, 2005 5:39 pm
Posts: 12
Location: Ann Arbor, MI
Hi again,

Regarding your first problem, I researched this a while ago, using the Criteria class, and ran into a roadblock. I'm afraid I can't remember what the roadblock was, so I'm not sure if it still exists, but to get around it I used the sqlRestriction(...) method from the Restrictions class, which lets you add a criteria-restriction using native SQL. This isn't the most efficient way to do this, but to me it was worth it to be able to continue to use the Criteria object.

I'm adapting my code to your example, assuming that each Keyword has a name property. This also assumes that you've created a Book object filled with all your search criteria -- e.g. getKeywords() returns a list of all the keywords selected via a search form by your applicaton's user. And, finally, this assumes that you're passing in a Criteria object created on the Book entity.

Code:
    private void handleKeywords(final Book book, Criteria criteria) {
        List<Keyword> keywords = book.getKeywords();
            /*
             * Adding type/name pairs for each Keyword
             */
            ArrayList<String> names = new ArrayList<String>();
            ArrayList<Type> types = new ArrayList<Type>();
            for (Keyword keyword : keywords) {
                names.add(keyword.getName());
                types.add(new StringType());
            }

            /*
             * Building the query -- {alias} refers to the Book entity the
             *   criteria is based on -- this notation must be used because
             *   Hibernate needs to be able to replace it with its own
             *   alias for the Book table.
             */
            StringBuffer sql = new StringBuffer("EXISTS (SELECT name "
                    + "FROM BookKeywords "
                    + "INNER JOIN Keywords "
                    + "ON BookKeywords.keywordID = "
                    + "Keywords.keywordID "
                    + "WHERE bookID = {alias}.bookID "
                    + "AND name IN (null");
            for (int i = 0; i < keywords.size(); ++i) {
                sql.append(", ?");
            }
            sql.append("))");

            /*
             * Setting the parameters to the query
             */
            criteria.add(Restrictions.sqlRestriction(
                    sql.toString(),
                    names.toArray(new String[0]),
                    types.toArray(new Type[0])));
    }


-- Jim Steinberger


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 18, 2006 6:51 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
This isn't about the original query. Bipsterite, I just wanted to clear up your misunderstanding:

Quote:
the Java primitive-wrapper classes should be used for all properties you intend to persist via Hibernate.

This is not correct. <propery name="attr" type="int"/> works just fine with "int getAttr()" and "void setAttr(int a)". There is no need to use Integer, unless you specifically use type="java.lang.Integer". And even then, if you're using java5 then you can use primitive ints, as autoboxing will look after the conversion for you.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 18, 2006 7:20 pm 
Newbie

Joined: Thu Jul 14, 2005 5:39 pm
Posts: 12
Location: Ann Arbor, MI
Tenwit's right; I'd remembered primitives causing a problem but I misspoke as to exactly what that problem is.

It's not that Hibernate requires you to use wrapper classes, as it, of course, does not.

However, if you use primitives, then if Hibernate ever tries to assign a NULL value to it, it'll throw a NullPointerException (check out http://www.hibernate.org/116.html#A16). I'd have expected this to have been a problem when you had the single row with the NULL value in it, but at any rate your parentId should be an Integer instead of an int.

Jim


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 19, 2006 7:52 am 
Newbie

Joined: Wed Jan 18, 2006 2:19 pm
Posts: 14
Hi, the entity class looks like this:

public class Cathegory implements Serializable{
private Integer id;
private Integer parentId;

public Cathegory() {}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public Integer getParentId() {
return parentId;
}

public void setParentId(Integer parentId) {
this.parentId = parentId;
}
}

I inserted rows into table using sql before, so now I tried to insert a row with the code like
...
tx=session.beginTransaction();
cathegory=new Cathegory();
cathegory.setParentId(parameter);
session.save(cathegory);
tx.commit();
...
the row (1 | null) is inserted correctly,
but the row (2 | 1) throws an exception.
I tried to find a mistake in my code, but obviously not successfully..
The exception was (it tells more than the previous one :)

Exception in thread "main" java.lang.RuntimeException: org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of Cathegory.id
at Librarian.newCathegory(Librarian.java:240)
at Main.main(Main.java:29)
Caused by: org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of Cathegory.id
at org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:171)
at org.hibernate.tuple.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:176)
at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:3257)
at org.hibernate.persister.entity.AbstractEntityPersister.isTransient(AbstractEntityPersister.java:2983)
at org.hibernate.engine.ForeignKeys.isTransient(ForeignKeys.java:181)
at org.hibernate.engine.ForeignKeys$Nullifier.isNullifiable(ForeignKeys.java:137)
at org.hibernate.engine.ForeignKeys$Nullifier.nullifyTransientReferences(ForeignKeys.java:69)
at org.hibernate.engine.ForeignKeys$Nullifier.nullifyTransientReferences(ForeignKeys.java:47)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:263)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:167)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:114)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:186)
at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:33)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:175)
at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:27)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:557)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:545)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:541)
at Librarian.newCathegory(Librarian.java:235)
... 1 more
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:145)
... 20 morevious :)


ps: thanks for the reply to many-to-many problem.. does everybody solve it using criteria? i still meditate over usage of two many-to-one mappings instead..


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 19, 2006 2:45 pm 
Newbie

Joined: Thu Jul 14, 2005 5:39 pm
Posts: 12
Location: Ann Arbor, MI
Oh! Found it :)

In your mapping file, you declare the following mapping:
Code:
<many-to-one name="parentId" class="Cathegory"/>


The "name" parameter is the name of the property of your class -- so it's looking for a property in your class named "parentId" of type Cathegory. Instead, your "parentId" property is an Integer.

You should change your mapping to:
Code:
<many-to-one name="parent" class="Cathegory" column="parentId" />


(or however you want to name the property) And then edit your class:
Code:
public class Cathegory ... {
  private Cathegory parent;
  // getters and setters
}


What happened was, since parentId was NULL in your first row, Hibernate ignored that property altogether. But when it was not-null for the second row, it was trying to interpet a Cathegory object as an Integer, which created an introspection-problem, hence the "java.lang.IllegalArgumentException: object is not an instance of declaring class".

Hope that's all there is to it, hehe.
-- Jim


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 

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.