Spring Data Mongo: bridge MongoDB and Spring
MongoDB is one of the most popular NoSQL products, Spring Data Mongo(Maven archetype id is spring-data-mongodb) tries to provides a simple approach to access MongoDB.
Configuration
Add the following code fragments in your Spring configuration.
<!-- Mongo config --> <mongo:db-factory id="mongoDbFactory" host="localhost" port="27017" dbname="conference-db" /> <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> <constructor-arg ref="mongoDbFactory" /> </bean> <mongo:repositories base-package="com.hantsylabs.example.spring.mongo" />
Firstly, declare a DbFactory which is responsible for connecting to the MongoDB server.
Then, register the MongoTemplate object which envelop the data operations on MongoDB.
Lastly add mongo:repositories with an essential base-packageattribute, Spring will discovery your defined Repositories in the packages.
Note: There is no transaction manager declared, Spring Data Mongo does not support Spring Transaction now.
Declare your Documents
Firstly, you have to create your domain classes,. As comparison with the JPA concept, you can consider one row of records in a table is aDocument in MongoDB, and a table which has many rows is a collection of Document.
@Document public class Conference { @Id private String id; @Version private Integer version; @NotNull private String name; @NotNull private String description; @NotNull @DateTimeFormat(style = "M-") private Date startedDate; @NotNull @DateTimeFormat(style = "M-") private Date endedDate; @NotNull private String slug; private Address address; //getters and setters. } public class Address { private String addressLine1; private String addressLine2; private String zipCode; private String city; private String country //getters and setters } @Document public class Signup { @Id private String id; @Version private Integer version; @DBRef private Conference conference; //other properites //getters and setters }
Conference and Signup are defined as @Document, and in Sginup, a@DBRef annotation is used to declare it is a reference of theConference document. There is no JPA @OneToMany like annotations to define the relations between documents for MongoDB.
Address is annotated with nothing, and it is an embedded object inConference document, in concept, it is very similar with the@Embedable class in JPA, and its lifecycle is fully controlled by its dependent Conference document.
In MongoDB, the data in the storage is presented as JSON like format.
The following is an example of Conference document.
{ "_id" : ObjectId("51b422f066d41dc05f0292f0"), "_class" : "com.hantsylabs.example.spring.model.Conference", "version" : 0, "name" : "Test JUD", "description" : "JBoss User Developer Conference 2013 Boston", "startedDate" : ISODate("2013-07-09T06:38:40.272Z"), "endedDate" : ISODate("2013-07-16T06:38:40.272Z"), "slug" : "test-jud", "address" : { "addressLine1" : "address line 1", "addressLine2" : "address line 2", "zipCode" : "510000", "city" : "NY", "country" : "US" } }
The following is an example of Signup document.
{ "_id" : ObjectId("51b422f066d41dc05f0292f1"), "_class" : "com.hantsylabs.example.spring.model.Signup", "version" : 0, "firstName" : "Hantsy", "lastName" : "Bai", "email" : "test@test.com", "phone" : "123 222 444", "occupation" : "Developer", "company" : "TestCompany", "comment" : "test comments", "createdDate" : ISODate("2013-06-09T06:38:40.288Z"), "status" : "PENDING", "conference" : DBRef("conference", ObjectId("51b422f066d41dc05f0292f0")) }
As you see, Address is embedded in the Conference document, and in Signup, there is a DBRef used to indicate it is a reference ofConference document.
@Id and @Version annotations are from the packageorg.springframework.data.annotation, which is similar with JPA, they are used to define the unique identification flag and version of a Document.
A String, BigInteger and Mongo specific ObjectId type property can be used as document id (annotated with the @Id annotation), Spring Data Mongo will convert it to Mongo internal id at runtime.
@Version is similar with the JPA specific @Version, Spring Data Mongo will fill this field automatically at runtime.
Spring Data Mongo also provides other annotations for the field mapping, such as @Field can be used to customize the filed name of a document in MongoDB, @Indexed is designated to create indies based on the fields at runtime.
Query
Before you do some query operations, you have to create aRepository class as the steps in before post.
@Repository public interface ConferenceRepository extends MongoRepository<Conference, String>{ }
MongoRepository is a Mongo specific Repository provided by Spring Data Mongo, it is dirven from PagingAndSortingRepository in Spring Data Commons.
Now you can use all the methods defined in thePagingAndSortingRepository and CrudRepository.
For example,
@Autowired ConferenceRepository conferenceRepository; conferenceRepository.save(); conferenceRepository.delete(); //etc
Firstly inject the ConferenceRepository in your classes, then use it as you expected.
The convention based methods are also supported by default in Spring Data Mongo, you have to research the Spring Data Mongo reference to get all available legal expression of the methods.
For example,
public Conference findBySlug(String slug);
It is used to find Conference by the slug property.
Custom Query
Like the Spring Data JPA, Spring Data Mongo also provides a@Query annotation(in the packageorg.springframework.data.mongodb.repository) to define and execute custom query.
For example,
@Query("{description:{ $regex: '*'+?0+'*', $options: 'i'}}") public List<Conference> searchByDescriptionLike(String like);
The value attribute of the @Query annotation can accept a Mongo aggregation expression, please refer to the official document for theAggregation.
You can also create a Custom interface and your implementation class to execute the custom query.
The steps are similar with ones in the Spring Data JPA post.
- Firstly create an interface make sure the class name is endedCustom.
public interface ConferenceRepositoryCustom { public List<Conference> searchByDescription(String like); public void updateConferenceDescription(String description, String id ); }
- Modify the ConferenceRepository and addConferenceRepositoryCustom interface to be extended.
@Repository public interface ConferenceRepository extends ConferenceRepositoryCustom, MongoRepository{ }
- Create your imeplementation class to implement the methods deifned in ConferenceRepositoryCustom interface.
public class ConferenceRepositoryImpl implements ConferenceRepositoryCustom { private static final Logger log = LoggerFactory .getLogger(ConferenceRepositoryImpl.class); @Autowired MongoTemplate mongoTemplate; @Override public List<Conference> searchByDescription(String d) { return mongoTemplate.find( Query.query(Criteria.where("description").regex( "[\\w]*" + d + "[\\w]*", "i")), Conference.class); } @Override public void updateConferenceDescription(String description, String id) { WriteResult result = mongoTemplate.updateMulti( Query.query(Criteria.where("id").is(id)), Update.update("description", description), Conference.class); log.debug("result @"+result.getField("description")); } }
Unlike the Spring Data JPA, there is no a Mongo SQL like language support in the Mongo Java Driver and Spring Data Mongo. In this implementation class, MongoTemplate is used to perform data operations.
Some other solutions I motioned before, such as DataNucleusprovides standard JPA and JDO APIs for NoSQL, so using JPA APIs on Mongo is possible when use DataNucleus.
MongoTemplate
MongoTemplate simplified the document collection based operations, please refer to the Mongo official document about Collection methods.
Criteria is a fluent API to build the query condition, it try to simplify the Mongo aggregation expression.
Query is used to combine the Criteria and other query condition, such as Pageable capability.
Update is the encapsulation of the Mongo core update operation, seeMongo core operations for more details.
In fact, you can use MongoTemplate freely in the classes outside of the Repository API.
@Repository public class AnyBean { @Autowired MongoTemplate mongoTemplate; }
And declare context:component-scan in your configuration to discover your beans.
<context:component-scan base-package="com.hantsylabs.example.spring.mongo"> </context:component-scan>
QueryDSL integration
QueryDSL supports MongoDB officially, but the Metamodel generation and data operations is dependent on a third party project named Morphia which provides JPA like APIs for Mongo operations.
But unluckily, Spring did not adopt it in Spring Data Mongo project.
- Extend the Spring specific QueryDslPredicateExecutor, it is from Spring Data Commons, the same interface we used in before post.
@Repository public interface ConferenceRepository extends ConferenceRepositoryCustom, MongoRepository<Conference, String>, QueryDslPredicateExecutor<Conference> { }
- Generate the Metamodels.
Spring Data Mongo provides a custom APT processor to generate the Metamodels instead of the one provided in QueryDSL, it will scan the Spring specific @Document instead of the Morphia specific annotations.
<plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.0.9</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/java</outputDirectory> <processor>org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor</processor> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>com.mysema.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <version>${querydsl.version}</version> </dependency> <dependency> <groupId>com.mysema.querydsl</groupId> <artifactId>querydsl-mongodb</artifactId> <classifier>apt</classifier> <version>${querydsl.version}</version> </dependency> </dependencies> </plugin>
Run
mvn compile
to generate the Metamodels. Open theQConference class, you will find there is no difference from the early version generated for JPA entities in last post.- Now you can use the APIs freely like the Spring Data JPA.
QConference qconf = QConference.conference; List<Conference> conferences = (List<Conference>) conferenceRepository .findAll(qconf.slug.eq("test-jud")); List<Conference> conferences2 = (List<Conference>) conferenceRepository .findAll(QConference.conference.address.country.eq("CN"));
All work as expected.
Try another one on Signup document collection.
List<Signup> signups = (List<Signup>) signupRepository .findAll(QSignup.signup.conference.eq(conf));
Unfortunately, this query does not work as expected, you can find some issues have been filed about this problem on Spring JIRA. Currently, QueryDSL integration in Spring Data Mongo does not support reference(the fields marked with the @DBRef annotation) in query path. In this example, the conference caused the problem.
Spring Data Mongo also provides Geo, GridFS support abstraction which are not motioned here.
Summary
Spring Data Mongo provides simple data operations on MongoDB, due to there is no query language like JPQL for Mongo, all features provided in Spring Data Mongo based on the concept of Mongo client tools and shell interaction.
The most regrettable is it can not provide consistent experience as JPA support when adopting QueryDSL in projects.
评论