How Inyect dependency by interface mapper - spring

I try to use mapstruct in java Spring to mapper object DTO to Object normal
I try to call the interface mapper since the service, but I have NullPointerException, seems the interface is not injected in service I used autowired and I quit this
Service
#Service
public class FollowService implements IFollowService{
#Autowired
IFollowRepository iFollowRepository;
private IUserMapper iUserMapper;
#Override
public UserDTOCount countFollowers(int userId) throws UserIdNotFoundException, UserNotFollowException {
return iUserMapper.toUserCount(iFollowRepository.getUserById(userId));
}
Mapper
#Mapper(componentModel = "spring")
public interface IUserMapper {
#Mappings({
#Mapping(source = "id" , target = "id"),
#Mapping(source = "name", target = "name"),
#Mapping(source = "followers", target = "followers", qualifiedByName = "followers")
})
UserDTOCount toUserCount(User user);
Error
processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
at com.reto1.demo.Service.FollowService.countFollowers(FollowService.java:54) ~[classes/:na]
I try to debug and I see the iUserMapper is Null, I dont know How call since service
Thanks!

The reason why the iUserMapper is null in your FollowService is because you are not injecting your mapper.
You need to add #Autowired in your service.
e.g.
#Service
public class FollowService implements IFollowService{
#Autowired
IFollowRepository iFollowRepository;
#Autowired
private IUserMapper iUserMapper;
#Override
public UserDTOCount countFollowers(int userId) throws UserIdNotFoundException, UserNotFollowException {
return iUserMapper.toUserCount(iFollowRepository.getUserById(userId));
}
}
Small remark: One small digression note from me. I would suggest not prefixing the interfaces with I. The IDEs can clearly show what is a class and what is an interface and it is also easier to see them in your tree structure as not everything is under "I"

After see many questions, I resolve the error this form
-First, add plugin in pom.xml
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source> <!-- depending on your project -->
<target>1.8</target> <!-- depending on your project -->
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
Second I added lombok
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.1.Final</version>
</path>
And finally, I add autowired
#Service
public class FollowService implements IFollowService{
#Autowired
IFollowRepository iFollowRepository;
#Autowired
IUserMapper iUserMapper;

Related

Springboot with both aspectj and Spring AOP

I am trying to get a springboot (2.6.2) project to work with both AspectJ and Spring AOP.
I have the following sample classes:
#Entity
public class Item {
#Id #Getter private String uuid = UUID.randomUUID().toString();
private String name;
#Verify.Access
public String getName() {
return name;
}
}
public #interface Verify {
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#interface Access {}
}
#Aspect
#Slf4j
public class MyAspect {
#Before("#annotation(Verify.Access)")
public void beforeAnnotation(JoinPoint joinPoint) {
log.error("BEFORE ANNOTATION");
}
}
#Aspect
#Service
public class OtherAspect {
#Autowired private MyUtility myUtility;
#Around("#annotation(SystemCall)")
public Object run(#NonNull final ProceedingJoinPoint join) throws Throwable {
return myUtility.getInfo();
}
}
#Service
#Data
public class MyUtility {
Object info;
}
My pom.xml file has the following plugins defined:
<plugin>
<groupId>com.nickwongdev</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.12.6</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<proc>none</proc>
<complianceLevel>${java.version}</complianceLevel>
<showWeaveInfo>true</showWeaveInfo>
<forceAjcCompile>true</forceAjcCompile>
<sources/>
<weaveDirectories>
<weaveDirectory>${project.build.directory}/classes</weaveDirectory>
</weaveDirectories>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>1.18.20.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>delombok</goal>
</goals>
</execution>
</executions>
<configuration>
<addOutputDirectory>false</addOutputDirectory>
<sourceDirectory>src/main/java</sourceDirectory>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
I have also defined a src/main/resources/org/aspectj/aop.xml:
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<include within="mypackage..*" />
<include within="org.springframework.boot..*" />
</weaver>
<aspects>
<aspect name="mypackage.MyAspect" />
</aspects>
</aspectj>
It seems to compile okay and I see the info messages that the join points are being advised.
However, in the OtherAspect the autowired MyUtility is not getting set.
From what I could find I would expect Spring to recognize OtherAspect as a Component and Autowire in MyUtility but instead I get a NullPointerException.
Any thoughts?
Thanks!
OK, I had a little bit of time and prepared the MCVE which actually would have been your job to provide. I made the following assumptions:
You need native AspectJ, because you want to weave a target class which is not a Spring bean.
You want to use compile-time, not load-time weaaving. Therefore, you would use AspectJ Maven Plugin.
You want to use Spring dependency injection for wiring Spring beans into native AspectJ aspects, as described in the Spring manual, i.e. using an aspectOf factory method for the native aspect in Spring.
You absolutely insist on combining Lombok and native AspectJ, even though they are incompatible out of the box. I.e., you need a workaround in Maven, either binary weaving (e.g. if Lombok is only used for your non-aspect classes) or a "delombok" build step (e.g. if your aspects also use Lombok, which unfortunately they do, using the #Slf4j Lombok annotation in MyAspect.
What I changed in your setup:
I removed the dependency on Spring Data JPA to make things easier, because I was too lazy to set up a dummy in-memory database. It is not relevant for the solution here. I.e., I also commented out the #Entity and #Id annotations in class Item.
You already configured a "delombok" build step, which I wanted to stick with, because it seems to be your preference. Hence, your sample code only compiles with AspectJ Maven when using ${project.build.directory}/generated-sources/delombok as the source directory. Your idea to use a <weaveDirectory> does not work, because the aspect with the Lombok annotation does not compile that way, as it refers to the Lombok-generated static log field.
I removed the #Service annotation from the native AspectJ aspect, because that would lead to problems when wiring the application. Instead, I added a #Bean factory method to OtherAspect, so we can use #Autowired MyUtility myUtility there. In the same aspect, I also switched from #annotation(SystemCall) (due to missing code in your example) to #annotation(Verify.Access) in order to have something to test against.
I removed the superfluous aop.xml file.
I added a little Spring Boot driver application.
I switched from the no longer maintained com.nickwongdev AspectJ Maven plugin to the current dev.aspectj plugin which has more features and supports Java 17+, too.
The whole application looks like this:
<?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>org.example</groupId>
<artifactId>SO_AJ_SpringAutowireBeanNativeAspect_74661663</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<aspectj.version>1.9.9.1</aspectj.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>dev.aspectj</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.13.1</version>
<configuration>
<complianceLevel>${maven.compiler.target}</complianceLevel>
<proc>none</proc>
<showWeaveInfo>true</showWeaveInfo>
<forceAjcCompile>true</forceAjcCompile>
<sources>
<source>
<basedir>${project.build.directory}/generated-sources/delombok</basedir>
</source>
</sources>
<!--
<weaveDirectories>
<weaveDirectory>${project.build.directory}/classes</weaveDirectory>
</weaveDirectories>
-->
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>1.18.20.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>delombok</goal>
</goals>
</execution>
</executions>
<configuration>
<addOutputDirectory>false</addOutputDirectory>
<sourceDirectory>src/main/java</sourceDirectory>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.2</version>
<scope>compile</scope>
</dependency>
<!--
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.6.2</version>
<scope>compile</scope>
</dependency>
-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
</dependencies>
</project>
package org.example;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public #interface Verify {
#Target({ ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
#interface Access {}
}
package org.example;
import lombok.Data;
import org.springframework.stereotype.Service;
#Service
#Data
public class MyUtility {
Object info;
}
package org.example;
import lombok.Getter;
//import javax.persistence.Entity;
//import javax.persistence.Id;
import java.util.UUID;
//#Entity
public class Item {
// #Id
#Getter
private String uuid = UUID.randomUUID().toString();
private String name;
public Item(String name) {
this.name = name;
}
#Verify.Access
public String getName() {
return name;
}
}
package org.example;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
#Slf4j
public class MyAspect {
#Before("#annotation(Verify.Access)")
public void beforeAnnotation(JoinPoint joinPoint) {
log.error("BEFORE ANNOTATION");
}
}
package org.example;
import lombok.NonNull;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
#Aspect
public class OtherAspect {
#Autowired
private MyUtility myUtility;
// #Around("#annotation(SystemCall)")
#Around("#annotation(Verify.Access)")
public Object run(#NonNull final ProceedingJoinPoint join) throws Throwable {
return myUtility.getInfo();
// return join.proceed();
}
}
package org.example;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.Aspects;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#SpringBootApplication
#Configuration
#Slf4j
public class Main {
#Bean
public OtherAspect otherAspect() {
return Aspects.aspectOf(OtherAspect.class);
}
public static void main(String[] args) {
try (ConfigurableApplicationContext appContext = SpringApplication.run(Main.class, args)) {
doStuff(appContext);
}
}
private static void doStuff(ConfigurableApplicationContext appContext) {
MyUtility myUtility = appContext.getBean(MyUtility.class);
myUtility.setInfo("my info");
Item item = new Item("my name");
log.info(item.getName());
}
}
If you run the Spring Boot application, you will see the following on the console (timestamps removed):
ERROR 20680 --- [ main] org.example.MyAspect : BEFORE ANNOTATION
INFO 20680 --- [ main] org.example.Main : my info
As you can see, both aspects kick in, the first one logging an ERROR and the other one changing the return value from "my name" to "my info".
The advantage of the "delombok" variant is that within the same Maven module, you can weave aspects into the Lombok-generated source code. The disadvantage is, that in your IDE you might not be able to compile the project imported from Maven because of the very unusual custom configuration. In IntelliJ IDEA, I had to delegate the build to Maven, but still the source code editor shows squiggly lines.
As an alternative, you could create one module with Lombok compilation (no "delombok") and a second module using binary weaving in order to weave aspects into the Lombok-enhanced class files, as described here. It would all be much easier without Lombok, though. The third alternative is compilation with Lombok and native AspectJ load-time weaving configured for Spring Boot instead of compile-time or binary weaving during build time. I cannot explain and show every variant in detail here, it is a long answer already.

MapStruct #SuperBuilder Return type error not cast

BaseDto:
#Data
#NoArgsConstructor
#SuperBuilder
public class BaseDto{
// Some fields
}
TestDto:
#Data
#NoArgsConstructor
#SuperBuilder
public class TestDto extends BaseDto {
// Some fields
}
Base Mapper:
#MapperConfig(
componentModel = "spring"
)
public interface BaseMapper<E extends BaseEntity, DTO extends BaseDto> {
DTO toDto(E entity);
....
}
Mapper Generate Impl:
#Component
public class TestMapperImpl implements BaseMapper {
#Override
public TestDto toDTO(Test entity) {
BaseDtoBuilder<?, ?> testDto= BaseDto.builder();
if ( entity != null ) {
if ( entity.getId() != null ) {
testDto.id(entity.getId() );
}
}
return testDto.build();
}
}
Mapper create Impl class automatically. Problem is return type.
BaseDtoBuilder<?, ?> testDto= BaseDto.builder();
return testDto.build();
Intelij give error return type, it must be cast as TestDto. Because of mapper return BaseDto. How can i solve this problem?
Note: if use #Builder, there is no error working fine, this time I can't access parent properties.
I solve problem with this configuration: MapStruct and Lombok not working together
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</path>
<!-- This is needed when using Lombok 1.18.16 and above -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
<!-- Mapstruct should follow the lombok path(s) -->
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
thanks for your help xerx593

mvn spring-boot:run "No primary or single public constructor found...TemplateLineItemInput" but only from commandline?

I've got an up-the-middle Spring Boot + Lombok project that works like a champ from the IDE but errors strangely when I run it from the command line through mvn spring-boot:run. Its a pretty recent version Spring Boot...
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
Lombok is unremarkable and came from the Spring Initializr thing (https://start.spring.io/).
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
The JavaBean its complaining about is equally boring...
import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;
#Data
#Builder(toBuilder = true)
public class TemplateLineItemInput {
private final String partMasterId;
private final String partMasterIdPath;
private final String actionType;
private final BigDecimal actionQuantity;
}
The API of this Boot project is GraphQL but when I execute the following mutation from a mvn spring-boot:run invocation it always comes back as an error (nothing on the console...the framework is kicking it out somehow).
Request...
mutation createTemplateLineItem($tbomId: ID!) {
createTemplateLineItem(
tbomId: $tbomId
input: {
partMasterId: "2"
partMasterIdPath: "808863036.1"
actionType: "ADD"
actionQuantity: "2"
}) {
...TBomFrag
}
}
...
{
"partMasterId": "5025489768",
"tbomId": "a4688d22-9d99-41a2-9777-6acc75b2aab9",
"lineItemId": "9e460199-34fb-432c-b971-8cd8321d3283"
}
Response...
{
"errors": [
{
"message": "No primary or single public constructor found for class aero.blue.ems.boa.domain.TemplateLineItemInput - and no default constructor found either",
"locations": [
{
"line": 20,
"column": 3
}
],
"path": [
"createTemplateLineItem"
],
"extensions": {
"classification": "INTERNAL_ERROR"
}
}
],
"data": {
"createTemplateLineItem": null
}
}
My spring-boot-maven-plugin is configured like...
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.5.4</version>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>repackage</id>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
...also from Spring Initializr
When I run the Application from the IDE directly, no issue. The mutation works fine.
Is there something missing from my spring-boot:run config in the pom.xml or something? Did I have to clue the plugin into annotation processing? This is really confusing.
Please,
Check the following config on section plugins at you pom.xml project, as following here:
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>getting-started</artifactId>
<!-- ... -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
For more information about, check this link: https://docs.spring.io/spring-boot/docs/2.5.4/maven-plugin/reference/htmlsingle/
Ultimately this comes down to Lombok misconfiguration of my beans. The fix is to add the #AllArgsConstructor to the bean definition
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;
#Data
#Builder(toBuilder = true)
#AllArgsConstructor
public class TemplateLineItemInput {
private final String partMasterId;
private final String partMasterIdPath;
private final String actionType;
private final BigDecimal actionQuantity;
}
How we figured this out was to "Delombok" the Bean and look at the resulting code. This observation matched the error message; there was no public constructor.
...
TemplateLineItemInput(String partMasterId, String partMasterIdPath, String actionType, BigDecimal actionQuantity) {
this.partMasterId = partMasterId;
this.partMasterIdPath = partMasterIdPath;
this.actionType = actionType;
this.actionQuantity = actionQuantity;
}
Somehow (I still don't fully get why), the #Builder(toBuilder=true) annotation had Lombok producing a package private constructor. Jackson needed something public.
Adding the #AllArgsConstructor annotation made that constructor public and all is well.
public TemplateLineItemInput(String partMasterId, String partMasterIdPath, String actionType, BigDecimal actionQuantity) {
this.partMasterId = partMasterId;
this.partMasterIdPath = partMasterIdPath;
this.actionType = actionType;
this.actionQuantity = actionQuantity;
}
Delombok was the key.

Spring Boot / MapStruct: Parameter 1 of constructor required a bean... Consider defining a bean of type

I don't know so much about Java, but I thought it should be enough to manage this little task...
I'm building a microservice, which provides Songs and list of songs via several Rest-Endpoints. But it doesn't just return a song when called, it also has to contact another service and enhance the song object with additional information. For this I implemented a Dto-Class and I use mapstruct to handle logic behind. I also did this in other projects with no problems. But now I'm struggling, because of this error, which I don't know how to solve - it says:
Parameter 1 of constructor in
mk.microservices.songsservice.services.SongServiceImpl required a bean
of type 'mk.microservices.songsservice.web.mapper.SongMapper' that
could not be found.
Action:
Consider defining a bean of type
'mk.microservices.songsservice.web.mapper.SongMapper' in your
configuration.
Here are excerpts from my code:
SongServiceImpl
import lombok.RequiredArgsConstructor;
import mk.microservices.songsservice.domain.Song;
import mk.microservices.songsservice.repositories.SongRepository;
import mk.microservices.songsservice.web.mapper.SongMapper;
import mk.microservices.songsservice.web.model.SongDto;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
#RequiredArgsConstructor
#Service
public class SongServiceImpl implements SongService {
private final SongRepository songRepository;
private final SongMapper songMapper;
#Override
public SongDto getSongById(Integer id) {
return songMapper.songToSongDto(songRepository.findById(id));
}
#Override
public List<Song> getAllSongs() {
return songRepository.findAll();
}
....
}
SongMapper
import org.mapstruct.DecoratedWith;
import java.util.Optional;
#MapStruct
#DecoratedWith(SongMapperDecorator.class)
public interface SongMapper {
SongDto songToSongDto(Optional<Song> song);
SongDto songToSongDtoWithSongInfo(Song song);
Song songDtoToSong(SongDto songDto);
}
SongMapperDecorator
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Optional;
public class SongMapperDecorator implements SongMapper {
private SongInfoService songInfoService;
private SongMapper mapper;
#Autowired
public void setMapper(SongMapper songMapper) { this.mapper = songMapper; }
#Override
public SongDto songToSongDto(Optional<Song> song) {
return mapper.songToSongDto(song);
}
#Override
public SongDto songToSongDtoWithSongInfo(Song song) {
SongDto songDto = mapper.songToSongDto(Optional.ofNullable(song));
SongInfo songInfo = songInfoService.getSongInfoBySongId(song.getId());
songDto.setDescription(songInfo.getDescription());
return songDto;
}
#Override
public Song songDtoToSong(SongDto songDto) {
return mapper.songDtoToSong(songDto);
}
}
Also did a clean, validate and compile without any errors. But when I did the verify, I got this:
[ERROR] Failed to execute goal
org.apache.maven.plugins:maven-compiler-plugin:3.8.0:compile
(default-compile) on project songs-service: Resolution of
annotationProcessorPath dependencies failed: Missing: [ERROR]
---------- [ERROR] 1) org.mapstruct:mapstruct-processor:jar:1.4.2
My POM looks like this:
The dependency for mapstruct:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
And the plugin for enabling mapstruct and lombok working together:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<compilerArg>
-Amapstruct.defaultComponentModel=spring
</compilerArg>
</compilerArgs>
</configuration>
</plugin>
Would be very glad, if someone could help me solving this. Already googled a lot and didn't find anything useful yet.
Br,
Mic
As it is clear from the error the SongServiceImpl requires a bean SongMapper but Mapstruct do not generate a spring bean by default. i,e it will not add #Component annotation in the generated class, so we need to explicitly mention to generate a class that can be used to create spring bean.
so instead of #MapStruct use #Mapper(componentModel = "spring") in the mapper interface.
Solved it finally, changed the mapstruct version in my POM to 1.4.2.Final (instead of just 1.4.2). Now it works...
Got the hint frome Mapstruct-Documentation here...
Solved this finally. I used this configuration (mapstruct + lombok)
Also installed m2e-apt plugin in eclipse.
pom.xml
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.2.Final</version>
</dependency>
------------------------------------------------------------
<configuration>
<source>1.8</source> <!-- depending on your project -->
<target>1.8</target> <!-- depending on your project -->
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
<compilerArgs>
<compilerArg>
-Amapstruct.defaultComponentModel=spring
</compilerArg>
</compilerArgs>
</configuration>

What is the appropriate driver to load for embedded DynamoDB in a Spring app?

I followed various tutorials to end up with the configuration below. There may also be incorrect configuration causing the problem. When I run the tests, I get:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.tomcat.jdbc.pool.DataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Cannot determine embedded database driver class for database type NONE. If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (the profiles "test" are currently active).
Research has lead me to believe that the appropriate driver is not being loaded and I have to add it to .properties. Is that something included in DynamoDBLocal library? I can't find it in the docs and it seems my only option is to get a 3rd party driver from the web.
Here's are the important parts:
pom.xml:
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-dynamodb</artifactId>
<version>1.11.34</version>
</dependency>
<dependency>
<groupId>com.github.derjust</groupId>
<artifactId>spring-data-dynamodb</artifactId>
<version>4.3.1</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>DynamoDBLocal</artifactId>
<version>1.10.5.1</version>
</dependency>
DataSourceConfigLocal:
#Configuration
#EnableWebMvc
#EnableDynamoDBRepositories(basePackages="com.cfa.dao")
#Profile({"local", "test"})
public class DataSourceConfigLocal {
#Value("${amazon.dynamodb.endpoint}")
private String amazonDynamoDBEndpoint;
#Value("${amazon.aws.accesskey}")
private String amazonAWSAccessKey;
#Value("${amazon.aws.secretkey}")
private String amazonAWSSecretKey;
#Bean
public AmazonDynamoDB amazonDynamoDB() {
AmazonDynamoDB amazonDynamoDB
= new AmazonDynamoDBClient(amazonAWSCredentials());
if (!StringUtils.isEmpty(amazonDynamoDBEndpoint)) {
amazonDynamoDB.setEndpoint(amazonDynamoDBEndpoint);
}
return amazonDynamoDB;
}
#Bean
public AWSCredentials amazonAWSCredentials() {
return new BasicAWSCredentials(
amazonAWSAccessKey, amazonAWSSecretKey);
}
}
IntegrationTest:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#Profile("test")
#TestPropertySource(properties = {
"amazon.dynamodb.endpoint=http://localhost:8000/",
"amazon.aws.accesskey=x",
"amazon.aws.secretkey=x" })
public class OrderRequestRepositoryIntegrationTest {
private DynamoDBMapper dynamoDBMapper;
#Autowired
private AmazonDynamoDB amazonDynamoDB;
#Autowired
OrderRequestDao orderRequestDao;
private static final String STORE_NUMBER = "100";
#Before
public void setup() throws Exception {
dynamoDBMapper = new DynamoDBMapper(amazonDynamoDB);
CreateTableRequest tableRequest = dynamoDBMapper
.generateCreateTableRequest(OrderRequest.class);
tableRequest.setProvisionedThroughput(
new ProvisionedThroughput(1L, 1L));
amazonDynamoDB.createTable(tableRequest);
dynamoDBMapper.batchDelete(
(List<OrderRequest>)orderRequestDao.findAll());
}
#Test
public void sampleTestCase() {
OrderRequest orderRequest = new OrderRequest(STORE_NUMBER);
orderRequestDao.save(orderRequest);
List<OrderRequest> result
= (List<OrderRequest>) orderRequestDao.findAll();
assertTrue("Not empty", result.size() > 0);
assertTrue("Contains item with expected cost",
result.get(0).getStoreNumber().equals(STORE_NUMBER));
}
}
I am not sure whether you have already referred this. I am adding this as it may help you.
Test using HTTP and without using HTTP
pom file which has the server runner and sql lite
1) Also, use the latest version 1.11.0.1 of the JAR.
2) SQL Lite lib in classpath
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>DynamoDBLocal</artifactId>
<version>${aws.dynamodblocal.version}</version>
</dependency>
<profile>
<id>start-dynamodb-local</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>${exec.maven.plugin.version}</version>
<executions>
<execution>
<phase>initialize</phase>
<configuration>
<executable>java</executable>
<arguments>
<argument>-cp</argument>
<classpath/>
<argument>-Dsqlite4java.library.path=${basedir}/target/dependencies</argument>
<argument>com.amazonaws.services.dynamodbv2.local.main.ServerRunner</argument>
<argument>-inMemory</argument>
<argument>-port</argument>
<argument>${dynamodb-local.port}</argument>
</arguments>
</configuration>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>

Resources