【问题标题】:Setting a JPA timestamp column to be generated by the database?设置由数据库生成的 JPA 时间戳列?
【发布时间】:2010-10-23 03:33:37
【问题描述】:

在我的 SQL Server 2000 数据库中,我有一个名为 lastTouched 的类型为 DATETIME 的时间戳(在函数中,不在数据类型中)列设置为 getdate() 作为其默认值/绑定。

我正在使用 Netbeans 6.5 生成的 JPA 实体类,并且在我的代码中有这个

@Basic(optional = false)
@Column(name = "LastTouched")
@Temporal(TemporalType.TIMESTAMP)
private Date lastTouched;

但是,当我尝试将对象放入我得到的数据库时,

javax.persistence.PersistenceException: org.hibernate.PropertyValueException: not-null property references a null or transient value: com.generic.Stuff.lastTouched

我尝试将@Basic 设置为(optional = true),但这会引发异常,指出数据库不允许TIMESTAMP 列的null 值,这不是设计使然。

ERROR JDBCExceptionReporter - Cannot insert the value NULL into column 'LastTouched', table 'DatabaseName.dbo.Stuff'; column does not allow nulls. INSERT fails.

我以前让它在纯 Hibernate 中工作,但我已经切换到 JPA 并且不知道如何告诉它应该在数据库端生成此列。请注意,我仍然使用 Hibernate 作为我的 JPA 持久层。

【问题讨论】:

    标签: java jpa persistence annotations timestamp


    【解决方案1】:

    我通过将代码更改为解决了这个问题

    @Basic(optional = false)
    @Column(name = "LastTouched", insertable = false, updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastTouched;
    

    因此在生成 SQL 插入时会忽略时间戳列。不确定这是否是解决此问题的最佳方法。欢迎反馈。

    【讨论】:

    • 其实想多了,我想我会需要更新的专栏,因为当我进行专栏更改时,我需要通过Java更新它。除非有办法以某种方式在数据库端进行列更新。
    • 根据stackoverflow.com/questions/36001/…,我应该在这里使用触发器。
    • 对于 LocalDateTime 等其他类型呢?有了这个解决方案,我得到了Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: @Temporal should only be set on a java.util.Date or java.util.Calendar
    【解决方案2】:

    我意识到这有点晚了,但我已经成功地用注释时间戳列了

    @Column(name="timestamp", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
    

    这也适用于CURRENT_DATECURRENT_TIME。我正在使用 JPA/Hibernate 和 Oracle,所以 YMMV。

    【讨论】:

    • 这对我们有用!我们将它与insertable=false, updatable=false 结合起来
    • ERROR 4371 --- [ main] org.hibernate.tool.hbm2ddl.SchemaExport : You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP` not null, full_content longtext not null' at line 1. My column is: @Column(nullable = false, name="created_at", updatable = false, columnDefinition= "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")` 和 private Timestamp createdAt;
    • 这是一个简短而甜蜜的解决方案。谢谢
    【解决方案3】:
    @Column(nullable = false, updatable = false)
    @CreationTimestamp
    private Date created_at;
    

    这对我有用。 more info

    【讨论】:

    【解决方案4】:

    添加@CreationTimestamp注解:

    @CreationTimestamp
    @Column(name="timestamp", nullable = false, updatable = false, insertable = false)
    private Timestamp timestamp;
    

    【讨论】:

    【解决方案5】:

    我在使用 JPA2.0 和 MySQL 5.5.10 时运行良好,对于我只关心最后一次修改行的情况。 MySQL 将在第一次插入时创建一个时间戳,并且每次在该行上调用 UPDATE。 (注意:如果我关心 UPDATE 是否确实进行了更改,这将是有问题的)。

    本例中的“timestamp”列就像“last-touched” column.x`

    下面的代码使用单独的列“版本”进行乐观锁定。

    private long version;
    private Date timeStamp
    
    @Version
    public long getVersion() {
        return version;
    }
    
    public void setVersion(long version) {
        this.version = version;
    }
    
    // columnDefinition could simply be = "TIMESTAMP", as the other settings are the MySQL default
    @Column(name="timeStamp", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
    @Temporal(TemporalType.TIMESTAMP)
    public Date getTimeStamp() {
        return timeStamp;
    }
    
    public void setTimeStamp(Date timeStamp) {
        this.timeStamp = timeStamp;
    }
    

    (注意:@Version 不适用于 MySQL“DATETIME”列,其中 Entity 类中的属性类型为“Date”。这是因为 Date 生成的值精确到毫秒,但 MySQL 没有存储毫秒,所以当它对数据库中的内容和“附加”实体进行比较时,它认为它们具有不同的版本号)

    From the MySQL manual regarding TIMESTAMP

    With neither DEFAULT nor ON UPDATE clauses, it is the same as DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP.
    

    【讨论】:

      【解决方案6】:

      我不认为每个数据库都有自动更新时间戳(例如 Postgres)。所以我决定在我的代码中的任何地方手动更新这个字段。这适用于每个数据库:

      thingy.setLastTouched(new Date());
      HibernateUtil.save(thingy);
      

      使用触发器是有原因的,但对于大多数项目来说,这不是其中之一。触发器可以让您更深入地了解特定的数据库实现。

      MySQL 5.6.28(Ubuntu 15.10,OpenJDK 64-Bit 1.8.0_66)似乎非常宽容,不需要任何超越

      @Column(name="LastTouched")
      

      MySQL 5.7.9(CentOS 6,OpenJDK 64-Bit 1.8.0_72)仅适用于

      @Column(name="LastTouched", insertable=false, updatable=false)
      

      不是:

      FAILED: removing @Temporal
      FAILED: @Column(name="LastTouched", nullable=true)
      FAILED: @Column(name="LastTouched", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
      

      我的其他系统信息(在两个环境中相同)

      • 休眠实体管理器 5.0.2
      • 休眠验证器 5.2.2
      • mysql-connector-java 5.1.38

      【讨论】:

      • 非常有用的信息,谢谢 - 我想很多人会被这次升级到 MySQL 5.7.x 所困扰
      • @James - 谢谢,但我只是列出了我所知道的差异。我不确定关键的区别是什么。它可能完全是另一回事。希望其他人可以建立在这个答案上。
      • 是的,对不起,我应该补充一点,我在 Ubuntu 14.04.1 上从 MySQL 5.6 升级到 5.7 后遇到了这个错误(操作系统和 JDK 没有更改 AFAIK)。所以我假设 MySQL 版本是这里的关键因素。
      • @James - 哇,这真的很有用。谢谢!我已经更新了我的答案以反映这一点。
      【解决方案7】:

      如果您使用@DynamicInsert 标记您的实体,例如

      @Entity
      @DynamicInsert
      @Table(name = "TABLE_NAME")
      public class ClassName implements Serializable  {
      

      Hibernate 将生成没有 null 值的 SQL。然后数据库将插入自己的默认值。这确实会影响性能,请参阅 [动态插入][1]。

      【讨论】:

        【解决方案8】:

        我发布这个是为了在使用 MySQL 和 Java Spring Boot JPA 时寻找答案的人,就像@immanuelRocha 说的那样,在 Spring 中只有 @CreationTimeStamp 到 @Column,并且在 MySQL 中将默认值设置为“CURRENT_TIMESTAMP ”。

        在 Spring 中只添加一行:

        @Column(name = "insert_date")
        @CreationTimestamp
        private Timestamp insert_date;

        【讨论】:

        • docs.jboss.org/hibernate/orm/4.3/javadocs/org/hibernate/… :属性值将设置为当前 VM 日期。这可能是也可能不是数据库系统设置的时间,具体取决于它们的时钟是否同步......在大多数情况下应该没问题,除非同步多个系统很关键。
        • 正确,在大多数情况下,此解决方案将起作用,除非该人需要另一个时钟时间。
        【解决方案9】:

        如果你在 Java 8 和 Hibernate 5 或 Spring Boot JPA 中进行开发,那么直接使用以下 annotation 在您的实体类中。 Hibernate 从 VM 获取当前时间戳,并将日期和时间插入数据库。

        public class YourEntity {
         
            @Id
            @GeneratedValue
            private Long id;
         
            private String name;
         
            @CreationTimestamp
            private LocalDateTime createdDateTime;
         
            @UpdateTimestamp
            private LocalDateTime updatedDateTime;
         
            …
        }
        

        【讨论】:

        【解决方案10】:

        这也适用于我:-

        @Temporal(TemporalType.TIMESTAMP)   
        @Column(name = "CREATE_DATE_TIME", nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
        public Date getCreateDateTime() {
            return createDateTime;
        }
        
        public void setCreateDateTime(Date createDateTime) {
            this.createDateTime = createDateTime;
        }
        

        【讨论】:

        • 假设本地时钟不同步,我想这会添加本地系统的时间,而不是数据库的系统。
        【解决方案11】:
        @Column(name = "LastTouched", insertable = false, updatable = false, columnDefinition = "TIMESTAMP default getdate()")
        @Temporal(TemporalType.TIMESTAMP)
        private Date LastTouched;`enter code here`
        

        【讨论】:

          【解决方案12】:

          这对我有用:

          @Column(name = "transactionCreatedDate", nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-04-02
            • 1970-01-01
            • 2014-01-27
            • 1970-01-01
            • 2011-03-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多