V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
NULL2020
V2EX  ›  Java

Jpa 插入时如何同步数据库字段的默认值

  •  
  •   NULL2020 · 2021-01-07 11:33:36 +08:00 · 2697 次点击
    这是一个创建于 1420 天前的主题,其中的信息可能已经有所发展或是发生改变。
    @Entity
    @Table(name = "tb_user")
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String username;
        private Boolean deleteFlag;
        private Boolean activeFlag;
    
        public User() {
        }
    }
    
    

    如上实体类代码,两个布尔值的数据库字段是设置了默认值 0 的,在新增保存时,如果没设置两个属性的值,插入后并不能得到数据库的默认值,即仍然是 null 。

    目前想到的办法有两个:

    1. 在实体属性上面加注解:@Generated(GenerationTime.INSERT)

      加这个注解后,JPA 在插入数据库后,会执行一次 select 查询把相关字段的值查询回来,然后赋值给对象。

    2. 在实体属性上加上默认值,如 private Boolean deleteFlag = false

      这种方法在阿里手册里不推荐,然而手册上面举的反例是时间字段,思考好久感觉并没有什么不妥的地方?

    综上,不知道有没有更好的解决方案?

    27 条回复    2021-01-08 13:40:41 +08:00
    Oktfolio
        1
    Oktfolio  
       2021-01-07 12:10:09 +08:00
    wangyanrui
        2
    wangyanrui  
       2021-01-07 12:14:24 +08:00 via Android
    为什么要相信阿里手册😂
    Oktfolio
        3
    Oktfolio  
       2021-01-07 12:18:54 +08:00
    @wangyanrui 实际确实不应该这么做。
    NULL2020
        4
    NULL2020  
    OP
       2021-01-07 12:19:17 +08:00
    @Oktfolio #1 这两个注解并不能解决该问题
    wangyanrui
        5
    wangyanrui  
       2021-01-07 12:25:19 +08:00 via Android
    @Oktfolio 😂为什么不应该这么做
    967182
        6
    967182  
       2021-01-07 12:54:34 +08:00
    boolean
    Oktfolio
        7
    Oktfolio  
       2021-01-07 13:59:50 +08:00
    @NULL2020 可以啊,怎么不行?我只给一个字段赋了值,Hibernate SQL 执行日志是 insert into table_name (field) values (?)。
    zoharSoul
        8
    zoharSoul  
       2021-01-07 14:06:34 +08:00
    @wangyanrui 因为很奇怪
    Oktfolio
        9
    Oktfolio  
       2021-01-07 14:06:54 +08:00
    @wangyanrui 如果只是逻辑删除字段默认给 false 的话,问题不大。但是如果是别的字段,你又使用 DO 去做查询的话就会有坑,特别是时间,除非你再赋值为 null 。
    NULL2020
        10
    NULL2020  
    OP
       2021-01-07 14:20:20 +08:00
    @Oktfolio #7 我的问题是要把实体里没设置值的字段,在插入后把数据库自动生成的默认值带回来。
    NULL2020
        11
    NULL2020  
    OP
       2021-01-07 14:21:16 +08:00
    @967182 #6 实体类还是要用包装类型的,不然查询返回是空的话,就会报错了
    Oktfolio
        12
    Oktfolio  
       2021-01-07 14:23:40 +08:00
    @NULL2020 entityManager.clear(); 再根据 save 返回的 id 查询出来。但是你也得加上这个注解插入到数据库的才不会为 null 。
    NULL2020
        13
    NULL2020  
    OP
       2021-01-07 14:30:42 +08:00
    @Oktfolio #12 所以最终还是得查一遍
    wangyanrui
        14
    wangyanrui  
       2021-01-07 15:24:54 +08:00 via Android
    @zoharSoul
    @Oktfolio
    还是没 GET 到哪里奇怪,数据库默认 false,代码也默认 false,一个标志位,蛮合适的呀
    ccccccccw
        15
    ccccccccw  
       2021-01-07 15:38:56 +08:00
    @wangyanrui new 一个空的对象作为查询条件的时候,本来 Boolean 的默认值是 null,jpa 就不会把他作为条件添加到 sql 中,但是如果设置了 false,jpa 就会将 false 添加到查询条件中
    ccccccccw
        16
    ccccccccw  
       2021-01-07 15:39:55 +08:00
    @wangyanrui 也就是说,where deleteFlag = false
    aragakiyuii
        17
    aragakiyuii  
       2021-01-07 15:42:06 +08:00 via iPhone
    @wangyanrui 如果用 Example 这种方式去查那得注意一下,不处理的话会默认当成查询条件
    aragakiyuii
        18
    aragakiyuii  
       2021-01-07 15:43:52 +08:00 via iPhone
    这种我习惯把字段=默认值语句写在有参构造函数里
    ApmI00
        19
    ApmI00  
       2021-01-07 16:14:12 +08:00
    所以,与其等插入后再查回,影响性能,不如在保存对象前,手动设置这两个字段,这样也不违反手册,就是代码啰嗦了一点。。。至于时间字段,怕是得用 @DynamicUpdate 配合建表 sql 了吧!!!不知道理解的可对
    DeepUse
        20
    DeepUse  
       2021-01-07 16:21:50 +08:00
    @Data
    @ToString
    @MappedSuperclass
    @EqualsAndHashCode
    public class BaseEntity {

    @Column(name = "create_time")
    @Temporal(TemporalType.TIMESTAMP)
    private Date createTime;

    @Column(name = "update_time")
    @Temporal(TemporalType.TIMESTAMP)
    private Date updateTime;

    @PrePersist
    protected void prePersist() {
    Date now = DateUtils.now();
    if (createTime == null) {
    createTime = now;
    }

    if (updateTime == null) {
    updateTime = now;
    }
    }

    @PreUpdate
    protected void preUpdate() {
    updateTime = new Date();
    }

    @PreRemove
    protected void preRemove() {
    updateTime = new Date();
    }

    }
    xuanbg
        21
    xuanbg  
       2021-01-07 16:34:06 +08:00
    我一般在 get 方法里面这样写。但得注意需要正常返回 null 的字段可别也这样干。
    pubic Boolean getDeleteFlag(){
    return deleteFlag == null ? false : deleteFlag;
    }
    472690980
        22
    472690980  
       2021-01-07 16:41:05 +08:00
    @Column(columnDefinition = "Integer default 0")
    private Integer accessFailedCount;

    可以设置字段默认值,或者你直接用小写的 bool 不就完事了 默认就是 false
    NULL2020
        23
    NULL2020  
    OP
       2021-01-07 17:32:48 +08:00
    @472690980 #22 实测无用,我需要的是保存前未设置属性值,保存后能返回对应字段里数据库的默认值。

    综上,应该是只能保存前想办法设置默认值,或者保存后再执行一次查询操作。
    wangyanrui
        24
    wangyanrui  
       2021-01-07 18:34:16 +08:00 via Android
    @ccccccccw
    可能意思没表达正确,保存时工厂方法构造对象的时候,默认设置 WWIfalse,不是直接字段值为 false.
    哈哈哈,尴尬了
    wangyanrui
        25
    wangyanrui  
       2021-01-07 18:36:38 +08:00
    @ccccccccw
    可能意思没表达正确,保存时工厂方法构造对象的时候,默认设置为 false,不是直接字段值为 false.
    哈哈哈,尴尬了
    472690980
        26
    472690980  
       2021-01-08 08:30:46 +08:00
    小写的 bool 不就完事了吗,说明你这个字段是不可空的
    zhenjiachen
        27
    zhenjiachen  
       2021-01-08 13:40:41 +08:00
    阿里不推荐属性默认值?那 kotlin 的 data class 全是默认值,那是 kotlin 错了?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5852 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 01:48 · PVG 09:48 · LAX 17:48 · JFK 20:48
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.