Showing posts with label spring_data. Show all posts
Showing posts with label spring_data. Show all posts

Custom converter for Mongodb and Spring Data for BigDecimal type

Recently I found that MongoDB does not support java's java.math.BigDecimal as you can read in the official spring data mongodb documentation

As it turns out java.math.BigDecimal will be converted to java.lang.String when saved to the database and parsed back to BigDecimal which in most cases I guess it's alright

However as I found out things get broken when trying to perform range queries on this field such as : $gte, $lte you will get inconsistent results since MongoDB sees this as a String

Luckily this can be easily fixed by creating 2 custom converters (one for each side), below is how I've done it :

1. Define custom converters by implementing the appropriate interface

import org.springframework.core.convert.converter.Converter;

import java.math.BigDecimal;

public class BigDecimalToDoubleConverter implements Converter<BigDecimal, Double> {

    @Override
    public Double convert(BigDecimal source) {
        return source.doubleValue();
    }
}

import org.springframework.core.convert.converter.Converter;

import java.math.BigDecimal;


public class DoubleToBigDecimalConverter implements Converter<Double, BigDecimal> {

    @Override
    public BigDecimal convert(Double source) {
        return new BigDecimal(source);
    }
}

2. Configure the MongoTemplate to use the custom converters



@Configuration
public class MongoDBConfig  {

   
     //MongoDB properties read from the application.yaml configuration file (to handle different profiles)
    @Value("${spring.data.mongodb.host}")
    private String mongoHost;

    @Value("${spring.data.mongodb.port}")
    private int mongoPort;

    @Value("${spring.data.mongodb.database}")
    private String mongoDatabase;

   
    @Bean
    public MongoTemplate mongoTemplate() throws Exception {

        MongoTemplate mongoTemplate = new MongoTemplate(mongo(), mongoDatabase);
        MappingMongoConverter mongoMapping = (MappingMongoConverter) mongoTemplate.getConverter();
        mongoMapping.setCustomConversions(customConversions()); // tell mongodb to use the custom converters
        mongoMapping.afterPropertiesSet();
        return mongoTemplate;

    }

    /**
    * Configure the MongoDB client
    * 
    **/
    @Bean
    public Mongo mongo() throws Exception {
        return new MongoClient(mongoHost, mongoPort);
    }


   /**
    * Returns the list of custom converters that will be used by the MongoDB template
    * 
    **/
    public CustomConversions customConversions() {
        return new CustomConversions(Arrays.asList(new DoubleToBigDecimalConverter(), new BigDecimalToDoubleConverter()));
    }
}


Please note that this is OK in most cases however please note that in some cases this conversion from BigDecimal to Double may lead to issues; more info : Oracle's official doc on floating point arithmetic

Spring boot and mongodb with geo-localisation queries 1/3

MongoDB offers a number of indexes and query mechanisms to handle geospatial information, below are some of the features offered by the database :

  • Surfaces
  • Location Data
  • Query Operations
  • Geospatial indexes
  • .....

More detailed information can be found at their website : MongoDB Geospatial queries

In this 3 part post we will be creating a sample REST API application using Spring Boot, Spring Data and MongoDB to determinate if a given "Interest Point" is within a certain distance of the provided coordinates

1. Setup

Before we start please ensure that you have a running MongoDB instance

So let's get things started, we will be using Gradle as our build tool for this project, if you're not familiar with this build tool you can read the documentation

Below is the build file used to build our project, there is some noise in here that is related to IDE support as well as a few things needed to deploy this application in heroku which can be avoided but I've chosen to leave here for the sake of completion, the most interesting parts for us are the dependencies



buildscript {
 ext {
  springBootVersion = '1.5.2.RELEASE'
 }
 repositories {
  mavenCentral()
 }
 dependencies {
  classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 
 }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'spring-boot'




jar {
 baseName = 'spring_mongodb_geospatial_api'
 version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
 mavenCentral()
}

task stage {
    dependsOn build
}


task wrapper(type: Wrapper) {
    gradleVersion = '2.6'
}


task copyToLib(type: Copy) {
    into "$buildDir/lib"
    from(configurations.compile)
}

stage.dependsOn(copyToLib)

dependencies {
 compile('org.springframework.boot:spring-boot-starter-data-mongodb') {
  exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
 }
 compile('org.springframework.boot:spring-boot-starter-web') {
  exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
 }
 compile('org.springframework.boot:spring-boot-starter-actuator'){
  exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
 }

 compile('org.springframework.boot:spring-boot-starter-log4j')

 compileOnly ("org.projectlombok:lombok:1.16.14")

 testCompile('org.springframework.boot:spring-boot-starter-test'){
  exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
 }
}


eclipse {
 classpath {
   containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
   containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
 }
}


Let's configure our spring boot application with the base info for our application to run (default profile, MongoDB info, etc.):

spring:
    application:
        name: spring_mongodb_geospatial_api
    profiles:
      active: dev



---
spring:
  profiles: dev
  data:
    mongodb:
      host: localhost
      port: 27017
      repositories:
        enabled: true

Let's now configure our bootstrap class :


package com.ufasoli.tutorials.mongogeospatial.ws;

import com.fasterxml.jackson.databind.Module;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.geo.GeoJsonModule;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@Configuration
@EnableAsync
public class SpringMongoDBGeospatial {

     public static void main(String[] args) {
 SpringApplication.run(SpringMongoDBGeospatial.class, args);
     }

    //Register a custom Serializer/Deserializer for the geospatial information
    @Bean
    public Module registerGeoJsonModule(){
        return new GeoJsonModule();
    }
}

This is a pretty standard spring boot class except for the fact that we are declaring a custom JSON Serializer/Deserializer for handling geospatial information

Finally let's create our data model :

package com.ufasoli.tutorials.mongogeospatial.ws.model;

import lombok.Data;

import java.util.Date;


@Data
public class Updatable {

    private Date createdAt;
    private Date updatedAt;

    private Long updatedTimestamp;


}



package com.ufasoli.tutorials.mongogeospatial.ws.model;

import lombok.Data;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.mapping.Document;

import java.io.Serializable;
import java.util.List;


@Document
@Data
public class InterestPoint extends Updatable implements Serializable {

    @Id
    private String uuid;

    @NotBlank
    private String displayName;

    private String displayPhoto;


    private List categories;

    @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)
    private GeoJsonPoint location;


    public InterestPoint() {
    }

    public InterestPoint(String uuid, String displayName) {
        this.uuid = uuid;
        this.displayName = displayName;
    }

    public InterestPoint(String uuid, String displayName, GeoJsonPoint location) {
        this.uuid = uuid;
        this.displayName = displayName;
        this.location = location;
    }


}


package com.ufasoli.tutorials.mongogeospatial.ws.model;

import java.io.Serializable;

/**
 * Created by ufasoli
 */
public enum Category implements Serializable{

    CAFE,
    BAR,
    RESTAURANT,
    MUSEUM,
    BANK,
    METRO_STATION,
    BUS_STATION,
    THEATRE,
    HOTEL
}


And that it's for today, on the next part we will set up the web application and controllers

Spring Data force version and lastmodified date to update on JPA Entity

Spring data has a feature that allows you to track automatically a certain amount of fields through annotations :

  • @CreatedDate : The date on which the entity was created
  • @CreatedBy : The user that created the entity
  • @LastModifiedDate : The date the entity was last modified
  • @LastModifiedBy : The user that modified last the entity
  • @Version : the version of the entity (increased each time the entity is modified and savesd)

Let's say for example we have this simple entity with the appropriate annotations :


@Entity
@EntityListeners(AuditingEntityListener.class)
public class Contact implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Version()
    private Long version = 0L;

    @NotNull
    @CreatedDate
    private Date creationDate;

    @NotNull
    @Size(max = 50)
    @CreatedBy
    private String creationUserLogin;

    @NotNull
    @LastModifiedDate
    private Date modificationDate;

    @NotNull
    @Size(max = 50)
    @LastModifiedBy
    private String modificationUserLogin;

   private String firstName;

   private String lastName;

 //GETTERS - SETTERS and other code
   

}

And we're using the following service class and repository :


@Service
public class ContactServiceImpl implements ContactService{

   @Autowired
   private ContactRepository contactRepository;


  public Contact save(Contact contact){
   
    return contactRepository.save(entity);
  }
}


public interface ContactRepository extends JpaRepository<Contact, Long> {

}

Now whenever you change the value of one of the fields in the entity; the fields marked with @Version, @LastModified and @LastModifiedBy will be updated with new values automatically.

Now recently I had the case where I had to "touch" an entity (like in linux) so that the version will be increased as well as @LastModified and @LastModifiedBy will be updated accordingly even though none of the fields had been updated

Now you cannot update the automatically handled fields manually, but there is a way you can achieve this by using Spring's AuditingHandler; so we could modify our service class as follows :


@Service
public class ContactServiceImpl implements ContactService{

    @Autowired
    private ContactRepository contactRepository;

    @Autowired
    private AuditingHandler auditingHandler;

   
    public Contact save(Contact contact, boolean forceVersionIncrease){
         //force version update even though no values have changed in the entity
         if(forceVersionIncrease){
             auditingHandler.markModified(contact);
         }
   
     return contactRepository.save(entity);
   }
}

And there you go, you should be able now to "touch" your entities and force date and version updates

Spring boot and spring data jpa tutorial - A sample application using spring 4.0 spring boot and JPA (part 2/2)

This is the second part of the Spring boot and spring data jpa tutorial - A sample application using spring 4.0 spring boot and JPA

At the end of the tutorial I promised you that we will be removing one of the steps when running the application, that is merge the package and run in a maven command and make him do the work

5.- Making maven run the JAR directly

We're going to make maven run the runnable JAR for us after it has been packaged, for this purpose we're going to use one of org.codehaus.mojo maven plugins



....

        
            
                org.springframework.boot
                spring-boot-maven-plugin
            

            
                org.codehaus.mojo
                exec-maven-plugin
                1.2.1
                
                    com.ufasoli.tutorials.spring.boot.HelloWorldApp
                
            
        
    
...

So what's happening here ? :

  • By adding the exec plugin to the build section of our pom.xml and providing a mainClass when we run the appropriate maven goals the application will be packaged and run directly from maven

You can try this by running :

    mvn clean package exec:java

By running this on your console the application will be packaged and run directly using maven

Now let's add some more interesting stuff to our app

6.- Adding Spring Data and JPA support

We're now going to add some Spring Data and JPA support to our app.

The first thing you need to do is add the additional spring boot starter module (spring-boot-starter-data-jpa) to your maven dependencies and since we will be using an embedded HSQL database the dependency for the HSQL driver too:

 
 ...
        
        
            org.springframework.boot
            spring-boot-starter-data-jpa
        

         
            hsqldb
            hsqldb
            ${version.hsqldb}
        
...


The next thing you need to do is tell spring to enable Spring Data and its repository features this is done by simply adding the @EnableJpaRepositories to your Spring App class

@ComponentScan 
@EnableAutoConfiguration
@EnableJpaRepositories
public class HelloWorldApp {

    public static void main(String[] args) throws Exception {

        SpringApplication.run(HelloWorldApp.class, args);

    }
}

And that's it if you now build and run your application you will have a Spring web application with Spring Data and JPA support even though the app won't do much for the moment ;).

So what's happening here?

  • Annotating the Spring Application class with the @EnableJpaRepositories will cause a lot of "magic" to happen and a lot of things will be configured/initialized for you such as an Entity Manager, DataSource (see next item), JPA Vendor adapter ..
  • Since we haven't defined a DataSource if Spring finds an in memory JDBC driver in the Classpath it will automatically create one for us using a set of predefined conventions

Now let's add some logic to our app :

7.- Adding some Spring data and JPA logic

The first thing we're going to be doing is adding a simple JPA entity to perform some CRUD operations later on



import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Book {


    @Id
    protected String isbn;
    protected String title;
    protected String author;
    protected String description;
    // omitting setters/getters/constructors
}

So now that we have our entity lets use one of the great features of Spring Data : The Repositories Abstraction :

The goal of Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores.

By annotating an interface with @Repository and making this interface extend CrudRepositoryYou will get among other things 2 pretty nifty goodies of the box:

  • Some utility CRUD methods such as findAll() or count()
  • Auto-implementation of methods based on naming convention such as findBooksByAuthor()

And the best part is you get all of this without writing any code logic for these methods (of course you can override these methods, and write some custom methods) as shown in the doc, you can even avoid yet to write some code by using for example the @Query annotation

So now lets create our CRUD repository for our entity with a custom method to find the books by their author


import com.ufasoli.tutorials.spring.boot.model.Book;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;


@Repository
public interface BookRepository extends CrudRepository {

    public Iterable findBooksByAuthor(@Param("author") String author);
}

This interface will be picked up by Spring an instantiated accordingly.

We're almost done with our JPA/database logic, one last thing I would like to do is to have some data inserted automatically into the database when the application starts (especially since we're dealing with an in-memory database

Once again we can take advantage of another Spring convention, if you place a data.sql file in your maven resources directory the script will be automatically executed when the database will be bootstrapped and after reverse engineering your entities into the database

INSERT INTO book(isbn, title, author,description) VALUES ('1111111', 'The Spirit Thief (The Legend of Eli Monpress #1)', 'Rachel Aaron',
                         'Eli Monpress is talented. He''s charming. And he''s a thief.But not just any thief. He''s the greatest thief of the age - and he''s also a wizard. And with the help of his partners - a swordsman with the most powerful magic sword in the world but no magical ability of his own, and a demonseed who can step through shadows and punch through walls - he''s going to put his plan into effect.The first step is to increase the size of the bounty on his head, so he''ll need to steal some big things. But he''ll start small for now. He''ll just steal something that no one will miss - at least for a while. Like a king');

INSERT INTO book(isbn, title, author,description) VALUES ('2222222', 'The Spirit Rebellion (The Legend of Eli Monpress, #2)', 'Rachel Aaron',
                         'Eli Monpress is brilliant. He''s incorrigible. And he''s a thief.He''s also still at large, which drives Miranda Lyonette crazy. While she''s been kicked out of the Spirit Court, Eli''s had plenty of time to plan his next adventure. But now the tables have turned, because Miranda has a new job -- and an opportunity to capture a certain thief. Things are about to get exciting for Eli. He''s picked a winner for his newest heist. His target: the Duke of Gaol''s famous ''thief-proof'' citadel. Eli knows Gaol is a trap, but what''s life without challenges? Except the Duke is one of the wealthiest men in the world, a wizard who rules his duchy with an iron fist, and an obsessive perfectionist with only one hobby: Eli. It seems that everyone is hunting for Eli Monpress');

INSERT INTO book(isbn, title, author,description) VALUES ('3333333', 'The Spirit Eater (The Legend of Eli Monpress, #3)', 'Rachel Aaron',
                         'With the pressure on after his success in Gaol, Eli Monpress, professional thief and degenerate, decides it''s time to lie low for a bit. Taking up residence in a tiny seaside village, Eli and his companions seize the chance for some fun and relaxation. Nico, however, is finding it a bit hard. Plagued by a demon''s voice in her head and feeling powerless, she only sees herself as a burden. Everyone''s holiday comes to an untimely close, though, when Pele arrives to beg Eli''s help for finding her missing father. But there are larger plans afoot than even Eli can see, and the real danger, and the solution, may lie with one of his own and her forgotten past. If only Nico could remember whose side she''s on.');

INSERT INTO book(isbn, title, author,description) VALUES ('4444444', 'The Spirit War (The Legend of Eli Monpress, #4)', 'Rachel Aaron',
                         'Eli Monpress is vain. He''s cocky. And he''s a thief. But he''s a thief who has just seen his bounty topped and he''s not happy about it. The bounty topper, as it turns out, is his best friend, bodyguard, and master swordsman, Josef. Who has been keeping secrets from Eli. Apparently, he''s the only prince of a rather feisty country and his mother (a formidable queen who''s every bit as driven and stubborn as he is) wants him to come home and do his duty, which means throwing over personal ambitions like proving he''s the greatest swordsman who ever lived. Family drama aside, Eli and Josef have their hands full. The Spirit Court has been usurped by the Council of Thrones and someone calling herself the Immortal Empress is staging a massive invasion. But it''s not just politics --- the Immortal Empress has a specific target in mind: Eli Monpress, the greatest thief in the world.');

INSERT INTO book(isbn, title, author,description) VALUES ('5555555', 'Spirit''s End (The Legend of Eli Monpress #5)', 'Rachel Aaron',
                         'First rule of thievery: don''t be a hero. When Eli broke the rules and saved the Council Kingdoms, he thought he knew the price, but resuming his place as the Shepherdess''s favorite isn''t as simple as bowing his head. Now that she has her darling back, Benehime is setting in motion a plan that could destroy everything she was created to protect, and even Eli''s charm might not be enough to stop her. But Eli Monpress always has a plan, and with disaster rapidly approaching, he''s pulling in every favor he can think of to make it work, including the grudging help of the Spirit Court''s new Rector, Miranda Lyonette. But with the world in panic, the demon stirring, and the Lord of Storms back on the hunt, it''s going to take more than luck and charm to pull Eli through this time. He''s going to have to break a few more rules and work with some old enemies if he''s going to survive');

8.- Wrapping things up with a REST controller

To wrap things up we'll add a simple RestController that will interact with our freshly created Repository


import com.ufasoli.tutorials.spring.boot.model.Book;
import com.ufasoli.tutorials.spring.boot.repositories.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController // assumes @ResponseBody annotation in methods annotated with  @RequestMapping
@RequestMapping(value = "/books", produces = MediaType.APPLICATION_JSON_VALUE)
public class BookController {

    @Autowired
    protected BookRepository bookRepository;

    @RequestMapping
    public Iterable books() {
        return bookRepository.findAll(); // uses the findAll() method inherited from CrudRepository
    }


    @RequestMapping(value = "/{isbn}")
    public Book book(@PathVariable("isbn") String isbn) {
        return bookRepository.findOne(isbn);// uses the findOne() method inherited from CrudRepository
    }


    @RequestMapping(value = "/{isbn}", method = RequestMethod.DELETE)
    public String deleteBook(@PathVariable("isbn") String isbn) {

        try {
            bookRepository.delete(isbn);
            return String.format("Book [%s] successfully deleted", isbn);// uses the delete() method inherited from CrudRepository
        } catch (Exception e) {
            return String.format("A problem occurred when deleting Book [%s]", e.getMessage());
        }
    }

    @RequestMapping("/author/{author}")
    public Iterable booksByAuthor(@PathVariable("author") String author) {

        return bookRepository.findBooksByAuthor(author);// uses the custom method defined in our BookRepository interface
    }


}

So that's it you can re-run your application now using the maven goal

     mvn clean package exec:java

You should see your application bootstrapping your database and inserting data, and once it's fully started you should be able to play with your Web Service

9.-Some final thoughts

I must say that I quite enjoyed playing with Spring boot, as long as you're using the full spring stack it helps you get things running quickly and has some very useful starter modules and conventions

I also added the actuator module which gives you some metrics and stats (as REST endpoints) on your application which is pretty cool to have when running the app in production

Check it out here

As usual you can find the code source for this application over at my github account here

Spring boot and spring data jpa tutorial - A sample application using spring 4.0 spring boot and JPA (part 1/2)

Spring boot is a strongly opinionated framework / generator based on the principle of convention over configuration, which according to the website:

Spring Boot makes it easy to create Spring-powered, production-grade applications and services with absolute minimum fuss. It takes an opinionated view of the Spring platform so that new and existing users can quickly get to the bits they need

Basically by using a few annotations and minimum boilerplate code the framework/generator will configure all the necessary beans and contexts based on a set of conventions.

Spring boot has a few modules that depending on your needs can speed-up development such as :

  • spring data jpa
  • spring data mongodb
  • spring web
  • spring actuator
  • ...

Personally I find that when using the spring stack this tool is pretty awesome!!

When using this tool you must know that there are actually 2 ways to use spring boot :

  1. Using the cli (and by extension groovy)
  2. Using good old maven and the spring-boot plugins

In this tutorial we will be building a simple Hello World tutorial as a RESTful web service

1.- Getting things started

Add the required maven dependencies to your pom.xml

        spring-boot-hello-world-ws
    spring-boot-hello-world-ws
    1.0-SNAPSHOT


    
    
        org.springframework.boot
        spring-boot-starter-parent
        1.0.0.RC1
    

    
    
        
            org.springframework.boot
            spring-boot-starter-web
        

    

    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            

        
    

    
    
    
        
            spring-snapshots
            http://repo.spring.io/snapshot
            
                true
            
        
        
            spring-milestones
            http://repo.spring.io/milestone
            
                true
            
        
    
    
        
            spring-snapshots
            http://repo.spring.io/snapshot
        
        
            spring-milestones
            http://repo.spring.io/milestone
        
    

So what's happening here :

  • Since spring-boot is not G.A. yet you will need to include the Spring milestones repositories
  • When using maven your project will have to inherit from the spring boot starter parent in order for it to work
  • We are using the web module (see dependencies)
  • The spring-boot-maven-plugin will build a runnable jar file

2.- Creating our first controller

With the basic configuration out of the way we can start building our app.

Since this is going to be a RESTful API we will create a REST controller. Spring included a new annotation in the latest version @RestController. Types that carry this annotation are treated as controllers where @RequestMapping methods assume @ResponseBody semantics by default; which in our case it's what we want.

So with that being said let's write our first controller :


package com.ufasoli.tutorials.spring.boot.web.controllers;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created with IntelliJ IDEA.
 * User: Ulises Fasoli
 * Date: 04.02.14
 * Time: 15:33
 */
@RestController
@RequestMapping(value = "/hello", produces = MediaType.APPLICATION_JSON_VALUE)
public class HelloWorldController {

    @RequestMapping
    public String sayHello(){
        return "Hello World";
    }
}

Nothing special here just a simple Spring MVC controller but using the new @RestController annotation

We are now 2 steps away from running our Spring app.

3.- Creating our Spring Application main class

In order for the Spring context to be bootstrapped you will need to create a class that will run the Spring application inside a main method and a few annotations.So let's go ahead and do that :

package com.ufasoli.tutorials.spring.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;

/**
 * Created with IntelliJ IDEA.
 * User: Ulises Fasoli
 * Date: 04.02.14
 * Time: 11:39
 *
 */
@ComponentScan // spring standard component scan
// spring boot autoconfiguration will bootstrap your Spring
//application while attempting to configure the beans you need
// it will also bootstrap an in-memory database if a driver is found
// in the classpath and no datasource is defined
@EnableAutoConfiguration
public class HelloWorldApp {

    public static void main(String[] args) throws Exception {

        SpringApplication.run(HelloWorldApp.class, args);

    }
}


What's happening here? :

  • The @Component is standard spring annotation to search for annotated beans in the classpath
  • The @EnableAutoConfiguration is spring boot specific and will be doing most of the "magic"

We are now one step away from running our spring application! Yep that's it no web.xml configuration, no XML files, nothing!!

4.- Taking it for a spin

With the application configured it's time to test our app, so fire-up your IDE or your console and execute the following maven goal :

  mvn clean package

Once your app is packaged you can run the generated runnable JAR file by simple going into the target folder and running :


  java -jar .\spring-boot-hello-world-ws-1.0-SNAPSHOT.jar
 

If everything worked as expected you should see the Spring boot / maven output in your console :


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::            (v1.0.0.RC1)

2014-02-04 15:46:47.628  INFO 12516 --- [           main] c.u.tutorials.spring.b
oot.HelloWorldApp  : Starting HelloWorldApp on LTULF01 with PID 12516 (C:\dev\wo
rkspaces\idea\java-blog-tutorials\spring-boot-hello-world-ws\target\spring-boot-
hello-world-ws-1.0-SNAPSHOT.jar started by ULF)
....

That's it you can now test your controller by calling the url :

 curl http://localhost:8080/hello.json

You should see the Hello world message printed out in the console

Personally it kind of bugs me to have to package the app and then run it manually so on part 2 of this tutorial we will change this so Maven will do the work for us.....

MongoDB spring data $elemMatch in field projection

Sometimes when querying a MongoDB document what you will actually need is an item of a given document's embedded collections

The issue with MongoDB is that mongo will filter out only first level documents that correspond to your query but it will not filter out embedded documents that do not correspond to your query criteria (i.e. If you are trying to find a specific book in a book saga) you will get the complete 'parent' documents with the entire sub collection you were trying to filter

So what can you do when you wish to filter a sub-collection to extract 1 subdocument ?

Enter the $elementMatch operator. Introduced in the 2.1 version the $elemMatch projection operator limits the contents of an array field that is included in the query results to contain only the array element that matches the predicate expressed by the operator.

Let's say I have a model like the following :

   {
     "id": "1",
     "series": "A song of ice and Fire",
     "author" : "George R.R. Martin",
     "books" : [
           {
               "title" : "A Game of Thrones",
               "pubYear" : 1996,
               "seriesNumber" : 1
           },
           {
               "title" : "A Clash of Kings",
               "pubYear" : 1998,
               "seriesNumber" : 2
           },
           {
               "title" : "A Storm of Swords",
               "pubYear" : 2000,
               "seriesNumber" : 3
           },
           {
               "title" : "A Feast for Crows",
               "pubYear" : 2005,
               "seriesNumber" : 4
           },
           {
               "title" : "A Dance with Dragons",
               "pubYear" : 2011,
               "seriesNumber" : 5
           },           
           {
               "title" : "The Winds of Winter",
               "pubYear" : 2014,
               "seriesNumber" : 6
           },           
           {
               "title" : "A Dream of Spring",
               "pubYear" : 2100,
               "seriesNumber" : 7
           }

      ]
}

Now let's say for example that I'm interested only in the 5th book of a given book series (A Song of ice and fire for example)

This can be accomplished in a number of different ways :

  • MapReduce functions : Supported by Spring data but maybe a bit cumbersome for what we are trying to accomplish (I will write in the future a tutorial on how to use MongoDB MapReduce functions with Spring Data and making those functions "templatable"
  • Mongo's aggregation framework : Version 2.1 introduced the aggregation framework that lets you do a lot of stuff (more info here ) however the aggregation framework is not currently supported by Spring Data
  • the $elemMatch operator

But what is the $elemMatch operator, and how do you use it?

Well You could say $elemMatch is a multi-purpose operator as it can be used either as part of the query but also as part of the fields object, where it acts as a simplified MapReduce kind-of :)

But as always there is a caveat though when using $elemMatch operator, and that is that if more than 1 embedded document matches your criteria, only the first one will be returned as stated MongoDB documentation

Now when using spring data with mongodb you can relatively easily do field projection using the fields() object exposed by the Query class like so :


  Query query = Query.query(Criteria.where("series").is("A song of ice and Fire"));
  query.fields().include("books");

However the Query class, even though is very easy to use and manipulate, doesn't give you access to some of Mongo's more powerful mechanisms like for example the $elemMatch as a projection operator.

In order for you to use the $elemMatch operator as a projection constraint you need to use a subclass of org.springframework.data.mongodb.core.query.Query i.e. org.springframework.data.mongodb.core.query.BasicQuery

So let's get down to business with an example.

Let's say we are interested only in the 4th book out of George R.R. Martin's saga a Song of ice and fire

Now if you were using the traditional Query class you will probably end up writing a 2 step logic that would look something like this (unless you were using a MapReduce function)

1. Retrieve the parent Book document that correspond to your search criteria


/**
*Returns the parent book corresponding to the sagaName criteria with the unfiltered child collection books
*/
public Book findBookNumberInSaga(String sagaName, Integer bookNumber){

   Query query = Query.query(Criteria.where("series").is(sagaName).and("books.seriesNumber").is(bookNumber));

   MongoTemplate template = getMongoTemplate();
   
   return template.find(query);

}


2. From the parent document iterate through the books collection (or use LambdaJ :p ) to recover the book you a really interested in


public class WithoutElememMatch{

public static void main(String[] args){

     Book saga = findBookNumberInSaga("A song of ice and Fire", 4);
     Book numberFor = null;
      
    Iterator<Book> books = saga.getBooks();

        while (books.hasNext()){
            Book  currentBook= books.next();

             if(book.getSeriesNumber() == 4){
                numberFor = currentBook;
             }           
        }
 
 }
//...
}



Even though the previous example is certantly not the best way to implement this logic, it works fine and it serves it purpose

Now let's implement the same thing but this time we will make the database work for us :

1. Fetch the book corresponding to the requested criteria but make the database do the work for you

Here we ask MongoDB to filter the elements in the sub-collection books to the one matching our criteria (i.e. the number 4 in the series)


/**
* Returns the parent book corresponding to the sagaName criteria with a size 1 collection 'children' collection whose seriesNumber
* property corresponds to the value of the seriesNumber argument
*/
public Book findBookNumberInSaga(String sagaName, Integer bookNumber){
        
        // the query object
        Criteria findSeriesCriteria = Criteria.where("title").is(title);
        // the field object
        Criteria findSagaNumberCriteria = Criteria.where("books").elemMatch(Criteria.where("seriesNumber").is(seriesNumber));
        BasicQuery query = new BasicQuery(findSeriesCriteria.getCriteriaObject(), findSagaNumberCriteria.getCriteriaObject());

        return  mongoOperations.find(query, Book.class);

}

 // omitted mongo template initialization




As you can see this time I didn't use the org.springframework.data.mongodb.core.query.Query class to build my query but instead I used the org.springframework.data.mongodb.core.query.BasicQuery class; because as I stated before you can only do projection in the field object by using these class

You will notice that the syntax for this class is a bit different as it takes 2 DbObjects (which are basically HashMaps) 1 for the query object and one for the field object. Much like the the mongo shell client syntax :

db.collection.find( ,  )

So now our method is implemented we can finally call it and check the results :


public class WithElememMatch{

public static void main(String[] args){

    // get the parent book
     Book parentBook = findBookNumberInSaga("A song of ice and Fire", 4);
      Book numberFor;
   
   // null checks
     if(book != null && CollectionUtils.isNotEmpty(book.getBooks()){
  // get the only book we are interested in
    numberFor = parentBook.getBooks().get(0);
  
  }
     
}
//...
 
}

So there you go hope this was useful to you, as usual you can find the code of this tutorial over at my github account here

Note : for this tutorial you will only find the 2 java classes mentioned earlier, there's no Spring / Spring data configuration (that's for another tutorial)

MongoDB & Spring Data remove Elements from Arrays

In this blogpost I will be showing how to execute a Mongo Query using Spring Data in order to "pull" multiple items in a object's collection. The model :

@Document(collection="Book")
 public class Book{

      private String ISBN;
      private String bookTitle;
      private List< String > categories;


  //SETTERS - GETTERS
  }

  public class Category{
    
      private String categoryId;
      private String categoryName;
   }

A typical Mongo Document would be something like :

   {
      "isbn" : "11111111", 
      "bookTitle" : "My Book Title", 
      "categories" :[
          {
            "categoryId" : "cat1",
            "categoryName": "My Category 1"
          },
          {
            "categoryId" : "cat2",
            "categoryName": "My Category 2"
          }
        ]
     }

    {
      "isbn" : "22222222222", 
      "bookTitle" : "My Book Title2", 
      "categories" :[
          {
            "categoryId" : "cat1",
            "categoryName": "My Category 1"
          },
          {
            "categoryId" : "cat3",
            "categoryName": "My Category 3"
          }
        ]
     }

     {
      "isbn" : "333333333", 
      "bookTitle" : "My Book Title3", 
      "categories" :[
          {
            "categoryId" : "cat2",
            "categoryName": "My Category 2"
          },
          {
            "categoryId" : "cat3",
            "categoryName": "My Category 3"
          }
        ]
     }


Now let's assume that cat1 and cat2 are no longer valid. It would be nice if we could remove these categories from all the books in one mongo update query. Here is how to do it using Spring Data.

@Component
public class MyDAO{
   
   // Mongo template injected through Spring

   @Autowired
   private MongoOperations mongoTemplate;

    public void deleteCategories(String[] categories){
     
      //find query
      // find all the programs containing the the categories passed as parameter
      Query findQuery = 
       Query.query(Criteria.where("categories.categoryId").in(Arrays.asList(categories)));      


       // build the Update
        DBObject pullUpdate = 
            BasicDBObjectBuilder.start().add(
               "categoryId",
                      BasicDBObjectBuilder.start()
                                              .add("$in",
                            categories).get()).get();
    
      Update update = new Update().pull("categories", pullUpdate );
     

     // execute the update
      template.updateMulti(findQuery, update, Book.class);

   }

}



    public class SpringDataDemo{
      
       public static void main(String[] args){

             ClassPathXmlApplicationContext context = 
                  new ClassPathXmlApplicationContext("classpath:spring-beans.xml");
             MyDAO myDao = context.getBean("myDao");
             //delete categories "cat1" and "cat2"
             mydao.deleteCategories(new String[]{"cat1", "cat2"});
       
       }
    }


Now the categories cat1 and cat2 will be removed from all the books in the collection.

Resolving Spring's data error message "Cannot resolve the name to a(n) 'element declaration"

While working on a project combining Spring Data JPA and Spring Data MongoDB I run into a lot of trouble when I introduced the









The error message :

Cannot resolve the name to a(n) 'element declaration

I spent some time looking for the cause, and then I realised that the version of SpringDataMongoDB and SpringDataJPA had dependencies on different versions of SpringDataCore. and it was causing trouble

So if you get the same error just check your maven dependency hierarchy or go check the POM files for the libraries.

You can check out the POM files in Spring's repo :

Spring Maven repository

OSX show used ports or listening applications with their PID

On OSX you can display applications listening on a given port using the lsof the commands described below will show listening application...