Building a Simple eXist Repository on Top of Spring Data

TL;DR This post walks through the implementation details of spring-data-xml. This library provides support when accessing XML databases like eXist-db with Java and XQuery. The code is available at GitHub: datenkollektiv/spring-data-xml.

Last week I had the opportunity to work with the NoSQL document database eXist. After an amazing session, tightly packed with a mix of XQuery basics and pearls I started to implement a simple Java xqj-example to get a better feeling for the rough edges of the new technology.

The moment I switched from querying simple integer sequences to real XML documents I resisted the initial attempt to copy large parts of the JUnit setup to a new test class. That was the moment I had a déjà vu: Don't repeat the DAO!.

I missed the "familiar and consistent, Spring-based programming model for data access" I got used to while working with Spring Data MongoDB.

It's a pity that eXist is missing from the list of data stores supported by Spring Data.

There is a promising project Spring XMLDB, but...Last Update: 2013-03-07 and the source seems to be available via CVS, only.

At that point, we had a quick kick-off and decided to start a draft implementation based on Spring Data.

In addition to the basic CRUD operations available from CrudRepository we decided to offer a simple way to allow direct execution of XQueries:

import org.springframework.data.repository.CrudRepository;

interface XQJRepository<T, ID> extends CrudRepository<T, ID> {

    Stream<T> execute(String xquery);

}

The following Spring integration tests demonstrates the simple use-case of an empty XQuery sequence:

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = ExistConfiguration.class)
class SimpleExistXQJRepositoryTests {

    @Inject
    private SimpleXQJRepository<Integer> existRepository;

    @Test
    void emptySequence() {
        Stream<Integer> result = existRepository.execute("()");

        assertEquals(0, result.count());
    }
}

Let's look under the hood. The SimpleXQJRepository main purpose is to delegate the database work to and convert the Java entities to XML documents and back.

public class SimpleXQJRepository<T> implements XQJRepository<T> {

    private final XQJOperations XQJOperations;

...
    @Override
    public Stream<T> execute(String xquery, VarBinder... varBinder) {
        return XQJOperations.execute(xquery, varBinder).map(this::toEntity);
    }
...
    @Override
    public <S extends T> S save(S entity) {
        String id = this.entityInformation.getId(entity);
        xqjOperations.store(collectionName, id, toDocumentConverter.convert(entity));
        return entity;
    }
...
    @Override
    public Optional<T> findById(String id) {
        return xqjOperations.load(collectionName, id).findFirst().map(this::toEntity);
    }
...
}

The methods of XQJOperations either return void or a Java Stream.

interface XQJOperations {

    Stream<XQItem> execute(String xquery, VarBinder... varBinder);

    void store(String collectionName, String documentName, Document documentToSave);

    Stream<XQItem> load(String collectionName, String documentName);

    void delete(String collectionName, String documentName);
...
}

The actual implementation of the CRUD operations are realized (as you might have already guessed) via XQuery:

@Override
public void store(String collectionName, String documentName, Document documentToSave) {
    String query = "declare variable $id external; " +
            "declare variable $doc external; " +
            "xmldb:store('" + collectionName + "', $id, $doc)";
    execute(query,
            expression -> expression.bindString(QName.valueOf("id"), "$documentName.xml", null),
            expression -> expression.bindNode(QName.valueOf("doc"), documentToSave.getDocumentElement(), null));
}

Conclusion

This library closes the Gap between Spring Developers and XQuery experts.

  • Without any XQuery know-how a Spring Developer can run basic CRUD operations against eXist-db.
  • An XQuery expert get's an easy to use Java abstraction plus the possibility to use plain XQuery where needed.

Despite being far from production-ready, we decided to polished the initial code and publish spring-data-xml.

The code is available at GitHub: datenkollektiv/spring-data-xml.

Happy coding with XQuery and the Spring Framework...