Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] transactional support #249

Open
wants to merge 55 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
ea3fabf
#80 create transaction manager
aburmeis May 11, 2022
9432a65
#80 expose database name
aburmeis May 11, 2022
c0ba609
#80 allow collections from queries
aburmeis May 11, 2022
3744296
#80 made QueryTransactionBridge an optional @Bean
May 11, 2022
8803e1d
#80 started tests
May 11, 2022
5150bb2
#80 fix merge error
aburmeis Aug 26, 2022
d07766f
#80 allow multiple transaction calls unless there are no additional c…
aburmeis May 13, 2022
a8a39d3
#80 more tests
aburmeis May 17, 2022
61ed67d
#80 refactor all operations with defaults without options, find also …
aburmeis May 17, 2022
55241c1
#80 allow commit/rollback if not started yet
aburmeis May 17, 2022
adc9ccf
#80 inject bridge to simple repo
aburmeis May 17, 2022
bb4bef5
#80 use options with transaction id where ever possible
aburmeis May 17, 2022
20c935a
#80 use another document for test
May 18, 2022
69ed72a
#80 improve error message
aburmeis Aug 26, 2022
a567c05
#80 do not add collections of abstract entity types
aburmeis Aug 26, 2022
0c4cb88
#80 refactor resolvers: move template up
aburmeis Aug 29, 2022
3282e9d
#80 refactor resolvers: prepare read and query options
aburmeis Aug 29, 2022
72e22c2
#80 prepare usage of stream transaction bridge and id for resolvers
aburmeis Aug 29, 2022
a39db92
#80 extract default resolver factory to allow bean dependency, pass q…
aburmeis Aug 29, 2022
7ced704
#80 simplify configurer usage
aburmeis Aug 29, 2022
ee69109
#80 refactor to own type instead of pair
aburmeis Aug 29, 2022
4a06157
#80 expose ta as bean
aburmeis Aug 29, 2022
0ad43eb
#80 refactor to sync resource, wrap db exceptions
aburmeis Aug 30, 2022
39972ca
#80 refactor to mutable sync resource, handle rollback only
aburmeis Aug 30, 2022
32992cb
#80 add template supporting labels
aburmeis Aug 30, 2022
6935c58
#80 add some docs
aburmeis Aug 30, 2022
05e53ed
#80 fix nested transaction behaviour keeping collections
aburmeis Aug 30, 2022
501305c
#80 minor code improvements
aburmeis Sep 1, 2022
8875188
#80 add missing license comment
aburmeis Sep 1, 2022
31e461b
#80 fix resolver setup
aburmeis Sep 1, 2022
a471901
#80 fix test setup
aburmeis Sep 1, 2022
77e9e55
#80 fix handling of nested transactions
aburmeis Mar 14, 2023
7268cb1
#80 allow sync callbacks after completion
aburmeis Mar 16, 2023
e55a7b0
#80 fix merge error
aburmeis May 31, 2023
ba7d206
#80 replace dbname by database itself
aburmeis Aug 23, 2023
170acaa
#80 avoid collection creation or index creation inside transaction
aburmeis Aug 23, 2023
2f4229c
#80 extract collection callback, improve implementation
aburmeis Aug 23, 2023
b7118cc
#80 ensure collections before transaction start
aburmeis Aug 23, 2023
7410798
#80 reuse collection name from persistent entity, escape collection n…
aburmeis Aug 23, 2023
f5773ac
#80 improve docs
aburmeis Aug 23, 2023
1d60c40
#80 reduce ensure index calls (ignore hash and skiplist index as they…
aburmeis Aug 23, 2023
44afc3c
#80 handle missing options
aburmeis Aug 23, 2023
d2e72ea
#80 log collection creation or index creation inside transaction
aburmeis Aug 24, 2023
90d47ad
#80 create only missing indexes, skip existing
aburmeis Aug 24, 2023
b84931f
#80 fix merge errors
aburmeis Aug 31, 2023
40c96b7
#80 move new test to junit5
aburmeis Sep 5, 2023
93b28b7
fix merge error
aburmeis Sep 12, 2023
c6912ab
no deprecation warning in tests
aburmeis Sep 19, 2023
1991dbc
fix generic
aburmeis Sep 19, 2023
7202d5b
#80 adjust order of repsertAll to insertAll
aburmeis Sep 19, 2023
7ea5d8d
fix tx of relation resolvers
aburmeis Sep 19, 2023
37fc779
#80 fix for spring 6.1
aburmeis Jan 30, 2024
9320edd
fix deprecation, but compatible to 3.0
aburmeis Feb 26, 2024
7e6f2f3
fix merge errors and unify parameter order with options
aburmeis Jul 18, 2024
63b0557
cleanup warnings
aburmeis Jul 18, 2024
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
1 change: 1 addition & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
- return updated entity from `ArangoOperations.repsert()` (#285)
- removed deprecated `AbstractArangoConfiguration` in favor of `ArangoConfiguration`
- removed support for Joda-Time
- added support for transactions offering a platform transaction manager based on stream transactions (#80)

## [3.10.0] - 2023-05-17

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
package com.arangodb.springframework.config;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;

import com.arangodb.ContentType;
Expand All @@ -23,27 +21,13 @@
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;

import com.arangodb.ArangoDB;
import com.arangodb.ArangoDBException;
import com.arangodb.springframework.annotation.Document;
import com.arangodb.springframework.annotation.Edge;
import com.arangodb.springframework.annotation.From;
import com.arangodb.springframework.annotation.Ref;
import com.arangodb.springframework.annotation.Relations;
import com.arangodb.springframework.annotation.To;
import com.arangodb.springframework.core.ArangoOperations;
import com.arangodb.springframework.core.convert.ArangoConverter;
import com.arangodb.springframework.core.convert.ArangoCustomConversions;
import com.arangodb.springframework.core.convert.ArangoTypeMapper;
import com.arangodb.springframework.core.convert.DefaultArangoConverter;
import com.arangodb.springframework.core.convert.DefaultArangoTypeMapper;
import com.arangodb.springframework.core.convert.resolver.DocumentFromResolver;
import com.arangodb.springframework.core.convert.resolver.DocumentToResolver;
import com.arangodb.springframework.core.convert.resolver.EdgeFromResolver;
import com.arangodb.springframework.core.convert.resolver.EdgeToResolver;
import com.arangodb.springframework.core.convert.resolver.RefResolver;
import com.arangodb.springframework.core.convert.resolver.ReferenceResolver;
import com.arangodb.springframework.core.convert.resolver.RelationResolver;
import com.arangodb.springframework.core.convert.resolver.RelationsResolver;
import com.arangodb.springframework.core.convert.resolver.DefaultResolverFactory;
import com.arangodb.springframework.core.convert.resolver.ResolverFactory;
import com.arangodb.springframework.core.mapping.ArangoMappingContext;
import com.arangodb.springframework.core.template.ArangoTemplate;
Expand Down Expand Up @@ -156,49 +140,8 @@ default ArangoTypeMapper arangoTypeMapper() throws Exception {
return new DefaultArangoTypeMapper(typeKey(), arangoMappingContext());
}

@Bean
default ResolverFactory resolverFactory() {
return new ResolverFactory() {
@SuppressWarnings("unchecked")
@Override
public <A extends Annotation> Optional<ReferenceResolver<A>> getReferenceResolver(final A annotation) {
ReferenceResolver<A> resolver = null;
try {
if (annotation instanceof Ref) {
resolver = (ReferenceResolver<A>) new RefResolver(arangoTemplate());
}
} catch (final Exception e) {
throw new ArangoDBException(e);
}
return Optional.ofNullable(resolver);
}

@SuppressWarnings("unchecked")
@Override
public <A extends Annotation> Optional<RelationResolver<A>> getRelationResolver(final A annotation,
final Class<? extends Annotation> collectionType) {
RelationResolver<A> resolver = null;
try {
if (annotation instanceof From) {
if (collectionType == Edge.class) {
resolver = (RelationResolver<A>) new EdgeFromResolver(arangoTemplate());
} else if (collectionType == Document.class) {
resolver = (RelationResolver<A>) new DocumentFromResolver(arangoTemplate());
}
} else if (annotation instanceof To) {
if (collectionType == Edge.class) {
resolver = (RelationResolver<A>) new EdgeToResolver(arangoTemplate());
} else if (collectionType == Document.class) {
resolver = (RelationResolver<A>) new DocumentToResolver(arangoTemplate());
}
} else if (annotation instanceof Relations) {
resolver = (RelationResolver<A>) new RelationsResolver(arangoTemplate());
}
} catch (final Exception e) {
throw new ArangoDBException(e);
}
return Optional.ofNullable(resolver);
}
};
return new DefaultResolverFactory();
}

}
143 changes: 96 additions & 47 deletions src/main/java/com/arangodb/springframework/core/ArangoOperations.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import com.arangodb.ArangoCursor;
import com.arangodb.ArangoDB;
import com.arangodb.ArangoDatabase;
import com.arangodb.entity.*;
import com.arangodb.model.*;
import com.arangodb.springframework.core.convert.ArangoConverter;
Expand Down Expand Up @@ -54,6 +55,14 @@ public interface ArangoOperations {
*/
ArangoDBVersion getVersion() throws DataAccessException;

/**
* Returns the underlying database. The database will be created if it does not exist.
*
* @return the database object
* @throws DataAccessException
*/
ArangoDatabase db() throws DataAccessException;

/**
* Performs a database query using the given {@code query} and {@code bindVars}, then returns a new
* {@code ArangoCursor} instance for the result list.
Expand Down Expand Up @@ -85,12 +94,14 @@ <T> ArangoCursor<T> query(String query, Map<String, Object> bindVars, AqlQueryOp
* @return cursor of the results
* @throws DataAccessException
*/
<T> ArangoCursor<T> query(String query, Map<String, Object> bindVars, Class<T> entityClass)
throws DataAccessException;
default <T> ArangoCursor<T> query(String query, Map<String, Object> bindVars, Class<T> entityClass)
throws DataAccessException {
return query(query, bindVars, new AqlQueryOptions(), entityClass);
}

/**
* Performs a database query using the given {@code query}, then returns a new {@code ArangoCursor} instance for the
* result list.
* Performs a database query using the given {@code query}, then returns a new {@code ArangoCursor}
* instance for the result list.
*
* @param query
* An AQL query string
Expand All @@ -101,11 +112,14 @@ <T> ArangoCursor<T> query(String query, Map<String, Object> bindVars, Class<T> e
* @return cursor of the results
* @throws DataAccessException
*/
<T> ArangoCursor<T> query(String query, AqlQueryOptions options, Class<T> entityClass) throws DataAccessException;
default <T> ArangoCursor<T> query(String query, AqlQueryOptions options, Class<T> entityClass)
throws DataAccessException {
return query(query, null, options, entityClass);
}

/**
* Performs a database query using the given {@code query}, then returns a new {@code ArangoCursor} instance for the
* result list.
* Performs a database query using the given {@code query}, then returns a new {@code ArangoCursor}
* instance for the result list.
*
* @param query
* An AQL query string
Expand All @@ -114,7 +128,9 @@ <T> ArangoCursor<T> query(String query, Map<String, Object> bindVars, Class<T> e
* @return cursor of the results
* @throws DataAccessException
*/
<T> ArangoCursor<T> query(String query, Class<T> entityClass) throws DataAccessException;
default <T> ArangoCursor<T> query(String query, Class<T> entityClass) throws DataAccessException {
return query(query, new AqlQueryOptions(), entityClass);
}

/**
* Deletes multiple documents from a collection.
Expand Down Expand Up @@ -143,7 +159,10 @@ <T> MultiDocumentEntity<DocumentDeleteEntity<T>> deleteAll(
* @return information about the documents
* @throws DataAccessException
*/
MultiDocumentEntity<DocumentDeleteEntity<?>> deleteAll(Iterable<?> values, Class<?> entityClass) throws DataAccessException;
default <T> MultiDocumentEntity<DocumentDeleteEntity<T>> deleteAll(Iterable<?> values, Class<T> entityClass)
throws DataAccessException {
return deleteAll(values, new DocumentDeleteOptions(), entityClass);
}

/**
* Deletes multiple documents with the given IDs from a collection.
Expand Down Expand Up @@ -172,7 +191,9 @@ <T> MultiDocumentEntity<DocumentDeleteEntity<T>> deleteAllById(
* @return information about the documents
* @throws DataAccessException
*/
MultiDocumentEntity<DocumentDeleteEntity<?>> deleteAllById(Iterable<?> ids, Class<?> entityClass) throws DataAccessException;
default <T> MultiDocumentEntity<DocumentDeleteEntity<T>> deleteAllById(Iterable<?> ids, Class<T> entityClass) throws DataAccessException {
return deleteAllById(ids, new DocumentDeleteOptions(), entityClass);
}

/**
* Deletes the document with the given {@code id} from a collection.
Expand All @@ -198,7 +219,9 @@ <T> MultiDocumentEntity<DocumentDeleteEntity<T>> deleteAllById(
* @return information about the document
* @throws DataAccessException
*/
DocumentDeleteEntity<?> delete(Object id, Class<?> entityClass) throws DataAccessException;
default <T> DocumentDeleteEntity<T> delete(Object id, Class<T> entityClass) throws DataAccessException {
return delete(id, new DocumentDeleteOptions(), entityClass);
}

/**
* Partially updates documents, the documents to update are specified by the _key attributes in the objects on
Expand Down Expand Up @@ -238,7 +261,10 @@ <T> MultiDocumentEntity<DocumentUpdateEntity<T>> updateAll(
* @return information about the documents
* @throws DataAccessException
*/
<T> MultiDocumentEntity<DocumentUpdateEntity<?>> updateAll(Iterable<? extends T> values, Class<T> entityClass) throws DataAccessException;
default <T> MultiDocumentEntity<DocumentUpdateEntity<T>> updateAll(Iterable<T> values, Class<T> entityClass)
throws DataAccessException {
return updateAll(values, new DocumentUpdateOptions(), entityClass);
}

/**
* Partially updates the document identified by document id or key. The value must contain a document with the
Expand Down Expand Up @@ -268,7 +294,9 @@ <T> MultiDocumentEntity<DocumentUpdateEntity<T>> updateAll(
* @return information about the document
* @throws DataAccessException
*/
DocumentUpdateEntity<?> update(Object id, Object value) throws DataAccessException;
default <T> DocumentUpdateEntity<T> update(Object id, T value) throws DataAccessException {
return update(id, value, new DocumentUpdateOptions());
}

/**
* Replaces multiple documents in the specified collection with the ones in the values, the replaced documents are
Expand Down Expand Up @@ -303,8 +331,10 @@ <T> MultiDocumentEntity<DocumentUpdateEntity<T>> replaceAll(
* @return information about the documents
* @throws DataAccessException
*/
<T> MultiDocumentEntity<DocumentUpdateEntity<?>> replaceAll(Iterable<? extends T> values, Class<T> entityClass)
throws DataAccessException;
default <T> MultiDocumentEntity<DocumentUpdateEntity<T>> replaceAll(Iterable<T> values, Class<T> entityClass)
throws DataAccessException {
return replaceAll(values, new DocumentReplaceOptions(), entityClass);
}

/**
* Replaces the document with {@code id} with the one in the body, provided there is such a document and no
Expand Down Expand Up @@ -332,17 +362,16 @@ <T> MultiDocumentEntity<DocumentUpdateEntity<?>> replaceAll(Iterable<? extends T
* @return information about the document
* @throws DataAccessException
*/
DocumentUpdateEntity<?> replace(Object id, Object value) throws DataAccessException;
default <T> DocumentUpdateEntity<T> replace(Object id, T value) throws DataAccessException {
return replace(id, value, new DocumentReplaceOptions());
}

/**
* Retrieves the document with the given {@code id} from a collection.
*
* @param id
* The id or key of the document
* @param entityClass
* The entity class which represents the collection
* @param options
* Additional options, can be null
* @param id The id or key of the document
* @param entityClass The entity class which represents the collection
* @param options Additional options, can be null
* @return the document identified by the id
* @throws DataAccessException
*/
Expand All @@ -358,29 +387,36 @@ <T> MultiDocumentEntity<DocumentUpdateEntity<?>> replaceAll(Iterable<? extends T
* @return the document identified by the id
* @throws DataAccessException
*/
<T> Optional<T> find(Object id, Class<T> entityClass) throws DataAccessException;
default <T> Optional<T> find(Object id, Class<T> entityClass) throws DataAccessException {
return find(id, entityClass, new DocumentReadOptions());
}

/**
* Retrieves all documents from a collection.
*
* @param entityClass
* The entity class which represents the collection
* @param entityClass The entity class which represents the collection
* @return the documents
* @throws DataAccessException
*/
<T> Iterable<T> findAll(Class<T> entityClass) throws DataAccessException;
<T> Iterable<T> findAll(DocumentReadOptions options, Class<T> entityClass) throws DataAccessException;

default <T> Iterable<T> findAll(Class<T> entityClass) throws DataAccessException {
return findAll(new DocumentReadOptions(), entityClass);
}

/**
* Retrieves multiple documents with the given {@code ids} from a collection.
*
* @param ids
* The ids or keys of the documents
* @param entityClass
* The entity class which represents the collection
* @param ids The ids or keys of the documents
* @param entityClass The entity class which represents the collection
* @return the documents
* @throws DataAccessException
*/
<T> Iterable<T> findAll(final Iterable<?> ids, final Class<T> entityClass) throws DataAccessException;
<T> Iterable<T> findAll(final Iterable<?> ids, DocumentReadOptions options, final Class<T> entityClass) throws DataAccessException;

default <T> Iterable<T> findAll(final Iterable<?> ids, final Class<T> entityClass) throws DataAccessException {
return findAll(ids, new DocumentReadOptions(), entityClass);
}

/**
* Creates new documents from the given documents, unless there is already a document with the _key given. If no
Expand Down Expand Up @@ -413,8 +449,10 @@ <T> MultiDocumentEntity<DocumentCreateEntity<T>> insertAll(
* @return information about the documents
* @throws DataAccessException
*/
<T> MultiDocumentEntity<DocumentCreateEntity<?>> insertAll(Iterable<? extends T> values, Class<T> entityClass)
throws DataAccessException;
default <T> MultiDocumentEntity<DocumentCreateEntity<T>> insertAll(Iterable<? extends T> values, Class<T> entityClass)
throws DataAccessException {
return insertAll(values, new DocumentCreateOptions(), entityClass);
}

/**
* Creates a new document from the given document, unless there is already a document with the _key given. If no
Expand All @@ -436,8 +474,9 @@ <T> MultiDocumentEntity<DocumentCreateEntity<?>> insertAll(Iterable<? extends T>
* A representation of a single document
* @return information about the document
*/
DocumentCreateEntity<?> insert(Object value) throws DataAccessException;

default <T> DocumentCreateEntity<T> insert(T value) throws DataAccessException {
return insert(value, new DocumentCreateOptions());
}
/**
* Creates a new document from the given document, unless there is already a document with the id given. In that
* case it replaces the document.
Expand All @@ -447,32 +486,40 @@ <T> MultiDocumentEntity<DocumentCreateEntity<?>> insertAll(Iterable<? extends T>
* @throws DataAccessException
* @since ArangoDB 3.4
*/
<T> T repsert(T value) throws DataAccessException;
<T> T repsert(T value, AqlQueryOptions options) throws DataAccessException;

default <T> T repsert(T value) throws DataAccessException {
return repsert(value, new AqlQueryOptions());
}

/**
* Creates new documents from the given documents, unless there already exists. In that case it replaces the
* documents.
*
* @param values
* A List of documents
* @param entityClass
* The entity class which represents the collection
* @param values A List of documents
* @param entityClass The entity class which represents the collection
* @throws DataAccessException
* @since ArangoDB 3.4
*/
<T> Iterable<T> repsertAll(Iterable<T> values, Class<? super T> entityClass) throws DataAccessException;
<T> Iterable<T> repsertAll(Iterable<T> values, AqlQueryOptions options, Class<? super T> entityClass) throws DataAccessException;

default <T> Iterable<T> repsertAll(Iterable<T> values, Class<? super T> entityClass) throws DataAccessException {
return repsertAll(values, new AqlQueryOptions(), entityClass);
}

/**
* Checks whether the document exists by reading a single document head
*
* @param id
* The id or key of the document
* @param entityClass
* The entity type representing the collection
* @param id The id or key of the document
* @param entityClass The entity type representing the collection
* @return true if the document exists, false if not
* @throws DataAccessException
*/
boolean exists(Object id, Class<?> entityClass) throws DataAccessException;
boolean exists(Object id, DocumentExistsOptions options, Class<?> entityClass) throws DataAccessException;

default boolean exists(Object id, Class<?> entityClass) throws DataAccessException {
return exists(id, new DocumentExistsOptions(), entityClass);
}

/**
* Drop an existing database
Expand Down Expand Up @@ -501,7 +548,9 @@ <T> MultiDocumentEntity<DocumentCreateEntity<?>> insertAll(Iterable<? extends T>
* @return {@link CollectionOperations}
* @throws DataAccessException
*/
CollectionOperations collection(String name) throws DataAccessException;
default CollectionOperations collection(String name) throws DataAccessException {
return collection(name, new CollectionCreateOptions());
}

/**
* Returns the operations interface for a collection. If the collection does not exists, it is created
Expand Down
Loading