使用OpenCSV映射CSV数据至JPA实体类时OneToMany关联导致busFee字段默认值不生效的问题解决咨询
嘿,这个问题我之前处理过类似的场景,咱们来拆解一下为啥加了@OneToMany之后默认值就不生效了,然后给出几个靠谱的解决方案:
先分析原因
你在字段上设置的private Integer busFee = 0是Java层面的默认值,但当JPA处理懒加载的关联关系(比如你的List<MarkSheet>)时,它可能会通过动态代理或者特殊的实例化方式创建实体对象,这时候字段的默认初始化逻辑可能没被执行到;另外如果OpenCSV在读取CSV时,对应位置没有数据,会把busFee设为null,直接覆盖了你设置的默认值。
解决方案一:让OpenCSV直接处理默认值
既然问题根源之一是CSV无数据时被设为null,那咱们可以直接在OpenCSV的绑定注解里指定默认值,这样读取的时候就不会出现null了:
@CsvBindByPosition(position = 2, defaultValue = "0") private Integer busFee = 0;
defaultValue参数会在CSV对应位置为空或者缺失时,自动把字段设为指定的值,从源头避免null进入实体。
解决方案二:给数据库列加默认值(双重保障)
即使实体层出现null,咱们也可以让数据库来兜底,给busFee字段加上数据库层面的默认值:
@Column(name = "bus_fee", columnDefinition = "INT DEFAULT 0") @CsvBindByPosition(position = 2) private Integer busFee = 0;
这样不管实体里的busFee是null还是啥,数据库在插入时都会自动用0填充,非常稳妥。
解决方案三:用@PrePersist在持久化前修正值
如果担心前面的方法有遗漏,可以在实体里加一个持久化前的回调方法,统一检查并修正busFee的值:
@PrePersist public void prePersist() { // 如果busFee为null,就设为0 this.busFee = Objects.requireNonNullElse(this.busFee, 0); }
这个方法会在JPA把实体保存到数据库之前执行,不管是CSV导入还是其他方式修改实体,都能确保busFee不会是null。
解决方案四:显式在构造函数中初始化字段
有时候JPA懒加载创建实体时,可能不会触发字段的默认初始化,这时候可以在无参构造函数里显式给busFee赋值:
public Student() { this.busFee = 0; }
这样不管实体是怎么被实例化的,busFee都会有初始值0。
推荐组合方案
我个人建议把方案一+方案二+方案三组合起来用,从CSV读取、实体初始化、数据库持久化三个层面都做保障,基本就不会再出现busFee存为null的问题了。
内容的提问来源于stack exchange,提问作者krishna

