-->
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.  [ 4 posts ] 
Author Message
 Post subject: NullPointerException on Session.flush(). Hibernate bug?
PostPosted: Wed Dec 21, 2005 5:30 pm 
Newbie

Joined: Wed Nov 23, 2005 11:37 am
Posts: 9
I'm not sure if this is the result of an error in my mapping file/code, or if I have stumbled across a bug in Hibernate. Any help would be appreciated.

I'm trying to map a class which has a Map collection property. This Map contains name/value pairs which are stored in another table, and joined by a foreign key column.

The tables in question are as follows:

Code:
SQL> desc t_identifier;
Name                                      Null?    Type
----------------------------------------- -------- ----------------------------
ID_KEY                                 NOT NULL NUMBER(12)
ID                                     NOT NULL RAW(64)
ID_CI                                  NOT NULL NUMBER(5)
ID_TYPE                                   NOT NULL NUMBER(4)
ATTRIBUTE_SET_ID                                   NUMBER(12)


SQL> desc t_attribute_set;
Name                                      Null?    Type
----------------------------------------- -------- ----------------------------
ATTRIBUTE_SET_ID                          NOT NULL NUMBER(12)
ATTRIBUTE_NAME                            NOT NULL VARCHAR2(60)
ATTRIBUTE_VALUE                           NOT NULL VARCHAR2(1000)



An "attribute set" is a collection of N unique name/value pairs, and every name/value pair in the set has the same attribute_set_id. In other words the primary key of t_attribute_set is (attribute_set_id, attribute_name).

Note that the Map is an optional property, meaning the foreign key column pointing to the Map table (i.e. t_identifier.attribute_set_id) is nullable.

My mapping file is as follows:

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

<hibernate-mapping>
   
    <class name="com.xxx.common.dto.IdentifierDTO" table="t_identifier">
        <id name="idKey" column="id_key">
            <generator class="sequence">
                <param name="sequence">seq_id_key</param>
            </generator>
        </id>
        <property name="id" column="id" type="binary"/>
        <property name="cryptoIndex" column="id_ci"/>
        <property name="idType" column="id_type"/>
        <property name="attributeSetID" column="attribute_set_id" not-null="false"/>
       
        <!-- attribute set -->
        <map name="attributeSet" table="t_attribute_set" lazy="true" cascade="all" inverse="false">
            <key property-ref="attributeSetID" column="attribute_set_id" not-null="false" update="false"/>
            <map-key column="attribute_name" type="string"/>
            <element column="attribute_value" type="string" not-null="true"/>
        </map>
    </class>
</hibernate-mapping>



And my Hibernate class is as follows:

Code:
package com.xxx.common.dto;

(Note: imports ommitted for simplicity)

public class IdentifierDTO extends DTO {

  private static final Crypto crypto;

  static {
    crypto = ApplicationContext.getContext().getCrypto();
  }

  private long idKey;
  private byte[] id;
  private int cryptoIndex;
  private int idType;
  private Long attributeSetID;
  private Map<String, String> attributeSet;
 

  public IdentifierDTO() { }

  public Long getAttributeSetID() {
    return attributeSetID;
  }

  public void setAttributeSetID(Long attributeSetID) {
    this.attributeSetID = attributeSetID;
  }

  public int getCryptoIndex() {
    return cryptoIndex;
  }

  public void setCryptoIndex(int cryptoIndex) {
    this.cryptoIndex = cryptoIndex;
  }

  public byte[] getId() {
    return id;
  }

  public void setId(byte[] id) {
    this.id = id;
  }

  public long getIdKey() {
    return idKey;
  }

  public void setIdKey(long idKey) {
    this.idKey = idKey;
  }

  public int getIdType() {
    return idType;
  }

  public void setIdType(int idType) {
    this.idType = idType;
  }

  public Map getAttributeSet() {
    return attributeSet;
  }

  public void setAttributeSet(Map<String, String> attributeSet) {
    this.attributeSet = attributeSet;
  }

  public void addAttribute(String key, String value) {
    if (attributeSet == null) {
      attributeSet = new HashMap<String, String>();
      DAOFactory daoFactory = DAOFactory.get();
      AttributeSetDAO attributeSetDAO = daoFactory.getDAO(AttributeSetDAO.class);
      this.setAttributeSetID(attributeSetDAO.getNextAttributeSetID());
    }
    attributeSet.put(key, value);
  }

}



So when *both* the attributeSetID and attributeSet properties of a IdentifierDTO instance are null, everything is fine when I flush a Session. However, when I actually set the attributeSetID property and add name/value pairs to the Map so that it's not null, I get a NullPointerException when doing a Session.flush. Here's the test code in question:

Code:

*** UNIT TEST MAIN CODE ***
public void testUpdate(){
try{
    /* get test DTO */
    T dto = getTestDTO();
    /* save this DTO */
    DAO<T> dao = getDAO();
    dao.save(dto);
    /* flush and clear the hibernate session */
    DAOFactory.flushAndClear();
    /* find this dto */
    dto = (T)dao.find(getPrimaryId(dto));
    /* update this DTO */
    modifyDTO(dto);
    dao.update(dto);
    /* flush and clear the hibernate session */
    DAOFactory.flushAndClear(); // ***THIS IS WHERE THE EXCEPTION OCCURS, when Session.flush() is called***
    /* find this dto */
    T dto2 = (T)dao.find(getPrimaryId(dto));
    /* verify that the DTOs are equal */
    assertEqualsDTO(dto, dto2);
} catch(ObjectNotFoundException e) {
     fail(e.getMessage());
} catch (Throwable e){
     logger.log(Level.SEVERE, "Failure in testUpdate", e);
     throw new RuntimeException(e);
} finally {
     /* rollback so that we can re-run this test */
     DAOFactory.rollback();
     DAOFactory.close();
}


*** CODE IN modifyDTO() ***
protected void modifyDTO(IdentifierDTO id) {
    // create a new attribute set
    id.addAttribute("whenRecieved", new Timestamp(System.currentTimeMillis()).toString());
    id.addAttribute("special", "false");
  }


When I do this I get the following exception:

Code:
FINE: listing entities:
Dec 21, 2005 1:28:39 PM org.hibernate.pretty.Printer toString
FINE: com.xxx.common.dto.IdentifierDTO{cryptoIndex=100, attributeSetID=305, idKey=3010, id=b1b2b3b4b5b6b7b8b9b0b1b2b3b4b5b6, attributeSet=[false, 2005-12-21 13:28:38.85], idType=1}
Dec 21, 2005 1:28:39 PM org.hibernate.event.def.AbstractFlushingEventListener performExecutions
FINEST: executing flush
Dec 21, 2005 1:28:39 PM com.xxx.common.dao.DAOTest testUpdate
SEVERE: Failure in testUpdate
java.lang.NullPointerException
        at org.hibernate.type.AbstractType.getHashCode(AbstractType.java:111)
        at org.hibernate.type.AbstractType.getHashCode(AbstractType.java:119)
        at org.hibernate.cache.CacheKey.<init>(CacheKey.java:40)
        at org.hibernate.action.CollectionAction.beforeExecutions(CollectionAction.java:73)
        at org.hibernate.engine.ActionQueue.prepareActions(ActionQueue.java:246)
        at org.hibernate.engine.ActionQueue.prepareActions(ActionQueue.java:152)
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingE
ventListener.java:273)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:
27)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:730)
        at com.xxx.common.dao.DAOFactory.flushAndClear(DAOFactory.java:189)
        at com.xxx.common.dao.DAOTest.testUpdate(DAOTest.java:95)
        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 junit.framework.TestCase.runTest(TestCase.java:154)
        at junit.framework.TestCase.runBare(TestCase.java:127)
        at junit.framework.TestResult$1.protect(TestResult.java:106)
        at junit.framework.TestResult.runProtected(TestResult.java:124)
        at junit.framework.TestResult.run(TestResult.java:109)
        at junit.framework.TestCase.run(TestCase.java:118)
        at junit.framework.TestSuite.runTest(TestSuite.java:208)
        at junit.framework.TestSuite.run(TestSuite.java:203)
        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.codehaus.surefire.battery.JUnitBattery.executeJUnit(JUnitBattery.java:246)
        at org.codehaus.surefire.battery.JUnitBattery.execute(JUnitBattery.java:220)
        at org.codehaus.surefire.Surefire.executeBattery(Surefire.java:204)
        at org.codehaus.surefire.Surefire.run(Surefire.java:153)
        at org.codehaus.surefire.Surefire.run(Surefire.java:77)
        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.codehaus.surefire.SurefireBooter.run(SurefireBooter.java:104)
        at org.apache.maven.test.SurefirePlugin.execute(SurefirePlugin.java:303)
        at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:399)
        at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor
.java:519)
        at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifec
ycleExecutor.java:469)
        at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.
java:448)
        at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultL
ifecycleExecutor.java:301)
        at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleE
xecutor.java:268)
        at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java
:137)
        at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:316)
        at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:113)
        at org.apache.maven.cli.MavenCli.main(MavenCli.java:249)
        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.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
        at org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
        at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
        at org.codehaus.classworlds.Launcher.main(Launcher.java:375)


Notice when it lists the entities prior to executing the flush() above that both attributeSetID and the attributeSet Map object are both not null, and the attributeSet Map has values in it. I added some debugging code to the org.hibernate.cache.CacheKey constructor and it is failing when trying to create a CacheKey for the attributeSet property of IdentifierDTO. It is the "Serializable id" parameter (i.e. CacheKey.key) that is null.

So why is the key null? Is it because the primary key of t_attribute_set is NOT just the attribute_set_id foreign key column in t_identifier, and I have to do some kind of composite-id type mapping? Is it a bug in Hibernate? Is this kind of mapping not possible in Hibernate?

Any and all help greatly appreciated. Thanks in advance.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 22, 2005 6:03 am 
Senior
Senior

Joined: Tue May 10, 2005 9:00 am
Posts: 125
Hi

I think you are trying to persist your collection using modifyDTO(IdentifierDTO id) but the key you are basing your collection on ('t_identifier.ATTRIBUTE_SET_ID') is still null (did you forget to set it to a non null value before adding items to the collection?). That is you technically want hibernate to put in t_attribute_set:

Code:
ATTRIBUTE_SET_ID ATTRIBUTE_NAME ATTRIBUTE_VALUE
NULL             "whenRecieved" currentTime
NULL             "special"      "false"

Which is obviously a design flaw.

I think hibernate is trying to do is, use the 'key' of the mapping to store it internally, to do that it does a hash on the key and gets a NullPointerException. Of course it shouldn't do that, it should, imho, send an exception telling you you are trying to to insert datas in a Collection where the Collection key is null.

To solve your problem you can, for example, do a id.setAttributeSetID(someValue) or change your shema so your t_attribute_set.ATTRIBUTE_SET_ID is pointing to t_identifier.ID_KEY, but then you can't share anymore one attribute set accross different t_identifier.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 22, 2005 1:46 pm 
Newbie

Joined: Wed Nov 23, 2005 11:37 am
Posts: 9
Hi tchize, thanks for the reply.

Actually I am setting the attributeSetID property on the IdentifierDTO object. If you take a look at the IdentifierDTO.addAttribute() method agan, you'll see that it checks if the collection is null, and if so, gets the next attributeSetID from the sequence, sets it on this, and adds the key/value to the Map:

Code:
  public void addAttribute(String key, String value) {
    if (attributeSet == null) {
      attributeSet = new HashMap<String, String>();
      DAOFactory daoFactory = DAOFactory.get();
      AttributeSetDAO attributeSetDAO = daoFactory.getDAO(AttributeSetDAO.class);
      this.setAttributeSetID(attributeSetDAO.getNextAttributeSetID());
    }
    attributeSet.put(key, value);
  }


So the key (attributeSetID) *shouldn't* be null in this case. And in fact when you look at the output when Hibernate lists the entities in the cache before doing the flush(), the attributeSetID is set to a non null value and the map has values in it:

Code:
FINE: listing entities:
Dec 21, 2005 1:28:39 PM org.hibernate.pretty.Printer toString
FINE: com.xxx.common.dto.IdentifierDTO{cryptoIndex=100, attributeSetID=305, idKey=3010, id=b1b2b3b4b5b6b7b8b9b0b1b2b3b4b5b6, attributeSet=[false, 2005-12-21 13:28:38.85], idType=1}


So my question remains: why is Hibernate complaining that my key is null, when it obviously isn't?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 23, 2005 10:46 am 
Senior
Senior

Joined: Tue May 10, 2005 9:00 am
Posts: 125
Right, then am out of idea, i would probably do a step by step analysis to find why this problem arise if it was me :)


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