Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data + MongoDB GridFS access via Repository possible?

I recently discovered GridFS which I'd like to use for file storage with metadata. I just wondered if it's possible to use a MongoRepository to query GridFS? If yes, can someone give me an example?

I'd also take a solution using Hibernate, if there is some.

The reason is: My metadata contains a lot of different fields and it would be much easier to query a repository than to write some new Query(Criteria.where(...)) for each scenario. And I hopefully could also simply take a Java object and provide it via REST API without the file itself.

EDIT: I'm using

  • Spring 4 Beta
  • Spring Data Mongo 1.3.1
  • Hibernate 4.3 Beta
like image 276
Benjamin M Avatar asked Sep 27 '13 05:09

Benjamin M


People also ask

Can we use Spring data JPA with MongoDB?

Yes, DataNucleus JPA allows it, as well as to many other databases.

Can you load initial data in MongoDB through spring boot?

You just need to configure @Bean public Mongobee mongobee in your spring boot and setup component scan for data ChangeLogs where data creation actually happens. Show activity on this post. Put test_data. json in resources directory.

Which is better MongoTemplate or MongoRepository?

So I'd say that MongoTemplate is a better option, unless you have a very elaborated POJO model or need the custom queries capabilities of MongoRepository for some reason. Good points/examples. However your race condition example and undesired result can be avoided using @Version to prevent that very scenario.


2 Answers

There is a way to solve this:

@Document(collection="fs.files")
public class MyGridFsFile {

    @Id
    private ObjectId id;
    public ObjectId getId() { return id; }

    private String filename;
    public String getFilename() { return filename; }

    private long length;
    public long getLength() { return length; }

    ...

}

You can write a normal Spring Mongo Repo for that. Now you can at least query the fs.files collection using a Spring Data Repo. But: You cannot access the file contents this way.

For getting the file contents itself, you've got (at least) 2 options:

  1. Use file = gridOperations.findOne(Query.query(Criteria.where("_id").is(id))); InputStream is = file.getInputStream();

  2. Have a look at the source code of GridFSDBFile. There you can see, how it internally queries the fs.chunks collection and fills the InputStream.

(Option 2 is really low level, Option 1 is a lot easier and this code gets maintained by the MongoDB-Java-Driver devs, though Option 1 would be my choice).


Updating GridFS entries:

  • GridFS is not designed to update file content!
  • Though only updating the metadata field can be useful. The rest of the fields is kinda static.

You should be able to simply use your custom MyGridFsFileRepo's update method. I suggest to only create a setter for the metadata field.


Different metadata for different files:

I solved this using an abstract MyGridFsFile class with generic metadata, i.e.:

@Document(collection="fs.files")
public abstract class AbstractMyGridFsFile<M extends AbstractMetadata> {

    ...

    private M metadata;
    public M getMetadata() { return metadata; }
    void setMetadata(M metadata) { this.metadata = metadata; }

}

And of course each impl has its own AbstractMetadata impl associated. What have I done? AbstractMetadata always has a field called type. This way I can find the right AbstractMyGridFsFile impl. Though I have also a generic abstract repository.

Btw: In the meantime I switched here from using Spring Repo, to use plain access via MongoTemplate, like:

protected List<A> findAll(Collection<ObjectId> ids) {
    List<A> files = mongoTemplate.find(Query.query(Criteria
            .where("_id").in(ids)
            .and("metadata.type").is(type) // this is hardcoded for each repo impl
    ), typeClass); // this is the corresponding impl of AbstractMyGridFsFile
    return files;
}

Hope this helps. I can write more, if you need more information about this. Just tell me.

like image 163
Benjamin M Avatar answered Sep 19 '22 23:09

Benjamin M


You can create a GridFS object with the database from your MongoTemplate, and then interact with that:

MongoTemplate mongoTemplate = new MongoTemplate(new Mongo(), "GetTheTemplateFromSomewhere");
GridFS gridFS = new GridFS(mongoTemplate.getDb());

The GridFS object lets you create, delete and find etc.

like image 41
Trisha Avatar answered Sep 21 '22 23:09

Trisha