views:

79

answers:

3

Hi

My basic question is: How can I force Hibernate to make float NULLable and accept NULL for float, datetime, blob, respectively? I really mean NULL, not (float) 0.0.

Even worse, when I try to store an object with the desired NULLable fields actually being NULL and using entity manager, I get errors for attributes, which are marked as NULLable by Hibernate even in the db table.

Here is what I have been trying in vain:

Consider this table:

+--------+--------------+------+-----+---------+----------------+
| Field  | Type         | Null | Key | Default | Extra          |
+--------+--------------+------+-----+---------+----------------+
| id     | int(11)      | NO   | PRI | NULL    | auto_increment |
| name   | varchar(255) | NO   |     | NULL    |                |
| number | float        | YES  |     | NULL    |                |
| url    | varchar(3)   | YES  |     | NULL    |                |
| mydate | date         | YES  |     | NULL    |                |
+--------+--------------+------+-----+---------+----------------+

which was created by these statements:

DROP SCHEMA IF EXISTS `mydb` ;
CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ;
USE `mydb` ;

DROP TABLE IF EXISTS `mydb`.`test` ;
CREATE  TABLE IF NOT EXISTS `mydb`.`test` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(255) NOT NULL ,
  `number` FLOAT NULL,
  `url` VARCHAR(3) NULL ,
  `mydate` DATE NULL ,
  PRIMARY KEY (`id`) ,
  UNIQUE INDEX `id_UNIQUE` (`id` ASC) )
ENGINE = InnoDB;

If I carry out following insert:

INSERT INTO `mydb`.`test` (`name`) VALUES ('MyName');
SELECT * FROM `mydb`.`test`;

I get following result:

+----+--------+--------+------+--------+
| id | name   | number | url  | mydate |
+----+--------+--------+------+--------+
|  1 | MyName |   NULL | NULL | NULL   |
+----+--------+--------+------+--------+
1 row in set (0.00 sec)

which is exactly what I want.

With Hibernate 3.5 / JPA2, I try do do my ORM like this:

BO:

public class BodyStat extends BusinessObject {

@Id
private int id;

private String name;

@Column(nullable = true)
private float number;

@Column(nullable = true)
private URL url;

@Column(nullable = true)
private Date myDate;

... C'Tor / setters / getters / eof

and the entity in my orm.xml looks like this:

<entity class="test">
    <table name="&quot;test&quot;"/>
    <attributes>
        <id name="id">
            <generated-value strategy="TABLE"/>
        </id>
        <basic name="name"/>
        <basic name="number" optional="true"/>
        <basic name="url"  optional="true"/>
        <basic name="myDate"  optional="true"/>
    </attributes>
</entity>

Now the table generated by Hibernate looks like this:

+--------+--------------+------+-----+---------+------------+
| Field  | Type         | Null | Key | Default | Extra      |
+--------+--------------+------+-----+---------+------------+
| id     | int(11)      | NO   | PRI | NULL    |            |
| name   | varchar(255) | NO   |     | NULL    |            |
| number | float        | NO   |     | NULL    |            |
| url    | tinyblob     | YES  |     | NULL    |            |
| mydate | date         | YES  |     | NULL    |            |
+--------+--------------+------+-----+---------+------------+

which is not quite what I want. As you can see

  1. float is not nullable,
  2. the type "url" turned out to as a blob rather than a varchar and
  3. mydate as "date" rather than "datetime"
A: 

A float primitive isn't nullable, but a java.lang.Float or java.lang.Double is.

If that doesn't work, maybe you need to think more abstractly. What does that float represent? A "number" isn't very descriptive. Is it an account balance? Something else that's meaningful for your problem? If yes, wrap it in an object that describes and abstracts it better.

Java's an object-oriented language, and the "O" in ORM stands for objects. Think less in terms of primitives and relational columns and more in terms of objects. You'll find Hibernate easier if you do so.

duffymo
Hi Duffy, thanks for the answer. I am now using "Float" instead of "float" and my table becomes nullable. However, I am still not able to leave it null, I have to create an Float (new Float(0.0)), which is still not what I want. I want the user to be able to leave this fiels blank. He will do so by default and this is, why I don't want to store any value within the db. Storing default values instead of NULL makes the db 1. bloated and 2. ambigus -> how shall I know the stored value is his real input or just my default value? Sorry, I need to get along with NULL ... thanks anyway.
btw, "number" represents an optional value of a time series. Any real value between -infinit and infinit is meaningful, so I cannot use a default value. Is there a different approach to represent an empty variable in ORM? NaN or something?
@erlord - Bloated? Not really. You're probably guilty of pre-mature optimization there. The right answer is your second one - it's a time series, so there is no default value. Why would you insert a row without a value at all?
duffymo
'... pre-mature optimization' no, I just want NULLs to be NULLs ... 'insert a row without a value' Consider an experiment with n sensors and random measurement. You never now which sensor will measure at which time, but the correct time must be preserved.
I can't see why you'd insert a row without a value. If a sensor takes a measurement, there will be a row inserted with a value; no measurement, no row. Your thought experiment makes no sense to me unless you insert rows at fixed times whether a sensor has a value or not. If that's true, I'd switch to an event-based design where I wouldn't insert a row unless I had a measurement.
duffymo
@Duffymo: What if my tupel consists of 1+n attributes (i.e. id + n sensors)? Well, yes, I could normalize further by creating a many-to-one relation, but is that really an appropiate measure? Furthermore: What is NULL within the database good for, if it cannot be used?
+1  A: 

float is not nullable,

This one is weird. nullable is used only as a column property for table creation and should do its job whether the mapped property is a primitive or not. At least your code works for me with Derby, I get a nullable column for the generated table. But obviously, using the wrapper (i.e. Float) at the object level would make sense if you want to be able to "pass" a null value.

the type "url" turned out to as a blob rather than a varchar and

From the JPA 1.0 specification:

2.1.1 Persistent Fields and Properties

...

The persistent fields or properties of an entity may be of the following types: Java primitive types; java.lang.String; other Java serializable types (including wrappers of the primitive types, java.math.BigInteger, java.math.BigDecimal, java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Time, java.sql.Timestamp, user-defined serializable types, byte[], Byte[], char[], and Character[]); enums; entity types and/or collections of entity types; and embeddable classes (see section 2.1.5).

The java.net.URL is serializable, that's all (from a JPA point of view), so it gets treated as serializable (and stored in a BLOB).

If you really want it to be stored as a String, either use a custom UserType (Hibernate specific) or use a String holder and provide getter/setter doing a conversion (and mark the getter as Transient). Something like this:

private String urlAsString;

@Transient
public URL getUrl() throws MalformedURLException {
    return new URL(this.urlAsString);
}

public void setUrl(URL url) {
    this.urlAsString = url.toString();
}

@Column(nullable = true)
protected String getUrlAsString() {
    return urlAsString; 
}

protected void setUrlAsString(String urlAsString) {
    this.urlAsString = urlAsString;
}

mydate as "date" rather than "datetime"

If you want to also store the time part of your date, map it as a timestamp using the @Temporal annotation:

@Column(nullable = true)
@Temporal(TemporalType.TIMESTAMP)
private Date myDate;

PS: By the way, why do you provide both annotations AND an orm.xml? Are you aware that they are somehow equivalent? If you can use annotations, just use them, no need for that orm.xml.

Pascal Thivent
Hi Pascal, again thank you very much for your answer, I shall try your suggestions in the evening after work. "annotations AND an orm.xml" I am still learning, but I guess I will switch to pure annotations soon.
BTW, do you have an idea what is going wrong as far as my secound paragraph is concerned? I get a null pointer exception when trying and storing an objects with no objects for nullable non-primitiv attributes even though they appear as nullable in the table. This is the most important issue to me.
@erlord: You're welcome. Regarding the insertion of NULLs, this should work (and the description is not enough to give an answer, some trace would help).
Pascal Thivent
I just ran a quick test: Hibernate3.5/JPA/MySQL5.1/Annotations/Spring throws a null pointer exception (trace follows this evening), but EclipseLink/JPA/MySQL5.1/Annotations does not, i.e. it works with EeclipseLink! But I can't get EclipseLink with Spring running so I have to rely on Hibernate. Anyone clues that Hibernate's JPA implementation is buggy?
@erlord I didn't see the trace so I can't say anything. And I wouldn't say that Hibernate's JPA implementation is buggy, I'm pretty happy with it.
Pascal Thivent
A: 

Hi all

Again thanks for your great support and suggestions. Actually, I have found the solution now. As Pascal mentioned, I had been using both, annotation driven and xml configuration mixed up. As soon as I deleted the xml config, this issue was solved.

I will not open an bug at hibernate, because it is best practice anyway not to mix up configuration types, hence, I would rather give the advice to carry out best practice than to mix up config types.

Cheers ER