Spring Hibernate Jackson Hibernate5Module - spring

I have set up spring 4.3.1 with Hibernate 5.1.0 and Jackson 2.7.5
I had some lazy init Exceptions because the Jackson ObjectMapper tries to convert my Objects to late when I am out of the Transactional Service.
Therefore I have read the Hibernate5Module for Jackson.
After adding the Module I do not get lazy Exceptions BUT all #JsonView Annotations are ignored and my lazy collections are 'null'
public class SpringWebConfig extends WebMvcConfigurerAdapter {
...
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof org.springframework.http.converter.json.MappingJackson2HttpMessageConverter) {
ObjectMapper mapper = ((MappingJackson2HttpMessageConverter) converter).getObjectMapper();
mapper.registerModule(new Hibernate5Module());
}
}
}
}
Is there anything I am doing wrong?
The Hibernate5Module should initialize the lazy collections ...

By creating your own ObjectMapper, you're overriding the one Spring Boot would set up, which would include a bunch of useful modules, such as Jdk8 module.
What you should do instead, is just add the Hibernate5() module to the Application Context and Spring Boot will automatically add it to the ObjectMapper that it sets up. Like this in any #Configuration class:
#Bean
public Hibernate5Module hibernate5Module() {
return new Hibernate5Module();
}

Got it to work with the following
#EnableWebMvc
#Configuration
#ComponentScan({ "..." })
public class SpringWebConfig extends WebMvcConfigurerAdapter {
#Autowired
SessionFactory sf;
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Hibernate5Module module = new Hibernate5Module(sf);
module.disable(Feature.USE_TRANSIENT_ANNOTATION);
module.enable(Feature.FORCE_LAZY_LOADING);
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.modulesToInstall(module);
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
}

I manage to make it work with the below implementation
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Hibernate5Module module = new Hibernate5Module(); // or Hibernate4Module ... depends on hibernate version you are using
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
converters.add(new MappingJackson2HttpMessageConverter(mapper));
}

jackson-datatype-hibernate5 bring many solutions but there are some default configurations as well.
Please have a look on
Below is the configuration I did as per my project requirements.
#Bean
public Hibernate5Module hibernateModule() {
Hibernate5Module module = new Hibernate5Module();
module.disable(Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION);
module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING);
return module;
}

Related

What is the suggested way to modify / override / extend built-in Jackson 2 ObjectMapper in Spring Webflux?

Currently I have a minimalistic Spring / Netty, Reactor / Web Flux project with Jackson libraries
#Configuration
public class EmbeddedSpringServer extends DelegatingWebFluxConfiguration {
#Bean
MyController controller() {
return new AdminController();
}
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(EmbeddedSpringServer.class);
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(applicationContext).build();
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create("0.0.0.0", 8082).newHandler(adapter).subscribe();
applicationContext.registerShutdownHook();
}
}
build.gradle:
compile 'org.springframework:spring-context:5.0.2.RELEASE'
compile 'org.springframework:spring-web:5.0.2.RELEASE'
compile 'org.springframework:spring-webflux:5.0.2.RELEASE'
compile 'io.projectreactor.ipc:reactor-netty:0.7.2.RELEASE'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.3'
Controller class works fine (it returns a Mono<> with a DTO type).
Because Jackson is present in classpath Web Flux automatically creates an Object Mapper instance via DefaultServerCodecConfigurer however it's not clear how to override object mapper instance, because most Web Flux configuration classes are package private.
What I'd like to achieve is to create my own object mapper to add custom LocalDateTime serialization implemented in jackson-modules-java8
ObjectMapper mapper = new ObjectMapper()
.registerModule(new ParameterNamesModule())
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule())
;
The problem is that's it's not clear how to modify Jackson2JsonEncoder created in package private org.springframework.http.codec.support.AbstractCodecConfigurer.AbstractDefaultCodecs.
You can disable that Jackson feature right from your application.properties file with:
spring.jackson.serialization.write-dates-as-timestamps=false
It turned out simpler than I thought initially as DelegatingWebFluxConfiguration already has a configureHttpMessageCodecs method overriding which is enough
#Configuration
public class EmbeddedSpringServer extends DelegatingWebFluxConfiguration {
#Bean
MyController controller() {
return new MyController();
}
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(EmbeddedSpringServer.class);
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(applicationContext).build();
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create("0.0.0.0", 8082).newHandler(adapter).subscribe();
applicationContext.registerShutdownHook();
}
#Bean
ObjectMapper objectMapper(){
return new ObjectMapper()
.registerModule(new ParameterNamesModule())
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule());
}
#Override
protected void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper()));
configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper()));
}
}
as for the Spring Boot I think it can be also achieved by returning a webFluxConfigurer bean
#Bean
WebFluxConfigurer webFluxConfigurer(ObjectMapper objectMapper) {
return new WebFluxConfigurer() {
#Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper());
configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper());
}
};
}
as they are picked by DelegatingWebFluxConfiguration created by #EnableWebFlux automatically.
N.B. default implementation of Jackson2ObjectMapperBuilder already registers these date modules automatically the problem with dates was not related, I ended with
#Override
protected void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(Jackson2ObjectMapperBuilder
.json()
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.build()));
}
to achieve simple date serialization.

Spring Boot 1.5.4 Filter out null values in json response

I am using Spring Boot 1.5.4 version . I am using spring-ws getWebServiceTemplate() to make a webservice call. The SOAP response has lot of null values for the fields.
I am trying to filter out the null values in the JSON response. None of the following approaches seem to work:
Setting the property in the application.properties:
spring.jackson.default-property-inclusion:NON_NULL`
Setting it in Configuration class using Jackson2ObjectMapperBuilder:
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
builder.serializationInclusion(JsonInclude.Include.NON_EMPTY);
return builder;
}
Please advise.
lva
I am using Spring Boot 1.5.6.RELEASE version, and you can reference
customize-the-jackson-objectmapper
Following code is work:
#SpringBootApplication
public class Application {
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
return builder;
}
public static void main(String[] args) {
SpringApplication.run(DbeeApiApplication.class, args);
}
}
Or you can filter from MappingJackson2HttpMessageConverter, for example:
#Configuration
class WebMvcConfiguration extends WebMvcConfigurationSupport {
#Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for(HttpMessageConverter<?> converter: converters) {
if(converter instanceof MappingJackson2HttpMessageConverter) {
ObjectMapper mapper = ((MappingJackson2HttpMessageConverter)converter).getObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
}
}
}
}
Using the following in application.properties worked.
spring.jackson.default-property-inclusion=NON_NULL

How to autowire dependency within a Spring Boot Converter?

I am struggling with a way to autowire a dependency within a converter class using spring boot. What is the most elegant solution to solve this problem?
Configuration
#Configuration
public class Config {
#Bean
public ConversionServiceFactoryBean conversionFacilitator() {
ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean();
factory.setConverters(getConverters());
return factory;
}
private Set<Converter> getConverters() {
Set<Converter> converters = new HashSet<>();
converters.add(new MyConverter());
return converters;
}
}
Converter class
#Component
public class MyConverter implements Converter<Type1, Type2> {
#Autowired
private Dependency dependency; // Null here due to the component not being injected
#Override
public Type2 convert(Type1 type1) {
return dependency.something(type1);
}
}
The dependency is not being injected because you are creating MyConverter with new, instead of let Spring create it.
You do not need a method to return set of converters. Spring can do it for you, just auto wiring it. Spring is smart enough to give you a set with all the converter implementations it finds.
You should use something like:
#Configuration
public class Config {
#Bean
#Autowired
public ConversionServiceFactoryBean conversionFacilitator(Set<Converter> converters) {
ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean();
factory.setConverters(converters);
return factory;
}
}

Integrating spring-boot with RESTEasy

I am trying to prototype a Spring Boot application. I'm coming from a Guice JAX-RS application, so I prefer the standard JAX-RS annotations to Spring MVC. I've gotten Jetty up and serving:
#Configuration
#Import({ResteasyBootstrap.class, SpringBeanProcessorServletAware.class, HttpServletDispatcher.class})
public class EmbeddedJetty {
#Bean
#Singleton
public EmbeddedServletContainerFactory servletContainer() {
JettyEmbeddedServletContainerFactory factory = new JettyEmbeddedServletContainerFactory();
factory.setPort(9000);
factory.setSessionTimeout(10, TimeUnit.MINUTES);
return factory;
}
}
However, I just can't figure out how to get RESTEasy hooked up correctly. With the above SpringBeanProcessorServletAware it bails, seemingly the ServletContext is not injected through ServletContextAware before it ends up being used:
java.lang.NullPointerException: null
at org.jboss.resteasy.plugins.spring.SpringBeanProcessorServletAware.getRegistry(SpringBeanProcessorServletAware.java:30)
at org.jboss.resteasy.plugins.spring.SpringBeanProcessor.postProcessBeanFactory(SpringBeanProcessor.java:247)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:284)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:174)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:680)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:522)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766)
I also tried using the SpringContextLoaderListener, but that seems to conflict with the spring-boot AnnotationConfigEmbeddedWebApplicationContext class.
I'm using spring-boot 1.3.3 and spring-framework 4.3.0.rc1
The other answer won't have your resources as spring beans, this autoconfiguration will integrate them properly:
The Configuration class:
#Configuration
#ConditionalOnWebApplication
public class RestEasyAutoConfigurer {
private Environment environment;
#Bean(name = "resteasyDispatcher")
public ServletRegistrationBean resteasyServletRegistration() {
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HttpServletDispatcher(), getPrefix()
+ "/*");
registrationBean.setInitParameters(ImmutableMap.of("resteasy.servlet.mapping.prefix", "/rs/")); // set prefix here
registrationBean.setLoadOnStartup(1);
return registrationBean;
}
#Bean(destroyMethod = "cleanup")
public static RestEasySpringInitializer restEasySpringInitializer() {
return new RestEasySpringInitializer();
}
#Bean
// use Spring Boot configured Jackson
public CustomResteasyJackson2Provider jackson2Provider(ObjectMapper mapper) {
return new CustomResteasyJackson2Provider(mapper);
}
public static class RestEasySpringInitializer
implements
ServletContextInitializer,
ApplicationContextAware,
BeanFactoryPostProcessor {
private ResteasyDeployment deployment;
private ConfigurableApplicationContext applicationContext;
private ConfigurableListableBeanFactory beanFactory;
public void cleanup() {
deployment.stop();
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
ListenerBootstrap config = new ListenerBootstrap(servletContext);
deployment = config.createDeployment();
deployment.start();
servletContext.setAttribute(ResteasyProviderFactory.class.getName(), deployment.getProviderFactory());
servletContext.setAttribute(Dispatcher.class.getName(), deployment.getDispatcher());
servletContext.setAttribute(Registry.class.getName(), deployment.getRegistry());
SpringBeanProcessor processor = new SpringBeanProcessor(deployment.getDispatcher(),
deployment.getRegistry(), deployment.getProviderFactory());
processor.postProcessBeanFactory(beanFactory);
applicationContext.addApplicationListener(processor);
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
}
}
}
And the Jackson provider:
#Provider
#Consumes({"application/*+json", "text/json"})
#Produces({"application/*+json", "text/json"})
public class CustomResteasyJackson2Provider extends ResteasyJackson2Provider {
private ObjectMapper mapper;
public CustomResteasyJackson2Provider(ObjectMapper mapper) {
this.mapper = mapper;
}
#Override
public ObjectMapper locateMapper(Class<?> type, MediaType mediaType) {
return Optional.ofNullable(_mapperConfig.getConfiguredMapper()).orElse(mapper);
}
}
NOTE: this is a working configuration for Spring Boot 1.3.3 / RESTEasy 3.0.16
You can use RESTEasy Spring Boot starter. Here is how you do it:
Adding POM dependency
Add the Maven dependency below to your Spring Boot application pom file.
<dependency>
<groupId>com.paypal.springboot</groupId>
<artifactId>resteasy-spring-boot-starter</artifactId>
<version>2.1.1-RELEASE</version>
<scope>runtime</scope>
</dependency>
Registering JAX-RS application classes
Just define your JAX-RS application class (a subclass of Application) as a Spring bean, and it will be automatically registered. See the example below. See section JAX-RS application registration methods in How to use RESTEasy Spring Boot Starter for further information.
package com.test;
import org.springframework.stereotype.Component;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#Component
#ApplicationPath("/sample-app/")
public class JaxrsApplication extends Application {
}
Registering JAX-RS resources and providers
Just define them as Spring beans, and they will be automatically registered. Notice that JAX-RS resources can be singleton or request scoped, while JAX-RS providers must be singletons.
Further information at the project GitHub page.
Here is fully working example.
First, a sample JAX-RS endpoint:
#Path("/api")
public class SampleResource {
#GET
#Path("/sample")
#Produces(MediaType.APPLICATION_JSON)
public String getSample() {
return "Some JSON";
}
}
Next, a JAX-RS configuration class that loads all endpoints.
import javax.ws.rs.core.Application;
public class RestEasyConfig extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(SampleRest.class);
return classes;
}
}
Finally, in your Spring configuration, initialize RESTEast filter and inform the framework about its existence.
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.jboss.resteasy.plugins.server.servlet.FilterDispatcher;
...
#Bean
public FilterRegistrationBean filterRegistrationBean() {
Map<String, String> initParams = new HashMap<>();
initParams.put("javax.ws.rs.Application", RestEasyConfig.class.getCanonicalName());
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new FilterDispatcher());
registrationBean.setInitParameters(initParams);
return registrationBean;
}
Your endpoint should be up and running. If you are missing the FilterDispatcher class on your class path, add the resteasy-jaxrs library to your build descriptor.

Using Jackson in Jersey with multiple configured ObjectMappers

Is it possible to setup Jersey using Jackson for serialization/deserialization using multiple configured ObjectMappers?
What I would like to be able to do is register a "default" Jackson ObjectMapper and then have the ability to register another feature which provides an ObjectMapper with some specialized configuration which under certain circumstance will "override" the "default" ObjectMapper.
For example, this ContextResolver would be for the "default" mapper:
#Provider
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class JacksonMapperProvider implements ContextResolver<ObjectMapper> {
private final ObjectMapper mObjectMapper;
public JacksonMapperProvider() {
mObjectMapper = createMapper();
}
protected abstract ObjectMapper createMapper() {
ObjectMapper mapper = createMapper();
return mapper
.setSerializationInclusion(Include.ALWAYS)
.configure(JsonParser.Feature.ALLOW_COMMENTS, true)
.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mObjectMapper;
}
}
And this ContextResolver would be to override the "default" mapper:
#Provider
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class SpecializedMapperProvider implements ContextResolver<ObjectMapper> {
private final ObjectMapper mObjectMapper;
public SpecializedMapperProvider() {
mObjectMapper = createMapper();
}
protected abstract ObjectMapper createMapper() {
ObjectMapper mapper = createMapper();
return mapper
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"))
.registerModule(new SpecializedModule1())
.registerModule(new SpecializedModule2());
}
#Override
public ObjectMapper getContext(Class<?> type) {
if(SomeType.isAssignableFrom(type)) {
return mObjectMapper;
}
return null;
}
}
I see in the JacksonJsonProvider code that Jackson supports ObjectMapper provider injection/resolution. However, in practice, what I am seeing is that the "order" of the providers seems random (I'm guessing it's not, but I can't sort out how to control the order). Sometimes the "override" comes before the "default" and everything works, but on the next server startup the order changes.
I have attempted to get this to work in a number of ways including:
Registering the ContextResolver<ObjectMapper> implementations manually (in differing orders)
Registering the ContextResolver<ObjectMapper> implementations via #Provider annotations
Specifying a priority when registering
I am using the following:
Jersey 2.8
Jackson 2.3.3
Perhaps I am taking a completely incorrect approach?
Is there a better way to achieve what I am trying to do?
Maybe I should just define two separate JAX-RS applications and have a single ObjectMapper configuration for each?
You can configure the order of providers, but it would actually be best to use one provider in this situation:
#Provider
public class JacksonMapperProvider implements ContextResolver<ObjectMapper> {
private final ObjectMapper defaultMapper;
private final ObjectMapper specializedMapper;
public JacksonMapperProvider() {
defaultMapper = createDefaultMapper();
specializedMapper = createSpecializedMapper();
}
private static ObjectMapper createDefaultMapper() {
return new ObjectMapper()
.setSerializationInclusion(Include.ALWAYS)
.configure(JsonParser.Feature.ALLOW_COMMENTS, true)
.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
}
private static ObjectMapper createSpecializedMapper() {
return new ObjectMapper()
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"))
.registerModule(new SpecializedModule1())
.registerModule(new SpecializedModule2());
}
#Override
public ObjectMapper getContext(Class<?> type) {
if (SomeType.isAssignableFrom(type)) {
return specializedMapper;
}
else {
return defaultMapper;
}
}
}
The newest way is
new ObjectMapper().configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
to get ALLOW_UNQUOTED_FIELD_NAMES in recent versions of jackson.

Resources