CodecConfigurationException Can't find a codec for DateTime In MongoDB - mongodb

I'm using Mongodb with Spring Boot (1.4.4.RELEASE), I store an object in a MongoDB database and am getting a CodecConfigurationException When i try to get Object with AggregationOperation.
My MongoDbConfig is
#Configuration
public class MongoDbConfig {
#Value("${mongodb_host}")
private String host;
#Value("${mongodb_port}")
private String port;
#Value("${mongodb_name}")
private String dataBase;
#Value("${mongodb_username}")
private String userName;
#Value("${mongodb_password}")
private String password;
public #Bean MongoClient mongoClient() {
return new MongoClient(new ServerAddress("127.0.0.1", 27017), new ArrayList<MongoCredential>() {
{
add(MongoCredential.createCredential(userName, dataBase,password.toCharArray()));
}
});
}
public #Bean MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(mongoClient(), dataBase);
}
#Bean
public MongoTemplate mongoTemplate() throws Exception {
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory());
return mongoTemplate;
}
}
My Document is
#Document(collection = "FuelBook")
public class FuelBook implements Serializable {
private String orgId;
private String branchId;
private String vehicleId;
private String driverName;
private DateTime date;
private boolean isActive = true;
private Double kmsReading;
private float costOfFuel;
private String typeOfFuel;
//getter and setter
}
I am creating Document using below code
public String addFuelBookDetails(AddFuelBookDetailsDTO request,
String orgId, String branchId, String vehicleId) {
try{
if(!mongoTemplate.collectionExists(FuelBook.class)){
mongoTemplate.createCollection(FuelBook.class);
}
FuelBook fuelBook = new FuelBook();
fuelBook.setOrgId(orgId);
fuelBook.setBranchId(branchId);
fuelBook.setVehicleId(vehicleId);
fuelBook.setDriverName(request.getDriverName());
fuelBook.setDate(TimeConversionHelper.getJodaDate(request.getDate(),
"yyyy-MM-dd"));
fuelBook.setCostOfFuel(Float.parseFloat((request.getCostOfFuel())));
fuelBook.setKmsReading(Double.parseDouble(request.getKmsReading()));
fuelBook.setTypeOfFuel(request.getTypeOfFuel());
mongoTemplate.insert(fuelBook, "FuelBook");
return StringConstants.SUCCESS;
} catch(Exception e) {
return null;
}
}
And this code to create String To DateTime
public static DateTime getJodaDate(String dateString, String format) {
DateTime dateTime = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat(format);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = (Date) sdf.parse(dateString);
long timeInMillis = date.getTime();
dateTime = new DateTime(timeInMillis);
} catch (ParseException e1) {
e1.printStackTrace();
}
return dateTime;
}
Using below code try to get All FuelBooks
AggregationOperation org = Aggregation.match(Criteria.where("orgId").is(orgId));
AggregationOperation branch = Aggregation.match(Criteria.where("branchId").is(branchId));
AggregationOperation vehicle = Aggregation.match(Criteria.where("vehicleId").is(vehicleId));
DateTime jodaStartTime = TimeConversionHelper.getJodaDate(request.getStartTime(), "yyyy-MM-dd");
DateTime jodaEndTime = TimeConversionHelper.getJodaDate(request.getEndTime(), "yyyy-MM-dd");
AggregationOperation startTime = Aggregation.match(Criteria.where("date").gte(jodaStartTime));
AggregationOperation endTime = Aggregation.match(Criteria.where("date").lte(jodaEndTime));
AggregationOperation offsetAggregation=Aggregation.skip(offset);
AggregationOperation limitAggregation=Aggregation.limit(limit);
Aggregation aggregation = Aggregation.newAggregation(org, branch, vehicle, startTime, endTime,offsetAggregation,limitAggregation);
AggregationResults<FuelBook> result = mongoTemplate.aggregate(aggregation, "FuelBook" ,FuelBook.class);
if(result!=null && result.getMappedResults()!=null && !result.getMappedResults().isEmpty()) {
List<FuelBook> fuelLists = result.getMappedResults();
However, I'm running into this error message:
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class org.joda.time.DateTime.
at org.bson.codecs.configuration.CodecCache.getOrThrow(CodecCache.java:46)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:37)
at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:210)
at com.mongodb.DBObjectCodec.encodeMap(DBObjectCodec.java:220)
at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:196)
at com.mongodb.DBObjectCodec.encodeMap(DBObjectCodec.java:220)
at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:196)
at com.mongodb.DBObjectCodec.encodeMap(DBObjectCodec.java:220)
at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:196)
at com.mongodb.DBObjectCodec.encodeIterable(DBObjectCodec.java:269)
at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:198)
at com.mongodb.DBObjectCodec.encode(DBObjectCodec.java:128)
at com.mongodb.DBObjectCodec.encode(DBObjectCodec.java:61)
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63)
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29)
at com.mongodb.connection.RequestMessage.addDocument(RequestMessage.java:253)
at com.mongodb.connection.RequestMessage.addDocument(RequestMessage.java:205)
at com.mongodb.connection.CommandMessage.encodeMessageBodyWithMetadata(CommandMessage.java:75)
at com.mongodb.connection.RequestMessage.encodeWithMetadata(RequestMessage.java:160)
at com.mongodb.connection.CommandProtocol.sendMessage(CommandProtocol.java:184)
at com.mongodb.connection.CommandProtocol.execute(CommandProtocol.java:108)
at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:159)
at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:286)
at com.mongodb.connection.DefaultServerConnection.command(DefaultServerConnection.java:173)
at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:215)
at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:186)
at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:178)
at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:91)
at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:84)
at com.mongodb.operation.CommandReadOperation.execute(CommandReadOperation.java:55)
at com.mongodb.Mongo.execute(Mongo.java:773)
at com.mongodb.Mongo$2.execute(Mongo.java:760)
at com.mongodb.DB.executeCommand(DB.java:653)
at com.mongodb.DB.command(DB.java:423)
at com.mongodb.DB.command(DB.java:439)
at com.mongodb.DB.command(DB.java:394)
at org.springframework.data.mongodb.core.MongoTemplate$3.doInDB(MongoTemplate.java:390)
at org.springframework.data.mongodb.core.MongoTemplate$3.doInDB(MongoTemplate.java:388)
at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:446)
at org.springframework.data.mongodb.core.MongoTemplate.executeCommand(MongoTemplate.java:388)
at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:1556)
at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:1491)
at com.icarat.eshiksha.mongodb.dao.impl.FuelBookDAOImpl.getFuelBookDetails(FuelBookDAOImpl.java:153)
at com.icarat.eshiksha.rest.controller.FuelBookController.getFuelBookDetails(FuelBookController.java:132)
Can anyone can provide some insight on why this is happening? Thank you

I found solution from this link
I change my mongoClient in MongoDbConfig like
public #Bean MongoClient mongoClient() {
BSON.addEncodingHook(DateTime.class, new JodaTimeTransformer());
BSON.addDecodingHook(Date.class, new JodaTimeTransformer());
return new MongoClient(new ServerAddress("127.0.0.1", 27017), new ArrayList<MongoCredential>() {
{
add(MongoCredential.createCredential(userName, dataBase,password.toCharArray()));
}
});
}
And i just add JodaTimeTransformer class into my project classPath

Related

Springboot #Transactional annotation not working with datasource

I am trying to configure springBoot to have 2 datasources ('pims' & 'powwow') by following this tutorial.
It kind of works, but only for the #Primary datasource. How do I get it to work for the other datasource?
application.properties
# pims datasource
spring.datasource1.driver-class-name=org.postgresql.Driver
spring.datasource1.jdbc-url=jdbc:postgresql://localhost:5432/pims
spring.datasource1.username=postgres
spring.datasource1.password=postgres
# powwow datasource
spring.datasource2.driver-class-name=org.postgresql.Driver
spring.datasource2.jdbc-url=jdbc:postgresql://localhost:5432/powwow
spring.datasource2.username=postgres
spring.datasource2.password=postgres
PersistencePimsAutoConfiguration.java
#Configuration
#PropertySource({"classpath:application.properties"})
#EnableJpaRepositories(
basePackages = {"com.clubtravel.powwow.dao.pims", "com.clubtravel.powwow.repositories.pims"},
entityManagerFactoryRef = "pimsEntityManager",
transactionManagerRef = "pimsTransactionManager")
public class PersistencePimsAutoConfiguration {
private Logger logger = LogManager.getLogger(PersistencePimsAutoConfiguration.class);
#Value("${spring.datasource1.jdbc-url}")
private String url;
#Value("${spring.datasource1.username}")
private String username;
#Value("${spring.jpa.hibernate.ddl-auto}")
private String hbm2ddl;
#Value("${spring.jpa.database-platform}")
private String platform;
#Value("${spring.jpa.properties.hibernate.dialect}")
private String dialect;
#Value("${spring.profiles.active}")
private String profile;
#Bean
#ConfigurationProperties(prefix="spring.datasource1")
public DataSource pimsDataSource() {
return DataSourceBuilder.create().build();
}
//#Bean(name = "pimsEntityManager")
#Bean
public LocalContainerEntityManagerFactoryBean pimsEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(pimsDataSource());
em.setPackagesToScan(new String[] {"com.clubtravel.powwow.entities.pims"});
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", hbm2ddl);
properties.put("hibernate.dialect", dialect);
em.setJpaPropertyMap(properties);
String host = null;
try {
host = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
logger.info("Setting spring.datasource1 (pims): hibernate.hbm2ddl.auto='"+hbm2ddl+"', platform='"+platform+"', url='"+url+"', username='"+username+"', host='"+host+"', profile='"+profile+"'.");
return em;
}
#Bean
public PlatformTransactionManager pimsTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(pimsEntityManager().getObject());
return transactionManager;
}
}
PersistencePowwowAutoConfiguration.java
#Configuration
#PropertySource({"classpath:application.properties"})
#EnableJpaRepositories(
basePackages = {"com.clubtravel.powwow.dao.powwow", "com.clubtravel.powwow.repositories.powwow"},
entityManagerFactoryRef = "powwowEntityManager",
transactionManagerRef = "powwowTransactionManager")
public class PersistencePowwowAutoConfiguration {
private Logger logger = LogManager.getLogger(PersistencePowwowAutoConfiguration.class);
#Value("${spring.datasource2.jdbc-url}")
private String url;
#Value("${spring.datasource2.username}")
private String username;
#Value("${spring.jpa.hibernate.ddl-auto}")
private String hbm2ddl;
#Value("${spring.jpa.database-platform}")
private String platform;
#Value("${spring.jpa.properties.hibernate.dialect}")
private String dialect;
#Value("${spring.profiles.active}")
private String profile;
#Bean
#Primary
#ConfigurationProperties(prefix="spring.datasource2")
public DataSource powwowDataSource() {
return DataSourceBuilder.create().build();
}
//#Bean(name = "powwowEntityManager")
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean powwowEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(powwowDataSource());
em.setPackagesToScan(new String[] { "com.clubtravel.powwow.entities.powwow" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", hbm2ddl);
properties.put("hibernate.dialect", dialect);
em.setJpaPropertyMap(properties);
String host = null;
try {
host = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
logger.info("Setting spring.datasource2 (powwow): hibernate.hbm2ddl.auto='"+hbm2ddl+"', platform='"+platform+"', url='"+url+"', username='"+username+"', host='"+host+"', profile='"+profile+"'.");
return em;
}
#Bean
#Primary
public PlatformTransactionManager powwowTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(powwowEntityManager().getObject());
return transactionManager;
}
}
I thought that when I used the secondary datasource, if I added the #Trannsactional annotation, it would work.
e.g.
MerchantsDao.java
#Transactional("pimsTransactionManager")
public List<MerchantsEntity> findByCustomerNumber(String customerNumber) {
Query qry = entityManager.createNamedQuery("Merchants.findByCustomerNo");
qry.setParameter(1, customerNumber);
List<MerchantsEntity> entities = (List<MerchantsEntity>) qry.getResultList();
return entities;
}
However, it says it cannot find the query:
java.lang.IllegalArgumentException: No query defined for that name
[Merchants.findByCustomerNo]
I also try adding #Transactional("pimsTransactionManager") at the DAO's class level, but still get the same error above. If I switch the #Primary annotation to the 'pimsTransactionManager' datasource beans, then that datasources works but the 'powwowTransactionManager' datasource gets this error.
So it looks like even though I define two datasources, it only works with the primary one.

MongoRepository Save method does not insert in database

I have created a SpringBoot project with Jhipster. The database I am using is MongoDB.
In the application-dev.yml I have the following configuration:
data:
mongodb:
uri: mongodb://<user>:<pass>#<ip>:<port>
database: gateway
The user, password, ip Address, and port, in my application-dev are real values.
The DatabaseConfiguration.java is:
#Configuration
#EnableMongoRepositories("es.second.cdti.repository")
#Profile("!" + JHipsterConstants.SPRING_PROFILE_CLOUD)
#Import(value = MongoAutoConfiguration.class)
#EnableMongoAuditing(auditorAwareRef = "springSecurityAuditorAware")
public class DatabaseConfiguration {
private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class);
#Bean
public ValidatingMongoEventListener validatingMongoEventListener() {
return new ValidatingMongoEventListener(validator());
}
#Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
#Bean
public MongoCustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(DateToZonedDateTimeConverter.INSTANCE);
converters.add(ZonedDateTimeToDateConverter.INSTANCE);
return new MongoCustomConversions(converters);
}
#Bean
public Mongobee mongobee(MongoClient mongoClient, MongoTemplate mongoTemplate, MongoProperties mongoProperties) {
log.debug("Configuring Mongobee");
Mongobee mongobee = new Mongobee(mongoClient);
mongobee.setDbName(mongoProperties.getMongoClientDatabase());
mongobee.setMongoTemplate(mongoTemplate);
// package to scan for migrations
mongobee.setChangeLogsScanPackage("es.second.cdti.config.dbmigrations");
mongobee.setEnabled(true);
return mongobee;
}}
The CloudDatabaseConfiguration is:
#Configuration
#EnableMongoRepositories("es.second.cdti.repository")
#Profile(JHipsterConstants.SPRING_PROFILE_CLOUD)
public class CloudDatabaseConfiguration extends AbstractCloudConfig {
private final Logger log = LoggerFactory.getLogger(CloudDatabaseConfiguration.class);
#Bean
public MongoDbFactory mongoFactory() {
return connectionFactory().mongoDbFactory();
}
#Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
#Bean
public ValidatingMongoEventListener validatingMongoEventListener() {
return new ValidatingMongoEventListener(validator());
}
#Bean
public MongoCustomConversions customConversions() {
List<Converter<?, ?>> converterList = new ArrayList<>();
converterList.add(DateToZonedDateTimeConverter.INSTANCE);
converterList.add(ZonedDateTimeToDateConverter.INSTANCE);
converterList.add(DurationToLongConverter.INSTANCE);
return new MongoCustomConversions(converterList);
}
#Bean
public Mongobee mongobee(MongoDbFactory mongoDbFactory, MongoTemplate mongoTemplate, Cloud cloud) {
log.debug("Configuring Cloud Mongobee");
List<ServiceInfo> matchingServiceInfos = cloud.getServiceInfos(MongoDbFactory.class);
if (matchingServiceInfos.size() != 1) {
throw new CloudException("No unique service matching MongoDbFactory found. Expected 1, found "
+ matchingServiceInfos.size());
}
MongoServiceInfo info = (MongoServiceInfo) matchingServiceInfos.get(0);
Mongobee mongobee = new Mongobee(info.getUri());
mongobee.setDbName(mongoDbFactory.getDb().getName());
mongobee.setMongoTemplate(mongoTemplate);
// package to scan for migrations
mongobee.setChangeLogsScanPackage("es.second.cdti.config.dbmigrations");
mongobee.setEnabled(true);
return mongobee;
}
}
The cdtiApp.java is:
#SpringBootApplication
#EnableConfigurationProperties({ApplicationProperties.class})
public class CdtiApp implements InitializingBean{
private static final Logger log = LoggerFactory.getLogger(CdtiApp.class);
private final Environment env;
public CdtiApp(Environment env) {
this.env = env;
}
/**
* Initializes cdti.
* <p>
* Spring profiles can be configured with a program argument --spring.profiles.active=your-active-profile
* <p>
* You can find more information on how profiles work with JHipster on https://www.jhipster.tech/profiles/.
*/
#PostConstruct
public void initApplication() {
Collection<String> activeProfiles = Arrays.asList(env.getActiveProfiles());
if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
log.error("You have misconfigured your application! It should not run " +
"with both the 'dev' and 'prod' profiles at the same time.");
}
if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)) {
log.error("You have misconfigured your application! It should not " +
"run with both the 'dev' and 'cloud' profiles at the same time.");
}
}
/**
* Main method, used to run the application.
*
* #param args the command line arguments.
*/
public static void main(String[] args) {
SpringApplication app = new SpringApplication(CdtiApp.class);
DefaultProfileUtil.addDefaultProfile(app);
Environment env = app.run(args).getEnvironment();
logApplicationStartup(env);
}
private static void logApplicationStartup(Environment env) {
String protocol = "http";
if (env.getProperty("server.ssl.key-store") != null) {
protocol = "https";
}
String serverPort = env.getProperty("server.port");
String contextPath = env.getProperty("server.servlet.context-path");
if (StringUtils.isBlank(contextPath)) {
contextPath = "/";
}
String hostAddress = "localhost";
try {
hostAddress = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.warn("The host name could not be determined, using `localhost` as fallback");
}
log.info("\n----------------------------------------------------------\n\t" +
"Application '{}' is running! Access URLs:\n\t" +
"Local: \t\t{}://localhost:{}{}\n\t" +
"External: \t{}://{}:{}{}\n\t" +
"Profile(s): \t{}\n----------------------------------------------------------",
env.getProperty("spring.application.name"),
protocol,
serverPort,
contextPath,
protocol,
hostAddress,
serverPort,
contextPath,
env.getActiveProfiles());
String configServerStatus = env.getProperty("configserver.status");
if (configServerStatus == null) {
configServerStatus = "Not found or not setup for this application";
}
log.info("\n----------------------------------------------------------\n\t" +
"Config Server: \t{}\n----------------------------------------------------------", configServerStatus);
}
#Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
}
}
The Vehicle entity:
#org.springframework.data.mongodb.core.mapping.Document(collection = "vehicle")
public class Vehicle implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private String id;
#NotNull
#Field("plate")
private String plate;
#NotNull
#Field("registrationDate")
private Instant registrationDate;
#NotNull
#Field("brand")
private String brand;
#NotNull
#Field("model")
private String model;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPlate() {
return plate;
}
public void setPlate(String plate) {
this.plate = plate;
}
public Instant getRegistrationDate() {
return registrationDate;
}
public void setRegistrationDate(Instant registrationDate) {
this.registrationDate = registrationDate;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
The VehicleDTO is:
public class VehicleDTO {
private String id;
private String plate;
private Instant registrationDate;
private String brand;
private String model;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPlate() {
return plate;
}
public void setPlate(String plate) {
this.plate = plate;
}
public Instant getRegistrationDate() {
return registrationDate;
}
public void setRegistrationDate(Instant registrationDate) {
this.registrationDate = registrationDate;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
The VehicleMapper is:
#Mapper(componentModel = "spring")
public interface VehicleMapper{
Vehicle toEntity(VehicleDTO source);
VehicleDTO toDto(Vehicle target);
}
The VehicleResource is:
#RestController
#RequestMapping("/api")
#CrossOrigin(origins = "*", methods = { RequestMethod.GET, RequestMethod.POST })
public class VehicleResource {
private final Logger log = LoggerFactory.getLogger(VehicleResource.class);
#Value("${jhipster.clientApp.name}")
private String applicationName;
#Autowired
private final VehicleService vehicleService;
public VehicleResource(VehicleService vehicleService) {
this.vehicleService = vehicleService;
}
#PostMapping("/vehicle")
#PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")")
public ResponseEntity<Vehicle> createVehicle(#Valid #RequestBody VehicleDTO vehicleDTO) throws URISyntaxException {
log.debug("REST request to save Vehicle : {}", vehicleDTO);
Vehicle newVehicle = vehicleService.createVehicle(vehicleDTO);
return ResponseEntity.created(new URI("/api/vehicle/" + newVehicle.getPlate()))
.headers(HeaderUtil.createAlert(applicationName, "vehicleManagement.created", newVehicle.getPlate()))
.body(newVehicle);
}
}
The VehicleService interface is:
public interface VehicleService {
Vehicle createVehicle(VehicleDTO vehicleDTO);
}
The VehicleServiceImpl is:
#Service
public class VehicleServiceImpl implements VehicleService{
#Autowired
private final VehicleRepository vehicleRepository;
#Autowired
private final VehicleMapper mapper;
public VehicleServiceImpl(VehicleRepository vehicleRepository, VehicleMapper mapper) {
this.vehicleRepository = vehicleRepository;
this.mapper = mapper;
}
private final Logger log = LoggerFactory.getLogger(VehicleServiceImpl.class);
#Override
public Vehicle createVehicle(VehicleDTO vehicleDTO) {
Vehicle vehicle = vehicleRepository.save(mapper.toEntity(vehicleDTO));
log.debug("Created Information for vehicle: {}", vehicle);
return vehicle;
}
}
The VehicleRepository interface is:
/**
* Spring Data MongoDB repository for the {#link Vehicle} entity.
*/
#Repository
public interface VehicleRepository extends MongoRepository<Vehicle, String> {
}
From the Swagger console I access the Vehicle-Resource:
Swagger console
Click on the button and write in the text box the json with the vehicle data:
enter JSON data
As we can see in the following image, the answer is 201. Initially the vehicle was saved with the identifier "id": "60e740935ed5a10e2c2ed19e".
Send request
I access the database to check that the vehicle has been correctly stored in the vehicle table. To my surprise ... there is no vehicle in the vehicle table:
show database
I can make sure that the data in the database application-dev is OK. I don't have any other databases.
I suspect that transactions with the database are not actually being made. This data is somehow stored in memory because if I do a findAllVehicles from Swagger it does return the vehicle.
I have a eureka server running (jhipster-registry) and two microservices that synchronize with it. The Gateway, which acts as a reverse proxy and the Vehiculos microservice. The Swagger console is the gateway, from where I make the request to insert vehicles. Everything seems to work, but as I say in bbdd does not save anything.

How to supply LocalDateTime to a jpa/hibernate query?

I'm building a query in my #RepositoryRestResource
where the query looks like this:
#Query("Select DISTINCT comp from InsuranceCompany comp " +
"LEFT JOIN comp.orders ord " +
"wHERE ord.invoiced = false " +
"and (:date is null or :date >= ord.completionTime)"
)
public Page<InsuranceCompany> method(LocalDateTime date, Pageable pageable);
But it throws the following excpetion
Failed to convert from type [java.lang.String] to type [java.time.LocalDateTime] for value '2020-02-14T15:50:24'
when I call the end point with:
GET /method?date=2020-02-14T15:50:24
Mark it with #DateTimeFormat to have Spring converted it correctly:
public Page<InsuranceCompany> method(#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime date,
Pageable pageable);
Spring by default cannot convert REST parameters to LocalDateTime. You need to provide information on the format of the date, at a parameter level with the #DateTimeFormat annotation, or globally using the DateTimeFormatterRegistrar.
This article explains the two alternatives: https://www.baeldung.com/spring-date-parameters
Option 1: Setting the date/time format globally for all Spring Boot App REST Endpoints
You can configure spring globally to use a certain date / date-time format for your REST endpoints. Suggesting that you use the default Jackson for handling JSON mapping, you can create a configuration class as follows where you set the formats:
#Configuration
public class DateTimeSerializationConfiguration implements Jackson2ObjectMapperBuilderCustomizer {
private static final DateTimeFormatter DATE_FORMATTER = ISO_LOCAL_DATE;
private static final DateTimeFormatter DATE_TIME_FORMATTER = ISO_DATE_TIME;
private static final DateTimeFormatter TIME_FORMATTER = ofPattern("HH:mm");
#Bean
public Formatter<LocalDate> localDateFormatter() {
return new Formatter<LocalDate>() {
#Override
public LocalDate parse(String text, Locale locale) {
return LocalDate.parse(text, DATE_FORMATTER);
}
#Override
public String print(LocalDate object, Locale locale) {
return DATE_FORMATTER.format(object);
}
};
}
#Bean
public Formatter<LocalDateTime> localDateTimeFormatter() {
return new Formatter<LocalDateTime>() {
#Override
public LocalDateTime parse(String text, Locale locale) {
return LocalDateTime.parse(text, DATE_TIME_FORMATTER);
}
#Override
public String print(LocalDateTime object, Locale locale) {
return DATE_TIME_FORMATTER.format(object);
}
};
}
#Bean
public Formatter<LocalTime> localTimeFormatter() {
return new Formatter<LocalTime>() {
#Override
public LocalTime parse(String text, Locale locale) {
return LocalTime.parse(text, TIME_FORMATTER);
}
#Override
public String print(LocalTime object, Locale locale) {
return TIME_FORMATTER.format(object);
}
};
}
#Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
jacksonObjectMapperBuilder.serializers(
new LocalDateSerializer(DATE_FORMATTER),
new LocalDateTimeSerializer(DATE_TIME_FORMATTER),
new LocalTimeSerializer(TIME_FORMATTER));
jacksonObjectMapperBuilder.deserializers(
new LocalDateDeserializer(DATE_FORMATTER),
new LocalDateTimeDeserializer(DATE_TIME_FORMATTER),
new LocalTimeDeserializer(TIME_FORMATTER));
}
}
Then, you can create controller methods like this:
#RestController
public class BookingController {
private final YourService yourService;
#Autowired
public BookingController(YourService yourService) {
this.yourService = yourService;
}
#GetMapping("/your/api/endpoint")
public YourObject yourControllerMethod(#RequestParam LocalDate date, Pageable pageable) {
return yourService.yourServiceMethod(date, pageable);
}
// Or: with LocalDateTime
#GetMapping("/your/api/endpoint")
public YourObject yourControllerMethod(#RequestParam LocalDateTime dateTime, Pageable pageable) {
return yourService.yourServiceMethod(dateTime, pageable);
}
}
Option 2: Setting the date/time format for each REST Endpoint individually
If you prefer to set the format for each endpoint individually, you have to annotate the request parameter with #DateTimeFormat and specify the expected format. The example below shows different examples on how to accomplish this:
#RestController
public class BookingController {
private final YourService yourService;
#Autowired
public BookingController(YourService yourService) {
this.yourService = yourService;
}
#GetMapping("/your/api/endpoint")
public YourObject yourControllerMethod(#RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date, Pageable pageable) {
return yourService.yourServiceMethod(date, pageable);
}
// Or: with LocalDateTime
#GetMapping("/your/api/endpoint")
public YourObject yourControllerMethod(#RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime dateTime, Pageable pageable) {
return yourService.yourServiceMethod(dateTime, pageable);
}
// Or: with your custom pattern
#GetMapping("/your/api/endpoint")
public YourObject yourControllerMethod(#RequestParam #DateTimeFormat(pattern = "dd.MM.yyyy") LocalDate date, Pageable pageable) {
return yourService.yourServiceMethod(date, pageable);
}
}

Spring Data MongoDB Converter not getting registered

I have a setup of multiple MongoDB configuration. Here is the configuration class
#Configuration
#RequiredArgsConstructor
#EnableConfigurationProperties(MongoConfigProperties.class)
public class MultipleMongoConfig {
private static final Logger logger = LoggerFactory.getLogger(MultipleMongoConfig.class);
private final MongoConfigProperties mongoProperties;
#Primary
#Bean(name = "sysdiagMongoTemplate")
public MongoOperations sysdiagMongoTemplate() {
MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(new DefaultDbRefResolver(sysdiagFactory(mongoProperties.getSysdiag())),
new MongoMappingContext());
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(new AggregationResultReadConverter());
mappingMongoConverter.setCustomConversions(new CustomConversions(CustomConversions.StoreConversions.NONE, converters));
mappingMongoConverter.afterPropertiesSet();
boolean canConvert = mappingMongoConverter.getConversionService().canConvert(Document.class, AggregationResult.class);
mappingMongoConverter.afterPropertiesSet();
logger.info("canConvertFromDocumentToAggResult:: " + canConvert); //gives TRUE
return new MongoTemplate(sysdiagFactory(this.mongoProperties.getSysdiag()), mappingMongoConverter);
}
#Bean(name = "monitoringMongoTemplate")
public MongoOperations monitoringMongoTemplate() {
return new MongoTemplate(monitoringFactory(this.mongoProperties.getMonitoring()));
}
public MongoDbFactory sysdiagFactory(final MongoProperties mongo) {
return new SimpleMongoDbFactory(new MongoClient(mongo.getHost(), mongo.getPort()),
mongo.getDatabase());
}
public MongoDbFactory monitoringFactory(final MongoProperties mongo) {
return new SimpleMongoDbFactory(new MongoClient(mongo.getHost(), mongo.getPort()),
mongo.getDatabase());
}
}
Here is the read converter class (I only require reading from MongoDB). We have dynamic keys in the document due to which I need to convert them into a Map
public class AggregationResultReadConverter implements Converter<Document, AggregationResult> {
#Override
public AggregationResult convert(Document source) {
AggregationResult aggregationResult = new AggregationResult();
aggregationResult.setData(new HashMap());
for(Map.Entry<String,Object> entry : source.entrySet()){
if(entry.getKey().matches("[A-Z][A-Z][A-Z]")){
aggregationResult.getData().put(entry.getKey(), entry.getValue());
}
}
return aggregationResult;
}
}
Here is the mapping configuration for one of the MongoDB database
#Configuration
#EnableMongoRepositories(basePackages = {"com.hns.services.restapi.db.mongo.sysdiag.entity", "com.hns.services.restapi.db.mongo.sysdiag.repo"}, mongoTemplateRef = "sysdiagMongoTemplate")
public class SysdiagMongoConfig {
}
And here is the repository interface
#Repository
public interface AggregationResultRepository extends MongoRepository<AggregationResult, ObjectId> {
#Query("{ TIME: {$gte : ?0, $lt: ?1}}")
List<AggregationResult> findInTimeRange(Long startTime, Long endTime);
}
When I query using AggregationResultRepository, I expect the converter code to be executed so that I can convert the fields and put them in the Entity (Document) class object as per the logic. The query is going fine as I saw in the debug logs and I see an output but the converter is not getting called.
The converted is getting registered with the mongo template as the canConvertFromDocumentToAggResult logger gives TRUE. I tried changing the converted from Document -> AggregationResult to DBObject -> AggregationResult but no luck. Not sure what am I missing here.

Execute Stored Procedure in JPA 2.0

I have the following problem and I do not how to solve it.
I have a stored procedure with one parameter ( a date in the format: yyyy-MM-dd ) on my MSSQL Server 2008.
Then I have an #Entity class with a #NamedNativeQuery:
#NamedNativeQuery(name = "my_stored_proc",query = "? = exec EMIR_GUI.get_OTCLite_ACKNACK_Report ?", resultClass = EmirFacade.class)
#Entity
public class EmirFacade {
#Column(name="MessageType", nullable=false)
#Basic(fetch = FetchType.EAGER)
private String mesageType;
My Bean class looks like this:
#PersistenceContext(unitName=Globals.__TWHUNITNAME)
private EntityManager em;
public List<EmirFacade> get_EmirReport(Date date) {
try {
#SuppressWarnings("unchecked")
Query q = em.createNamedQuery("my_stored_proc").setParameter(1, date);
List<EmirFacade> emir_report = q.getResultList();
//List emir_report = q.getResultList();
return emir_report;
} catch (Exception e) {
return Collections.emptyList();
}
}
Now, I always get back the following error message ( it is in german, so I have to translate it as good as I can )
Index "0" is out of range.
I tried nearly everything but I cannot find any way to solve my problem.
Maybe, somebody has a good suggestion for me?
Thank you very much!
JPA 2.0 has no explicit support for stored procedures (JPA 2.1 has).
One workaround is to use native queries (like {CALL APURARCAMPANHASBRINDES.PROC_APURARCAMPANHA(?1, ?2, ?3, ?4, ?5, ?6, ?7)}), but that doesn’t work when the procedure has out-parameters.
Here is a sample implementation that uses Hibernate’s Work interface:
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Types;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.hibernate.Session;
import org.hibernate.jdbc.Work;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
#Service
public class CampanhaBrindesStoredProcedure {
#PersistenceContext
private EntityManager entityManager;
private String mensagem;
private String geroubrinde;
#Transactional
public void apurarCampamha(Long numeroPedido, String codigoFilialNF,
String cgcEntCGCENT, Long numeroSequencia, String integradora) {
try {
MyStoredProc storedProc = new MyStoredProc(numeroPedido, codigoFilialNF,
cgcEntCGCENT, numeroSequencia, integradora);
entityManager.unwrap(Session.class).doWork(storedProc);
setGeroubrinde(storedProc.getGeroubrinde());
setMensagem(storedProc.getMensagem());
} catch (Exception e) {
e.printStackTrace();
}
}
public String getGeroubrinde() {
return geroubrinde;
}
public void setGeroubrinde(String geroubrinde) {
this.geroubrinde = geroubrinde;
}
public String getMensagem() {
return mensagem;
}
public void setMensagem(String mensagem) {
this.mensagem = mensagem;
}
private static final class MyStoredProc implements Work {
private final Long numeroPedido;
private final String codigoFilialNF;
private final String cgcEntCGCENT;
private final Long numeroSequencia;
private final String integradora;
private String mensagem;
private String geroubrinde;
private MyStoredProc(Long numeroPedido, String codigoFilialNF,
String cgcEntCGCENT, Long numeroSequencia, String integradora) {
this.numeroPedido = numeroPedido;
this.codigoFilialNF = codigoFilialNF;
this.cgcEntCGCENT = cgcEntCGCENT;
this.numeroSequencia = numeroSequencia;
this.integradora = integradora;
}
#Override
public void execute(Connection conn) throws SQLException {
try (CallableStatement stmt = conn
.prepareCall("{CALL APURARCAMPANHASBRINDES.PROC_APURARCAMPANHA(?1, ?2, ?3, ?4, ?5, ?6, ?7)}")) {
stmt.setLong(1, numeroPedido);
stmt.setString(2, codigoFilialNF);
stmt.setString(3, cgcEntCGCENT);
stmt.setLong(4, numeroSequencia);
stmt.setString(5, integradora);
stmt.registerOutParameter(6, Types.VARCHAR);
stmt.registerOutParameter(7, Types.VARCHAR);
stmt.executeUpdate();
mensagem = stmt.getString(6);
geroubrinde = stmt.getString(7);
if (stmt.wasNull()) {
geroubrinde = null;
mensagem = null;
}
}
}
public String getMensagem() {
return mensagem;
}
public String getGeroubrinde() {
return geroubrinde;
}
}
}
If you can switch to JPA 2.1 (and I strongly suggest you to do so) you can simply do:
StoredProcedureQuery storedProcedure = em.createStoredProcedureQuery("yourStoredprocedure");
// set parameters
storedProcedure.registerStoredProcedureParameter("parameterName", String.class, ParameterMode.IN);
storedProcedure.setParameter("parameterName", "yourParameter");
// execute stored procedure
storedProcedure.execute();
otherwise it's a bit more convoluted, simply follow this tutorial.