Monday 9 December 2019

Spring boot: MongoDB: Create Custom type converter

Spring provides default type mapping between Java and MongoDB documents. MongoDB drivers has native support to map java primitive types to a mogoDB document types. Apart from tis, Spring Data MongoDB provides built-in in converters to map LocalDate, DateTime etc.,

If you want to map a custom type or types that do not have build-in converters, you can create a Custom Type converter by implementing Converter interface.

Let’s write a converter to convert ZonedDateTime to Date and vice versa.

Step 1: Create a reading converter that converts a Date to ZonedDateTime.
@Component
@ReadingConverter
public class ZonedDateTimeReadConverter implements Converter<Date, ZonedDateTime> {

 @Override
 public ZonedDateTime convert(Date date) {
  System.out.println("Reading Converter called");
  return date.toInstant().atZone(ZoneOffset.UTC);
 }

} 

Step 2: Create a writing converter that converts ZonedDateTime to Date.
@Component
@WritingConverter
public class ZonedDateTimeWriteConverter implements Converter<ZonedDateTime, Date> {

 @Override
 public Date convert(ZonedDateTime zonedDateTime) {
  System.out.println("Writing Converter called");
  return Date.from(zonedDateTime.toInstant());
 }

}

Step 3: Create an instance of MongoCustomConversions and register the converters ZonedDateTimeReadConverter, ZonedDateTimeWriteConverter.
@Bean
public MongoCustomConversions customConversions() {
 List<Converter<?, ?>> converters = new ArrayList<>();
 converters.add(new ZonedDateTimeReadConverter());
 converters.add(new ZonedDateTimeWriteConverter());
 return new MongoCustomConversions(converters);
}

That’s it you are done.

Find the below working application.

Employee.java    
package com.sample.app.entity;

import java.time.ZonedDateTime;

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

@Document("employees")
public class Employee {
 @Id
 private String id;

 @Field("employee_first_name")
 @Indexed(name = "emp_first_name", direction = IndexDirection.ASCENDING)
 private String firstName;

 @Field("employee_last_name")
 private String lastName;

 @Transient
 private String fullName;

 private ZonedDateTime dateOfBirth;

 public Employee() {
 }

 public Employee(String firstName, String lastName, ZonedDateTime dateOfBirth) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.dateOfBirth = dateOfBirth;
 }

 public String getId() {
  return id;
 }

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

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public String getFullName() {
  return this.firstName + "," + this.lastName;
 }

 public void setFullName(String fullName) {
  this.fullName = fullName;
 }

 public ZonedDateTime getDateOfBirth() {
  return dateOfBirth;
 }

 public void setDateOfBirth(ZonedDateTime dateOfBirth) {
  this.dateOfBirth = dateOfBirth;
 }

 @Override
 public String toString() {
  StringBuilder builder = new StringBuilder();
  builder.append("Employee [id=").append(id).append(", firstName=").append(firstName).append(", lastName=")
    .append(lastName).append(", fullName=").append(fullName).append(", dateOfBirth=").append(dateOfBirth)
    .append("]");
  return builder.toString();
 }

}

EmployeeRepository.java
package com.sample.app.repository;

import java.util.List;
import java.util.stream.Stream;

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;

import com.sample.app.entity.Employee;

@Repository
public interface EmployeeRepository extends MongoRepository<Employee, String> {
 List<Employee> findByFirstName(String firstName);
 
 List<Employee> findByLastName(String lastName);
 
 List<Employee> findByFirstNameAndLastName(String firstName, String lastName);
 
 @Query("{'firstName' : ?0}")
 Stream<Employee> findAllByCustomQueryAndStream(String firstName);
}

ZonedDateTimeReadConverter.java
package com.sample.app.converter;

import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Date;

import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.stereotype.Component;

@Component
@ReadingConverter
public class ZonedDateTimeReadConverter implements Converter<Date, ZonedDateTime> {

 @Override
 public ZonedDateTime convert(Date date) {
  System.out.println("Reading Converter called");
  return date.toInstant().atZone(ZoneOffset.UTC);
 }

}

ZonedDateTimeWriteConverter.java
package com.sample.app.converter;

import java.time.ZonedDateTime;
import java.util.Date;

import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.stereotype.Component;

@Component
@WritingConverter
public class ZonedDateTimeWriteConverter implements Converter<ZonedDateTime, Date> {

 @Override
 public Date convert(ZonedDateTime zonedDateTime) {
  System.out.println("Writing Converter called");
  return Date.from(zonedDateTime.toInstant());
 }

}

application.properties
spring.data.mongodb.database=myorg
spring.data.mongodb.port=27017
spring.data.mongodb.host=localhost

logging.level.org.springframework.data=WARN

pom.xml
<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>springDataMongo</groupId>
 <artifactId>springDataMongo</artifactId>
 <version>1</version>

 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.1.6.RELEASE</version>
 </parent>

 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-mongodb</artifactId>
  </dependency>
 </dependencies>
</project>

App.java
package com.sample.app;

import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;

import com.mongodb.MongoClient;
import com.sample.app.converter.ZonedDateTimeReadConverter;
import com.sample.app.converter.ZonedDateTimeWriteConverter;
import com.sample.app.entity.Employee;
import com.sample.app.repository.EmployeeRepository;

@SpringBootApplication
public class App {
 @Value("${spring.data.mongodb.database}")
 private String database;

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

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

 @Autowired
 private EmployeeRepository empRepository;

 public static void main(String[] args) {

  SpringApplication.run(App.class, args);
 }

 public void dropPreviousData() {
  MongoClient mongoClient = new MongoClient(host, port);

  MongoOperations mongoOps = new MongoTemplate(mongoClient, database);

  mongoOps.dropCollection("employees");
 }

 @Bean
 public CommandLineRunner demo() {
  return (args) -> {

   dropPreviousData();

   ZonedDateTime dateOfBirth1 = ZonedDateTime.of(1965, 5, 15, 12, 5, 43, 1, ZoneOffset.UTC);
   ZonedDateTime dateOfBirth2 = ZonedDateTime.of(1975, 6, 16, 13, 6, 44, 2, ZoneOffset.UTC);
   ZonedDateTime dateOfBirth3 = ZonedDateTime.of(1985, 7, 17, 14, 7, 45, 3, ZoneOffset.UTC);
   ZonedDateTime dateOfBirth4 = ZonedDateTime.of(1995, 8, 18, 15, 8, 46, 4, ZoneOffset.UTC);

   Employee emp1 = new Employee("Phalgun", "Garimella", dateOfBirth1);
   Employee emp2 = new Employee("Sankalp", "Dubey", dateOfBirth2);
   Employee emp3 = new Employee("Arpan", "Garimella", dateOfBirth3);
   Employee emp4 = new Employee("Phalgun", "Dubey", dateOfBirth4);

   empRepository.saveAll(Arrays.asList(emp1, emp2, emp3, emp4));

   List<Employee> emps = empRepository.findAll();
   emps.forEach(System.out::println);

  };
 }

 @Bean
 public MongoCustomConversions customConversions() {
  List<Converter<?, ?>> converters = new ArrayList<>();
  converters.add(new ZonedDateTimeReadConverter());
  converters.add(new ZonedDateTimeWriteConverter());
  return new MongoCustomConversions(converters);
 }
}

Total project structure looks like below.


Run App.java, you can see below messages in console.
Writing Converter called
Writing Converter called
Writing Converter called
Writing Converter called
Reading Converter called
Reading Converter called
Reading Converter called
Reading Converter called
Employee [id=5d54c216e4c7d861d5fb57c2, firstName=Phalgun, lastName=Garimella, fullName=null, dateOfBirth=1965-05-15T12:05:43Z]
Employee [id=5d54c216e4c7d861d5fb57c3, firstName=Sankalp, lastName=Dubey, fullName=null, dateOfBirth=1975-06-16T13:06:44Z]
Employee [id=5d54c216e4c7d861d5fb57c4, firstName=Arpan, lastName=Garimella, fullName=null, dateOfBirth=1985-07-17T14:07:45Z]
Employee [id=5d54c216e4c7d861d5fb57c5, firstName=Phalgun, lastName=Dubey, fullName=null, dateOfBirth=1995-08-18T15:08:46Z]

You can download complete working application from this link.

Previous                                                    Next                                                    Home

No comments:

Post a Comment