标题翻译
Problem testing a Bean with a List property
问题
@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")
private List<Product> productList;
// getters and setters ...
}
@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 setters ...
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ProductDTO {
private Long id;
private String name;
private Double price;
@JsonIgnore
private Boolean onSale;
private String event;
// constructors, getters, and setters ...
}
public class BrandDTO {
private String name;
private List<ProductDTO> productList;
// getters and setters ...
}
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();
}
}
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;
}
}
@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);
}
@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));
}
}
}
Please note that the provided code snippets are translations of the original code you provided, focusing solely on the code itself without any additional content.
英文翻译
Hi guys so here is my project:
Two entities connected with each other over a @OneToMany
relationship:
@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;
}
}
A simple DAO Class:
public interface BrandRepository extends JpaRepository<Brand, Long>{
List<Brand> findAllByOrderByName();
}
A simple service:
public interface BrandService {
List<Brand> getAllBrands();
}
@Service
public class BrandServiceImpl implements BrandService {
@Autowired
BrandRepository myBrandRepo;
@Override
public List<Brand> getAllBrands(){
return myBrandRepo.findAllByOrderByName();
}
}
and a controller which maps Products to ProductDTOs usinf my custom programmed 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 is quite the same as Product entity so I will spare the time ans space writing it down.
So here comes the Problem. I am using Junit to test my Controller having the following class run the test:
@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));
}
}
}
And when I run it I get the following error:
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)
Am I doing something wrong here ? Why am I not getting the List filled with my entities but instead filled with LinkedHashMaps? Is there a solution in order to get a List of Products?
EDIT:
I am also providing here my project dependencies and I added on top of my post also the DTOs:
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'
}
答案1
得分: 1
问题在于无法从TreeMap.class
推断出对TreeMap<String, List<ProductDTO>>
的参数化,而这是在restTemplate.getForObject(...)
中发生的。
您应该使用更通用的版本:
restTemplate.exchange(
"http://localhost:" + port + "/v1/search/get/products/",
HttpMethod.GET,
new HttpEntity<>(null, headers),
new ParameterizedTypeReference<TreeMap<String, List<ProductDTO>>>() {}
)
英文翻译
The problem is that parameterization of TreeMap<String, List<ProductDTO>>
cannot be inferred from TreeMap.class
inside restTemplate.getForObject(...)
You'll want to use the more generic version:
restTemplate.exchange(
"http://localhost:"+port+"/v1/search/get/products/",
HttpMethod.GET,
new HttpEntity<>(null, headers),
new ParameterizedTypeReference<TreeMap<String, List<ProductDTO>>() {}
)
专注分享java语言的经验与见解,让所有开发者获益!
评论