null in maps are handled inconsistently
Description
is duplicated by
relates to
Activity

Former user June 3, 2011 at 6:54 PM
This is an issue for me, as well. In my case, the value is a POJO with several fields. They're normally not null, but can be cleared by application users. If they're all null, this issue occurs.
As a workaround, I put a dummy property on my POJO. I'm a bit irritated that I have to waste DB space with a dummy column and pollute my POJOs to get around this buggy behavior.

Former user April 29, 2011 at 1:12 PM
I want to share how this old and insane hibernate issue could be circumvented fairly easy.
Let's take the next example:
public class Owner {
private Map<KeyClass, ValueClass> mapping = new HashMap<KeyClass, ValueClass>();
public ValueClass getMappingValue(KeyClass keyClass) {
return mapping.get(keyClass);
}
public void addMapping(KeyClass keyClass, ValueClass valueClass) {
mapping.put(keyClass, valueClass);
}
}
This wouldn't work if the value of the map is null.
owner.addMapping(keyClass, null)
1) Add a new dummy boolean column to the mapping table:
ALTER TABLE MAPPING_TABLE ADD DUMMY SMALLINT DEFAULT 0 NOT NULL;
2) Add a <composite-element> as the value of the map:
<map name="mapping" cascade="all" fetch="select" lazy="true" mutable="true" inverse="false" table="MAPPING_TABLE">
<cache usage="read-write" />
<key column="OWNER_ID" not-null="true" />
<map-key-many-to-many class="KeyClass" column="KEY_ID" />
<!-- This composite element is only created as Hibernate doesn't allow null as map value, see -->
<composite-element class="MapValueClass">
<many-to-one name="mapValue" column="VALUE_ID" class="ValueClass" />
<property name="dummy" column="DUMMY" not-null="true" type="boolean" />
</composite-element>
</map>
3) Create a MapValueClass class:
public class MapValueClass {
private ValueClass valueClass;
@SuppressWarnings("unused")
private boolean dummy;
@SuppressWarnings("unused")
private MapValueClass() {
// Hibernate
}
public MapValueClass(ValueClass valueClass) {
this.valueClass = valueClass;
}
public ValueClass getValueClass() {
return valueClass;
}
}
4) Change the mapping configuration in Owner class:
class Owner {
Map<KeyClass, MapValueClass> mapping = new HashMap<KeyClass, MapValueClass>();
public ValueClass getMappingValue(KeyClass keyClass) {
return mapping.get(keyClass).getValueClass();
}
public void addMapping(KeyClass keyClass, ValueClass valueClass) {
mapping.put(keyClass, new MapValueClass(valueClass));
}
}
Now the owner.addMapping(keyObject, null) works fine.

Kai Kunstmann April 5, 2011 at 8:19 PM
After long thinking about it, I realize now how this "feature request" of allowing null-values in a map is hard to implement reasonably. I'm writing here to help other people understand the problem and maybe introduce another idea for a solution:
It is true, that a java.util.Map
is defined in terms of a java.util.Set
of java.util.Map.Entry
with no entry having a key equal to any other entry's key in the same entry-set, and that people want to persist every entry of that set no matter if its missing a value, i.e. they want to persist the absence of a value while not giving up the relation between key and map-owner – especially with a <map-key-many-to-many>
. There doubtlessly are use-cases for this, and it truly is a reasonable request – if you look at it from the Java point-of-view. I would like to do that, too, instead of coding complicated workarounds. However, with a simple <one-to-many>
mapping in place of the map-value (even so with a <composite-element>
) it becomes impossible to express the desired absence of value, since the key would have to go into the same row as the missing entity (or component), which means having an empty row with nothing in it – especially no id – but a key and a reference to the owner to express the absence of an entity. This is obviously absurd, and in my opinion the only true reason why this issue won't fix. With a <one-to-many>
mapping the value of a map-entry essentially is the entry, which is likewise questionable.
A solution to this predicament could be the introduction of an <idmap>
with a surrogate <collection-id>
, which obviously cannot contain a <one-to-many>
mapping but could be made capable of expressing absence of value by the requirement of table
attribute and a <many-to-many>
, <many-to-any>
, <element>
or <composite-element>
mapping as with the <idbag>
to serve as a standalone entry-row with an explicit id. There certainly are other considerations to make – it's just an idea...

Stefan Fromm February 10, 2010 at 2:54 PM
I also stumbled on this issue. I expected the map to persist null values. Could not there be a setting in the mappings definition, with which one could enable the saving of keys with null values for map properties? Thus old behaviour could be preserved.

Nelson Murphy February 5, 2010 at 9:34 PM
In case this is useful to anyone else, I ended up forking the Hibernate core (v3.3.1.GA) and modifying PersistentMap to handle null values.
PersistentMap.java.ApplyTo.3.3.1.svn.patch
Again, this patch is for v3.3.1 from a year ago – I haven't checked to see if the class has changed since then.

Michael Newcomb February 2, 2010 at 9:48 PM
Just spent an hour trying to figure out what I was doing wrong... come to find out I wasn't doing anything wrong...
As to not obeying the Map api, on a new entity, the map you populate is your map until you persist the entity, so they can't really do anything at that point. Now when you load an entity PersistentMap could throw NullPointerException if someone tried to put a null value in. So, they can stop it on a Hibernate loaded entity, but not from one you just persisted. From that perspective it is inconsistent but they can't really do anything about it.
IMHO, since they can't do anything about it, they should persist everything they can in a map because the only alternative is inconsistency. I give you a map with some null values and Hibernate silently drops them on the floor. However, they are still in the map of the entity until a new one is loaded. That is by definition inconsistent.
Another alternative would be for Hibernate to throw an exception when a Map is persisted with null values. I could live with that.
On a positive note, Hibernate KICKS ASS and these issues are relatively minor.
On a negative note, there is an attitude of superiority that emanates from the direction of the Hibernate team that is not helpful and I think it causes some bugs to be dropped on the floor. : Map.remove() is broken, yet a patch has existed for almost 2 years!

Kirk Rasmussen November 17, 2009 at 12:41 AM
So much for transparent persistence! I guess Gavin doesn't have to deal with real world issues like Oracle treating empty strings as null. I suppose in his view we should put in DB specific workarounds and waste QA time because Gavin doesn't think we have to support "insane schemas".
At a minimum this is clearly a bug because it doesn't honor the Map interface but is letting his ego getting in the way from fixing it. Others have already submitted fixes to this issue and he hasn't supplied a rationale reason not to do it.
See http://www.thunderguy.com/semicolon/2003/04/26/oracle-empty-string-null/
See http://stackoverflow.com/questions/203493/why-does-oracle-9i-treat-an-empty-string-as-null
Tom Kyte VP of Oracle:
A ZERO length varchar is treated as NULL.
'' is not treated as NULL.
'' when assigned to a char(1) becomes ' ' (char types are blank padded strings).
'' when assigned to a varchar2(1) becomes '' which is a zero length string and a zero length string is NULL in Oracle (it is no long '')

Richard Dingwall August 5, 2009 at 6:00 AM
I also have a use case that requires this behaviour. Java supports it, the database supports it, why can't Hibernate or NHibernate support it?
Otherwise at least an exception should be thrown alerting the caller that it cannot be persisted (as would occur in other situations).
Details
Details
Assignee
Reporter

regarding case 00004729.
group.getUsers().put("something", null);
Does not result in any insert.
Inserting "something", null manually into the underlying table and
when hibernate reads the map will have "something"->null in the map.