📜 ⬆️ ⬇️

Easy start: Spring + MongoDB



I looked for similar articles in Habré , found only Morphia - lightweight ORM for MongoDB, managed by annotations , did not find anything on the Spring Data + MongoDB bundle, therefore decided to write a post from the “for little ones” section on setting up and using the Spring + MongoDB bundle .

What will be the application itself:
Let's make the simplest contact manager, in order to try on the example of elementary CRUD operations.

Used libraries:

The project will be assembled by Maven, and I personally write the code in Intellij IDEA (I think this is the best IDE for Java). By the way, asolntsev in his post told about the advantages of this environment in his time. Why IDEA is better than Eclipse .
')
Project configuration

First, let's describe the Maven configuration:

pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <packaging>war</packaging> <groupId>habra</groupId> <artifactId>habr</artifactId> <version>1.0-SNAPSHOT</version> <!--    ,     --> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <version.jdk>1.6</version.jdk> <version.spring>4.0.2.RELEASE</version.spring> <version.spring.mongodb>1.4.0.RELEASE</version.spring.mongodb> <version.jackson>1.9.13</version.jackson> </properties> <dependencies> <!-- ,    Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${version.spring}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${version.spring}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${version.spring}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${version.spring}</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-mongodb</artifactId> <version>${version.spring.mongodb}</version> </dependency> <!-- MongoDB  --> <dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-driver</artifactId> <version>2.11.4</version> </dependency> <!-- Jackson JSON Mapper   ,    API   JSON .        . --> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>${version.jackson}</version> </dependency> <!-- Servlet Api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!--  --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.5</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.5</version> </dependency> <!-- TEST --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!-- Apache Commons,       -  .    . --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-war-plugin</artifactId> <version>2.4</version> <configuration> <webXml>src/main/webapp/WEB-INF/web.xml</webXml> </configuration> </plugin> </plugins> </build> </project> 

Now that all the libraries are described, you can proceed to the description of the Spring configuration files.
For this, I usually create a spring folder in src / main / resources, some store Spring configuration files in webapp / WEB-INF / *, but this is already convenient for someone. I will say right away, the configuration file will be 2, at least I think that this is the most correct version of the configuration description. The 1st file will include the configuration for creating bins, connecting to the database, etc., so to speak, the context configuration of the entire application. The 2nd file is the description of the work of DispatcherServlet, in general, everything that is already connected with the display of pages, and in general with Spring MVC.

Let's start by describing the context of the entire application:

src / main / resources / spring / applicationContext.xml
 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mongo="http://www.springframework.org/schema/data/mongo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd"> <!--     @Service, @Controller, @Repository... --> <context:annotation-config/> <!--  Spring ,      ,   @Service, @Repository,    ,     @Controller, ..        . --> <context:component-scan base-package="ru.habrahabr.sm"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!--  properties    Spring (.. ).        ${mongo.host} ( . ) --> <context:property-placeholder location="classpath:database.properties"/> <!--   'mongo' --> <mongo:mongo host="${mongo.host}" port="${mongo.port}"/> <!--   'mongoDbFactory'.  MongoDB   ,   username, password   --> <mongo:db-factory username="${mongo.username}" password="${mongo.password}" dbname="${mongo.db}" mongo-ref="mongo"/> <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/> </bean> </beans> 

Description of the Spring MVC Context:

src / main / resources / spring / dispatcherServlet.xml
 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--  MVC  --> <mvc:annotation-driven/> <!--  MVC Resources  ,     webapp/resources/    : localhost/resources/ --> <mvc:resources mapping="/resources/**" location="/resources/"/> <!--  Spring MVC   - --> <context:component-scan base-package="ru.habrahabr.sm"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <!--  Spring MVC     View,     "/WEB-INF/pages/" --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/"/> <property name="suffix" value=".jsp"/> </bean> </beans> 

Well, Spring is set up, but if you carefully read the configuration files, you noticed that in applicationContext.xml we pulled " classpath: database.properties ". This means that you need to create a database.properties file with the following content:

src / main / resources / database.properties
 mongo.host=localhost mongo.port=27017 mongo.db=mydb mongo.username=username mongo.password=password 

Of course, you need to replace the data after the '=' sign with your own. Note that the username, password fields are optional, and if you don’t need authorization to access MongoDB, look at applicationContext.xml, which indicates which fields need to be removed.
Immediately create a configuration file for the logging system:

src / main / resources / log4j.properties
 log4j.rootLogger=INFO, stdout # Direct log messages to stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.conversionPattern=%d{dd.MM.yy HH:mm:ss} %5p - %m%n log4j.appender.stdout.encoding=UTF-8 

Finally, you can proceed to the final stage of configuring a Java Web application — the web.xml description:

src / main / webapp / WEB-INF / web.xml
 <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <!-- Spring Application Context --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <!-- /Spring Application Context --> <!-- Spring Dispatcher Servlet Context --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/dispatcherServlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- /Spring Dispatcher Servlet Context --> <!-- Filters --> <!-- Character Filter --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- /Character Filter --> <!-- /Filters --> </web-app> 

At this stage, you should already have the following file structure in the project:

In the folder “src / webapp / resources /” you can put any static content (images, styles, js scripts, etc.).

Code writing
Finally, the project is configured, and you can start writing the application code itself. Let's start with a description of the model and implementation of the layer for working with the database. I want to note the following: in MongoDB there is no integer AUTO_INCREMENT PK field, and the default object is assigned an ID of the following type: ObjectId ("5326b46f44ae9e6328b4566c"). Spring Data understands this ID as an object of type String. Simply put, if you need to make sure that your object ID is integer and auto increasing, you will have to work on it yourself. But in reality there is nothing difficult in this and you should not be afraid of it, and I will now describe how this is done! If you are satisfied with a long String as an object ID, then skip all classes (and, accordingly, their use) in which the word “Sequence” is found.

To begin, create a collection in the database with the name of sequences. And with hands we bring in an object (insert):
 { "_id" : "contacts", "sequence" : 0 } 
In this collection we will store a pair: “table name” - “last ID”, which means that for each table (collection) in which you want to use our own AUTO_INCREMENT implementation, you need to make a new record, as indicated above, only instead of “contacts” enter the name of the collection you need. By the way, if you still do not know which tool (management-tool) to use for MongoDB, then I recommend Robomongo .

Now we create a wrapper class for this collection:

Sequence.java
 package ru.habrahabr.sm.model; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; /** * Date: 26.03.2014 * Time: 15:38 * * @author Ruslan Molchanov (ruslanys@gmail.com) */ @Document(collection = Sequence.COLLECTION_NAME) public class Sequence { public static final String COLLECTION_NAME = "sequences"; @Id private String id; private Long sequence; public Sequence() { } public String getId() { return id; } public void setId(String id) { this.id = id; } public Long getSequence() { return sequence; } public void setSequence(Long sequence) { this.sequence = sequence; } } 

Pay attention to the entry "@Document (collection = Sequence.COLLECTION_NAME)" in fact, one could write "@Document (collection =" sequences ")", but this is rather a matter of taste. Plus there is one clear advantage of such a method, then you will understand which one.

Now we will describe the layer for working with the database for the wrapper class " Sequence ", I will say right away, so as not to stretch the article, I will not create interfaces for services and DAO, I will use object implementations (although this does not seem to be very good), I hope that You understand what is at stake.

SequenceDao.java
 package ru.habrahabr.sm.dao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.FindAndModifyOptions; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.stereotype.Repository; import ru.habrahabr.sm.exceptions.SequenceException; import ru.habrahabr.sm.model.Sequence; /** *      www.mkyong.com * http://mkyong.com/mongodb/spring-data-mongodb-auto-sequence-id-example/ */ @Repository public class SequenceDao { @Autowired private MongoOperations mongoOperations; public Long getNextSequenceId(String key) { //   Sequence    Query query = new Query(Criteria.where("id").is(key)); //   sequence   Update update = new Update(); update.inc("sequence", 1); //  ,      FindAndModifyOptions options = new FindAndModifyOptions(); options.returnNew(true); //   :) Sequence sequence = mongoOperations.findAndModify(query, update, options, Sequence.class); // if no sequence throws SequenceException if(sequence == null) throw new SequenceException("Unable to get sequence for key: " + key); return sequence.getSequence(); } } 

Now the exception class, which is thrown, if in our collection of `sequences` there is no corresponding record in the database (about which I wrote above):

SequenceException.java
 package ru.habrahabr.sm.exceptions; /** * Date: 26.03.2014 * Time: 16:09 * * @author Ruslan Molchanov (ruslanys@gmail.com) */ public class SequenceException extends RuntimeException { public SequenceException(String message) { super(message); } } 

Now we create a collection of contacts in the database, and a wrapper class:

Contact.java
 package ru.habrahabr.sm.model; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import java.io.Serializable; /** * Date: 26.03.2014 * Time: 15:29 * * @author Ruslan Molchanov (ruslanys@gmail.com) */ @Document(collection = Contact.COLLECTION_NAME) public class Contact implements Serializable { public static final String COLLECTION_NAME = "contacts"; @Id private Long id; /* *******************************************************   ,  ID     (     ),    ID : @Id private String id; ********************************************************* */ private String name; private String number; private String email; public Contact() { } public Contact(String name, String number, String email) { this.name = name; this.number = number; this.email = email; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } 

Now we describe the DAO layer for our class “Contact”:

ContactDao.java
 package ru.habrahabr.sm.dao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.stereotype.Repository; import ru.habrahabr.sm.model.Contact; import java.util.List; /** * Date: 26.03.2014 * Time: 19:13 * * @author Ruslan Molchanov (ruslanys@gmail.com) */ @Repository public class ContactDao { @Autowired private MongoOperations mongoOperations; public void save(Contact contact) { mongoOperations.save(contact); } public Contact get(Long id) { return mongoOperations.findOne(Query.query(Criteria.where("id").is(id)), Contact.class); } public List<Contact> getAll() { return mongoOperations.findAll(Contact.class); } public void remove(Long id) { mongoOperations.remove(Query.query(Criteria.where("id").is(id)), Contact.class); } } 

I would like to note that you can update the record in the database at the DAO level as follows:
 mongoOperations.updateFirst(query, update, Contact.class); 
Dealing with this on your own is easier than ever and will not be difficult for you. But I would like to note that if you do:
 mongoOperations.save(contact); 
on an object whose ID is already set and exists in the database, then the object will be overwritten in the database under the specified ID.

Now we will describe the business logic layer for the class “Contact”:

ContactService.java
 package ru.habrahabr.sm.services; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import ru.habrahabr.sm.dao.ContactDao; import ru.habrahabr.sm.dao.SequenceDao; import ru.habrahabr.sm.model.Contact; import java.util.List; /** * Date: 26.03.2014 * Time: 20:09 * * @author Ruslan Molchanov (ruslanys@gmail.com) */ @Service public class ContactService { @Autowired private SequenceDao sequenceDao; @Autowired private ContactDao contactDao; public void add(Contact contact) { contact.setId(sequenceDao.getNextSequenceId(Contact.COLLECTION_NAME)); contactDao.save(contact); } public void update(Contact contact) { contactDao.save(contact); } public Contact get(Long id) { return contactDao.get(id); } public List<Contact> getAll() { return contactDao.getAll(); } public void remove(Long id) { contactDao.remove(id); } } 

Well, actually, everything, everything that concerns the bundle of Spring + MongoDB we have done, i.e. Now you can use ORM Spring Data with the MongoDB already connected to the project, but since I promised to show the work from the database using the example of the reference book, then it remains to write another controller class:

MainController.java
 package ru.habrahabr.sm.web; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.ModelAndView; import ru.habrahabr.sm.model.Contact; import ru.habrahabr.sm.services.ContactService; /** * Date: 26.03.2014 * Time: 20:30 * * @author Ruslan Molchanov (ruslanys@gmail.com) */ @Controller public class MainController { @Autowired private ContactService contactService; @RequestMapping(value = "/", method = RequestMethod.GET) public ModelAndView showAll() { ModelAndView modelAndView = new ModelAndView("all"); modelAndView.addObject("contacts", contactService.getAll()); return modelAndView; } @RequestMapping(value = "/add", method = RequestMethod.GET) public ModelAndView showAddForm() { return new ModelAndView("add_form", "contact", new Contact()); } @RequestMapping(value = "/add", method = RequestMethod.POST) public String addContact(@ModelAttribute("contact") Contact contact) { if(contact.getId() == null) contactService.add(contact); else contactService.update(contact); return "redirect:/"; } @RequestMapping(value = "/edit", method = RequestMethod.GET) public ModelAndView showEditForm(@RequestParam(required = true) Long id) { return new ModelAndView("add_form", "contact", contactService.get(id)); } @RequestMapping(value = "/delete", method = RequestMethod.GET) public String deleteContact(@RequestParam(required = true) Long id) { contactService.remove(id); return "redirect:/"; } } 

Well and views:

src / webapp / WEB-INF / pages / add_form.jsp
 <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title> </title> </head> <body> <form:form method="POST" action="/add" modelAttribute="contact"> <form:hidden path="id" /> <table> <tr> <td>Name:</td> <td><form:input path="name" /></td> </tr> <tr> <td>Number:</td> <td><form:input path="number" /></td> </tr> <tr> <td>E-mail:</td> <td><form:input path="email" /></td> </tr> <tr> <td colspan="2"> <input type="submit" /> </td> </tr> </table> </form:form> </body> </html> 

Another one:

src / webapp / WEB-INF / all.jsp
 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title> </title> </head> <body> <table width="600px"> <tr> <td><b>ID</b></td> <td><b>Name</b></td> <td><b>Number</b></td> <td><b>E-mail</b></td> <td><b>Action</b></td> </tr> <c:forEach var="contact" items="${contacts}"> <tr> <td>${contact.id}</td> <td>${contact.name}</td> <td>${contact.number}</td> <td>${contact.email}</td> <td><a href="/edit?id=${contact.id}">Edit</a> | <a href="/delete?id=${contact.id}">Delete</a></td> </tr> </c:forEach> <tr> <td colspan="5"> <a href="/add"> </a> </td> </tr> </table> </body> </html> 


Well, that's all, collect the project, run, and voila:


Link to sources: github.com/ruslanys/sample-spring-mongodb

PS I would be happy if this post is useful to someone. I would be grateful for tips and corrections.

UPD. On the recommendation of comrades from Habra, he brought all the listings under the spoiler.

Source: https://habr.com/ru/post/217391/


All Articles