Update an entity with Morphia and its BasicDAO - java

I make a REST webservice with SpringBoot web and the Morphia DAO (to use MongoDB).
As I did when I used Hibernate on MySQL, I would like to use Generic entities, repositories and endpoints so that I just have to set my entities, inherit Repositories and Services and let use the generated CRUD with REST calls.
It almost done, but I encounter a problem with the generic update of my entities with Morphia. Everything I've seen so far talks about manually setting a request with the fields that have to change ; but in the Hibernate way, we just setted the Id field, called persist() and it automatically knowed what changed and applied changes in database.
Here is some code.
BaseEntity.java
package org.beep.server.entity;
import org.mongodb.morphia.annotations.Entity;
#Entity
abstract public class BaseEntity {
public static class JsonViewContext {
public interface Summary {}
public interface Detailed extends Summary{}
}
protected String id;
public void setId(String id) {
this.id = id;
}
}
User.java (one of my final entities)
package org.beep.server.entity;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.*;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
import org.mongodb.morphia.annotations.*;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.ws.rs.FormParam;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
#Entity
#Indexes(
#Index(value="identifier", fields=#Field("email"))
)
#Builder
#NoArgsConstructor
#AllArgsConstructor
final public class User extends BaseEntity {
/**
* User's id
*/
#Id
#JsonView(JsonViewContext.Summary.class)
private String id;
/**
* User's email address
*/
#Getter #Setter
#JsonView(JsonViewContext.Summary.class)
#FormParam("email")
#Indexed
#Email
private String email;
/**
* User's hashed password
*/
#Getter
#JsonView(JsonViewContext.Detailed.class)
#FormParam("password")
#NotEmpty
private String password;
/**
* Sets the password after having hashed it
* #param clearPassword The clear password
*/
public void setPassword(String clearPassword) throws NoSuchAlgorithmException, InvalidKeySpecException {
PasswordEncoder encoder = new BCryptPasswordEncoder();
String hashedPassword = encoder.encode(clearPassword);
setHashedPassword(hashedPassword);
}
/**
* Directly sets the hashed password, whithout hashing it
* #param hashedPassword The hashed password
*/
protected void setHashedPassword(String hashedPassword) {
this.password = hashedPassword;
}
/**
* Converts the user to a UserDetail spring instance
*/
public UserDetails toUserDetails() {
return new org.springframework.security.core.userdetails.User(
getEmail(),
getPassword(),
true,
true,
true,
true,
AuthorityUtils.createAuthorityList("USER")
);
}
}
EntityRepository.java (my base repository, inheriting from the Morphia one)
package org.beep.server.repository;
import org.beep.server.entity.BaseEntity;
import org.bson.types.ObjectId;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.dao.BasicDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
public class EntityRepository<Entity extends BaseEntity> extends BasicDAO<Entity, ObjectId> {
#Autowired
protected EntityRepository(Datastore ds) {
super(ds);
}
}
UserRepository.java (my user repository)
package org.beep.server.repository;
import org.beep.server.entity.User;
import org.bson.types.ObjectId;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.dao.BasicDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
#Repository
public class UserRepository extends EntityRepository<User> {
#Autowired
protected UserRepository(Datastore ds) {
super(ds);
}
}
EntityService.java (the generic service, used from the Rest endpoints)
package org.beep.server.service;
import org.beep.server.entity.BaseEntity;
import org.beep.server.exception.EntityNotFoundException;
import org.beep.server.exception.UserEmailAlreadyExistsException;
import org.beep.server.repository.EntityRepository;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.query.Query;
import org.mongodb.morphia.query.UpdateOperations;
import java.util.List;
public abstract class EntityService<Entity extends BaseEntity, Repository extends EntityRepository> implements ServiceInterface<Entity> {
protected Repository repository;
public EntityService(Repository repository) {
this.repository = repository;
}
/**
* {#inheritDoc}
*/
public Entity create(Entity entity) throws UserEmailAlreadyExistsException {
repository.save(entity);
return entity;
}
/**
* {#inheritDoc}
*/
public void delete(String id) throws EntityNotFoundException {
//repository.deleteById(id).;
}
/**
* {#inheritDoc}
*/
public List<Entity> findAll() {
return repository.find().asList();
}
/**
* {#inheritDoc}
*/
public Entity findOneById(String id) throws EntityNotFoundException {
return (Entity) repository.get(id);
}
/**
* {#inheritDoc}
*/
public Entity update(String id, Entity entity) {
// Try to get the old entity, and to set the Id on the inputed one
// But how can I merge the two ? If I persist like that, I will just have the modified fields, others
// will be set to null...
Entity oldEntity = (Entity) repository.get(id);
entity.setId(id);
repository.save(entity);
// Create update operations works, but I have to set the changing fields manually...
// not so compatible with generics !
/*final Query<Entity> updateSelection = repository.createQuery().filter("_id",id);
repository.createUpdateOperations().
repository.update(updateSelection,entity);*/
return entity;
}
}
UserService.java
package org.beep.server.service;
import org.beep.server.entity.Message;
import org.beep.server.entity.User;
import org.beep.server.exception.EntityNotFoundException;
import org.beep.server.exception.UserEmailAlreadyExistsException;
import org.beep.server.repository.UserRepository;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Key;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.ws.rs.BadRequestException;
import java.util.List;
import java.util.Optional;
#Service
public class UserService extends EntityService<User, UserRepository> {
#Autowired
public UserService(UserRepository repository) {
super(repository);
}
}
RestResource.java (my base Rest Endpoint)
package org.beep.server.api.rest.v1;
import com.fasterxml.jackson.annotation.JsonView;
import org.beep.server.entity.BaseEntity;
import org.beep.server.entity.User;
import org.beep.server.entity.BaseEntity;
import org.beep.server.exception.EntityNotFoundException;
import org.beep.server.exception.UserEmailAlreadyExistsException;
import org.beep.server.service.EntityService;
import org.beep.server.service.ServiceInterface;
import org.beep.server.service.UserService;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
public class RestResource<Entity extends BaseEntity, Service extends EntityService> {
protected Service service;
// Default constructor private to avoid blank constructor
protected RestResource() {
this.service = null;
}
/**
* Creates an object
* #param object Object to create
* #return The newly created object
*/
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
#JsonView(BaseEntity.JsonViewContext.Detailed.class)
Entity create(#RequestBody #Valid Entity object) throws UserEmailAlreadyExistsException {
return service.create(object);
}
/**
* Deletes an object from its id
* #param id Object to delete id
* #return The deleted object
* #throws EntityNotFoundException
*/
#RequestMapping(value = "{id}", method = RequestMethod.DELETE)
#JsonView(BaseEntity.JsonViewContext.Detailed.class)
User delete(#PathVariable("id") String id) throws EntityNotFoundException {
service.delete(id);
return new User();
}
/**
* Gets all the objects
* #return All the objects
*/
#RequestMapping(method = RequestMethod.GET)
#JsonView(BaseEntity.JsonViewContext.Summary.class)
List<Entity> findAll() {
return service.findAll();
}
/**
* Finds one object from its id
* #param id The object to find id
* #return The corresponding object
* #throws EntityNotFoundException
*/
#RequestMapping(value = "{id}", method = RequestMethod.GET)
#JsonView(BaseEntity.JsonViewContext.Detailed.class)
Entity findById(#PathVariable("id") String id) throws EntityNotFoundException {
return service.findOneById(id);
}
/**
* Updates an object
* #param object The object to update
* #return The updated object
*/
#RequestMapping(value = "{id}", method = RequestMethod.PUT)
#JsonView(BaseEntity.JsonViewContext.Detailed.class)
Entity update(#PathVariable String id, #RequestBody #Valid Entity object) {
return service.update(id, object);
}
/**
* Handles the EntityNotFound exception to return a pretty 404 error
* #param ex The concerned exception
*/
#ExceptionHandler
#ResponseStatus(HttpStatus.NOT_FOUND)
public void handleEntityNotFound(EntityNotFoundException ex) {
}
/**
* Handles the REST input validation exceptions to return a pretty 400 bad request error
* with more info
* #param ex The validation exception
* #return A pretty list of the errors in the form
*/
#ExceptionHandler
#ResponseStatus(HttpStatus.BAD_REQUEST)
public List<ObjectError> handleValidationFailed(MethodArgumentNotValidException ex) {
// TODO : Check and improve the return of this method according to the front
// The concept is to automatically bind the error dans the failed parameter
return ex.getBindingResult().getAllErrors();
}
}

You've run into one of the difficult questions with Morphia. Based on the code you posted above you should look into the merge method here.
The important thing to remember is that this is not a deep merge, only top level fields, if you have complicated data objects this probably won't help.
It essentially works like this:
T Entity -> Map and then takes the map and runs a recursive update over the non-null fields like so: update({_id:#Id-field},{$set:mapOfEntityFields})
The standard transformation rules from T Entity apply ->
Map, like it does for save.
For a deep merging of any generic entity it would require you to handle this yourself with a custom method.
This is a good example from another question on SO dealing with deep merging using JSON partials in Spring over complex entities using the org.codehaus.jackson.map.ObjectMapper. It should be easily adapted to your problem.
If neither of these help you, please comment in my answer and we can work out a custom recursive method that should work for you. Hope that helps.

Related

MongoDb insert data to different DBs based on request SpringBoot Java

Hi I have mongoDb database which contains separate db for different shops, but the collections inside all the dbs have same structure, when I get the request from post service I want to insert the data to the respective database based in the id in the request. Please advice how to do this in springboot java or Kotlin
AMAZON
- ProductDetails
FLIPKART
- ProductDetails
EBAY
- ProductDetails
Now I have a single database and insert all product details in a single database and I want to add different databases for different shops
spring.data.mongodb.host=mongo
spring.data.mongodb.port=27017
spring.data.mongodb.authentication-database=admin
spring.data.mongodb.username=admin
spring.data.mongodb.password=pass
spring.data.mongodb.database=admin
Since you are new to Spring boot and MongoDB, I am providing you the detailed steps as follows to connect multiple mongo DB in single application. This is one of the most simple ways to configure and connect multiple mongo DB. Hopefully, it will be helpful (don't forget to vote up if it is :-)) -
1) Package Structure -
2) Create an abstract MongoDB Config class -
package com.akash.mongo.multidb.config;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
/**
* Abstract class for configuring different MongoTemplate for different DB
* #author Akash
*
*/
public abstract class AbstractMongoDbConfig {
private String host;
private String username;
private String password;
private String database;
private int port;
public void setHost(String host) {
this.host = host;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setDatabase(String database) {
this.database = database;
}
public void setPort(int port) {
this.port = port;
}
public MongoDbFactory mongoDbFactory() {
MongoCredential mongoCredential = MongoCredential.createCredential(username, database, password.toCharArray());
MongoClient mongoClient = new MongoClient(new ServerAddress(host, port), mongoCredential, new MongoClientOptions.Builder().build());
return new SimpleMongoDbFactory(mongoClient, database);
}
public abstract MongoTemplate getMongoTemplate() throws Exception;
}
3) Extend the abstract class to create configuration for each DB
AmazonDbConfig
package com.akash.mongo.multidb.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
/**
* Configuration class for Amazon DB
* #author Akash
*
*/
#Configuration
#ConfigurationProperties(prefix="amazon.mongodb")
#EnableMongoRepositories(basePackages= {"com.akash.mongo.multidb.repository.amazon"}, mongoTemplateRef="amazonMongoTemplate")
public class AmazonDbConfig extends AbstractMongoDbConfig {
private static final Logger logger = LoggerFactory.getLogger(AmazonDbConfig.class);
#Override
#Bean(name="amazonMongoTemplate")
public MongoTemplate getMongoTemplate() throws Exception {
logger.info("Creating MongoTemplate for Amazon DB");
return new MongoTemplate(mongoDbFactory());
}
}
EbayDbConfig
package com.akash.mongo.multidb.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
/**
* Configuration class for ebay DB
* #author Akash
*
*/
#Configuration
#ConfigurationProperties(prefix="ebay.mongodb")
#EnableMongoRepositories(basePackages= {"com.akash.mongo.multidb.repository.ebay"}, mongoTemplateRef="ebayMongoTemplate")
public class EbayDbConfig extends AbstractMongoDbConfig {
private static final Logger logger = LoggerFactory.getLogger(EbayDbConfig.class);
#Override
#Bean(name="ebayMongoTemplate")
public MongoTemplate getMongoTemplate() throws Exception {
logger.info("Creating MongoTemplate for Ebay DB");
return new MongoTemplate(mongoDbFactory());
}
}
FlipkartDbConfig
package com.akash.mongo.multidb.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
/**
* Configuration class for Flipkart DB
* #author Akash
*
*/
#Configuration
#ConfigurationProperties(prefix="flipkart.mongodb")
#EnableMongoRepositories(basePackages= {"com.akash.mongo.multidb.repository.flipkart"}, mongoTemplateRef="flipkartMongoTemplate")
public class FlipkartDbConfig extends AbstractMongoDbConfig {
private static final Logger logger = LoggerFactory.getLogger(FlipkartDbConfig.class);
#Override
#Primary
#Bean(name="flipkartMongoTemplate")
public MongoTemplate getMongoTemplate() throws Exception {
logger.info("Creating MongoTemplate for Flipkart DB");
return new MongoTemplate(mongoDbFactory());
}
}
Notice that each of these configuration class is creating its own MongoTemplate and it is enabling its own MongoRepository. Also one of these needs to be #Primary otherwise the spring boot will throw error. It doesn't matter which of these is primary; ultimately these will be connecting to their own repository
4) Create entities and a repository for each DB.
You need to create a repository for each DB now. Given that your collection is same for all the DBs, I have created following sample entity -
package com.akash.mongo.multidb.entity;
import java.io.Serializable;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
/**
* Sample Entity class
* #author Akash
*
*/
#Document(collection="productDetails")
public class ProductDetails implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private ObjectId id;
#Field("productName")
private String productName;
#Field("productDesc")
private String productDesc;
#Field("productQuantity")
private String productQuantity;
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getProductDesc() {
return productDesc;
}
public void setProductDesc(String productDesc) {
this.productDesc = productDesc;
}
public String getProductQuantity() {
return productQuantity;
}
public void setProductQuantity(String productQuantity) {
this.productQuantity = productQuantity;
}
}
You can create/modify the entity class as per your collection details.
AmazonRepository
package com.akash.mongo.multidb.repository.amazon;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import com.akash.mongo.multidb.entity.ProductDetails;
/**
*
* #author Akash
*
*/
#Repository
public interface AmazonRepository extends MongoRepository<ProductDetails, ObjectId> {
}
FlipkartRepository
package com.akash.mongo.multidb.repository.flipkart;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import com.akash.mongo.multidb.entity.ProductDetails;
#Repository
public interface FlipkartRepository extends MongoRepository<ProductDetails, ObjectId> {
}
EbayRepository
package com.akash.mongo.multidb.repository.ebay;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import com.akash.mongo.multidb.entity.ProductDetails;
/**
*
* #author Akash
*
*/
#Repository
public interface EbayRepository extends MongoRepository<ProductDetails, ObjectId> {
}
Again, each repository needs to be its own package otherwise there will be errors while running the application. This is the one disadvantage of this solution where you have to create as many repository packages as no of DBs you want to connect.
5) Service implementation and connecting to different repositories
ProductDetailsService Interface
package com.akash.mongo.multidb.service;
import com.akash.mongo.multidb.entity.ProductDetails;
/**
* Sample interface with one add method
* #author Akash
*
*/
public interface ProductDetailsService {
/**
*
* #param productOrigin - the shop name i.e. Amazon, Flipkart or ebay.
* #param productDetails - the product details to add
*/
public void addProductDetails(String productOrigin, ProductDetails productDetails) throws RuntimeException;
}
ProductDetailsServiceImpl Class -
package com.akash.mongo.multidb.service;
import java.util.Map;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.akash.mongo.multidb.entity.ProductDetails;
/**
* Implementation of ProductDetailsService interface
* #author Akash
*
*/
public class ProductDetailsServiceImpl implements ProductDetailsService {
private static final Logger logger = LoggerFactory.getLogger(ProductDetailsServiceImpl.class);
/*
* Spring boot will autowire all the repositories along with their name
* amazonRepository - amazon repository instance
* ebayRepository - ebay repository instance and so on
*/
#Autowired
Map<String, MongoRepository<ProductDetails, ObjectId>> repositories;
#Override
public void addProductDetails(String productOrigin, ProductDetails productDetails) throws RuntimeException {
logger.info("Adding product details into {} db", productOrigin);
//if productOrigin is Amazon; repositoryName will be amazonRepository which is already present in spring boot
String repositoryName = productOrigin.toLowerCase()+"Repository";
if(repositories.containsKey(repositoryName)) {
repositories.get(repositoryName).save(productDetails);
} else {
logger.error("{} shop is undefined in DB. Check and try again", productOrigin);
throw new RuntimeException("Shop doesnot exist in MongoDb");
}
}
}
ProductOrigin you can derive from your request or headers whatever information is available to you.
6) Lastly, application.properties
Change the database, username and password details for each DB. Try not to use Admin credentials; Instead create username & password for each DB separately and update application.properties.
#MongoDb connection properties for Flipkart DB
flipkart.mongodb.database=flipkart
flipkart.mongodb.host=http://127.0.0.1
flipkart.mongodb.port=27017
flipkart.mongodb.username=flipkart
flipkart.mongodb.password=flipkart
#MongoDb connection properties for Amazon DB
amazon.mongodb.database=amazon
amazon.mongodb.host=http://127.0.0.1
amazon.mongodb.port=27017
amazon.mongodb.username=amazon
amazon.mongodb.password=amazon
#MongoDb connection properties for ebay DB
ebay.mongodb.database=ebay
ebay.mongodb.host=http://127.0.0.1
ebay.mongodb.port=27017
ebay.mongodb.username=ebay
ebay.mongodb.password=ebay
Now, if you need to add any new database, you just need to add one config class similar to AmazonDbConfig, one more package with the required repositories for that DB and connection details in application.properties. No change is required in service till your collection is same for all the DBs.
If you have multiple collections, you can add entity and repository for each collection (group all the respositories for single shop in one package) and solution should still hold good.

Spring Around Aspect does not get called in controller

The problem is that when I want to print some logs from some controller methods the around annotated method with LogInit annotation does not get called, but when I am annotating some other service method with the same annotation the around method does get called. I am stuck for some time in this problem and I need to make it work.
I have the following aspect:
package com.db.mybank.backend.aop;
import com.db.mybank.backend.model.presentation.CustomerDataVO;
//import org.apache.log4j.LogManager;
import org.apache.log4j.MDC;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class LogInitAspect {
private static final Logger LOGGER = LoggerFactory.getLogger("SPLUNK");
private static final String METRIC_SPLITTER = ";";
/**
* Creates AOP pointcut and advice
* #param joinPoint (the executing method which has been annotated with #LogInit )
* #return
* #throws Throwable
*/
#Around("#annotation(com.db.mybank.backend.aop.LogInit)")
public void logInitSplunk(ProceedingJoinPoint joinPoint) {
initMDC();
CustomerDataVO proceed = null;
try {
proceed = (CustomerDataVO) joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
LOGGER.info(getFormattedLogLine());
return;
}
private String getFormattedLogLine() {
return MDC.get(MDCEnum.TIMESTAMP.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.CCCARD.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.SESSIONID.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.TIMESTAMP_REQUEST.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.TIMESTAMP_RESPONSE.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.SERVICE_ID.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.SERVICE_URI.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.RESULT.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.DEVICE_INFO.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.LOGIN_TIMESTAMP.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.RESULT_LOGIN.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.LOGOUT_TIMESTAMP.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.IP_ADDRESS_CLIENT.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.USER_AGENT_CLIENT.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.TIMESTAMP_AUTHORIZATION.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.TRANSACTION_SERVICE_NAME.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.VALUE_AND_CURRENCY.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.TRANSACTION_ID.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.TIMESTAMP_TRANSACTION_SUBMIT_TO_BACKEND.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.NDG.getValue())+METRIC_SPLITTER;
}
private void initMDC(){
MDC.put(MDCEnum.TIMESTAMP.getValue(),"N/A");
MDC.put(MDCEnum.CCCARD.getValue(),"N/A");
MDC.put(MDCEnum.SESSIONID.getValue(),"N/A");
MDC.put(MDCEnum.TIMESTAMP_REQUEST.getValue(),"N/A");
MDC.put(MDCEnum.TIMESTAMP_RESPONSE.getValue(),"N/A");
MDC.put(MDCEnum.SERVICE_ID.getValue(),"N/A");
MDC.put(MDCEnum.SERVICE_URI.getValue(),"N/A");
MDC.put(MDCEnum.RESULT.getValue(),"N/A");
MDC.put(MDCEnum.DEVICE_INFO.getValue(),"N/A");
MDC.put(MDCEnum.LOGIN_TIMESTAMP.getValue(),"N/A");
MDC.put(MDCEnum.RESULT_LOGIN.getValue(),"N/A");
MDC.put(MDCEnum.LOGOUT_TIMESTAMP.getValue(),"N/A");
MDC.put(MDCEnum.IP_ADDRESS_CLIENT.getValue(),"N/A");
MDC.put(MDCEnum.USER_AGENT_CLIENT.getValue(),"N/A");
MDC.put(MDCEnum.TIMESTAMP_AUTHORIZATION.getValue(),"N/A");
MDC.put(MDCEnum.TRANSACTION_SERVICE_NAME.getValue(),"N/A");
MDC.put(MDCEnum.VALUE_AND_CURRENCY.getValue(),"N/A");
MDC.put(MDCEnum.TRANSACTION_ID.getValue(),"N/A");
MDC.put(MDCEnum.TIMESTAMP_TRANSACTION_SUBMIT_TO_BACKEND.getValue(),"N/A");
MDC.put(MDCEnum.NDG.getValue(),"N/A");
}
}
And I have the following controller method which is not working.
#RequestMapping(value = "/prelogin", method = RequestMethod.POST)
#ResponseBody
#LogInit
public AuthDataVO preLogin(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {

Hibernate doesn't save entity, only if I double save

I'm developing an application using Vaadin and I am having some trouble:
When I try to save and object with Hibernate, nothing appears in my table unless i double save it.
Code from my class:
if (referenciacao==null){
referenciacao = (Referenciacao)referenciacaoSession.newEntity();
}
referenciacao.setMedico((Medico)cbbMedico.getValue());
referenciacao.setOrigem((Origem)cbbOrigem.getValue());
referenciacao.setReferenciacaotipo((ReferenciacaoTipo)cbbReferenciacaoTipo.getValue());
referenciacao=referenciacaoSession.saveAndFlush(referenciacao);
My Referenciacao Entity:
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
/**
*
* #author pepatusco
*/
#SuppressWarnings("serial")
#Entity
#Table(name = "referenciacao")
public class Referenciacao extends BaseBean implements Serializable {
public static final String PROPERTY_ID = "id";
public static final String PROPERTY_MEDICO = "medico";
public static final String PROPERTY_ORIGEM = "origem";
public static final String PROPERTY_REFTIPO = "referenciacaoTipo";
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "medico_id")
private Medico medico;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "origem_id")
private Origem origem;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "referenciacao_tipo_id")
private ReferenciacaoTipo referenciacaoTipo;
public Medico getMedico() {
return medico;
}
public void setMedico(Medico medico) {
this.medico = medico;
}
public Origem getOrigem() {
return origem;
}
public void setOrigem(Origem origem) {
this.origem = origem;
}
public ReferenciacaoTipo getReferenciacaotipo() {
return referenciacaoTipo;
}
public void setReferenciacaotipo(ReferenciacaoTipo referenciacaoTipo) {
this.referenciacaoTipo = referenciacaoTipo;
}
public Referenciacao() {
//this.id = null;
}
}
My Referenciacao DAO:
import java.util.List;
import javax.transaction.Transactional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import pt.app.dpn.base.common.beans.entity.Medico;
import pt.app.dpn.base.common.beans.entity.Origem;
import pt.app.dpn.base.common.beans.entity.Referenciacao;
import pt.app.dpn.base.common.beans.entity.ReferenciacaoTipo;
/**
*
* #author pepatusco
*/
#Repository
#Transactional
public interface ReferenciacaoRepository extends JpaRepository<Referenciacao, Long> {
List<Referenciacao> findByMedico(Medico medico);
List<Referenciacao> findByOrigem(Origem origem);
List<Referenciacao> findByReferenciacaoTipo(ReferenciacaoTipo referenciacaoTipo);
Referenciacao findByMedicoAndOrigemAndReferenciacaoTipo(Medico medico, Origem origem, ReferenciacaoTipo referenciacaoTipo);
Referenciacao findById(Long id);
}
My ReferenciacaoSession:
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.enterprise.inject.Default;
import javax.inject.Inject;
import javax.transaction.Transactional;
import pt.app.dpn.base.common.beans.dao.ReferenciacaoRepository;
import pt.app.dpn.base.common.beans.entity.BaseBean;
import pt.app.dpn.base.common.beans.entity.Medico;
import pt.app.dpn.base.common.beans.entity.Origem;
import pt.app.dpn.base.common.beans.entity.Referenciacao;
import pt.app.dpn.base.common.beans.entity.ReferenciacaoTipo;
/**
*
* #author pepatusco
*
*/
#Default
#Stateless
#Local(ReferenciacaoSessionLocal.class)
public class ReferenciacaoSession implements ReferenciacaoSessionLocal, Serializable {
#Inject
private ReferenciacaoRepository referenciacaoRepository;
#Override
public void flush() {
referenciacaoRepository.flush();
}
#Transactional
#Override
public <S extends Referenciacao> S saveAndFlush(S s) {
return referenciacaoRepository.saveAndFlush(s);
}
#Override
public Long getCount() {
return referenciacaoRepository.count();
}
#Override
public List<BaseBean> findAll() {
List<BaseBean> listBaseBean = new ArrayList<>();
referenciacaoRepository.findAll().forEach((referenciacao) -> {
listBaseBean.add(referenciacao);
});
return listBaseBean;
}
#Transactional
#Override
public void save(Object o) {
referenciacaoRepository.save((Referenciacao)o);
referenciacaoRepository.flush();
}
#Override
public void remove(Object o) {
referenciacaoRepository.delete((Referenciacao)o);
}
#Override
public Object newEntity() {
return new Referenciacao();
}
#Override
public List<Referenciacao> findByMedico(Medico medico) {
return referenciacaoRepository.findByMedico(medico);
}
#Override
public List<Referenciacao> findByOrigem(Origem origem) {
return referenciacaoRepository.findByOrigem(origem);
}
#Override
public Referenciacao findById(Long id) {
return referenciacaoRepository.findById(id);
}
#Override
public List<Referenciacao> findByReferenciacaoTipo(ReferenciacaoTipo referenciacaoTipo) {
return referenciacaoRepository.findByReferenciacaoTipo(referenciacaoTipo);
}
#Override
public Referenciacao findByMedicoAndOrigemAndReferenciacaoTipo(Medico medico, Origem origem, ReferenciacaoTipo referenciacaoTipo) {
return referenciacaoRepository.findByMedicoAndOrigemAndReferenciacaoTipo(medico, origem, referenciacaoTipo);
}
}
Can anyone tell me why, when I do save, nothing appears in the table but when I do save again it saves a Referenciacao in the table?
I can't see the reason for this behavior, so let me at least try to help to debug this thing.
I see multiple possible things that might be going on:
The repository for your entity doesn't get called on the first try, for some reason outside of the code you showed us.
The repository does not store your entity.
Something goes haywire with your transactions, causing you not to see the changes (I'm actually betting on this one).
In order to rule out (1) put a breakpoint or a logging statement next to the referenciacaoRepository.save and referenciacaoRepository.saveAndFlush statements to ensure, that code path gets executed.
In order to rule out (2) activate SQL logging, to see if SQL statements actually get executed as expected. Obviously, you should see insert statements getting executed.
To tackle the third one, activate transaction logging to see when transactions start and end. There should be a transaction getting started before the save and getting commited afterward. Verify that this actually happens and that there are no other transactions running.
Let us know if you were able to solve your problem.
Side note: Why do you have ReferenciacaoSession? It really doesn't do anything and seems to be perfectly replaceable by the repository.

RESTful web service with JSON : PUT and DELETE not working

I am trying to set up a simple RESTful web service on Netbeans. Basicly, I have a container where the components are simple order objects with 3 attributes : id, total, and list of items.
The GET and the POST on the container work fine as well as the GET on the components. What is not working is the PUT and the DELETE on the components: I don't receive any error, just nothing happens.
Since the GET is working, probably the error is on the client side, so I am posting the client class in charge of the single orders here.
package restclientjson;
import javax.ws.rs.ClientErrorException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.WebTarget;
public class JSONOrderClient {
private WebTarget webTarget;
private Client client;
private static final String BASE_URI = "http://localhost:8080/RESTServerJSON/webresources";
public JSONOrderClient(String id) {
client = javax.ws.rs.client.ClientBuilder.newClient();
String resourcePath = java.text.MessageFormat.format("orders/{0}", new Object[]{id});
webTarget = client.target(BASE_URI).path(resourcePath);
}
public void setResourcePath(String id) {
String resourcePath = java.text.MessageFormat.format("orders/{0}", new Object[]{id});
webTarget = client.target(BASE_URI).path(resourcePath);
}
public void putJson(Object requestEntity) throws ClientErrorException {
webTarget.request(javax.ws.rs.core.MediaType.TEXT_PLAIN).put(javax.ws.rs.client.Entity.entity(requestEntity, javax.ws.rs.core.MediaType.APPLICATION_JSON));
}
public void delete() throws ClientErrorException {
webTarget.request().delete();
}
public <T> T getJson(Class<T> responseType) throws ClientErrorException {
WebTarget resource = webTarget;
return resource.request(javax.ws.rs.core.MediaType.APPLICATION_JSON).get(responseType);
}
public void close() {
client.close();
}
}
Edit: Here is the Server side code:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package ServiceCore;
import dto.Order;
import java.util.Map;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.DELETE;
import javax.ws.rs.core.MediaType;
/**
* REST Web Service OrderResource
*
*
*/
public class OrderResource {
private String id;
private Map<String,Order> orderMap;
/**
* Creates a new instance of OrderResource
*/
private OrderResource(String id,Map<String,Order> orderMap) {
this.id = id;
this.orderMap = orderMap;
}
/**
* Get instance of the OrderResource
*/
public static OrderResource getInstance(String id,Map<String,Order> orderMap) {
// The user may use some kind of persistence mechanism
// to store and restore instances of OrderResource class.
return new OrderResource(id,orderMap);
}
/**
* Retrieves representation of an instance of ServiceCore.OrderResource
* #return an instance of dto.Order
*/
#GET
#Produces(MediaType.APPLICATION_JSON)
public Order getJson() {
return orderMap.get(id);
}
/**
* PUT method for updating or creating an instance of OrderResource
* #param content representation for the resource
*/
#PUT
#Consumes(MediaType.APPLICATION_JSON)
public void putJson(Order content) {
orderMap.put(id, content);
}
/**
* DELETE method for resource OrderResource
*/
#DELETE
public void delete() {
orderMap.remove(id);
}
}
package ServiceCore;
import dto.Order;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.PathParam;
import javax.ws.rs.POST;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
* REST Web Service
*
*/
#Path("/orders")
public class OrdersResource {
#Context
private UriInfo context;
private static Map<String, Order> orderMap= new HashMap<String,Order>();
{
orderMap.put("1", new Order(1,2,new String[]{"water","coffee"}));
orderMap.put("2", new Order(3,400,new String[]{"milk"}));
}
private static int id = 3;
/**
* Creates a new instance of OrdersResource
*/
public OrdersResource() {
}
/**
* Retrieves representation of an instance of ServiceCore.OrdersResource
* #return an instance of dto.Order[]
*/
#GET
#Produces(MediaType.APPLICATION_JSON)
public dto.Order[] getJson() {
Order[] orders = new Order[orderMap.size()];
for(int i=0; i<orderMap.size(); i++){
orders[i]=orderMap.get((i+1)+"");
}
return orders;
}
/**
* POST method for creating an instance of OrderResource
* #param content representation for the new resource
* #return an HTTP response with content of the created resource
*/
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response postJson(Order content) {
orderMap.put(id+"", content);
id++;
return Response.created(context.getAbsolutePath()).build();
}
/**
* Sub-resource locator method for {id}
*/
#Path("{id}")
public OrderResource getOrderResource(#PathParam("id") String id) {
return OrderResource.getInstance(id,orderMap);
}
}
In Java, most web frameworks use more than one Servlet to handle requests, often discarding the servlet as soon as the request is handled.
You probably need to put your Map of items into a different context, like the Application context, which is persistent across the entire life of the Application.

How to create a constraint validator for multiple fields through annotation in spring 4.*

How to create a validator restrictions for more fields through annotation in spring 4.* for example
#UniqueValidator
#Entity
#Table(name = "persons")
#UniqueValidator(message="Peson already exist",columns={"name","lastName"})
public class {
#Column
private String name;
#Column
private String lastName;
}
roughly speaking..
select ... from persons where name='qwerty' and lastName='asdfgh'
Here's one way to do it in Spring MVC and JSR 303 validation:
Create a constraint annotation:
package com.awgtek.model.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
/**
* The target field should be unique in the data store.
*/
#Documented
#Constraint(validatedBy = UniqueNameValidator.class)
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface UniqueName {
/**
* #return the error message template
*/
String message() default "{com.awgtek.model.validation.UniqueName.message}";
/**
* #return the groups the constraint belongs to
*/
Class<?>[] groups() default {};
/**
* #return the payload associated to the constraint
*/
Class<? extends Payload>[] payload() default {};
/**
* #return a class an instance of which will be used to check the existence of a name.
*/
Class<? extends NameExistenceChecker> nameExistenceChecker();
}
Apply this annotation at the class level to the model class:
package com.awgtek.model;
import com.awgtek.model.validation.MyNameExistenceChecker;
import com.awgtek.model.validation.UniqueName;
import com.awgtek.model.validation.UniqueNameConstraint;
#UniqueName(groups = UniqueNameConstraint.class,
nameExistenceChecker = MyNameExistenceChecker.class)
public class ConfigurationAttribute {
private String name;
private String lastName;
// getters and setters omitted
}
The UniqueNameValidator:
package com.awgtek.model.validation;
import com.awgtek.service.MyService;
import javax.inject.Inject;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class UniqueNameValidator implements ConstraintValidator<UniqueName, Object> {
#Inject
private NameExistenceCheckerFactory nameExistenceCheckerFactory;
private NameExistenceChecker nameExistenceChecker;
#Override
public void initialize(UniqueName constraintAnnotation) {
nameExistenceChecker = nameExistenceCheckerFactory.get(constraintAnnotation.nameExistenceChecker());
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
return !nameExistenceChecker.nameExists(value);
}
}
The UniqueNameValidator then depends on a couple of classes:
package com.awgtek.model.validation;
import javax.inject.Inject;
import org.springframework.stereotype.Component;
#Component
public class NameExistenceCheckerFactory {
#Inject
private NamespaceNameExistenceChecker namespaceNameExistenceChecker;
#Inject
private MyNameExistenceChecker myNameExistenceChecker;
public NameExistenceChecker get(Class<? extends NameExistenceChecker> clazz) {
if (clazz.equals(MyNameExistenceChecker.class)) {
return myNameExistenceChecker;
}
throw new IllegalStateException("Unknown NameExistenceChecker");
}
}
package com.awgtek.model.validation;
public interface NameExistenceChecker {
boolean nameExists(Object objectWithName);
}
The implementation class that actually looks up whether the item exists in the database (via a service):
package com.awgtek.model.validation;
import com.awgtek.model.MyModelClass;
import com.awgtek.service.MyService;
import javax.inject.Inject;
import org.springframework.stereotype.Component;
#Component
public class AttributeNameExistenceChecker implements NameExistenceChecker {
#Inject
private MyService myService;
#Override
public boolean nameExists(Object objectWithName) {
MyModelClass myObject = (MyModelClass) objectWithName;
return myService.itemAlreadyExists(myObject.getName(), myObject.getLastName());
}
}

Categories

Resources