`
sanyecao2314
  • 浏览: 131621 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

spring-data-mongodb的@version注解的后台实现逻辑简单解析.

阅读更多

在model的某个属性上添加@version注解,系统每次保存会自动给该属性值加1,保证数据没有脏写入.但实际使用中,发现使用不当,会导致该检查频繁报错.简单分析@version字段的自增机制.

调用repo*的save方法.最终会调用org.springframework.data.mongodb.repository.support.SimpleMongoRepository类的save方法.

public <S extends T> S save(S entity) {

		Assert.notNull(entity, "Entity must not be null!");
		//isNew方法实现写在下面了.
		if (entityInformation.isNew(entity)) {
			/*
			 * insert对version的处理比较简单,直接判定是否存在version注解,存在则设置version对应字段为0
			 */
			mongoOperations.insert(entity, entityInformation.getCollectionName());
		} else {
			mongoOperations.save(entity, entityInformation.getCollectionName());
		}

		return entity;
	}

	/** 判断一下主键的值是否存在,存在返回false,反正为true.所以这里的判断是不严谨的,通过前台给设置主键Id的,就会走save,而不是insert了.*/
	public boolean isNew(T entity) {

		ID id = getId(entity);
		Class<ID> idType = getIdType();

		if (!idType.isPrimitive()) {
			return id == null;
		}

		if (id instanceof Number) {
			return ((Number) id).longValue() == 0L;
		}

		throw new IllegalArgumentException(String.format("Unsupported primitive id type %s!", idType));
	}

 org.springframework.data.mongodb.core.MongoTemplate的save方法会调用doSaveVersioned方法,在该方法中设置version注解对应字段的值.

private <T> void doSaveVersioned(T objectToSave, MongoPersistentEntity<?> entity, String collectionName) {

		//这个类主要是给model设置值和获取model的值,实现了PersistentPropertyAccessor接口,这个接口在jdk和spring中都有类似的接口,也是一个非常有意思的东西.有兴趣可以具体看看.
		ConvertingPropertyAccessor convertingAccessor = new ConvertingPropertyAccessor(
				entity.getPropertyAccessor(objectToSave), mongoConverter.getConversionService());

		MongoPersistentProperty idProperty = entity.getIdProperty();
		MongoPersistentProperty versionProperty = entity.getVersionProperty();

		Object version = convertingAccessor.getProperty(versionProperty);
		Number versionNumber = convertingAccessor.getProperty(versionProperty, Number.class);

		// version注解对应的值不存在,转走insert,这里应该是我们出问题的核心所在,因为我们定义的version注解对应的字段是int,都有默认值的.所以只需将之改为Integer,则可以按这里的逻辑判断来处理.
		// Fresh instance -> initialize version property
		if (version == null) {
			doInsert(collectionName, objectToSave, this.mongoConverter);
		} else {
			//判断Id字段和值是否存在
			assertUpdateableIdIfNotSet(objectToSave);

			// Create query for entity with the id and old version
			Object id = convertingAccessor.getProperty(idProperty);
			Query query = new Query(Criteria.where(idProperty.getName()).is(id).and(versionProperty.getName()).is(version));

			// Bump version number !!注意,在这里version字段值加1,
			//后面doUpdate中还有一段判断逻辑:如果要更新的部分字段不包含version对应的字段,而该model有字段使用version注解,则将version的字段=1,并加入更新字段列表中.
			convertingAccessor.setProperty(versionProperty, versionNumber.longValue() + 1);

			BasicDBObject dbObject = new BasicDBObject();

			maybeEmitEvent(new BeforeConvertEvent<T>(objectToSave, collectionName));
			this.mongoConverter.write(objectToSave, dbObject);

			maybeEmitEvent(new BeforeSaveEvent<T>(objectToSave, dbObject, collectionName));
			Update update = Update.fromDBObject(dbObject, ID_FIELD);

			doUpdate(collectionName, query, update, objectToSave.getClass(), false, false);
			maybeEmitEvent(new AfterSaveEvent<T>(objectToSave, dbObject, collectionName));
		}
	}

 

更改相关字段类型后,测试验证通过.

0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics