1

I defined the mapper but Spring Boot can't detect it. I couldn't find the problem for days. Please help. Tried on IDEA and Netbeans. Tried to add some annotations on main class from this thread.

Description:

Parameter 1 of constructor in com.example.springmysqlelastic.service.impl.UserService required a bean of type 'com.example.springmysqlelastic.mapper.UserMapper' that could not be found.

Action:

Consider defining a bean of type 'com.example.springmysqlelastic.mapper.UserMapper' in your configuration.

UserMapper.java

package com.example.springmysqlelastic.mapper;

import com.example.springmysqlelastic.model.Food;
import com.example.springmysqlelastic.model.FoodModel;
import com.example.springmysqlelastic.model.User;
import com.example.springmysqlelastic.model.UserModel;
import com.example.springmysqlelastic.model.dto.FoodDTO;
import com.example.springmysqlelastic.model.dto.UserDTO;
import org.mapstruct.Mapper;
import org.springframework.stereotype.Component;

import java.util.List;
//@Component
@Mapper(componentModel = "spring")
public interface UserMapper {

    UserDTO toUserDTO(User user);

    List<UserDTO> toUserDtos(List<User> users);

    User toUser(UserDTO userDTO);

    List<User> toUsers(List<UserDTO> userDTOS);

    UserModel toUserModel(User user);
/*
    FoodDTO toFoodDTO(Food food);

    List<FoodDTO> toFoodDtos(List<Food> foods);

    Food toFood(FoodDTO foodDTO);

    List<Food> toFoods(List<FoodDTO> foodDTOS);

    FoodModel toFoodModel(Food food);*/
}

UserService

package com.example.springmysqlelastic.service.impl;

import com.example.springmysqlelastic.mapper.UserMapper;
import com.example.springmysqlelastic.model.User;
import com.example.springmysqlelastic.model.dto.UserDTO;
import com.example.springmysqlelastic.repo.IUserDAO;
import com.example.springmysqlelastic.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService implements IUserService {

    private IUserDAO userDAO;
    private UserMapper userMapper;

    @Autowired
    public UserService(IUserDAO userDAO, UserMapper userMapper) {
        this.userDAO = userDAO;
        this.userMapper = userMapper;
    }
    
    @Override
    public UserDTO save(UserDTO userDTO) {
        User user = this.userDAO.save(this.userMapper.toUser(userDTO));
        return this.userMapper.toUserDTO(user);
    }

    @Override
    public UserDTO findById(Long id) {
        return this.userMapper.toUserDTO(this.userDAO.findById(id).orElse(null));
    }

    @Override
    public List<UserDTO> findAll() {
        return this.userMapper.toUserDtos(this.userDAO.findAll());
    }
}

UserController.java

package com.example.springmysqlelastic.rest;

import com.example.springmysqlelastic.model.dto.UserDTO;
import com.example.springmysqlelastic.service.IUserService;
import com.example.springmysqlelastic.utils.PathResources;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/user") //PathResources.USER
public class UserController {

    private final IUserService userService;

    @Autowired
    public UserController(IUserService userService) {
        this.userService = userService;
    }

    @PostMapping("/save") //PathResources.SAVE
    public ResponseEntity<UserDTO> saveUser(@RequestBody UserDTO userDTO) {
        return new ResponseEntity<>(this.userService.save(userDTO), HttpStatus.OK);
    }

    @GetMapping("/find-one/{id}") //PathResources.FIND_ONE + "/{" + PathResources.ID + "}"
    public ResponseEntity<UserDTO> findById(@PathVariable Long id) {
        return new ResponseEntity<>(this.userService.findById(id), HttpStatus.OK);
    }

    @GetMapping("/find-all") //PathResources.FIND_ALL
    public ResponseEntity<List<UserDTO>> findById() {
        return new ResponseEntity<>(this.userService.findAll(), HttpStatus.OK);
    }
}

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.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>spring-mysql-elastic</artifactId>
    <packaging>jar</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-mysql-elastic</name>
    <description>Demo project for Mysql and ElasticSearch Synchronization in Spring</description>

    <properties>
        <java.version>1.8</java.version>
        <mapstruct.version>1.4.0.Beta3</mapstruct.version>
        <org.json.version>20190722</org.json.version>
        <swagger.version>2.9.2</swagger.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-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </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.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.mapstruct</groupId>
            <artifactId>mapstruct-jdk8</artifactId>
            <version>${mapstruct.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.json/json -->
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>${org.json.version}</version>
        </dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson 
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.73</version>
</dependency>-->

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <!--<version>1.18.12</version>-->
            <type>jar</type>
        </dependency>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.6</version>
</dependency>
        <dependency>
            <groupId>jakarta.validation</groupId>
            <artifactId>jakarta.validation-api</artifactId>
            <version>2.0.2</version>
            <type>jar</type>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                        <configuration>
                            <mainClass>com.example.springmysqlelastic.SpringMysqlElasticApplication</mainClass>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${mapstruct.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Main class

package com.example.springmysqlelastic;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
//@SpringBootApplication(scanBasePackages={"com.example.springmysqlelastic"})
@EnableElasticsearchRepositories("com.example.springmysqlelastic.repo.elastic")
@EnableScheduling
//@ComponentScan(scanBasePackages = {"com.example.springmysqlelastic"})
//@EntityScan("com.example.springmysqlelastic.model")
//@EnableJpaRepositories("com.example.springmysqlelastic")
//@ComponentScan(basePackages = {"com.example.springmysqlelastic"})
//@EnableAutoConfiguration

public class SpringMysqlElasticApplication {

    public static void main(String[] args) {

        SpringApplication.run(SpringMysqlElasticApplication.class, args);
    }

}
emretosunkaya
  • 365
  • 1
  • 4
  • 13

2 Answers2

1

In the documentation says that in addition to the main dependency, you need to add annotationProcessor

Example for Gradle:

dependencies {
    implementation 'org.mapstruct:mapstruct:1.4.2.Final'
    annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}
jerekian
  • 11
  • 1
0

I think its an issue with swagger conflicting with the mapstruct library..

I modified your POM like below and it started working for me. They have some documentation here that talks about it. https://www.programmersought.com/article/6208308983/

<?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.3.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>com.example</groupId>
  <artifactId>spring-mysql-elastic</artifactId>
  <packaging>jar</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>spring-mysql-elastic</name>
  <description>Demo project for Mysql and ElasticSearch Synchronization in Spring</description>

  <properties>
    <java.version>1.8</java.version>
    <mapstruct.version>1.4.0.Beta3</mapstruct.version>
    <org.json.version>20190722</org.json.version>
    <swagger.version>2.9.2</swagger.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-tomcat</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </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.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.mapstruct</groupId>
      <artifactId>mapstruct-jdk8</artifactId>
      <version>${mapstruct.version}</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.json/json -->
    <dependency>
      <groupId>org.json</groupId>
      <artifactId>json</artifactId>
      <version>${org.json.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.73</version>
    </dependency>-->

    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger2</artifactId>
      <version>${swagger.version}</version>
      <exclusions>
        <exclusion>
          <groupId>org.mapstruct</groupId>
          <artifactId>mapstruct</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger-ui</artifactId>
      <version>${swagger.version}</version>
      <exclusions>
        <exclusion>
          <groupId>org.mapstruct</groupId>
          <artifactId>mapstruct</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <!--<version>1.18.12</version>-->
      <type>jar</type>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.6</version>
    </dependency>
    <dependency>
      <groupId>jakarta.validation</groupId>
      <artifactId>jakarta.validation-api</artifactId>
      <version>2.0.2</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.mapstruct</groupId>
      <artifactId>mapstruct</artifactId>
      <version>1.4.0.Beta3</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>repackage</goal>
            </goals>
            <configuration>
              <mainClass>com.example.springmysqlelastic.SpringMysqlElasticApplication</mainClass>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>${java.version}</source>
          <target>${java.version}</target>
          <annotationProcessorPaths>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>1.4.0.Beta3</version>
            </path>
          </annotationProcessorPaths>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

PS: I have removed JPA dependency to make it work without spring. Forget it.

You can find that I have changed swagger dependencies to look like below -

<dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger2</artifactId>
      <version>${swagger.version}</version>
      <exclusions>
        <exclusion>
          <groupId>org.mapstruct</groupId>
          <artifactId>mapstruct</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger-ui</artifactId>
      <version>${swagger.version}</version>
      <exclusions>
        <exclusion>
          <groupId>org.mapstruct</groupId>
          <artifactId>mapstruct</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

Can you check if it works for you?

Update After codegen step, this is what it generates -

package com.example.springmysqlelastic.mapper;

import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import org.springframework.stereotype.Component;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2020-08-11T16:40:57+0530",
    comments = "version: 1.4.0.Beta3, compiler: javac, environment: Java 1.8.0_265 (Private Build)"
)
@Component
public class UserMapperImpl implements UserMapper {

    @Override
    public UserDTO toUserDTO(User user) {
        if ( user == null ) {
            return null;
        }

        UserDTO userDTO = new UserDTO();

        return userDTO;
    }

    @Override
    public List<UserDTO> toUserDtos(List<User> users) {
        if ( users == null ) {
            return null;
        }

        List<UserDTO> list = new ArrayList<UserDTO>( users.size() );
        for ( User user : users ) {
            list.add( toUserDTO( user ) );
        }

        return list;
    }

    @Override
    public User toUser(UserDTO userDTO) {
        if ( userDTO == null ) {
            return null;
        }

        User user = new User();

        return user;
    }

    @Override
    public List<User> toUsers(List<UserDTO> userDTOS) {
        if ( userDTOS == null ) {
            return null;
        }

        List<User> list = new ArrayList<User>( userDTOS.size() );
        for ( UserDTO userDTO : userDTOS ) {
            list.add( toUser( userDTO ) );
        }

        return list;
    }
}
Anand Vaidya
  • 1,374
  • 11
  • 26
  • 1
    Thanks for your help but still same when i add exclusions to swagger. I'm using JPA library for extending repositories for Mysql, then sync to elasticsearch. This time asks bean for JPA repository. – emretosunkaya Aug 11 '20 at 11:42
  • Can you open the sources of `UserMapperImpl` class and see whats being generated there? – Anand Vaidya Aug 11 '20 at 12:02
  • Codegen looks good. Its creating the bean properly. Appears that you might have some issue with component scan, or the related class file isnt picked up in the compilation.. How are you building it? Eclipse/ Idea? – Anand Vaidya Aug 11 '20 at 13:01
  • 1
    Tried with IDEA & Netbeans still same :( Consider defining a bean of type 'com.example.springmysqlelastic.mapper.UserMapper' in your configuration. – emretosunkaya Aug 12 '20 at 06:43