Editing list using spring JSP input form tag

I recently had to allow the user to edit a list of Strings and was concerned with having to keep track of which Strings were being edited. Spring has made this simple and very clean.

On Form.java, there is a

List<String> ipList;

Within the JSP you add a forEach with an input tag as follows:

<c:forEach var="ip" items="${form.ipList}" varStatus="status">
   <div style="clear: both">
      <form:input path="ipList[${status.index}]"/>
   </div>
</c:forEach>
Advertisements

Spring Data Repository for MongoDB

First, I have to mention that I am highly impressed with almost everything I find in Spring. So I should not have been so surprised about how amazing Spring Data is and it’s integration with Mongo. Now to the interesting stuff.

I have multiple databases with multiple collections all on the same mongo server. I could not find an example anywhere that accomplished this. Here is how I did it:

To begin, you have to add one dependency. I use maven, so here is the maven dependency:

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-mongodb</artifactId>
            <version>1.0.0.RELEASE</version>
        </dependency>

Now, you need to configure your mongo connection like usual. I like to just put it in the application-context.xml:

<mongo:mongo id="mongoConnection" host="my.mongo.server" port="12345" write-concern="1"/>

One thing to mention here is write-concern. It defaults to 0 which is basically fire and forget, and it will not return an error if one is thrown. Here is the API page with the available values:
http://api.mongodb.org/java/2.5/com/mongodb/WriteConcern.html

Next, configure a template for each database.

    <bean id="imageTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg ref="mongoConnection"/>
        <constructor-arg name="databaseName" value="imagedatabase"/>
    </bean>

    <bean id="vehicleTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg ref="mongoConnection"/>
        <constructor-arg name="databaseName" value="vehicledatabase"/>
    </bean>

Now, you need to tell Spring where your repositories are so it can inject them. They must all be in the same directory. I tried to have them in different sub-directories, and it did not work correctly. So they are all in the repository directory.

    <mongo:repositories base-package="my.package.repository">
        <mongo:repository id="imageRepository" mongo-template-ref="imageTemplate"/>
        <mongo:repository id="carRepository" mongo-template-ref="vehicleTemplate"/>
        <mongo:repository id="truckRepository" mongo-template-ref="vehicleTemplate"/>
    </mongo:repositories>

Each repository is an Interface and is written as follows (yes, you can leave them blank):

@Repository
public interface ImageRepository extends MongoRepository<Image, String> {
}
@Repository
public interface TruckRepository extends MongoRepository<Truck, String> {
}

NOTE: If you have a custom query, here is some crazy magic for you. Just add a method to the interface, spring knows how to interpret it! Here is an example:

@Repository
public interface CarRepository extends MongoRepository<Car, String> {
    Car findByModelAndYearAndLicensePlate(String model, String year, String licensePlate);
}

Here is a table with the expected keywords from the Spring documentation:

Keyword Sample Logical Result
GreaterThan findByAgeGreaterThan(int age) {“age” : {“$gt” : age}}
LessThan findByAgeLessThan(int age) {“age” : {“$lt” : age}}
Between findByAgeBetween(int from, int to) {“age” : {“$gt” : from, “$lt” : to}}
IsNotNull, NotNull findByFirstnameNotNull() {“age” : {“$ne” : null}}
IsNull, Null findByFirstnameNull() {“age” : null}
Like findByFirstnameLike(String name) {“age” : age} ( age as regex)
(No keyword) findByFirstname(String name) {“age” : name}
Not findByFirstnameNot(String name) {“age” : {“$ne” : name}}

Here is one of the java objects:

@Document
public class Image {

    @Id
    private String id;

    private Date createTime;
    private Date updateTime;

    @Field(value = "fName")
    private String fileName;
    private byte[] imageData;

    //Getters and setters

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
}

You do not have to annotate the POJOs with @Document, but it helps with efficiency because it allows the classpath scanner to find and pre-process your domain objects to extract the necessary metadata. Visit this blog for more details: http://krams915.blogspot.com/2011/04/spring-data-mongodb-revision-for-100m2.html

The @Id annotation will tell spring to make that property an ObjectId and give it the _id name which is a Mongo standard. So you can make it whatever type and name you want in java, but it will convert it back and forth between java and mongo.

The @Field annotation is used if you want to save the name differently in mongo

Now, here is one of the really cool things about Spring Data Mongo. The name of the POJO is the collection! Image.java will be saved to the image collection within the imagedb database.

Here is how you can find, insert, and delete records:

@Service
public class ImageService {

    @Autowired
    private ImageRepository imageRepository;

    public Image findById(String id) {
        return imageRepository.findOne(id);
    }

    public List getList() {
        return imageRepository.findAll();
    }

    public Image insert(Image image) {
        //Make sure id is null, so mongo will generate an id, if it is not null, then mongo will use whatever you set it to, even an empty string
        image.setId(null);
        
        return imageRepository.save(image);
    }

    public Image update(Image image) {
        image.setUpdateTime(new Date());
        return imageRepository.save(image);
    }

    public void delete(String id) {
        imageRepository.delete(id);
    }
}

That is all you need. Finally, we do not have to write basic data access code. I love it when code is smart!