Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* DISCLAIMER
*
* Copyright 2018 ArangoDB GmbH, Cologne, Germany
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright holder is ArangoDB GmbH, Cologne, Germany
*/

package com.arangodb.springframework.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.data.annotation.ReadOnlyProperty;

/**
* Representation of ArangoDB document field {@code _id}
*
* @author Mark Vollmary
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
@ReadOnlyProperty
public @interface ArangoId {

}
Original file line number Diff line number Diff line change
Expand Up @@ -624,11 +624,24 @@ private void writeEntity(
}
});

addKeyIfNecessary(entity, source, sink);
addTypeKeyIfNecessary(definedType, source, sink);

sink.close();
}

private void addKeyIfNecessary(
final ArangoPersistentEntity<?> entity,
final Object source,
final VPackBuilder sink) {
if (!entity.hasIdProperty() || entity.getIdentifierAccessor(source).getIdentifier() == null) {
final Object id = entity.getArangoIdAccessor(source).getIdentifier();
if (id != null) {
sink.add(_KEY, MetadataUtils.determineDocumentKeyFromId((String) id));
}
}
}

private void writeProperty(final Object source, final VPackBuilder sink, final ArangoPersistentProperty property) {
if (source == null) {
return;
Expand Down Expand Up @@ -859,7 +872,11 @@ private Optional<String> getRefId(final Object source, final ArangoPersistentEnt
}

final Optional<Object> id = Optional.ofNullable(entity.getIdentifierAccessor(source).getIdentifier());
return id.map(key -> MetadataUtils.createIdFromCollectionAndKey(entity.getCollection(), convertId(key)));
if (id.isPresent()) {
return id.map(key -> MetadataUtils.createIdFromCollectionAndKey(entity.getCollection(), convertId(key)));
}

return Optional.ofNullable((String) entity.getArangoIdAccessor(source).getIdentifier());
}

private static Collection<?> asCollection(final Object source) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* DISCLAIMER
*
* Copyright 2018 ArangoDB GmbH, Cologne, Germany
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright holder is ArangoDB GmbH, Cologne, Germany
*/

package com.arangodb.springframework.core.mapping;

import java.util.Optional;

import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.TargetAwareIdentifierAccessor;
import org.springframework.util.Assert;

/**
* @author Mark Vollmary
*
*/
public class ArangoIdPropertyIdentifierAccessor extends TargetAwareIdentifierAccessor {

private final ArangoPersistentProperty arangoIdProperty;
private final PersistentPropertyAccessor accessor;

public ArangoIdPropertyIdentifierAccessor(final ArangoPersistentEntity<?> entity, final Object target) {
super(target);

Assert.notNull(entity, "PersistentEntity must not be null!");
final Optional<ArangoPersistentProperty> aip = entity.getArangoIdProperty();
Assert.isTrue(aip.isPresent(), "PersistentEntity must have an arango identifier property!");
Assert.notNull(target, "Target bean must not be null!");

this.arangoIdProperty = aip.get();
this.accessor = entity.getPropertyAccessor(target);
}

@Override
public Object getIdentifier() {
return accessor.getProperty(arangoIdProperty);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.Optional;

import org.springframework.context.ApplicationContextAware;
import org.springframework.data.mapping.IdentifierAccessor;
import org.springframework.data.mapping.PersistentEntity;

import com.arangodb.model.CollectionCreateOptions;
Expand All @@ -45,6 +46,8 @@ public interface ArangoPersistentEntity<T>

CollectionCreateOptions getCollectionOptions();

Optional<ArangoPersistentProperty> getArangoIdProperty();

Optional<ArangoPersistentProperty> getRevProperty();

Collection<HashIndex> getHashIndexes();
Expand All @@ -67,4 +70,6 @@ public interface ArangoPersistentEntity<T>

Collection<ArangoPersistentProperty> getFulltextIndexedProperties();

IdentifierAccessor getArangoIdAccessor(Object bean);

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public interface ArangoPersistentProperty extends PersistentProperty<ArangoPersi

String getFieldName();

boolean isArangoIdProperty();

boolean isRevProperty();

Optional<Ref> getRef();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,15 @@
import org.springframework.context.expression.BeanFactoryAccessor;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.data.mapping.IdentifierAccessor;
import org.springframework.data.mapping.TargetAwareIdentifierAccessor;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;

import com.arangodb.entity.CollectionType;
Expand Down Expand Up @@ -72,6 +75,7 @@ public class DefaultArangoPersistentEntity<T> extends BasicPersistentEntity<T, A
private String collection;
private final StandardEvaluationContext context;

private ArangoPersistentProperty arangoIdProperty;
private ArangoPersistentProperty revProperty;
private final Collection<ArangoPersistentProperty> hashIndexedProperties;
private final Collection<ArangoPersistentProperty> skiplistIndexedProperties;
Expand Down Expand Up @@ -184,6 +188,9 @@ public void setApplicationContext(final ApplicationContext applicationContext) t
@Override
public void addPersistentProperty(final ArangoPersistentProperty property) {
super.addPersistentProperty(property);
if (property.isArangoIdProperty()) {
arangoIdProperty = property;
}
if (property.isRevProperty()) {
revProperty = property;
}
Expand All @@ -194,6 +201,11 @@ public void addPersistentProperty(final ArangoPersistentProperty property) {
property.getFulltextIndexed().ifPresent(i -> fulltextIndexedProperties.add(property));
}

@Override
public Optional<ArangoPersistentProperty> getArangoIdProperty() {
return Optional.ofNullable(arangoIdProperty);
}

@Override
public Optional<ArangoPersistentProperty> getRevProperty() {
return Optional.ofNullable(revProperty);
Expand Down Expand Up @@ -278,4 +290,24 @@ public <A extends Annotation> Set<A> findAnnotations(final Class<A> annotationTy
return (Set<A>) repeatableAnnotationCache.computeIfAbsent(annotationType,
it -> AnnotatedElementUtils.findMergedRepeatableAnnotations(getType(), it));
}

private static class AbsentAccessor extends TargetAwareIdentifierAccessor {

public AbsentAccessor(final Object target) {
super(target);
}

@Override
@Nullable
public Object getIdentifier() {
return null;
}
}

@Override
public IdentifierAccessor getArangoIdAccessor(final Object bean) {
return getArangoIdProperty().isPresent() ? new ArangoIdPropertyIdentifierAccessor(this, bean)
: new AbsentAccessor(bean);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.util.StringUtils;

import com.arangodb.springframework.annotation.ArangoId;
import com.arangodb.springframework.annotation.Field;
import com.arangodb.springframework.annotation.From;
import com.arangodb.springframework.annotation.FulltextIndexed;
Expand Down Expand Up @@ -65,6 +66,11 @@ protected Association<ArangoPersistentProperty> createAssociation() {
return new Association<>(this, null);
}

@Override
public boolean isArangoIdProperty() {
return findAnnotation(ArangoId.class) != null;
}

@Override
public boolean isRevProperty() {
return findAnnotation(Rev.class) != null;
Expand Down Expand Up @@ -93,7 +99,9 @@ public Optional<To> getTo() {
@Override
public String getFieldName() {
final String fieldName;
if (isIdProperty()) {
if (isArangoIdProperty()) {
fieldName = "_id";
} else if (isIdProperty()) {
fieldName = "_key";
} else if (isRevProperty()) {
fieldName = "_rev";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,12 +557,23 @@ public DocumentEntity insert(final String collectionName, final Object value) th
return insert(collectionName, value, new DocumentCreateOptions());
}

private Object getDocumentKey(final ArangoPersistentEntity<?> entity, final Object value) {
Object id = entity.getIdentifierAccessor(value).getIdentifier();
if (id == null) {
final Object docId = entity.getArangoIdAccessor(value).getIdentifier();
if (docId != null) {
id = MetadataUtils.determineDocumentKeyFromId((String) docId);
}
}
return id;
}

@Override
public <T> void upsert(final T value, final UpsertStrategy strategy) throws DataAccessException {
final Class<? extends Object> entityClass = value.getClass();
final ArangoPersistentEntity<?> entity = getConverter().getMappingContext().getPersistentEntity(entityClass);

final Object id = entity.getIdentifierAccessor(value).getIdentifier();
final Object id = getDocumentKey(entity, value);
if (id != null && (!(value instanceof Persistable) || !Persistable.class.cast(value).isNew())) {
switch (strategy) {
case UPDATE:
Expand Down Expand Up @@ -591,7 +602,7 @@ public <T> void upsert(final Iterable<T> value, final UpsertStrategy strategy) t
final Collection<T> withId = new ArrayList<>();
final Collection<T> withoutId = new ArrayList<>();
for (final T e : value) {
final Object id = entity.getIdentifierAccessor(e).getIdentifier();
final Object id = getDocumentKey(entity, e);
if (id != null && (!(e instanceof Persistable) || !Persistable.class.cast(e).isNew())) {
withId.add(e);
continue;
Expand Down Expand Up @@ -650,6 +661,7 @@ private void updateDBFields(final Object value, final DocumentEntity documentEnt
if (idProperty != null) {
accessor.setProperty(idProperty, documentEntity.getKey());
}
entity.getArangoIdProperty().ifPresent(arangoId -> accessor.setProperty(arangoId, documentEntity.getId()));
entity.getRevProperty().ifPresent(rev -> accessor.setProperty(rev, documentEntity.getRev()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@
import com.arangodb.model.AqlQueryOptions;
import com.arangodb.springframework.AbstractArangoTest;
import com.arangodb.springframework.ArangoTestConfiguration;
import com.arangodb.springframework.annotation.ArangoId;
import com.arangodb.springframework.annotation.Document;
import com.arangodb.springframework.annotation.Field;
import com.arangodb.springframework.core.ArangoOperations.UpsertStrategy;
import com.arangodb.springframework.core.mapping.testdata.BasicTestEntity;
import com.arangodb.springframework.testdata.Actor;
import com.arangodb.springframework.testdata.Movie;
Expand Down Expand Up @@ -374,4 +376,38 @@ public void twoTypesInSameCollection() {
assertThat(findB.get().value, is("testB"));
assertThat(findB.get().b, is("testB"));
}

static class ArangoIdOnlyTestEntity {
@ArangoId
private String id;
}

@SuppressWarnings("deprecation")
@Test
public void arangoIdOnly() {
final ArangoIdOnlyTestEntity entity = new ArangoIdOnlyTestEntity();
template.upsert(entity, UpsertStrategy.UPDATE);
assertThat(entity.id, is(notNullValue()));
final Optional<ArangoIdOnlyTestEntity> find = template.find(entity.id, ArangoIdOnlyTestEntity.class);
assertThat(find.isPresent(), is(true));
assertThat(find.get().id, is(entity.id));
}

static class ArangoIdAndIdTestEntity {
@ArangoId
private String arangoId;
@Id
private String id;
}

@Test
public void arangoIdAndId() {
final ArangoIdAndIdTestEntity entity = new ArangoIdAndIdTestEntity();
entity.arangoId = "arangoIdAndIdTestEntity/test";
template.insert(entity);
assertThat(entity.arangoId, is("arangoIdAndIdTestEntity/test"));
assertThat(entity.id, is("test"));
assertThat(template.find(entity.id, ArangoIdAndIdTestEntity.class).isPresent(), is(true));
assertThat(template.find(entity.arangoId, ArangoIdAndIdTestEntity.class).isPresent(), is(true));
}
}
Loading