Monday, February 24, 2014

Spring & MongoTemplate

To keep up with Spring templates theme, I will show you how to use MongoTemplate to access MongoDB. Assuming you are already familiar with MongoDB and the fact that it's a document centric datastore, the first thing we should do is define objects representing documents. In this tutorial, my application will manage books in a MongoDB database. Here is the definition of a book stored as a document.
package com.noushin.spring.monspr.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

/**
 * This class represents a book as a MongoDB document.
 * 
 * @author nbashir
 * 
 */
@Document
public class Book {

   @Id
   private String id;

   @Indexed
   private String name;

   private String author;

   public Book(String id, String name, String author) {
      super();
      this.id = id;
      this.name = name;
      this.author = author;
   }

   public String getId() {
      return id;
   }

   public void setId(String id) {
      this.id = id;
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public String getAuthor() {
      return author;
   }

   public void setAuthor(String author) {
      this.author = author;
   }
}
Now I need a class to access books in MongoDB. This is where I will utilize MongoTemplate.
package com.noushin.spring.monspr.dao;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Repository;

import com.noushin.spring.monspr.model.Book;

/**
 * This class handles accessing books in MongoDB.
 * 
 * @author nbashir
 *
 */
@Repository
public class BookRepositroy {
   
   @Autowired
   MongoTemplate mongoTemplate;
      
   public void save(Book book) {
      mongoTemplate.save(book);
   }
   
   public void save(List<Book> books) {
      mongoTemplate.save(books);
   }

   public Book get(String id) {
      return mongoTemplate.findById(id, Book.class);
   }
   
   public List<Book> getAll() {
      return mongoTemplate.findAll(Book.class);
   }
   
   public void delete(Book book) {
      mongoTemplate.remove(book);
   }
}
And a typical class to place in the service layer:
package com.noushin.spring.monspr.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.noushin.spring.monspr.dao.BookRepositroy;
import com.noushin.spring.monspr.model.Book;

/**
 * This class handles any business logic related to handling books.
 * 
 * @author nbashir
 *
 */
@Component
public class BookService {

   @Autowired 
   BookRepositroy bookRepo;
   
   public void save(Book book) {
      bookRepo.save(book);
   }
   
   public void save(List<Book> books) {
      bookRepo.save(books);
   }

   public Book get(String id) {
      return bookRepo.get(id);
   }
   
   public List<Book> getAll() {
      return bookRepo.getAll();
   }
   
   public void delete(Book book) {
      bookRepo.delete(book);
   }
}
And last, but not least, a class to load Spring context and do some basic data access operations in regards to our books example:
package com.noushin.spring.monspr;

import java.util.ArrayList;
import java.util.UUID;

import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.noushin.spring.monspr.model.Book;
import com.noushin.spring.monspr.service.BookService;

/**
 * Main class to demonstrate accessing MongoDB with Spring Data and MongoTemplate.
 * 
 * @author nbashir
 *
 */
public class MongoDbMain {

   final static Logger logger = Logger.getLogger(MongoDbMain.class);

   public static void main(String[] args) {
      try {
         ApplicationContext ctx = new ClassPathXmlApplicationContext("application-context.xml");
         if (ctx != null) {
            logger.info(ctx.getApplicationName() + "MongoDbMain successfuly started. ");
            BookService service = ctx.getBean(BookService.class);
                       
            Book book;
            book = new Book(UUID.randomUUID().toString(), "Ragtime", "E.L. Doctrow");
            service.save(book);
            book = new Book(UUID.randomUUID().toString(), "The March", "E.L. Doctrow");
            service.save(book);
            book = new Book(UUID.randomUUID().toString(), "Andrew's Brain", "E.L. Doctrow");
            service.save(book);
            book = new Book(UUID.randomUUID().toString(), "Wild: From Lost to Found on the Pacific Crest Trail ",
                  "Cheryl Strayed");
            service.save(book);

            ArrayList<Book> books = (ArrayList<Book>) service.getAll();
            for (int i = 0; i < books.size(); i++)
               System.out.println("Book " + i + " : " + books.get(i).getId() + " - " + books.get(i).getName());
            
            for (int i = 0; i < books.size(); i++)
               service.delete(books.get(i));
      
            ArrayList<Book> deltedBooks =  (ArrayList<Book>) service.getAll();
            if (deltedBooks.size() > 0 ) {
               for (int i = 0; i < deltedBooks.size(); i++)
                  System.out.println("Book " + i + " : " + deltedBooks.get(i).getId() + " - " + deltedBooks.get(i).getName());
            }
            else
               System.out.println("All book are successfully removed.");

         }
      }
      catch (Exception ex) {
         logger.error("MongoDbMain encountered an error and ended.", ex);
      }
   }
}
Now that we have the basic Java code in place, we need to configure our app, so it knows how to access the datastore. As with any other Spring application, configuration details are in application-context.xml.
<?xml version="1.0" encoding="UTF-8"?>
<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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- Activate annotation configured components -->
    <context:annotation-config />

    <!-- Scan components for annotations within the configured package -->
    <context:component-scan base-package="com.noushin.spring.monspr" />

    <!-- Mongo config -->
    <!-- Factory bean that creates the Mongo instance -->

    <bean id="mongo" class="org.springframework.data.mongodb.core.MongoFactoryBean">
        <property name="host" value="[your-mongodb-host]" />
    </bean>

    <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg name="mongo" ref="mongo" />
        <constructor-arg name="databaseName" value="nbdb" />
    </bean>
</beans>
In the above configuration, my app will be connecting to "nbdb" database, with MongoDB running on a server specified by "host" property of MongoFactoryBean.

To complete this example, here is my pom.xml where application dependecies are listed:
<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>

  <groupId>com.noushin.spring</groupId>
  <artifactId>monspr</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
  <name>monspr</name>
  <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <junit.version>4.11</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <spring.version>3.2.4.RELEASE</spring.version>
        <spring.data.version>1.3.4.RELEASE</spring.data.version>
    </properties>

    <dependencies>
        <!-- Test -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        
        <!-- Logging -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
            
        <!-- Spring Data -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-mongodb</artifactId>
            <version>${spring.data.version}</version>
        </dependency>
        
        <!-- Spring Context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
    </dependencies>
</project>

MongoDB Installation on Linux

To install it on CentOS, run the following commands as root or using sudo.
vi /etc/yum.repos.d/mongodb.repo
Add:
[mongodb]
name=MongoDB Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64/
gpgcheck=0
enabled=1
Run:
yum install mongo-10gen mongo-10gen-server
Start mongod as a service:
service mongod start
Stop mongod service:
service mongod stop
View MongoDB log file:
tail -f /var/log/mongo/mongod.log
View MongoDB configuration:
more /etc/mongod.conf
View mongod script:
more /etc/rc.d/init.d/mongod
To access MongoDB admin console:
http://[mongodb-host]:28017
To access MongoDB from the command line, login to the server running it:
mongo
show dbs (list of existing databases)
use [db-name] (ex: use nbdb; if it doesn't exist, create it)
show collections (list of document collections)
db.[collection-name].find() (list the contents of a collection)
exit
On MacOS:

brew update
brew install mongodb
sudo mkdir -p /data/db
sudo mongod

Sunday, February 23, 2014

Spring & JdbcTemplate

In this blog, I will show you how to use Spring JdbcTemplate to access a PostgreSQL database. I am assuming you are already familiar with DAOs, JDBC, Spring, Maven, SQL, and PostgreSQL.

1. Assuming you already have a table in your database to access, create a DAO class. Here is an example:

package com.noushin.spring.db;

import java.util.UUID;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

/**
 * This class handles accessing data in a PostgreSQL database.
 * 
 * @author nbashir
 *
 */
@Repository
public class MyDao {

   private JdbcTemplate jdbcTemplate;

   @Autowired
   public void setDataSource(DataSource dataSource) {
       this.jdbcTemplate = new JdbcTemplate(dataSource);
   }

   public UUID getId() {
      String sql = "SELECT id FROM my_schema.my_table WHERE my_ip_address=inet('10.1.1.1')";
      UUID id = jdbcTemplate.queryForObject(sql, UUID.class);
      return id;
   }
}

2. Create a class to test MyDao.
package com.noushin.spring.db;

import java.util.UUID;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MyDaoTest {

   @Autowired
   MyDao myDao;
   
   @Test
   public void testGetId() {
      UUID id = myDao.getd();
      System.out.println("Id: " + id);
   }
}
3. Create a test configuration file in ~/workspace/db/src/test/resources/com/noushin/spring/db called MyDaoTest-context.xml.
<?xml version="1.0" encoding="UTF-8"?>
<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"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package="com.noushin.spring.db" />
    <context:property-placeholder location="jdbc.properties" />
    
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>
</beans>
4. Add jdbc.properties to ~/workspace/db/src/test/resources
jdbc.driverClassName=org.postgresql.Driver
jdbc.url=jdbc:postgresql://my.db.server:my.port/my.db.name
jdbc.username=my.username
jdbc.password=my.password
5. And last but not least, here is your pom file.
<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>
    <groupId>com.noushin.spring</groupId>
    <artifactId>db</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>db</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <commons-dbcp.version>1.2.2</commons-dbcp.version>
        <junit.version>4.10</junit.version>
        <postgres.version>9.1-901.jdbc4</postgres.version>
        <spring.version>3.2.4.RELEASE</spring.version>
    </properties>

    <dependencies>
        <!-- Test -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- Spring JDBC -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>${commons-dbcp.version}</version>
        </dependency>
        <!-- Postgres Driver -->
        <dependency>
            <groupId>postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>${postgres.version}</version>
        </dependency>
    </dependencies>

</project>
6. Populate your database with some data, and you should be able to run your test case.