【问题标题】:Mapstruct ImplementationMapstruct 实现
【发布时间】:2021-04-15 00:45:12
【问题描述】:

我正在使用 mapstruct 将我的模型映射到我的 DTO。 我想按全名搜索记录。 我不明白为什么会出现以下错误:

Error creating bean with name 'customerController'
Error creating bean with name 'customerServiceImpl'
Error creating bean with name 'customerRepository'
No property name found for type Customer!

这是我的项目

public interface CustomerMapper {

    CustomerMapper INSTANCE = Mappers.getMapper(CustomerMapper.class);
    @Mapping(source = "lastName", target = "lastName")
    CustomerDTO customerToCustomerDTO(Customer customer);
}
@Data
public class CustomerDTO {
    private String firstName;
    private String lastName;
}
@Data
@Entity
@Getter
@Setter
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String firstName;
    private String name;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CustomerListDTO {
    List<CustomerDTO> categories;
}
@Controller
@RequestMapping("api/v1/customers")

public class CustomerController {
    private final CustomerService customerService;


    public CustomerController(CustomerService customerService) {
        this.customerService = customerService;
    }

    @GetMapping("{name}")
    public ResponseEntity<CustomerDTO> getCustomerByName(@PathVariable String name) {
        return new ResponseEntity<>(
                customerService.getCustomerByName(name), HttpStatus.OK
        );
    }
public interface CustomerRepository extends JpaRepository<Customer, Long> {
    Customer findByName(String x);
}
public interface CustomerService {
    CustomerDTO getCustomerByName(String name);
}
@AllArgsConstructor
@Service
public class CustomerServiceImpl implements CustomerService {

    CustomerMapper customerMapper;
    CustomerRepository customerRepository;

    @Override
    public CustomerDTO getCustomerByName(String lastName) {
        return customerMapper.customerToCustomerDTO(customerRepository.findByName(lastName));
    }
}

这是一个潜在的解决方法:将在 CustomerMapper 中映射以下内容,但对我来说感觉不对。

@Mapping(source = "name", target = "lastName")
@Mapping(source = "firstName", target = "firstName")

在文档中,据说您可以将模型中的任何字段映射到 DTO,我认为我的代码可能有问题。 我尝试在 repo、service、controller 中实现的方式。

编辑:

也许解决方案是在存储库中使用 DTO?

更新:

@Override
    public CustomerDTO getCustomerByName(String lastName) {
        return customerRepository.findByName(lastName).map(customerMapper::customerToCustomerDTO);
    }

.map 无法使用。

要使用 .map 我应该使用这样的代码

    .findAll()
    .stream()
    .map(customerMapper::customerToCustomerDTO)
    .collect(Collectors.toList());

我正在使用 findByName 方法,但它无法访问 .map。

我该如何解决这个问题?

编辑

这就是我认为我的客户应该是什么样子

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CustomerDTO {
    private String id;
    private String firstName;
    private String lastName;
}

【问题讨论】:

    标签: spring mapstruct


    【解决方案1】:

    “未找到类型 Customer 的属性名称!”

    在您的餐桌客户中,您有一个名为“name”的列?

    下面我对您的代码进行了一些更改,但是如果您需要按名称查找您的存储库,则需要找到正确的搜索。当您使用 findByName 时,仅返回名称等于传入参数的名称的行。示例:findByName("Scilla") 仅返回列名等于“Scilla”的行,如果列名具有“scilla”(下)或“Scilla abc”等值,则查询未返回此条目。

    值为“Scilla”的方法 findByName 生成此查询:

    select * from customer where name = 'Scilla';
    

    代码更改

    import java.util.List;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface CustomerRepository extends JpaRepository<Customer, Long> {
        
        Customer findByLastName(String lastName);
        
        List<Customer> findByLastNameContainingIgnoreCase(String name);
    
        List<Customer> findByLastNameContaining(String name);
    
    }
    
    import org.mapstruct.Mapper;
    import org.mapstruct.factory.Mappers;
    
    @Mapper
    public interface CustomerMapper {
    
        CustomerMapper INSTANCE = Mappers.getMapper(CustomerMapper.class);
    
        CustomerDTO customerToCustomerDTO(Customer customer);
    
        Customer toDomain(CustomerDTO customerDTO);
        
    }
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @Entity
    @NoArgsConstructor
    public class Customer {
        
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        
        private String firstName;
        
        private String lastName;
        
    }
    
    import lombok.Data;
    
    @Data
    public class CustomerDTO {
    
        private String firstName;
        private String lastName;
    
    }
    
    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;
    
    @RestController
    @RequestMapping("api/v1/customers")
    public class CustomerController {
    
        private final CustomerService customerService;
    
    
        public CustomerController(CustomerService customerService) {
            this.customerService = customerService;
        }
    
        @GetMapping("{name}")
        public ResponseEntity<CustomerDTO> getCustomerByName(@PathVariable String name) {
            return new ResponseEntity<>(
                    customerService.getCustomerByName(name), HttpStatus.OK
            );
        }
        
        @PostMapping
        public ResponseEntity<CustomerDTO> getCustomerByName(@RequestBody CustomerDTO customerDTO ) {
            return new ResponseEntity<>(
                    customerService.save(customerDTO), HttpStatus.OK
            );
        }
        
    }
    

    重要

    下面放Spring Data查询和翻译查询。

    List<Customer> findByLastNameContainingIgnoreCase(String name)
    
    select * from customer where last_name ilike = '%name%';
    

    pom.xml 定义

        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>11</java.version>
            
            <org.mapstruct.version>1.4.1.Final</org.mapstruct.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-data-jpa</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
                <scope>runtime</scope>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct</artifactId>
                <version>1.4.1.Final</version>
            </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>
    
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>${java.version}</source>
                        <target>${java.version}</target>
                        <forceJavacCompilerUse>true</forceJavacCompilerUse>
                        <release>11</release>
                        <annotationProcessorPaths>
                            <path>
                                <groupId>org.mapstruct</groupId>
                                <artifactId>mapstruct-processor</artifactId>
                                <version>${org.mapstruct.version}</version>
                            </path>
                            <path>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                                <version>${lombok.version}</version>
                            </path>
                        </annotationProcessorPaths>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    

    【讨论】:

    • 很抱歉,我认为您的帖子是多余的,正如您所做的那样,我在我的问题中提出了解决方案。
    • 您需要多加注意,我做了一些更改以消除错误并正确使用mapstruct。要按全名搜索,您需要更具体,列名有全名吗?你想用 like 或 ilike 的方法找到?
    • 检查我的最新编辑。这就是我的客户应该是什么样子。我想将客户的姓氏映射到 DTO 中的姓氏。当我这样做时,我得到了我提到的错误。使用您从 getCustomerByName 获得的回报,让我遇到同样的错误。
    • 如果 Dto 对象字段与域对象中的字段具有相同的名称,则您不需要使用“@Mapping”注释,在我的回答中,我编辑了 CustomerMapper 以删除“@Mapping”注释。你能分享你的数据库表创建吗?因为如果您在表客户中有带有姓氏的“@Entity”客户,您需要有一个名称为“last_name”的列或使用“@Column”注释定义列的名称。在您的存储库中,您需要将 findByName 更改为 findByLastName。
    • 删除@Mapping 并没有解决问题。我没有创建表。我在内存中使用 H2。很明显,问题在于您的情况下 getCustomerByName 中的返回不知道如何将其映射到 lastName。
    【解决方案2】:

    要将您的客户实体映射到 DTO 并在 Spring 应用程序中使用它,您应该使用以下映射器(带有参数 componentModel = "spring"):

    @Mapper(
        componentModel = "spring",
        nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT,
        nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
        nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE
    )
    public interface CustomerMapper {
       @Mapping(target = "lastName", source = "name")
       CustomerDto toDto(Customer customer);
    }
    

    MapStruct 不知道如何将实体中的 name 属性映射到 DTO 中的 lastName 属性,因此您必须在 @Mapping 注释中指定。

    (我还建议使用“策略”参数的指定值 - 您可以在 javadoc 中查看它们的用途。)

    在这种情况下,MapStruct 会生成一个适当的 Spring bean,其中包含映射器的实现,如下所示:

    @Component
    public class CustomerMapperImpl {
    
        public CustomerDto toDto(Customer customer) {
            CustomerDto dto = new CustomerDto();
            if (customer != null) {
                if (customer.getFirstName() != null) {
                    dto.setFirstName(customer.getFirstName());
                }
                if (customer.getName() != null) {
                    dto.setLastName(customer.getName());
                }        
            }
            return dto;
        }
    }
    

    因此 Spring 将能够在您的服务中注入该 bean(不要忘记更正您的 repo 的 findByName 方法以返回 Optional):

    @RequiredArgsConstructor
    @Service
    public class CustomerServiceImpl implement CustomerService {
    
        private final CustomerRepo repo;
        private final CustomerMapper mapper;
    
        @Override 
        public Optional<CustomerDto> getByName(@NonNull String name) {
            return repo.findByName(name).map(mapper::toDto)
        }
        
        @Override 
        public List<CustomerDto> getAll() {
            return repo.findAll().stream().map(mapper::toDto).collect(Collectors.toList());
        }
    }
    

    然后在您的 REST 控制器中使用此服务:

    @RequiredArgsConstructor
    @RestController
    @RequestMapping("api/v1/customers")
    public class CustomerController {
    
        private final CustomerService service;
    
        @GetMapping("/{name}")
        public CustomerDto getByName(@PathVariable String name) {
            return service.getByName()
                .orElseThrow(() -> new ResponseStatusException("Customer not found"));
        }
    
        @GetMapping
        public List<CustomerDto> getAll() {
            return service.getAll();
        }
    }
    
    

    不要忘记将您的项目配置为同时使用 MapStruct 和 Lombok:

          <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
              <annotationProcessorPaths>
                <path>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <version>${lombok.version}</version>
                </path>
                <path>
                  <groupId>org.mapstruct</groupId>
                  <artifactId>mapstruct-processor</artifactId>
                  <version>${mapstruct.version}</version>
                </path>
              </annotationProcessorPaths>
            </configuration>
          </plugin>
    

    【讨论】:

    • 我想我可能让你错过了重点。我的意思是这个实现是错误的。 Customer 类中的“name”应该是 lastName。所以,那是我得到错误的时候。抱歉,我一定是复制了解决问题的 Customer 类。
    • 即便如此。 findByNAme(name).map 无法解析。 CustomerMapperImp 是由 map 构造生成的类,因此是多余的。我有那个。
    • 但是,您的回复中还有一些额外的提示可以使这更加强大!但问题并没有解决。
    猜你喜欢
    • 2020-08-25
    • 2021-03-06
    • 2019-10-10
    • 1970-01-01
    • 2020-08-14
    • 2021-09-23
    • 2021-12-21
    • 1970-01-01
    • 2018-12-05
    相关资源
    最近更新 更多