Orika mapper spring boot doesn't map when I define a CustomMapper - spring-boot

I have the following structure:
Controller (model classes) -> service -> (domain classes) repository.
This is what I do in the service layer when I have to persist something coming into the request body:
Map the model class (coming from the body request) into the domain class (Trying to use Orika)
Persist the domain class and after getting an OK from the DB
map the domain class saved in the DB into the model class (Trying to use Orika)
return back the response to the controller
This is what I have so far but it's not working:
#Component
public class CustomMapper extends ConfigurableMapper {
private MapperFactory factory = new DefaultMapperFactory.Builder().build();
#Override
protected void configure(MapperFactory factory) {
System.out.println("Spring boot mapper initializing.....");
factory.classMap(ModelSource.class, ModelDestination.class)
.byDefault()
.register();
}
I can see the message "Spring boot mapper initializing....." running spring boot so that means the component is loading OK.
#Autowired
private CustomMapper mapperFacade;
ModelDestination destination = mapperFacade.map(ModelSource.class, ModelDestination.class);
When I check all the destination fields, they are all null. What am I doing wrong? Thanks

Using the last Orika version 1.5.4. This fixed my issue:
.field("chain{}", "chain{}")

Related

Programmatic RedissonClient in Spring boot project

I am trying to implement Hibernate second level caching in a Spring boot project using Redisson.
I have followed this blog as a reference
https://pavankjadda.medium.com/implement-hibernate-2nd-level-cache-with-redis-spring-boot-and-spring-data-jpa-7cdbf5632883
Also i am trying to initialize the RedissionClient programmatically and not through declaratively /through a config file
Created a spring bean to be initialized which should create the RedissonClient instance.
#Configuration
#Lazy(value = false)
public class RedissonConfig {
#Bean
public RedissonClient redissionClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
return Redisson.create(config);
}
}
However this bean is never intialized and i get the following error while application startup.
Caused by: org.hibernate.cache.CacheException: Unable to locate Redisson configuration
at org.redisson.hibernate.RedissonRegionFactory.createRedissonClient(RedissonRegionFactory.java:107) ~[redisson-hibernate-53-3.12.1.jar:3.12.1]
at org.redisson.hibernate.RedissonRegionFactory.prepareForUse(RedissonRegionFactory.java:83) ~[redisson-hibernate-53-3.12.1.jar:3.12.1]
It seems Spring boot Hibernate still trying to load the Redisson config through a config file.
is it possible to load the Redission config in spring boot programmatically ?
Best Regards,
Saurav
I just did exactly this, here is how:
you need a custom RegionFactory that is similar to the JndiRedissonRegionFactory but gets its RedissonClient injected somehow.
an instance of this Class, fully configured, is put into the hibernate-properties map. Hibernates internal code is flexible: if the value of hibernate.cache.region.factory_class is a string it is treated as a FQDN. If it is an instance of Class<?>, it will be instantiated. If it is an Object, it will be used.
Spring offers a rather simple way to customize hibernate properties with a bean:
#AutoConfiguration(after = RedissonAutoConfiguration.class, before = JpaAutoConfiguration.class)
#ConditionalOnProperty("spring.jpa.properties.hibernate.cache.use_second_level_cache")
public class HibernateCacheAutoConfiguration {
#Bean
public HibernatePropertiesCustomizer setRegionFactory(RedissonClient redisson) {
return hibernateProperties -> hibernateProperties.put(AvailableSettings.CACHE_REGION_FACTORY, new SpringBootRedissonRegionFactory(redisson));
}
}
My RegionFactory is really simple:
#AllArgsConstructor
public class SpringBootRedissonRegionFactory extends RedissonRegionFactory {
private RedissonClient redissonClient;
#Override
protected RedissonClient createRedissonClient(Map properties) {
return redissonClient;
}
#Override
protected void releaseFromUse() {
}
}
I used the redisson-starter to get a RedissonClient, hence the reference to RedissonAutoConfiguration, but you could just create an instance by hand.
It is possible, but then you need to provide a custom implementation of RegionFactory to Hibernate, which can extends RedissonRegionFactory but uses your own client instance.

Using Vertx with spring-data-neo4j outside Spring container

We have DAO layer which uses Spring data to communicate with Neo4j. Inside DAO layer, it uses the spring annotations #Autowired, #Repository etc. We have API Layer which is written in Vertx. DAO layer is used as a library inside the Vertx application. I am able to use #Inject to inject Models with help of Guice. However, the code block like personDo.findByName('test') fails because it is not able to connect to neo4j outside of spring env. Any suggestions or pointer on how to use spring data without spring application?
DAO layer
// PersonDao.java
#Repository
public interface PersonDao extends CustomerDaoCustom, BaseDao<CustomerDo> {
Optional<PersonDo> findByName(String name);
}
Vertx Application:
import com.example.dao.PersonDao;
import com.example.models.PersonDo;
...
#Inject
private PersonDao personDao;
...
public void findPerson(ServiceRequest request, Handler<AsyncResult<ServiceResponse>> resultHandler) {
String personName = "Sam";
logger.info("Example: finding person with name={}", personName);
Optional<PersonDO> personOpt = personDao.findByName(personName);
personOpt.ifPresentOrElse(person -> {
logger.info("\t Person found: {}", person);
logger.info("\t Person UUID: {}", person.getUuid());
logger.info("\t Details: {}", person.dump());
}, () -> logger.info("\t Person with name={} not found", personName));
personDao is null instead of org.springframework.data.neo4j.repository.query.SimpleQueryByExampleExecutor#379ce046
This can be achieved easily in API if I use the spring boot application to start my vertx service but I am trying not to use Spring boot in my API layer.

How to use application.properties in org.quartz.Job Class

I have created a spring boot application to implement Quartz scheduler. In Job class, I want to use some property from application.properties. How to inject that?
I have tried below but getting null:
#Component
public class ScheduleJob implements org.quartz.Job {
private static final Logger LOGGER = LogManager.getLogger(ScheduleJob.class);
public ScheduleJob() {
}
#Value("${ijobs.service.url}")
private String ijobsUrl;
public void execute(JobExecutionContext context) throws JobExecutionException {
LOGGER.info("Job exceution starts--->");
System.out.println("-------------------"+ijobsUrl);
Spring requires annotating the class with #Component for it to manage it (including loading any properties into the class). If you use "new" to create the object, then it is outside Spring's control and it won't inject the dependencies/values into it.
On side note, there is native support for Quartz if using Spring Boot: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-quartz.html
Firstly, the ScheduleJob class should be annotated with #Component for spring to manage it. Secondly, if you want any property to be injected you could do it in a similar way to how you are injecting value of ijobsUrl.
put your required property in application.properties
#Value("${my.property}")
private String myProperty

Spring Data Rest + ResourceProcessor

I have a ResourceProcessor which shall add a link to a certain PersistentEntityResource (Foo).
The problem is that it kicks in for EVERY PersistentEntityResource in my project which ends in ClassCastExceptions.
When I create a new project with only 2 entities and repositories the processor works fine and only for the Entity which it implements as target.
My question is: What can cause SDR to use this Processor for every PersistentEntityResource and not only for the once for which it is implemented?
#Component
public class FooResourceProcessor implements ResourceProcessor<Resource<Foo>> {
#Override
public Resource<Foo> process(Resource<Foo> resource) {
resource.add(new Link("/foooooooo"));
return resource;
}
I use Spring Boot 1.5.4

#Endpoint and #Transactional on the same class using Spring-ws library

I am trying to implement a web-service endpoint which would be transactional because I don't want to create a special "worker" class with transactional methods. I'm using Spring-ws library together with Spring framework.
Here is my class definition:
#Endpoint
#Transactional
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS)
public class MyEndpoint implements ApplicationContextAware { ... }
Notice that I explicitly specified proxying method to force using CGLIB.
Also notice that my class implements some interface(s), so by default Spring uses JDK dynamic proxy unless another proxying method is specified. This kind of proxies is not appropriate in my case.
The problem begins on application deployment when PayloadRootAnnotationMethodEndpointMapping class starts working. It collects names of all Spring beans with #Endpoint annotation. My endpoint class is counted twice with names "myEndpoint" and "scopedTarget.myEndpoint". This duplication causes ApplicationContextException with message "Cannot map endpoint [...] on registration key [...]: there's already endpoint [...] mapped".
Question: how can I make my endpoint class being transactional?
You might write your own PayloadRootAnnotationMethodEndpointMapping extension and override the initApplicationContext method. There you can check for the scopedTarget. prefix to filter out unwanted beans:
public class ProxiedBeanAwareEndpointMapping extends PayloadRootAnnotationMethodEndpointMapping {
#Override
protected void initApplicationContext() throws BeansException {
initInterceptors();
String[] beanNames = getApplicationContext().getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
if (!beanName.startsWith("scopedTarget.")) {
Class<?> endpointClass = getApplicationContext().getType(beanName);
if (endpointClass != null && AnnotationUtils.findAnnotation(endpointClass, getEndpointAnnotationType()) != null) {
registerMethods(beanName);
}
}
}
}
}
Or you can use the open session in view approach so you don't need to proxy your #Endpoints.

Resources