Привет, ребята, вот мой проект:
Два объекта, связанных друг с другом через @OneToMany
отношения:
@Entity
public class Brand {
@Id
@GeneratedValue
private Long id;
@Column
@NotNull(message = "Brand can not have empty name.")
private String name;
@Column
private String description;
@OneToMany(mappedBy="brand"
, cascade= {CascadeType.DETACH,
CascadeType.MERGE,
CascadeType.PERSIST,
CascadeType.REFRESH},
fetch = FetchType.EAGER)
@OrderBy("price ASC") // order products by price
private List<Product> productList;
// getters and setter following here ...
}
@Entity
public class Product {
@Id
@GeneratedValue
private Long id;
@Column
@NotNull(message = "Product can not have empty name.")
private String name;
@Column
private Double price;
@Column
private Boolean onSale=false;
@ManyToOne(cascade= {CascadeType.DETACH,
CascadeType.MERGE,
CascadeType.PERSIST,
CascadeType.REFRESH})
@JoinColumn(name="brand_id", nullable=false)
private Brand brand;
// getters and setter following here ...
}
package com.microservices.product.datatranferobject;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ProductDTO {
private Long id;
private String name;
private Double price;
@JsonIgnore // omit this property when exposing data over the API
private Boolean onSale;
private String event;
public ProductDTO() {
}
public ProductDTO(Long id, String name, Double price, String event, Boolean onSale) {
super();
this.id = id;
this.name = name;
this.price = price;
this.event = event;
this.onSale=onSale;
}
public Boolean getOnSale() {
return onSale;
}
public void setOnSale(Boolean onSale) {
this.onSale = onSale;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public String getEvent() {
return onSale?"ON SALE":null;
}
public void setEvent(String event) {
this.event = event;
}
}
public class BrandDTO {
private String name;
private List<ProductDTO> productList;
public BrandDTO() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ProductDTO> getProductList() {
return productList;
}
public void setProductList(List<ProductDTO> productList) {
this.productList = productList;
}
}
Простой класс DAO:
public interface BrandRepository extends JpaRepository<Brand, Long>{
List<Brand> findAllByOrderByName();
}
Простой сервис:
public interface BrandService {
List<Brand> getAllBrands();
}
@Service
public class BrandServiceImpl implements BrandService {
@Autowired
BrandRepository myBrandRepo;
@Override
public List<Brand> getAllBrands(){
return myBrandRepo.findAllByOrderByName();
}
}
и контроллер, который отображает Продукты в ProductDTO, используя мой индивидуально запрограммированный Mapper:
public class ModelMapper {
public static BrandDTO makeBrandDTO(Brand myBrand)
{
BrandDTO myBrandDTO=new BrandDTO();
org.modelmapper.ModelMapper myStandardMapper=new org.modelmapper.ModelMapper();
List<ProductDTO> myProductsList=myBrand.getProductList()
.stream()
.map(product->myStandardMapper.map(product, ProductDTO.class))
.collect(Collectors.toList());
myBrandDTO.setProductList(myProductsList);
myBrandDTO.setName(myBrand.getName());
return myBrandDTO;
}
}
@RestController
@RequestMapping("v1/search")
public class BrandController {
@Autowired
BrandService myBrandService;
@GetMapping("/get/products/")
public Map<String, List<ProductDTO>> getAllProductsMap(){
List<BrandDTO> myList= myBrandService.getAllBrands()
.stream().map(brand->ModelMapper.makeBrandDTO(brand))
.collect(Collectors.toList());
Map<String, List<ProductDTO>> myMap=new TreeMap<String, List<ProductDTO>> ();
myList.forEach(brand->myMap.put(brand.getName(), brand.getProductList()));
return myMap;
}
}
ProductDTO во многом совпадает с сущностью Product поэтому я сэкономлю время и пространство, записывая его.
Итак, возникает проблема. Я использую Junit для тестирования моего контроллера, который выполняет следующий класс:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ProductApplication.class,webEnvironment=WebEnvironment.RANDOM_PORT)
public class ControllerLayerTester {
private static Log myLogger = LogFactory.getLog(ControllerLayerTester.class);
private static RestTemplate restTemplate;
private static HttpHeaders headers;
@LocalServerPort
private int port;
@BeforeClass
public static void runBeforeAllTestMethods() {
restTemplate = new RestTemplate();
headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
}
/**
* simple test to verify the given requirements
*/
@Test
public void testBrandListIsOrdered() {
TreeMap<String, List<ProductDTO>> productList=restTemplate.getForObject("http://localhost:"+port+"/v1/search/get/products/",TreeMap.class);
for (List<ProductDTO> myProductList:productList.values()) {
myProductList.forEach(product->assertTrue(product.getId()!=null));
}
}
}
И когда я запускаю его, я получаю следующую ошибку:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.microservices.product.datatranferobject.ProductDTO
at java.util.ArrayList.forEach(ArrayList.java:1257)
at com.microservices.product.ControllerLayerTester.testBrandListIsOrdered(ControllerLayerTester.java:81)
Я делать что-то не так здесь? Почему я не получаю Список, заполненный моими сущностями, а вместо этого заполненный LinkedHashMaps? Есть ли решение для получения списка продуктов?
РЕДАКТИРОВАТЬ:
Я также предоставляю здесь свои зависимости проекта, и я добавил поверх мой пост также DTO:
buildscript {
ext {
springBootVersion = '2.1.0.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.microservices'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
implementation('org.springframework.boot:spring-boot-starter-data-jpa')
implementation('org.springframework.boot:spring-boot-starter-web')
compile("org.springframework.boot:spring-boot-devtools")
compile group: 'org.modelmapper', name: 'modelmapper', version: '2.1.0'
runtimeOnly('com.h2database:h2')
testImplementation('org.springframework.boot:spring-boot-starter-test')
testImplementation 'org.assertj:assertj-core:3.15.0'
compile group: 'com.google.guava', name: 'guava', version: '23.5-jre'
compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.7.0'
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.7.0'
}