Using lombok dependency in my spring-boot project but the getter method yields error at runtime (built successfully though) - spring-boot

I am developing a spring-boot project using Gradle as the build tool on ItelliJ IDE.
I have the dependency of lombok declared in gradle.build:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web:2.5.3'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
compileOnly 'org.projectlombok:lombok:1.18.20'
}
I have a model class:
import lombok.Data;
import java.math.BigDecimal;
#Data
public class ProductModel {
private String name;
private BigDecimal price;
private Integer quantity;
}
As you can see I have annotated with #Data.
My controller has the method to handle a POST request, its payload is mapped to the ProductModel:
#PostMapping
public String createProduct(#RequestBody ProductModel productPayload) {
// Runtime error: error: cannot find symbol, 'getName' in 'ProductModel'
productPayload.getName();
}
I know I need to install the lombok plugin on my IntelliJ IDE in order to avoid compiler error on the getter method. So I did that. But when I run my application I get error:
error: cannot find symbol
symbol: method getName()
location: variable productPayload of type CreateProductRestModel
I also tried change the dependency from compileOnly to implementation:
implementation 'org.projectlombok:lombok:1.18.20'
It doesn't help. Why is that? What am I missing?
(I have enabled annotationProcessor on my IntelliJ too)

In order for Gradle to pick up on annotation processors, they have introduced a separate configuration that will generate all the new code ahead of the "normal" compilation.
For Lombok, it would look something like this:
dependencies {
compileOnly 'org.projectlombok:lombok:1.18.20'
annotationProcessor 'org.projectlombok:lombok:1.18.20'
testCompileOnly 'org.projectlombok:lombok:1.18.20'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.20'
}

Related

Added Vaadin to Java Spring project in IntelliJ and getting build error with add()

I have created a new project with Spring Initializr:
Project: Gradle Project
Language: Java
Spring Boot: 2.7.4
Packaging: JAR
Java: 8
Dependencies:
Spring Boot Actuator, Spring Data JPA, Spring Web, H2 Database, PostgresSQL Driver, Spring Configuration Processor
After this I added some code to be able to interact with REST APIs (GET & POST). I was able to build, run, & test the project.
The next step was add Vaading, so I did the following:
Created a new package "views.main" package under the source section.
Added a MainView.java class with the following contents:
package io.enfuse.demo.fundemo.views.main;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.Route;
#Route("")
public class MainView {
public MainView() {
VerticalLayout todosList = new VerticalLayout();
TextField taskField = new TextField();
Button addButton = new Button("Add");
addButton.addClickListener(click -> {
Checkbox checkbox = new Checkbox(taskField.getValue());
todosList.add(checkbox);
});
addButton.addClickShortcut(Key.ENTER);
add(
new H1("Vaadin Todo"),
todosList,
new HorizontalLayout(
taskField,
addButton
)
);
}
}
I also updated the build.gradle file to include Vaadin items:
plugins {
id 'org.springframework.boot' version '2.7.4'
id 'io.spring.dependency-management' version '1.0.14.RELEASE'
id 'com.vaadin' version '23.2.1'
id 'java'
}
group = 'io.enfuse.demo'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
ext {
set('vaadinVersion', "23.2.1")
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.vaadin:vaadin-spring-boot-starter'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
imports {
mavenBom "com.vaadin:vaadin-bom:${vaadinVersion}"
}
}
tasks.named('test') {
useJUnitPlatform()
}
Restart IntelliJ
At this point when I go to build I get the following error:
```
C:\Temp\fundemo_v2\fundemo\src\main\java\io\enfuse\demo\fundemo\views\main\MainView.java:24: error: cannot find symbol
add(
^
symbol: method add(H1,VerticalLayout,HorizontalLayout)
location: class MainView
```
I can look at the dependecies that show Vaadin is included:
What is missing exactly is not setup correctly?
Your MainView does not extend a Vaadin component and that's why there is no add method.
Try this:
public class MainView extends Div() {

How resolve error injecting bean MapStruct in Spring

I'm trying to Inject my mapper using mapstruct, but spring doesn't recognize the bean.
There is my mapper
package com.api.gestioncartera.Services.Mappers;
import org.mapstruct.Mapper;
import org.springframework.stereotype.Component;
import com.api.gestioncartera.Entities.CollectionCompany;
import com.api.gestioncartera.Services.DTO.CollectionCompanyDto;
#Mapper(componentModel = "spring")
public interface CollectionCompanyMapper {
CollectionCompanyDto collectionCompanyToCollectionCompanyDto(CollectionCompany collectionCompany);
}
There is my Service where I'm trying to inject it
#Service
#Transactional
public class CollectionCompanyServiceImp implements CollectionCompanyService{
#Autowired
private CollectionCompanyMapper companyMapper;
}
My gradle config
plugins {
id 'org.springframework.boot' version '2.5.6'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
...
dependencies {
...
implementation 'org.mapstruct:mapstruct:1.4.2.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}
compileJava {
options.compilerArgs += [
'-Amapstruct.suppressGeneratorTimestamp=true',
'-Amapstruct.suppressGeneratorVersionInfoComment=true',
'-Amapstruct.verbose=true',
'-Amapstruct.defaultComponentModel=spring'
]
}
I also enable enable annotation processing in the IDE
Properties in the IDE
The error is:
Consider defining a bean of type 'com.api.gestioncartera.Services.Mappers.CollectionCompanyMapper' in your configuration.
I noticed that I don't have any plugin referencing mapstruct, can be this the problem? Image:
I'm using Spring Tool Suite 4 (Eclipse) + Gradle 6.8 + SrpingBoot 2.5.6
Please help!!
Eclipse has its problems with annotation processing.
I solved the issue with my projects using this plugin:
https://plugins.gradle.org/plugin/
Add this to the top of your gradle plugins.
plugins {
id "eclipse"
id "com.diffplug.eclipse.apt" version "3.37.1"
}
then do a gradle refresh.
If it‘s still not working, run
./gradlew eclipse eclipseJdtApt eclipseFactorypath
Hope this helps!

Springboot Webflux endpoint not found by test - what is causing it?

I have the following code, and am consistently receiving 404 not found errors? Any advice would be much appreciated!
I've researched conflicting dependencies which does not seem to be the problem. I've also ensured that I am returning the correct content type.
One thing that I am not sure I'm doing correctly is annotating with the Bean and Autowired annotations. I have little understanding of what those do at the moment.
Router class below
#Configuration
#AllArgsConstructor
public class AgencyRouter {
#Bean
public RouterFunction<ServerResponse> agencyRoutes(AgencyController agencyController) {
return RouterFunctions
.route(RequestPredicates.POST("/agency").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), agencyController::createAgency);
}
}
Controller/Handler class below
#Component
public class AgencyController {
public Mono<ServerResponse> createAgency(ServerRequest request){
return ServerResponse.ok()
// .contentType(MediaType.APPLICATION_JSON)
.body(
Flux.just("Test", "message")
.delayElements(Duration.ofSeconds(1)).log(), String.class
);
}
}
Test class
#AutoConfigureWebTestClient
public class AgencyControllerTest {
#Autowired
private WebTestClient webClient;
#Test
void testCreateAgency() {
AgencyRequest request = new AgencyRequest(new AgencyRequestFields("TestName", true));
webClient.post()
.uri("/agency")
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(request))
.exchange()
.expectStatus().is2xxSuccessful();
}
}
build.gradle dependencies
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
//implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.flywaydb:flyway-core'
implementation 'org.springdoc:springdoc-openapi-webflux-ui:1.5.2'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:postgresql'
}
Thanks in advance!!!
The easiest way is just to add #SpringBootTest on your AgencyControllerTest class (along with #AutoConfigureWebTestClient which you already have):
#SpringBootTest
#AutoConfigureWebTestClient
public class AgencyControllerTest {
...which sets up full auto-configuration along with your web test client. Then you don't need to do anything else - all your controllers, routes etc. will be available. This is probably easiest to remember if you're just getting started.
Alternatively, you can use #WebFluxTest and #ContextConfiguration to just instantiate the context you need for this particular test:
#WebFluxTest
#AutoConfigureWebTestClient
#ContextConfiguration(classes = {AgencyController.class, AgencyRouter.class})
public class AgencyControllerTest {
If you have other routes, controllers, beans, etc. set up and only need a small subset then this approach is more efficient, as you're not setting up and tearing down the entire context for each test (only what you need.)
One thing that I am not sure I'm doing correctly is annotating with the Bean and Autowired annotations. I have little understanding of what those do at the moment.
I'd recommend taking a good look at dependency injection & inversion of control (both theoretically and in a Spring context) - this is pretty much the foundation of Spring, and you'll almost certainly come unstuck at some point unless you have (at least) a base level understanding of this.

SpringBoot - Can't resolve #RunWith - cannot find symbol

SpringBoot project.
In build.gradle:
dependencies {
implementation 'com.google.code.gson:gson:2.7'
implementation 'com.h2database:h2'
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml'
implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'
implementation('com.squareup.retrofit2:retrofit:2.4.0')
implementation('com.squareup.retrofit2:converter-gson:2.4.0')
implementation group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
Here my test class:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
#RunWith(SpringRunner.class)
#DataJpaTest
public class CategoryRepositoryIntegrationTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
private CategoryRepository productRepository;
But I get error:
error: cannot find symbol
#RunWith(SpringRunner.class)
^
symbol: class RunWith
1 error
FAILURE: Build failed with an exception
You mixed JUnit 4 and 5. You use the Test annotation from JUnit 5 and the RunWith annotation is from JUnit 4. I would recommend using JUnit 5. For this you only need to replace RunWith with the following line:
#ExtendWith(SpringExtension.class)
Or if you use SpringBoot 2.1 or older, you can remove the RunWith annotation and it should also work.
I found this from Spring boot doc, not sure could help with your question.
If you are using JUnit 4, don’t forget to also add #RunWith(SpringRunner.class) to your test, otherwise the annotations will be ignored. If you are using JUnit 5, there’s no need to add the equivalent #ExtendWith(SpringExtension.class) as #SpringBootTest and the other #…Test annotations are already annotated with it.
here is the link https://docs.spring.io/spring-boot/docs/2.1.5.RELEASE/reference/html/boot-features-testing.html
and check section 46.3

Spring #DataJpaTest with JUnit 5

There doesn't seem to be a specific standard way I can find online that makes #DataJpaTest to run correctly.
Is it true that #DataJpaTest is not being used nowadays and all tests are run at the service or controller level using #SpringBootTest?
#Repository
public interface MyBeanRepository extends JpaRepository<MyBean, Long> {
}
#Configuration
#EnableJpaRepositories("com.app.repository.*")
#ComponentScan(basePackages = { "com.app.repository.*" })
public class ConfigurationRepository {
}
#Entity
public class MyBean {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Version
#Column(name = "version")
private Integer version;
#NotNull
#Size(min = 2)
private String name;
}
#DataJpaTest
public class MyBeanIntegrationTest {
#Autowired
MyBeanRepository myBeanRepository;
#Test
public void testMarkerMethod() {
}
#Test
public void testCount() {
Assertions.assertNotNull(myBeanRepository , "Data on demand for 'MyBean' failed to initialize correctly");
}
}
Running using the Eclipse->Run Junit shows these logs.
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
at org.springframework.util.Assert.state(Assert.java:73)
Running using gradle test shows the error that init failed.
FAILURE: Build failed with an exception.
* What went wrong:
Test failed.
Failed tests:
Test com.app.repository.MyBeanIntegrationTest#initializationError (Task: :test)
Here is the gradle script.
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin")
}
}
plugins {
id 'org.springframework.boot' version '2.1.5.RELEASE'
id 'java'
id 'eclipse'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.app'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenLocal()
jcenter()
mavenCentral()
}
test {
useJUnitPlatform()
}
dependencies {
// This dependency is exported to consumers, that is to say found on their compile classpath
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
runtimeOnly 'org.hsqldb:hsqldb'
testImplementation ('org.springframework.boot:spring-boot-starter-test') {
// exlcuding junit 4
exclude group: 'junit', module: 'junit'
}
/**
Test Dependencies Follows
**/
// junit 5 api
testCompile "org.junit.jupiter:junit-jupiter-api:5.2.0"
// For junit5 parameterised test support
testCompile "org.junit.jupiter:junit-jupiter-params:5.2.0"
// junit 5 implementation
testRuntime "org.junit.jupiter:junit-jupiter-engine:5.2.0"
// Only required to run junit5 test from IDE
testRuntime "org.junit.platform:junit-platform-launcher"
}
EDIT:
This has been solved and committed at the same repository.
https://github.com/john77eipe/spring-demo-1-test
It was a good idea to add a Github link. I can see following issues there:
1) If you can't have a main class annotated with #SpringBootApplication, you can use:
#SpringBootConfiguration
#EnableAutoConfiguration
#ComponentScan(basePackages = "com.app.repository")
public class MySampleApplication {
}
2) Change annotations over your ConfigurationRepository class to:
#EnableJpaRepositories("com.app.repository")
#ComponentScan(basePackages = { "com.app.repository" })
public class ConfigurationRepository {
That should let us proceed to the next point:
3) Your MyBeanIntegrationTest should be annotated as:
#SpringBootTest(classes = MyAppApplication.class)
public class MyBeanIntegrationTest {
4) In application.yml you have a small issue with indentation in the last line. Convert tab so spaces and it should be fine.
5) Next thing is MyBeanRepository interface. You can't use a method named findOne there. Thing is, that in interfaces marked as JpaRepository or CrudRepository and so on, methods names need to follow certain rules. If you mark that it will be a repository containing type MyBean your method name should be changed to findById, because Spring will look for a property named id in your bean. Naming it by findOne will cause test to fail with:
No property findOne found for type MyBean!
After fixing these things, your tests pass on my env.
I hope this helps!

Resources