-->
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: Mapping problem: not-null property references a null value
PostPosted: Wed Jun 09, 2004 8:41 am 
Newbie

Joined: Fri Apr 23, 2004 4:18 pm
Posts: 15
Location: Oslo, Norway
I am having some trouble solving a problem where Hibernate complaints about "not-null property references a null or transient value" during flush after a delete.

Basically I can not decide wether my model is wrong or if I am using Hibernate incorretly. It surely is not a Hibernate bug :-)

Our product uses Hibernate upon a model of roughly 40 entines which are thightly coupled. We use a variant of the thread-local pattern by allocating a Hibernate session per API-call within a dynamic proxy. JBossCache is our 2nd level cache.

To simplify the scenario for this posting I have put together two entities -- user and authorization. Users can authorize other users.

Mapping file for users:
Code:
<hibernate-mapping>
    <class name="com.foo.PWVUser" table="wvUser" lazy="true">
        <id name="id" type="string" length="32" unsaved-value="null">
            <generator class="uuid.hex"/>
        </id>
         <cache usage="transactional"/>
        <map name="reactiveAuthorizations" inverse="true" cascade="all-delete-orphan" lazy="true">
            <cache usage="transactional"/>
            <key column="wvUserId"/>
            <index column="authorizedWVUserId" type="string"/>
            <one-to-many class="com.foo.PReactiveAuthorization"/>
        </map>
    </class>
</hibernate-mapping>


Mapping for authorizations:
Code:
<hibernate-mapping>
    <class name="com.foo.PReactiveAuthorization" table="reactiveAuth" lazy="true">
        <id name="id" type="string" length="32" unsaved-value="null">
            <generator class="uuid.hex"/>
        </id>

         <cache usage="transactional"/>

        <many-to-one name="authorizingWVUser" not-null="true" class="com.foo.PWVUser">
            <meta attribute="field-description">Authorizing user.</meta>
            <column name="wvUserId" index="reactWvUserId"/>
        </many-to-one>

        <many-to-one name="authorizedWVUser" column="authorizedWVUserId" not-null="true" class="com.foo.PWVUser"/>
            <meta attribute="field-description">Authorized user.</meta>
        </many-to-one>
    </class>
</hibernate-mapping>


User class:
Code:
ublic class PWVUser {
    private String id;
    private Map reactiveAuthorizations;

    public PWVUser() {}

    public String getId() {
        return this.id;
    }

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

    public Map getReactiveAuthorizations() {
        return this.reactiveAuthorizations;
    }

    public void setReactiveAuthorizations(Map reactiveAuthorizations) {
        this.reactiveAuthorizations = reactiveAuthorizations;
    }
}


Authorization class:
Code:
public class PReactiveAuthorization {
    private String id;
    private PWVUser authorizingWVUser;
    private PWVUser authorizedWVUser;

    public PReactiveAuthorization() {}
    }

    public String getId() {
        return this.id;
    }

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

    public PWVUser getAuthorizingWVUser() {
        return this.authorizingWVUser;
    }

    public void setAuthorizingWVUser(PWVUser authorizingWVUser) {
        this.authorizingWVUser = authorizingWVUser;
    }

    public PWVUser getAuthorizedWVUser() {
        return this.authorizedWVUser;
    }

    public void setAuthorizedWVUser(PWVUser authorizedWVUser) {
        this.authorizedWVUser = authorizedWVUser;
    }
}


If a user B wants to authorize a user A, an authorization is created and added to the collection of authorizations on user B.

The problem with not-null field arises when I try to delete the authorized user (user A). Since I do not have a any references of authorizations given upon a user (should I?), I simply do a query to locate all users authorizing the user I am trying to delete, and then update their collection of authorizations:

Code:
List reactivlyAuthorizingUsers = session.createQuery("select auth.authorizingWVUser from " + PReactiveAuthorization.class.getName() + " auth where auth.authorizedWVUser.id = :wvUserId")
        .setParameter("wvUserId", userA.getId())
        .list();
for (Iterator i = reactivlyAuthorizingUsers.iterator(); i.hasNext();) {
     ((PWVUser) i.next()).getReactiveAuthorizations().remove(userA.getId());
}


When I then do a delete on the userA and flush the session, Hibernate throws a PropertyValueException compliainging about PReactiveAuthorization.authorizedWVUser is null.

I guess that this has something to do with link-owners, but I can not figure out what it is.

Could I use any other relational mapping (ternary?), or should I manage two collections on the user; One for authorizations given to other users and one for authorizations given upon a user. Other suggestions?

System setup:
Hibernate: 2.1.4
Oracle: 9.2.0.1.0
Tomcat: 5.0.19
JVM: Sun 1.4.2_03
Cache: JBoss TreeCache 1.01

Simple test that fails:
Code:
public class LinkOwnerBugOnDeleteTest extends TestCase {
    private net.sf.hibernate.SessionFactory factory;

    public LinkOwnerBugOnDeleteTest(String s) throws Exception {
        super(s);
        SessionFactory.initializeHibernate();
        factory = SessionFactory.getFactory();
    }

    protected void setUp() throws Exception {
        super.setUp();
    }

    protected void tearDown() throws Exception {
        super.tearDown();
    }

    public void testDeleteBug() throws Exception {
        // create user A and B
        PWVUser userA = new PWVUser();
        userA.setUserRef("userA");

        PWVUser userB = new PWVUser();
        userB.setUserRef("userB");
        userB.setReactiveAuthorizations(new HashMap(0));

        Session session;
        Transaction t = null;
        try {
            session = factory.openSession();
            session.setFlushMode(FlushMode.NEVER);
            session.save(userA);
            session.save(userB);
            t = session.beginTransaction();
            session.flush();
            t.commit();
            session.close();
        } finally {
            if (t != null && !t.wasCommitted()) {
                t.rollback();
            }
            t = null;
        }

        Serializable userAId = userA.getId();
        Serializable userBId = userB.getId();

        // add authorization for user A on user B
        try {
            session = factory.openSession();
            session.setFlushMode(FlushMode.NEVER);
            userA = (PWVUser) session.load(PWVUser.class, userAId);
            userB = (PWVUser) session.load(PWVUser.class, userBId);
            PReactiveAuthorization pra = new PReactiveAuthorization();
            pra.setAuthorizedWVUser(userA);
            pra.setAuthorizingWVUser(userB);
            userB.getReactiveAuthorizations().put(userA.getId(), pra);
            t = session.beginTransaction();
            session.flush();
            t.commit();
            session.close();
        } finally {
            if (t != null && !t.wasCommitted()) {
                t.rollback();
            }
            t = null;
        }

        // delete user A
        try {
            session = factory.openSession();
            session.setFlushMode(FlushMode.COMMIT);
            userA = (PWVUser) session.load(PWVUser.class, userAId);
            // delete authorization made upon userA
            List reactivlyAuthorizingUsers = session.createQuery("select auth.authorizingWVUser from " + PReactiveAuthorization.class.getName() + " auth where auth.authorizedWVUser.id = :wvUserId")
                    .setParameter("wvUserId", userA.getId())
                    .list();
            for (Iterator i = reactivlyAuthorizingUsers.iterator(); i.hasNext();) {
                PWVUser otherUser = (PWVUser) i.next();
                Map ras = otherUser.getReactiveAuthorizations();
                ras.remove(userA.getId());
            }
            session.delete(userA);
            t = session.beginTransaction();
            session.flush();
            t.commit();
            session.close();
        } finally {
            if (t != null && !t.wasCommitted()) {
                t.rollback();
            }
            t = null;
        }
    }
}


Thanks for your time :-)



Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 10, 2004 6:07 am 
Newbie

Joined: Fri Apr 23, 2004 4:18 pm
Posts: 15
Location: Oslo, Norway
Hmm... it seems like I'm quering deaf ears here.

Is my question stupid, not ment for this list or simply to difficult to comment?




Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 03, 2004 8:22 am 
Newbie

Joined: Tue Nov 18, 2003 11:56 am
Posts: 16
Hi, (I guess you found an answer to your problem by now, this is just for other users with a similar problem)

A just had a similar problem and it turned out that earlier in the same (Hibernate) session I've tried to create a domain object where not all required fields were set (doing some junit testing). This resulted in a HibernateException. But I never closed the session in the catch block.

So, the next operation (a delete operation on antoher domain object of the same type but with a different id) I got a exception with the message "not-null property references a null or transient value".

The solution for me was to add a finally block where I close the Hibernate session. Then it worked as expected.

Another idea would be to use the Spring framework to avoid all the try/catch/finally of Hibernate.

_________________
Thanks
Per Thomas


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 27, 2004 8:46 am 
Newbie

Joined: Fri Aug 27, 2004 8:27 am
Posts: 4
Hi. I have same problem.
Here is my code:
Code:
            Session hsess = sessionFactory.openSession();
            Transaction tx=null;
            try {
                tx = hsess.beginTransaction();
                User user = (User) hsess.load(User.class, userName);
                hsess.delete(user);
                tx.commit();
            } catch (HibernateException he){
                if (tx!=null) tx.rollback();
                throw he;
            } finally {
                hsess.close();
            }


Here is hibernate log output:
Code:
SessionImpl:1158 - deleting a persistent instance
SessionImpl:1178 - deleting [com.exshop.User#fghfgh]
JDBCTransaction:59 - commit
SessionImpl:2242 - flushing session
SessionImpl:2435 - Flushing entities and processing referenced collections
AbstractEntityPersister:274 - com.exshop.Country.code2 is dirty
AbstractEntityPersister:274 - com.exshop.Country.code3 is dirty
SessionImpl:2529 - Updating entity: [com.exshop.Country#178]
JDBCTransaction:82 - rollback


I tried to delete com.exshop.User. I didn't change com.exshop.Country.code2 and code3 propertyes in my application. And i even didn't read their values. They couldn't change (i think). But hibernate detected them "dirty", tried to update Country object and finally exception was throwed: PropertyValueException: not-null property references a null or transient value: com.exshop.Country.code3.
Same exception throws when i try to update User object.

Here are mappings for User and Country objects:
Code:
<hibernate-mapping>

    <class name="com.exshop.User" table="exshop.USERS">

        <id name="name" type="string" unsaved-value="null" >
            <column name="UserName" sql-type="VARCHAR(20)" not-null="true"/>
            <generator class="assigned"/>
        </id>

        <property name="password">
            <column name="Password" sql-type="VARCHAR(15)" not-null="true"/>
        </property>

        <property name="FName">
            <column name="FName" sql-type="VARCHAR(35)" not-null="false"/>
        </property>

        <property name="SName">
            <column name="SName" sql-type="VARCHAR(35)" not-null="false"/>
        </property>

                                     <<skiped>>

        <many-to-one name="country" class="com.exshop.Country" column="COUNTRY_ID" unique="true" cascade="none"/>

        <many-to-one name="region" class="com.exshop.Region" column="REGION_ID" unique="true"/>

        <set name="roles" table="exshop.USERS_ROLES" lazy="false">
            <key column="UserName"/>
            <many-to-many class="com.exshop.Role" column="RoleName"/>
        </set>

        <set name="orders" lazy="false">
            <key column="UserName"/>
            <one-to-many class="com.exshop.Order"/>
        </set>
    </class>

</hibernate-mapping>

Code:
<hibernate-mapping>
    <class name="com.exshop.Country" table="exshop.COUNTRIES">

        <id name="id" type="integer" unsaved-value="0" >
            <column name="ID" sql-type="INTEGER" not-null="true"/>
            <generator class="increment"/>
        </id>

        <property name="name">
            <column name="Name" sql-type="VARCHAR(70)" not-null="true"/>
        </property>

        <property name="code2">
            <column name="Code2" sql-type="VARCHAR(2)" not-null="true"/>
        </property>

        <property name="code3">
            <column name="Code3" sql-type="VARCHAR(3)" not-null="true"/>
        </property>

        <property name="regionName">
            <column name="RegionName" sql-type="VARCHAR(20)" not-null="false"/>
        </property>

        <set name="regions" lazy="true">
            <key column="COUNTRY_ID"/>
            <one-to-many class="com.exshop.Region"/>
        </set>

    </class>

</hibernate-mapping>



I don't know what to think about it. Please, help.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 27, 2004 8:49 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Use your debugger. It is probably just a broken get/set pair.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 27, 2004 9:04 am 
Newbie

Joined: Fri Aug 27, 2004 8:27 am
Posts: 4
After i removed properties "code2" and "code3" from mapping file exception disappeared. No other changes were made.

Here is code for Country object:
Code:
public class Country {
    String name;
    String code2;
    String code3;
    String regionName;
    Set regions;
    int id;

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

    public int getId(){
        return this.id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name){
        this.name=name;
    }

    public String getCode2() {
        return this.code2;
    }

    public void setCode2(String code2){
        this.code2=code2;
    }

    public String getCode3() {
        return this.code3;
    }

    public void setCode3(String code3){
        this.code2=code3;
    }

    public String getRegionName(){
        return this.regionName;
    }

    public void setRegionName(String regionName){
        this.regionName=regionName;
    }

    public Set getRegions(){
        return this.regions;
    }

    public void setRegions(Set regions){
        this.regions=regions;
    }

    public boolean equals(Object other){
        if (this==other) return true;
        if (other instanceof Country){
            Country another=(Country) other;
            boolean eq=true;
            eq = eq && ((name==another.name) ||(name!=null && name.equals(another.name)));
            eq = eq && ((code2==another.code2) ||(code2!=null && code2.equals(another.code2)));
            eq = eq && ((code3==another.code3) ||(code3!=null && code3.equals(another.code3)));
            eq = eq && ((regionName==another.regionName) ||(regionName!=null && regionName.equals(another.regionName)));
            return eq;
        }
        return false;
    }

    public int hashCode(){
        int hash=7;
        hash=31*hash+(null == name ? 0 : name.hashCode());
        hash=31*hash+(null == code2 ? 0 : code2.hashCode());
        hash=31*hash+(null == code3 ? 0 : code3.hashCode());
        hash=31*hash+(null == regionName ? 0 : regionName.hashCode());
        return hash;
    }
}



Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 30, 2004 2:10 am 
Newbie

Joined: Fri Aug 27, 2004 8:27 am
Posts: 4
Hi.
It seems i was blind. I found error in previous listing. (in setCode3 method).
So, i have no more questions. Thanks to gavin.


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.