开发者

Hibernate / JPA -> Nullable values & objects?

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:

+--------+-开发者_如何学JAVA-------------+------+-----+---------+----------------+
| 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"


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.


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


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜