Spring Boot: HATEOAS and custom JacksonObjectMapper - java

After I added dependency for HATEOAS to Maven, Spring Boot does not start:
Added dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
Full pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>ru.example</groupId>
<artifactId>testapp</artifactId>
<version>1.0</version>
<name>testapp</nAfter I added dependency for HATEOAS to Maven, Spring Boot does not startame>
<description>Test</description>
<properties>
<java.version>1.8</java.version>
<h2.version>1.4.200</h2.version>
<jackson-json.version>2.10.2</jackson-json.version>
<jsoup.version>1.12.1</jsoup.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.17.Final</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>5.3.0.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-json.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>${jackson-json.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson-json.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
SecurityConfig.class:
package ru.example.testapp;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import ru.example.testapp.dao.UserRepository;
import ru.example.testapp.service.UserServiceImpl;
#Configuration
#RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserRepository userRepository;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(new UserServiceImpl(userRepository))
.passwordEncoder(new BCryptPasswordEncoder());
}
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/rest/admin/**").hasRole("ADMIN").and().httpBasic().and()
.authorizeRequests()
.antMatchers("/rest/user/**").hasAnyRole("USER","ADMIN").and().httpBasic().and()
.authorizeRequests().and()
.csrf().ignoringAntMatchers("/rest/**");
}
}
JacksonObjectMapper.class:
package ru.example.testapp.util.json;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.stereotype.Component;
#Component
public class JacksonObjectMapper extends ObjectMapper {
private static final ObjectMapper MAPPER = new JacksonObjectMapper();
public static ObjectMapper getMapper() {
return MAPPER;
}
private JacksonObjectMapper() {
registerModule(new Hibernate5Module());
registerModule(new JavaTimeModule());
configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
}
In console I have following error:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'securityConfig': Unsatisfied dependency
expressed through method 'setContentNegotationStrategy' parameter 0;
nested exception is
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name
'org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration':
Unsatisfied dependency expressed through method 'setConfigurers'
parameter 0; nested exception is
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'hypermediaWebMvcConfigurer' defined in
class path resource
[org/springframework/hateoas/config/WebMvcHateoasConfiguration.class]:
Unsatisfied dependency expressed through method
'hypermediaWebMvcConfigurer' parameter 0; nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'hypermediaWebMvcConverters' defined in class
path resource
[org/springframework/hateoas/config/HateoasConfiguration.class]: Bean
instantiation via factory method failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate [org.springframework.hateoas.config.WebConverters]:
Factory method 'hypermediaWebMvcConverters' threw exception; nested
exception is java.lang.IllegalStateException: Failed copy():
ru.example.wmanage.util.json.JacksonObjectMapper (version: 2.10.2)
does not override copy(); it has to
What could be the problem?
In error - something with securityConfig, JacksonObjectMapper and hateoas.
If I delete spring-boot-starter-hateoas at dependencies, then all works. But I need hateoas.
Please help.
UPDATED:
The problem occurs when using custom JacksonObjectMapper with annotation #Component. As soon as spring-boot-starter-hateoas added to dependencies, then Spting Boot does not startup.
QUESTION: How to use custom JacksonObjectMapper and hateoas together?
PROBLEM NOT RESOLVED

I don't think you have any reason to be extending ObjectMapper. You should instantiate an ObjectMapper like normal and then configure it by its exposed methods and register it as a bean in your configuration.
#Bean
public ObjectMapper createMapper() {
return new ObjectMapper().registerModule(new Hibernate5Module());
.registerModule(new JavaTimeModule());
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}

I dont know why one should not extend ObjectMapper.
probably Deadron Mar can explain, but this class is not final.
In my case it is extended deeply in our corporal framework and i got same error message.
i just did another extension
#Component
public class MyCustomizedMapper extends AnotherExtendedMapper {
public MyCustomizedMapper copy() {
return new MyCustomizedMapper(); // we have default constructor for this
` }
}
and wonder - it works)

Related

Spring Boot app: Could not resolve placeholder in application.properties?

I have a spring boot app. It works when fine when I click "run" button in SpringToolSuit4.
But when I run as "maven install", I got the error message:
<error message="Failed to load ApplicationContext" type="java.lang.IllegalStateException">java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'homeController': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'envVersionNum' in value "${envVersionNum}"
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'envVersionNum' in value "${envVersionNum}"
</error>
The content of my application properties:
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.jpa.show-sql=true
spring.thymeleaf.cache=false
version=${envVersionNum}
If I change it to
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.jpa.show-sql=true
spring.thymeleaf.cache=false
version=1.1.1
It works fine. But I want to get the envVersionNum from user, which is stored in .bash_profile (export envVersionNum= 1.1.1).
the content of pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jrp</groupId>
<artifactId>project-management</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>project-management</name>
<description>project management application</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
The code of homeController:
package com.jrp.pma.controllers;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jrp.pma.dao.EmployeeRepository;
import com.jrp.pma.dao.ProjectRepository;
import com.jrp.pma.dto.ChartData;
import com.jrp.pma.dto.EmployeeProject;
import com.jrp.pma.entities.Project;
#Controller
public class HomeController {
#Value("${version}")
private String ver;
#Autowired
ProjectRepository proRepo;
#Autowired
EmployeeRepository empRepo;
#GetMapping("/")
public String displayHome(Model model) throws JsonProcessingException {
model.addAttribute("versionNumber", ver);
Map<String, Object> map = new HashMap<>();
// we are querying the database for projects
List<Project> projects = proRepo.findAll();
model.addAttribute("projectsList", projects);
List<ChartData> projectData = proRepo.getProjectStatus();
// Lets convert projectData object into a json structure for use in javascript
ObjectMapper objectMapper = new ObjectMapper();
String jsonString = objectMapper.writeValueAsString(projectData);
//[["NOTSTARTED", 1], ["INPROGRESS", 2], ["COMPLETED", 1]]
model.addAttribute("projectStatusCnt", jsonString);
// we are querying the database for employees
List<EmployeeProject> employeesProjectCnt = empRepo.employeeProjects();
model.addAttribute("employeesListProjectsCnt", employeesProjectCnt);
return "main/home";
}
}
Any suggestions gratefully accepted.
If you're setting any env variable in export command then it it visible only through the command line only.
If you're using Eclipse or any other IDE then it won't get the values from the export command because the scope of the export is inside the terminal.
Try by running your jar after exporting the variables in command line it will work.

How to fix Error creating bean with name 'requestMappingHandlerMapping'

I'm creating a web application using spring-boot with annotation based configuration.
At first, I created a spring application with annotation based config using Hibernate and JPA. It was working fine but then I needed to add spring-boot to my application and now cannot start it. Here is the code:
#SpringBootApplication
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class Runner {
public static void main(String[] args) {
SpringApplication.run(Runner.class, args);
}
}
#Configuration
#EnableTransactionManagement
#ComponentScans(value = { #ComponentScan("com.alex.pharm.dao"),
#ComponentScan("com.alex.pharm.service") })
public class AppConfig {
#Bean
public LocalEntityManagerFactoryBean geEntityManagerFactoryBean() {
LocalEntityManagerFactoryBean factoryBean = new LocalEntityManagerFactoryBean();
factoryBean.setPersistenceUnitName("LOCAL_PERSISTENCE");
return factoryBean;
}
#Bean
#ConditionalOnMissingBean(type = "JpaTransactionManager")
public JpaTransactionManager geJpaTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(geEntityManagerFactoryBean().getObject());
return transactionManager;
}
}
#RestController
public class HelloController {
#RequestMapping("/")
public String index() {
return "Greetings from Spring Boot!";
}
}
<groupId>com.sachatol.pharmacy</groupId>
<artifactId>pharmacyWebStore</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<!-- Hibernate 5.2.9 Final -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.9.Final</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- Spring ORM -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
But when I try to run it I get the following exception:
ConfigServletWebServerApplicationContext : Exception encountered during context initialization
- cancelling refresh attempt:org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'requestMappingHandlerMapping'
defined in class path resource
[org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]:
Initialization of bean failed; nested exception is java.lang.NoSuchMethodError:
org.springframework.web.servlet.handler.AbstractHandlerMapping.obtainApplicationContext()
Lorg/springframework/context/ApplicationContext;
How can I fix it?
Issue found - I used the old version for spring-context in pom (4.3.7.RELEASE) which override the one that spring-boot has and was the reason of NoSuchMethodError. So spring-context dependency was old and obsolete after removing or changing it to newer version (5.0.3) application started fine
Your #RequestMapping seems missing some parts, try with something like that:
#RequestMapping(value = "/", method = RequestMethod.GET)
This also happens if you have two methods with same request mapping values. Make sure you have unique RequestMapping values.
In your request mapping, you have incorrectly used :
#RequestMapping("/")
Instead, use this mapping to specify the end-point and the request-method like this :
#RequestMapping(value = "/", method = RequestMethod.GET)
Hope it will solve your problem.

#QuerydslPredicate throwing exception

I'm trying to understand how to use #QuerydslPredicate but my test API fails when it is called:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: No primary or default constructor found for interface com.querydsl.core.types.Predicate] with root cause
java.lang.NoSuchMethodException: com.querydsl.core.types.Predicate.< init >()
This is the method in my Controller class:
#ResponseBody
#RequestMapping(value = "/user/query", method = RequestMethod.GET)
public Iterable<User> getByCriteria(#QuerydslPredicate(root = User.class) Predicate predicate)
{
return this.userDao.getByCriteria(predicate);
}
I've used this spring blog post and this example to try and implement my API but I don't understand what I am doing wrong.
Edit #1
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>email-encrypt</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
</parent>
<name>email-encrypt</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<querydsl.version>4.1.4</querydsl.version>
<springfox.version>2.6.1</springfox.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.59</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.59</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcmail-jdk15on</artifactId>
<version>1.59</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.14.1.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${springfox.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>${build.directory}/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Edit #2
I've create a sample project here.
If you comment out the #Configuration on the swagger configuration class it will work. I'm still trying to understand why, I guess that with that annotation the way spring loads the configuration is different and this is causing the issue.
//#Configuration
#EnableSwagger2
public class SwaggerConfiguration extends WebMvcConfigurationSupport
I think your problem lies in your pom.xml. Be sure you are using compatible version of query-dsl. For instance if you use spring-data-jpa 2.0.8, you should use querydsl-* 4.1.4+
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-*</artifactId>
<version>4.1.4</version>
</dependency>
you can check in maven repository which version you need
Edit 1 try to add the querydsl-core to your maven:
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-core</artifactId>
<version>4.1.4</version>
</dependency>
Extending on #cristobalrosa's answer, this might be due to web application not being configured by Spring Boot. For instance my project also had a SwaggerConfig extending WebMvcConfigurationSupport:
#Configuration
#EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurationSupport {
// Docket bean
#Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
}
}
I removed the inheritance and manual resource handlers, and it works fine now.
Note: In addition to WebMvcConfigurationSupport, things like #EnableWebMvc & WebMvcConfigurer might also lead to Spring Boot's web autoconfiguration not being used.
Sources: Swagger Issue comment
Same error with me.
I just want to share the situation to may give some hints. So look at my config and dependencies and read articles that I linked. try #EnableWebMvc instead of 'WebMvcConfigurationSupport'
Error
java.lang.IllegalStateException: No primary or default constructor found for interface com.querydsl.core.types.Predicate
Caused by: java.lang.NoSuchMethodException: com.querydsl.core.types.Predicate.<init>()
at java.base/java.lang.Class.getConstructor0(Class.java:3427)
at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2631)
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.createAttribute(ModelAttributeMethodProcessor.java:216)
dependencies
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation "org.springframework.data:spring-data-commons"
implementation "com.querydsl:querydsl-apt:4.3.1"
implementation "com.querydsl:querydsl-jpa:4.3.1"
}
Querydsl web support is available in spring-data-commons since 1.11
from https://www.baeldung.com/rest-api-search-querydsl-web-in-spring-data-jpa
Web Mvc Config
In my case, I have to implements WebMvcConfigurer and add #EnableWebMvc instead of WebMvcConfigurationSupport. I don't know why #EnableWebMvc is needed even I had extended WebMvcConfigurationSupport with #Configuration. I just guess WebMvcConfigurationSupport doesn't implements init() of Predicate.
#Configuration
//#EnableSpringDataWebSupport // <-- doesn't necessary for me
#EnableSwagger2
#EnableWebMvc // <-- add
public class SwaggerConfig implements WebMvcConfigurer { //<-- instead of 'WebMvcConfigurationSupport'
...
}
QueryDSL web support From Spring document
The feature will be automatically enabled along #EnableSpringDataWebSupport when Querydsl is found on the classpath.
https://docs.spring.io/spring-data/jpa/docs/1.9.0.RELEASE/reference/html/#core.web.type-safe
Controller
#GetMapping
public List<InputMethodDto.Response> getInputMethodTypeList(
#QuerydslPredicate(root = InputMethod.class) Predicate predicate) {
return service.getInputMethodList(predicate);
}
Repository
public interface InputMethodRepository extends JpaRepository<yourEntity, Long>, QuerydslPredicateExecutor<yourEntity>, QuerydslBinderCustomizer<QyourEntity> {
#Override
default void customize(final QuerydslBindings bindings, final QyourEntity root) {
bindings.bind(String.class).first((StringPath path, String value)-> path.eq(value));
}
}

Can't get any unit tests to run in Spring Boot

I'm trying to write a simple unit test. This test function will call my service, which grabs info from the database, pushes it to a list and returns it. I've looked high and low in the debug logs to find what could be causing, but it seems nothing on the web is helping me out. I'm not too familiar with Spring Boot, but it seems surprising that all this effort and I am unable to get a simple unit test to pass.
Error Logs
http://text-share.com/view/fb0369c3
Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed;
Example Test
package com.algoq.algoq;
import com.algoq.algoq.models.Subscriber;
import com.algoq.algoq.respositories.SubscriberRepository;
import com.algoq.algoq.services.AlgorithmService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.ArrayList;
import static org.assertj.core.api.Assertions.assertThat;
#RunWith(SpringRunner.class)
#TestConfiguration
#SpringBootTest(classes = {
AlgoQApplication.class,
})
public class ExampleTest extends AlgoQApplicationTests {
#Autowired
private AlgorithmService aService;
#MockBean
private SubscriberRepository employeeRepository;
#Bean
public AlgorithmService aService() {
return new AlgorithmService();
}
#Test
public void subscriberListNull() throws Exception {
ArrayList<Subscriber> subs = aService.getSubscribers();
assertThat(subs).isEmpty();
}
}
Service
package com.algoq.algoq.services;
import com.algoq.algoq.models.Subscriber;
import com.algoq.algoq.respositories.SubscriberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
#Service
public class AlgorithmService {
#Autowired
private SubscriberRepository subRep;
/**
* Gets a list of subscribers to return to the API
* #return
*/
public ArrayList<Subscriber> getSubscribers() {
ArrayList<Subscriber> subscribers = new ArrayList<>();
subRep.findAll()
.forEach(subscribers::add);
return subscribers;
}
/**
* Adds a new subscriber to the database
* #param sub
* #return
*/
public void addSubscriber(Subscriber sub) {
subRep.save(sub);
}
/**
* Finds a single user id
* #param email
* #return
*/
public List<Subscriber> getSubscriber(String email) {
return subRep.findByEmailAddress(email);
}
}
Application
package com.algoq.algoq;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#SpringBootApplication
//#EnableScheduling
public class AlgoQApplication {
public static void main(String[] args) {
SpringApplication.run(AlgoQApplication.class, args);
}
}
POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.algoQ</groupId>
<artifactId>algo-q</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>algo-q</name>
<description>An algorithm a day keeps the brain up to date</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.12</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
<!--<dependency>-->
<!--<groupId>com.h2database</groupId>-->
<!--<artifactId>h2</artifactId>-->
<!--<version>1.4.194</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.pygments</groupId>
<artifactId>pygments</artifactId>
<version>1.5</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
The real cause of the exception is
java.lang.ClassNotFoundException: javax.xml.bind.JAXBException
So I believe you need to add JAXBE library to your classpath (it was removed from JDK in java9 and I guess you are using java9)
Try to add this to your pom file
<!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
JDK
/Library/Java/JavaVirtualMachines/jdk-9.0.1.jdk/Contents/Home/bin/java
pom.xml
1.8
Are you using java 8 or 9 ?
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'entityManagerFactory' defined in class path
resource
[org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]:
Invocation of init method failed; nested exception is
java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
Issue with java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException ,
Run the application with Java 8 environement or Include the JAXB library for Java 9, JAXB API are not included in JAVA SE 9. Refer Deprecated, for removal: This API element is subject to removal in a future version.. Use --add-modules ( --add-modules java.xml.bind ) to add module in classpath. Jave has only deprecated and does not add javax.xml.bind module on classpath by default.
Otherwise include as dependency through maven.
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>

SpringData-MongoDB: No qualifying bean of type available

I'm trying to structure a project to connect to MongoDB using Spring Data as below:
SpringMongoConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import com.mongodb.MongoClient;
#Configuration
public class SpringMongoConfig {
#Bean
public MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(new MongoClient("127.0.0.1"), "ReconInput");
}
#Bean
public MongoTemplate mongoTemplate() throws Exception {
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory());
return mongoTemplate;
}
}
ReconInputRepository.java:
#Repository
public interface ReconInputRepository extends MongoRepository<ReconInput, String> {
public List<ReconInput> findByReportingDate(String reportingDate);
}
ReconInputService.java
public interface ReconInputService {
public List<ReconInput> getInputByReportingDate(String reportingDate);
}
ReconInputServiceImpl.java
#Service
public class ReconInputServiceImpl implements ReconInputService {
#Autowired
private ReconInputRepository reconInputRepository;
public List<ReconInput> getInputByReportingDate(String reportingDate) {
return reconInputRepository.findByReportingDate(reportingDate);
}
}
App.java
public class App
{
public static void main( String[] args )
{
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
ReconInputService reconInputService = ctx.getBean(ReconInputService.class);
List<ReconInput> inputData = reconInputService.getInputByReportingDate("2017 Nov 20");
System.out.println(inputData.get(0).getReportId());
}
}
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ups.mongodb</groupId>
<artifactId>MongoConnection</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MongoConnection</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>5.0.1.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.5.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-mongodb -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
</dependencies>
</project>
When I run the project, it throw an exception:
No qualifying bean of type 'ups.mongo.service.ReconInputService'
available.
Please help me any suggestion for this error. Thank you !
Update 1
Added #ComponentScan(basePackages = "ups.mongo") to SpringMongoConfig.java.
Then I got new issue:
Error creating bean with name 'ReconInputService': Unsatisfied dependency expressed through field 'reconInputRepository';
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'ups.mongo.repository.ReconInputRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Update 2
Instead of using Spring Data MongoRepository. I replaced ReconInputRepository.java that extends from Spring data by implement it by myself as below :
ReconInputRepository.java
public interface ReconInputRepository {
public List<ReconInput> findByReportingDate(String reportingDate);
}
ReconInputRepositoryImpl.java
#Repository
public class ReconInputRepositoryImpl implements ReconInputRepository {
#Autowired
MongoTemplate mongoTemplate;
public List<ReconInput> findByReportingDate(String reportingDate) {
List<ReconInput> reconInputList = null;
Query searchUserQuery = new Query(Criteria.where("reportingDate").is(reportingDate));
reconInputList = mongoTemplate.find(searchUserQuery, ReconInput.class);
return reconInputList;
}
}
Then it work correctly.
My Summary
The issue may come from Spring does not support inject interface - as #amdg suggest (but work in spring boot - I have no idea why, if someone know that please leave me some comment).
Reference: Spring interface injection example
Update 3
At last, I found the most simple way to make it correctly.
All I need to do is adding #EnableMongoRepositories({ "ups.mongo.repository" }) to the SpringMongoConfig.java
As I suspected, you mix plain old Spring with Spring Boot and want to get Spring Boot effect.
In order to use Spring Boot you should update your dependencies to use Spring Boot Starters.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ups.mongodb</groupId>
<artifactId>MongoConnection</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MongoConnection</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Then just add your config in the root package of your app (presumably it's ups.mongo):
#SpringBootApplication
#EnableMongoRepositories
public class App
{
public static void main( String[] args )
{
ConfigurableApplicationContext ctx = SpringApplication.run(App.class, args);
ReconInputService reconInputService = ctx.getBean(ReconInputService.class);
List<ReconInput> inputData = reconInputService.getInputByReportingDate("2017 Nov 20");
System.out.println(inputData.get(0).getReportId());
}
}
In this case, you do not even need SpringMongoConfig.class. Instead, add the following config in your application.properties:
spring.data.mongodb.host=127.0.0.1
spring.data.mongodb.port=27017
spring.data.mongodb.database=demo
Since you have annotated #Service on ReconInputServiceImpl so,
please add ReconInputServiceImpl.class in main class
ReconInputService reconInputService = ctx.getBean(ReconInputServiceImpl.class);
You can annotate your ReconInputServiceImpl class as #Service("reconInputService").
You can then access it as follows.
ReconInputService reconInputService = ctx.getBean("reconInputService",ReconInputService.class);
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
If you use above constructor, DI container will only load bean definitions from SpringMongoConfig class.
If You want DI container to scan all bean definitions recursively for a particular base package please use this constructor instead
AnnotationConfigApplicationContext(java.lang.String... basePackages)
I have previously added only spring-data-mongodb package which kept me busying debugging for "No qualifying bean for some repository".
After adding spring-boot-starter-data-mongodb, everything is handled by spring.
application.yaml
spring:
data:
mongodb:
uri: mongodb://username:password#localhost:27017
database: test
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>

Categories