1. 问题产生基础
传入save的参数中没有正确传入version值
//实体类
@Entity
@Data
@Table(name="A")
@DynamicInsert
@DynamicUpdate
public class A{
@Id
@GeneratedValue
private Integer id;
@Version
private Integer version;
//todo... 还有其他属性
}
2. 问题产生原因
首先要知道找到save方法的实现
@Transactional
public <S extends T> S save(S entity) {
//判断实体信息是否为new,如果返回true,那么久进行持久化存储(insert)
//如果返回false,那么就进行合并(update)
if (this.entityInformation.isNew(entity)) {
this.em.persist(entity);
return entity;
} else {
return this.em.merge(entity);
}
}
那么这个isNew到底是怎么实现的呢
JpaMetaModelEnityInformation是最底层实现,其部分源码为:
//另外的源码,逻辑是相同的;但是处理过程有些不同,之前没有使用map,也没有orElse这种处理。
public boolean isNew(T entity) {
//判断是否有version属性存在,该version属性的java类型不应该为原始类型
if (this.versionAttribute.isPresent() &&
!(Boolean)this.versionAttribute.
map(Attribute::getJavaType).
map(Class::isPrimitive).orElse(false)) {
//获取实例的包装bean
BeanWrapper wrapper = new DirectFieldAccessFallbackBeanWrapper(entity);
//通过包装bean判断实体内属性值不为null
return (Boolean)this.versionAttribute.map((it) -> {
return wrapper.getPropertyValue(it.getName()) == null;
}).orElse(true);
} else {
return super.isNew(entity);
}
}
3. 解决方案
如果实体类中有标注@Version的话,那么应该在传入参数的时候传入原有的版本信息,save方法才能正确识别应该进行update操作
4.感悟
太难了,这种问题真的不知道怎么说。如果一开始就看源码就会好过很多。我以为这种问题只要百度就能百度到,结果完全没有人出现过这种问题??(是我的问题吧,是吧是吧?)
其中还有很多不清楚的地方。比如
return (Boolean) this.versionAttribute.map((it)->{
return wrapper.getPropertyValue(it.getName())==null;
}).orElse(true);
这种代码
我原本出现问题的代码是spring-data-jpa:1.11.23
这部分的代码是
Object versionValue=wrapper.getPropertyValue(this.versionAttribute.getName());
return versionValue==null;
以后再探索这个map到底有多少优化吧。我困了