-->
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.  [ 13 posts ] 
Author Message
 Post subject: Is Reflection supposed to work on Hibernate managed objects?
PostPosted: Tue Feb 21, 2006 4:02 am 
Newbie

Joined: Tue Feb 21, 2006 3:33 am
Posts: 6
Hibernate version: 3.1
JDK version: Sun 1.4.2_10 (Linux)

I have inherited a project that does not use any of the entity objects directly, but does everything through the Reflection API. Ex;

Instead of

Contact primary = account.getPrimaryContact();

a reflective approach is taken;

Class entityClass = anUnknownEntity.getClass();
Method method = entityClass.getMethod( "getPrimaryAccount", null );
Object someOtherUnknown = method.invoke( anUnknownEntity, null );

The non persistence part of the application then do further introspection of the "someOtherUnkown" instance and put it into its context... irrelevant to my question.


Now, this application is running in Hibernate 2.1.8 without any problems in this area.
We came across a strange "OutOfMemoryException" during the creation of SessionFactory in Hibernate 2.1.8 and decided it was time to move to 3.x.


Now, we have problems with the above way of accessing Hibernate. And furthermore, it is not consistent.

Two instance of the same entity class, retrieved during the same query give us two different "Class" instances. One being the expected entity class (Account in the above example) and the next being an CGlib enhanced class of the super class of Account (Asset).

The Account and its super class does not share any method names other than toString(), equals() and hashCode(), nor do they implement any interfaces other than Serializable.

Furthermore, running the same app on the same computer would produce the same "NoSuchMethod" on the same instance in the DB. On another computer, same application, same data, different instance of the Account type fails, but again consistently same one.

Here is the actual mapping file of a case where we see the above happening. Superclass = TsCommand and the subclass is TsJavaCommand.

<hibernate-mapping>
<class name="com.tokuii.baseapp.conf.redbull.v1.data.TsCommand" table="TsCommand">
<id name="Id" type="java.lang.Long" unsaved-value="0">
<generator class="native"/>
</id>
<version column="TsVersion" name="TsVersion"/>
<many-to-one name="TsModifyUser" class="com.tokuii.baseapp.conf.redbull.v1.data.TsUser" />
<many-to-one name="TsCreateUser" class="com.tokuii.baseapp.conf.redbull.v1.data.TsUser" />
<property name="TsModifyTimestamp" type="java.lang.Long" />
<property name="TsCreateTimestamp" type="java.lang.Long" />
<property name="TsRecStat" type="java.lang.String" column="TsRecStat" not-null="false" />
<property name="TsSysRowState" type="java.lang.String" column="TsSysRowState" not-null="false" />
<property name="TsCommandName" type="java.lang.String" />
<joined-subclass name="com.tokuii.baseapp.conf.redbull.v1.data.TsJavaCommand">
<key column="TsJavaCommandId"/>
<property name="TsCommandClassName" type="java.lang.String" />
</joined-subclass>
<joined-subclass name="com.tokuii.baseapp.conf.redbull.v1.data.TsScriptCommand">
<key column="TsScriptCommandId"/>
<many-to-one name="TsType" class="com.tokuii.baseapp.conf.redbull.v1.data.TsScriptCommandType"/>
<property name="TsSimulatedScript" type="java.lang.String" />
<property name="TsNonSimulatedScript" type="java.lang.String" />
<many-to-one name="TsParentCommandId" class="com.tokuii.baseapp.conf.redbull.v1.data.TsCommand"/>
</joined-subclass>
</class>
</hibernate-mapping>


My questions are;
Is the Reflection API deliberately broken, so that we can't look for the methods in the Class instance even though calling the method on the down casted type works??
Or am I looking at some obscure bug that has a solution or workaround?
Would it help to introduce interfaces on each entity and use the interface to look up the method name, and then invoke the method on the enhanced class?

Btw, most of the application is "Hibernate agnostic" and it is not feasible to do Hibernate.getClass( entityObject ) for every call.


Any help is appreciated.

Thank you
Niclas Hedhman


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 21, 2006 5:01 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
i would assume it should work...did you also use lazy=true in hibernate 2?

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 21, 2006 5:59 am 
Newbie

Joined: Tue Feb 21, 2006 3:33 am
Posts: 6
Yes. Except for the DTD nothing is different in the HBM between Hibernate 2 and Hibernate 3.

Niclas


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 21, 2006 6:01 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
and the associations were also lazy in H2 ?

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 21, 2006 6:24 am 
Newbie

Joined: Tue Feb 21, 2006 3:33 am
Posts: 6
Yes, as I said, *nothing* has changed in the HBMs.

We have done a source upgrade and replaced the deprecated Hibernate 2 classes with the recommended approach throughout the codebase, i.e. no org.hibernate.classic usage.

Cheers
Niclas


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 21, 2006 8:12 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
but hibernate 3 has different defaults for lazy so if you did not have lazy="true" before it will now be there by default - changing the behavior as specificed in the migration guide.

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 21, 2006 9:20 am 
Newbie

Joined: Tue Feb 21, 2006 3:33 am
Posts: 6
OK, thanks, I will double-check that when I am back in the office to rerun the setup.

But I don't understand why this would be related to the fact that instances of the same entity class retrieved in the same query would be of different Classes, and that which of the entities become one of the two would be different on different machines.

Cheers
Niclas


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 21, 2006 9:54 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
http://www.hibernate.org/hib_docs/v3/re ... ng-proxies

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 21, 2006 10:28 pm 
Newbie

Joined: Tue Feb 21, 2006 3:33 am
Posts: 6
Ok, thanks for the pointer. Although I can see how this would explain part of the problem. Let's take the example from the section in the document. Cat and DomesticCat, no interfaces....


List cats = session.createQuery( "from Cat as cat" );
Iterator it = cats.iterator();
Class cat1 = it.next().getClass();
Class cat2 = it.next().getClass();
System.out.println( cat1 );
System.out.println( cat2 );

Output;
class org.hedhman.niclas.DomesticCat
class org.hedhman.niclas.Cat$$EnhancerByCGLIB$$39fa0e55


This is what I am observing on the computer I am sitting at now. On another machine, it will return DomesticCat for both of those, but somewhere further down the list choose to return a class org.hedhman.niclas.Cat$$EnhancerByCGLIB$$nnnnnnnn class.


The provided pointer seems to indicate that the enhance subclass of the superclass (Cat) would always be returned when using the proxies.


Reading further, it seems to me that I have two choices;

1. Introduce "proxy interface" and *hope* that the mile long threads complaining about proxies and inheritence, is not an issue. I presume that will need to look up the method from the interface as well, and possibly other complications.

2. Stop using the Reflection API and try to move to use ClassMetaData instead for manipulation of the values.


Does this seems like a correct assessment?


Cheers
Niclas


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 22, 2006 12:53 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
If a given Cat is already associated with that Session in proxied form, then the code you posted would return that proxied form. For example, say you did (two cats : 1 & 2):
Code:
Session session = ...;
session.load( Cat.class, 1 ); // returns a proxy
Iterator it = session.createQuery( "from Cat" ).iterate();
Class cat1 = it.next().getClass();
Class cat2 = it.next().getClass();
System.out.println( cat1 );
System.out.println( cat2 );

you'd get output like:
class org.hedhman.niclas.Cat$$EnhancerByCGLIB$$39fa0e55
class org.hedhman.niclas.DomesticCat

The first result would be a proxy because that cat was already associated with this session in proxied form...


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 22, 2006 1:03 am 
Newbie

Joined: Tue Feb 21, 2006 3:33 am
Posts: 6
Ok, but this is not my case. All this happens on "boot" wrapped in a single session and single transaction. All the "Hibernate Cats" are loaded once with a single query, then converted to "POJO Cats" and then lives until that user logs off.
Also doesn't explain that a different behavior is shown on a different machine, although the identical execution path in our code is taken.

Anyway, I will move towards my option 2. above.


Cheers
Niclas


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 22, 2006 1:08 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Cats have kittens; kittens have kittens of their own. Cats also have mates, and those mates have mates and kittens as well...


Top
 Profile  
 
 Post subject: Problem with hibernate proxy object and ClassMetaData
PostPosted: Tue Apr 11, 2006 11:28 am 
Newbie

Joined: Sat Nov 22, 2003 9:21 pm
Posts: 18
Location: Malaysia
Hi,

The problem can be summarised as follow:
- If an hibernate entity object has proxy attribute is defined inside class node of hbm.xml file, ClassMetaData.getPropertyValue() of any property will throw IllegalArgumentException;
- Regardless whether Reflection API is used or hibernate.cglib.use_reflection_optimizer is either set to true or false;
- If the proxy attribute is not defined, ClassMetaData::getPropertyValue() / ClassMetaData::getPropertyValues() returns the values successfully; and
- Accessing the TsRecStat property by invoking TsPassword::getTsRecStat() does not cause exception to be thrown in any of the case.

My question:
- If reflection API should not be used to access the property value, shouldn't hibernate ClassMetaData API be used to access the property value?

Tested environment:
- Hibernate 3.1.2
- Hibernate 3.1.3
- Postgresql 8.1
- Jdk 1.5.0_06
- Windows XP SP2

The test code:
Code:
    public void testAccessingTsPasswordProxyWithClassMetaDataAPI()
    {
        Session currentSession = null;
        Transaction transaction = null;
        try
        {
            ClassMetadata passwordClassMetaData = m_sessionFactory.getClassMetadata( TsPassword.class );
            currentSession = m_sessionFactory.openSession();
            transaction = currentSession.beginTransaction();

            Query query = currentSession.createQuery( "FROM TsPassword AS p" );
            Iterator it = query.iterate();
            while( it.hasNext() )
            {
                Object obj = it.next();

                ITsPassword itspassword = (ITsPassword) obj;
                Date tsExpiryTimestamp = itspassword.getTsExpiryTimestamp();
                passwordClassMetaData.getPropertyValue( itspassword, "TsRecStat", EntityMode.POJO );
                Object expiryFromPasswordInterface = passwordClassMetaData.getPropertyValue(
                    itspassword, "TsExpiryTimestamp", EntityMode.POJO
                );
                assertEquals( "Expiry timestamp from interface", tsExpiryTimestamp, expiryFromPasswordInterface );

                Object propertyValue = passwordClassMetaData.getPropertyValue(
                    obj, "TsExpiryTimestamp", EntityMode.POJO
                );
                assertEquals( "Expiry timestamp from raw object", tsExpiryTimestamp, propertyValue );
            }
            transaction.commit();
        } catch( HibernateException he )
        {
            he.printStackTrace();

            fail( "Hibernate exception occurred." );
            if( transaction != null )
            {
                transaction.rollback();
            }
        } catch( Exception e )
        {
            e.printStackTrace();
            fail( "Something else exception occured." );

            if( transaction != null )
            {
                transaction.rollback();
            }
        }
        finally
        {
            if( currentSession != null )
            {
                currentSession.close();
            }
        }
    }

Line 78 is
Code:
passwordClassMetaData.getPropertyValue( itspassword, "TsRecStat", EntityMode.POJO );


The exception:
Code:
IllegalArgumentException in class: com.tokuii.baseapp.conf.redbull.v1.data.TsPassword, getter method of property: TsRecStat
org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of com.tokuii.baseapp.conf.redbull.v1.data.TsPassword.TsRecStat
        at org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:171)
        at org.hibernate.tuple.AbstractEntityTuplizer.getPropertyValue(AbstractEntityTuplizer.java:270)
        at org.hibernate.tuple.AbstractEntityTuplizer.getPropertyValue(AbstractEntityTuplizer.java:280)
        at org.hibernate.persister.entity.AbstractEntityPersister.getPropertyValue(AbstractEntityPersister.java:3252)
        at com.tokuii.tests.component.hibernate.HibernateProxyTest.testAccessingTsPasswordProxyWithClassMetaDataAPI(Hibe
rnateProxyTest.java:78)

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)
        ... 50 more


Hbm.xml:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
  <class name="com.tokuii.baseapp.conf.redbull.v1.data.TsPassword" table="TsPassword" proxy="com.tokuii.system.ITsPassword">
    <id name="Id" type="long" unsaved-value="0">
      <generator class="native"/>
    </id>
    <version column="TsVersion" name="TsVersion"/>
    <many-to-one name="TsCreateUser" class="com.tokuii.baseapp.conf.redbull.v1.data.TsUser"/>
    <many-to-one name="TsModifyUser" class="com.tokuii.baseapp.conf.redbull.v1.data.TsUser"/>
    <property name="TsCreateTimestamp" type="timestamp" column="TsCreateTimestamp" not-null="false"/>
    <property name="TsModifyTimestamp" type="timestamp" column="TsModifyTimestamp" not-null="false"/>
    <property name="TsRecStat" type="java.lang.String" column="TsRecStat" not-null="false"/>
    <property name="TsSysRowState" type="java.lang.String" column="TsSysRowState" not-null="false"/>
    <property name="TsExpiryTimestamp" type="timestamp" column="TsExpiryTimestamp" not-null="false"/>
    <property name="TsPassword" type="java.lang.String" column="TsPassword" not-null="false" length="50"/>
    <property name="TsPwChangeRestriction" type="java.lang.Boolean" column="TsPwChangeRestriction" not-null="false"/>
    <property name="TsPwValidity" type="java.lang.String" column="TsPwValidity" not-null="false"/>
  </class>
</hibernate-mapping>


The TsPassword.java:
Code:
public class TsPassword implements com.tokuii.system.ITsPassword {

    private java.lang.Long m_Id;

    private int m_TsVersion = 1;

    private com.tokuii.system.ITsUser m_TsCreateUser;

    private com.tokuii.system.ITsUser m_TsModifyUser;

    private java.util.Date m_TsCreateTimestamp;

    private java.util.Date m_TsModifyTimestamp;

    private java.lang.String m_TsRecStat = "A";

    private java.lang.String m_TsSysRowState = "P";

    private java.util.Date m_TsExpiryTimestamp;

    private java.lang.String m_TsPassword;  // Natural Identifier

    private java.lang.Boolean m_TsPwChangeRestriction;

    private java.lang.String m_TsPwValidity;

    private com.tokuii.system.ITsUser m_TsUserId;  // Natural Identifier

    private java.util.Set m_TsPasswordIdTsUser;

    /** Non-persistent field. */
    private java.util.Map m_NonDBFields;

    public TsPassword() {
    }

    public java.lang.Long getId() {
        return m_Id;
    }

    public void setId(java.lang.Long newId) {
        m_Id = newId;
    }

    public int getTsVersion() {
        return m_TsVersion;
    }

    public void setTsVersion(int newTsVersion) {
        m_TsVersion = newTsVersion;
    }

    public Object getNonDBField(String fieldName) {
        synchronized (this) {
            if (m_NonDBFields == null) {
                return null;
            }

            return m_NonDBFields.get(fieldName);
        }
    }

    public void setNonDBField(String fieldName, Object value) {
        synchronized (this) {
            if (m_NonDBFields == null) {
                m_NonDBFields = new java.util.HashMap();
            }

            m_NonDBFields.put(fieldName, value);
        }
    }

    public com.tokuii.system.ITsUser getTsCreateUser() {

        return m_TsCreateUser;
    }

    public void setTsCreateUser(com.tokuii.system.ITsUser newTsCreateUser) {
        m_TsCreateUser = newTsCreateUser;
    }

    public com.tokuii.system.ITsUser getTsModifyUser() {

        return m_TsModifyUser;
    }

    public void setTsModifyUser(com.tokuii.system.ITsUser newTsModifyUser) {
        m_TsModifyUser = newTsModifyUser;
    }

    public java.util.Date getTsCreateTimestamp() {

        return m_TsCreateTimestamp;
    }

    public void setTsCreateTimestamp(java.util.Date newTsCreateTimestamp) {
        m_TsCreateTimestamp = newTsCreateTimestamp;
    }

    public java.util.Date getTsModifyTimestamp() {

        return m_TsModifyTimestamp;
    }

    public void setTsModifyTimestamp(java.util.Date newTsModifyTimestamp) {
        m_TsModifyTimestamp = newTsModifyTimestamp;
    }

    public java.lang.String getTsRecStat() {

        return m_TsRecStat;
    }

    public void setTsRecStat(java.lang.String newTsRecStat) {
        m_TsRecStat = newTsRecStat;
    }

    public java.lang.String getTsSysRowState() {

        return m_TsSysRowState;
    }

    public void setTsSysRowState(java.lang.String newTsSysRowState) {
        m_TsSysRowState = newTsSysRowState;
    }

    public java.util.Date getTsExpiryTimestamp() {

        return m_TsExpiryTimestamp;
    }

    public void setTsExpiryTimestamp(java.util.Date newTsExpiryTimestamp) {
        m_TsExpiryTimestamp = newTsExpiryTimestamp;
    }

    public java.lang.String getTsPassword() {

        return m_TsPassword;
    }

    public void setTsPassword(java.lang.String newTsPassword) {
        m_TsPassword = newTsPassword;
    }

    public java.lang.Boolean getTsPwChangeRestriction() {

        return m_TsPwChangeRestriction;
    }

    public void setTsPwChangeRestriction(java.lang.Boolean newTsPwChangeRestriction) {
        m_TsPwChangeRestriction = newTsPwChangeRestriction;
    }

    public java.lang.String getTsPwValidity() {

        return m_TsPwValidity;
    }

    public void setTsPwValidity(java.lang.String newTsPwValidity) {
        m_TsPwValidity = newTsPwValidity;
    }

    public com.tokuii.system.ITsUser getTsUserId() {

        return m_TsUserId;
    }

    public void setTsUserId(com.tokuii.system.ITsUser newTsUserId) {
        m_TsUserId = newTsUserId;
    }

    public java.util.Set getTsPasswordIdTsUser() {
        synchronized (this) {
            if (m_TsPasswordIdTsUser == null) {
                m_TsPasswordIdTsUser = new java.util.HashSet();
            }
        }

        return m_TsPasswordIdTsUser;
    }

    public void setTsPasswordIdTsUser(java.util.Set newTsPasswordIdTsUser) {
        m_TsPasswordIdTsUser = newTsPasswordIdTsUser;
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }

        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        final TsPassword that = (TsPassword) o;

        if (m_TsPassword != null ? !m_TsPassword.equals(that.m_TsPassword) : that.m_TsPassword != null) {
            return false;
        }

        Object thisId = m_TsUserId != null ? m_TsUserId.getId() : null;
        Object thatId = that.m_TsUserId != null ? that.m_TsUserId.getId() : null;
        if (thisId != null ? !thisId.equals(thatId) : thatId != null) {
            return false;
        }

        return true;
    }

    public int hashCode() {
        int result = 0;

        result = 29 * result + (m_TsPassword != null ? m_TsPassword.hashCode() : 0);

        result = 29 * result + (m_TsUserId != null && m_TsUserId.getId() != null ? m_TsUserId.getId().hashCode() : 0);

        return result;
    }
}


The interface:
Code:
public interface ITsPassword extends java.io.Serializable
{
    java.lang.Long getId();

    void setId( java.lang.Long aLong );

    int getTsVersion();

    void setTsVersion( int index );

    java.lang.Object getNonDBField( java.lang.String elementLinkName );

    void setNonDBField( java.lang.String elementLinkName, java.lang.Object object );

    com.tokuii.system.ITsUser getTsCreateUser();

    void setTsCreateUser( com.tokuii.system.ITsUser iTsUser );

    java.util.Date getTsCreateTimestamp();

    void setTsCreateTimestamp( java.util.Date date );

    com.tokuii.system.ITsUser getTsModifyUser();

    void setTsModifyUser( com.tokuii.system.ITsUser iTsUser );

    java.util.Date getTsModifyTimestamp();

    void setTsModifyTimestamp( java.util.Date date );

    java.lang.String getTsRecStat();

    void setTsRecStat( java.lang.String elementLinkName );

    java.lang.String getTsSysRowState();

    void setTsSysRowState( java.lang.String elementLinkName );

    java.util.Date getTsExpiryTimestamp();

    void setTsExpiryTimestamp( java.util.Date date );

    java.lang.String getTsPassword();

    void setTsPassword( java.lang.String elementLinkName );

    java.lang.Boolean getTsPwChangeRestriction();

    void setTsPwChangeRestriction( java.lang.Boolean aBoolean );

    java.lang.String getTsPwValidity();

    void setTsPwValidity( java.lang.String elementLinkName );

    com.tokuii.system.ITsUser getTsUserId();

    void setTsUserId( com.tokuii.system.ITsUser iTsUser );

    java.util.Set getTsPasswordIdTsUser();

    void setTsPasswordIdTsUser( java.util.Set set );
}


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 13 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.