【问题标题】:Hibernate / JPA -> Nullable values & objects?Hibernate / JPA - >可为空值和对象?
【发布时间】:2011-03-14 14:20:33
【问题描述】:

我的基本问题是:如何强制 Hibernate 使 float 为 NULLable 并分别为 float、datetime、blob 接受 NULL?我的意思是 NULL,而不是 (float) 0.0。

更糟糕的是,当我尝试使用实体管理器存储所需的 NULLable 字段实际上为 NULL 的对象时,我收到属性错误,即使在 db 表中也被 Hibernate 标记为 NULLable。

这是我一直在徒劳的尝试:

考虑这张表:

+--------+--------------+------+-----+---------+----------------+
| 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    |                |
+--------+--------------+------+-----+---------+----------------+

由这些语句创建:

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;

如果我执行以下插入:

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

我得到以下结果:

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

这正是我想要的。

使用 Hibernate 3.5 / JPA2,我尝试这样做我的 ORM:

博:

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

我的 orm.xml 中的实体如下所示:

<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>

现在Hibernate生成的表是这样的:

+--------+--------------+------+-----+---------+------------+
| 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    |            |
+--------+--------------+------+-----+---------+------------+

这不是我想要的。如您所见

  1. float 不能为空,
  2. “url”类型变成了 blob 而不是 varchar 和
  3. mydate 作为“日期”而不是“日期时间”

【问题讨论】:

    标签: java hibernate orm jpa nullable


    【解决方案1】:

    float 不能为空,

    这个很奇怪。 nullable用作创建表的列属性,并且应该完成它的工作无论映射的属性是否为基元。至少您的代码对我来说适用于 Derby,我为生成的表获得了一个可为空的列。但显然,如果您希望能够“传递”null 值,则在对象级别使用包装器(即Float)是有意义的。

    “url”类型变成了 blob 而不是 varchar 和

    来自 JPA 1.0 规范:

    2.1.1 持久字段和属性

    ...

    实体的持久字段或属性可以是以下类型: Java 原始类型; java.lang.String;其他 Java 可序列化类型(包括原始类型的包装器,java.math.BigIntegerjava.math.BigDecimaljava.util.Datejava.util.Calendarjava.sql.Datejava.sql.Timejava.sql.Timestamp,用户定义的可序列化类型,byte[]Byte[]char[]Character[]);枚举;实体类型和/或实体类型的集合;和可嵌入类(参见第 2.1.5 节)。

    java.net.URL 是可序列化的,仅此而已(从 JPA 的角度来看),因此它被视为可序列化的(并存储在 BLOB 中)。

    如果您确实希望将其存储为 String,请使用自定义 UserType(特定于 Hibernate)或使用 String 持有者并提供 getter/setter 进行转换(并将 getter 标记为 Transient )。像这样的:

    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 作为“日期”而不是“日期时间”

    如果您想存储日期的时间部分,请使用 @Temporal 注释将其映射为时间戳:

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

    PS:顺便说一句,你为什么同时提供注释和orm.xml?你知道它们在某种程度上是等价的吗?如果你可以使用注解,就使用它们,不需要那个orm.xml

    【讨论】:

    • Hi Pascal,再次非常感谢您的回答,我会在晚上下班后尝试您的建议。 “annotations AND an orm.xml” 我还在学习,但我想我很快就会切换到纯注解。
    • 顺便说一句,就我的第二段而言,您知道出了什么问题吗?在尝试和存储一个没有对象的对象时,我得到一个空指针异常,即使它们在表中显示为可为空的,也可以为可空的非原始属性。这对我来说是最重要的问题。
    • @erlord:不客气。关于 NULL 的插入,这应该可行(并且描述不足以给出答案,一些跟踪会有所帮助)。
    • 我刚刚运行了一个快速测试:Hibernate3.5/JPA/MySQL5.1/Annotations/Spring 抛出空指针异常(跟踪今晚跟踪),但 EclipseLink/JPA/MySQL5.1/Annotations不,即它适用于 EeclipseLink!但是我不能让 EclipseLink 运行 Spring,所以我必须依赖 Hibernate。任何线索表明 Hibernate 的 JPA 实现存在错误?
    • @erlord 我没有看到踪迹,所以我不能说什么。我不会说 Hibernate 的 JPA 实现有问题,我对此很满意。
    【解决方案2】:

    再次感谢您的大力支持和建议。实际上,我现在已经找到了解决方案。正如 Pascal 提到的,我一直在混合使用注释驱动和 xml 配置。一旦我删除了xml配置,这个问题就解决了。

    我不会在休眠时打开错误,因为无论如何最好不要混淆配置类型,因此,我宁愿建议执行最佳实践而不是混淆配置类型。

    干杯 呃

    【讨论】:

      【解决方案3】:

      float 原语不能为空,但 java.lang.Float 或 java.lang.Double 可以。

      如果这不起作用,也许您需要更抽象地思考。那个浮动代表什么? “数字”不是很具描述性。是账户余额吗?其他对您的问题有意义的事情?如果是,请将其包装在一个可以更好地描述和抽象它的对象中。

      Java 是一种面向对象的语言,ORM 中的“O”代表对象。少考虑基元和关系列,多考虑对象。如果你这样做,你会发现 Hibernate 更容易。

      【讨论】:

      • 嗨达菲,感谢您的回答。我现在使用“Float”而不是“float”,并且我的表可以为空。但是,我仍然无法将其保留为空,我必须创建一个 Float (new Float(0.0)),这仍然不是我想要的。我希望用户能够将此字段留空。他会默认这样做,这就是为什么我不想在数据库中存储任何值。存储默认值而不是 NULL 会使数据库 1. 臃肿和 2. 模糊 -> 我怎么知道存储的值是他的真实输入还是我的默认值?抱歉,我需要和 NULL 相处......无论如何,谢谢。
      • 顺便说一句,“数字”表示时间序列的可选值。 -infinit 和 infinit 之间的任何实际值都是有意义的,所以我不能使用默认值。有没有不同的方法来表示 ORM 中的空变量? NaN 还是什么?
      • @erlord - 臃肿?并不真地。您可能对那里的过早优化感到内疚。正确的答案是你的第二个 - 这是一个时间序列,所以没有默认值。为什么要插入一个根本没有值的行?
      • '...未成熟的优化'不,我只是希望 NULL 为 NULL...'插入没有值的行' 考虑一个使用 n 个传感器和随机测量的实验。您永远不会知道哪个传感器将在哪个时间测量,但必须保留正确的时间。
      • 我不明白您为什么要插入没有值的行。如果传感器进行测量,则会插入一行值;没有测量,没有行。你的思想实验对我来说毫无意义,除非你在固定时间插入行,无论传感器是否有值。如果这是真的,我会切换到基于事件的设计,除非我进行测量,否则我不会插入行。
      猜你喜欢
      • 2015-10-04
      • 2011-11-05
      • 2015-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-22
      • 2017-04-09
      • 2013-03-18
      相关资源
      最近更新 更多