1
\$\begingroup\$

I am creating an online store using Spring MVC. The point is that when you create a new product you need to upload a picture for it. I am using the Cloudinary service, so when you upload, the image server grabs it, sends it to the Cloudinary and gets a response, then gets the image URL from the response and writes that URL to the base.

All that goodness is working in like 5 seconds. Is there any way to optimize this code so it will work faster? By the way, this project is a test project for getting a job, so I want it to work well.

Here is the code that I wrote for the Controller. It gets the product with filled info and an image like a MultipartFile:

@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value = "/create", method = RequestMethod.POST)
public String createProduct(MultipartFile image, Model model, @ModelAttribute(name = "product") @Valid ProductDto product, BindingResult bindingResult) throws IOException {
   productValidator.validate(product,bindingResult);
    if (bindingResult.hasErrors()){
        model.addAttribute("categories", categoryService.findAll());
        return "admin/create";
    } else {

        productService.saveProductDto(product,image);
        return "redirect:/";
    }

}

@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value = "/create", method = RequestMethod.GET)
public String createProduct(Model model) {
    model.addAttribute("product", new ProductDto());
    model.addAttribute("categories", categoryService.findAll());
    return "admin/create";
}

After that it goes to the product service:

@Override
public Product saveProductDto(ProductDto productDto, MultipartFile image) throws IOException {
    List<Image> list = transformImageToList(image);
    Product product = new Product();
    product.setCategory(categoryService.findById(productDto.getCategoryId()));
    product.setDescription(productDto.getDescription());
    product.setTitle(productDto.getTitle());
    product.setPrice(productDto.getPrice());
    product.setImages(list);
productDao.create(product);
    return product;
}

@Override
public Product saveProduct(Product product, Long categoryId, MultipartFile file) throws IOException {
    Product deprecatedProduct = productDao.findById(product.getId());
    if (file.getSize() != 0) {
        List<Image> list = transformImageToList(file);
        product.setImages(list);
    } else {
        product.setImages(deprecatedProduct.getImages());
    }
    product.setComments(deprecatedProduct.getComments());
    product.setCategory(categoryService.findById(categoryId));
    productDao.update(product);
    return product;
}

@Override
public List<Image> transformImageToList(MultipartFile image) throws IOException {
    Image dbImage = new Image();
    List<Image> list = new ArrayList<>();
    if (image.getSize() == 0) {
        dbImage.setUrl("http://placehold.it/300x300");
        list.add(dbImage);
        return list;
    } else {
         dbImage.setUrl(imageService.uploadFile(image));
         list.add(dbImage);
        return list;
    }
}

This ProductService uses imageService:

@Autowired
private ImageDao imageDao;

Map<String, String> configuration() {
    Map<String, String> clouMap = new HashMap<>();
    clouMap.put("cloud_name", "dehsjhkzx");
    clouMap.put("api_key", "925657455192194");
    clouMap.put("api_secret", "hLAMwN8M-PtICNCtfr32hNIOFIE");
    return clouMap;

}

public String uploadFile(MultipartFile file) throws IOException {
    File convertedImage = new File(file.getOriginalFilename());
    file.transferTo(convertedImage);
    Cloudinary cloudinary = new Cloudinary(configuration());
    Map uploadResult = cloudinary.uploader().upload(convertedImage, new HashMap());
    convertedImage.delete();
    return (String) uploadResult.get("secure_url");
}

@Override
public Image findByUrl(String url) {
    return imageDao.findByUrl(url);
}

And after that the code finishes, so I get MultipartFile in a controller and then I cast it to File and then send it to the server. By the way, I will make the ImageService resize the image to 300x300, so in the future it will be even slower. Is there any way to refactor this code so that it will work faster?

Image entity:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotNull
private String url;

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public String getUrl() {
    return url;
}

public void setUrl(String url) {
    this.url = url;
}

Product Entity:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;


private String title;


@Column(columnDefinition = "TEXT")
private String description;


private Double price;

@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@JoinTable(name = "product_image", joinColumns = { @JoinColumn(name = "product_id") }, inverseJoinColumns = { @JoinColumn(name = "image_id") })
private List<Image> images;

@ManyToOne
@JoinColumn(name = "category_id")
private Category category;

@OneToMany(cascade=CascadeType.ALL)
@JoinTable(name = "product_comment", joinColumns = { @JoinColumn(name = "product_id") }, inverseJoinColumns = { @JoinColumn(name = "comment_id") })
private List<Comment> comments;

JSP page:

<spring:url value="/product/create" var="formUrl"/>
<form:form modelAttribute="product" action="${formUrl }" method="post" enctype="multipart/form-data">
    <div class="form-group">
        <label>Title</label>
        <form:input id="title"
                    cssClass="form-control" path="title"/>
        <form:errors path="title"/>
    </div>
    <div class="form-group">
        <label>Description</label>
        <form:textarea id="description" rows="10"
                       cssClass="form-control" path="description"/>
    </div>
    <div class="form-group">
        <label>Price</label>
        <form:input id="price" type="number"
                    cssClass="form-control" path="price"/>
        <form:errors path="description"/>
    </div>
    <div class="form-group">
        <label for="sel1">Select category</label>
        <form:select id="sel1" cssClass="form-control" path="categoryId">
            <form:options items="${categories}" itemValue="id" itemLabel="title"/>
        </form:select>
    </div>
    <label class="btn btn-default btn-file">
        Image <input type="file" multiple accept='image/*' ng-file-select="onFileSelect($files)" name="image" style="display: block;">
    </label>
    <br><br>
    <div class="text-center">
        <button type="submit" class="btn btn-lg btn-success text-center"><span
                class="fa fa-check"></span> Submit
        </button>
        <a href="/" class="btn btn-danger btn-lg  text-center"><span
                class="fa fa-times"></span> Cancel
        </a>
    </div>
</form:form>
\$\endgroup\$
  • 1
    \$\begingroup\$ Do you have any profiling result ? In peculiar, what portion of the 5sec does the calls to the ImageService methods take ? \$\endgroup\$ – Bastien Aracil Jan 2 '17 at 1:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.