jeudi 30 avril 2020

Comment mapper des objets avec MapStruct

Il n'est pas rare dans une application de devoir copier les valeurs d'un type d'objet à un autre. Par exemple les objets de la couche métier à ceux de la présentation.

Une multitude de librairie existe en Java pour effectuer cette opération.
Dans cet article nous utiliserons un des plus rapides soit MapStruct. Une comparaison est disponible à cette url.

Mappage avec les mêmes noms de champs

public class Person{
    private String name;
    private String lastname;
    private int age;
}

public class PersonDto{
    private String name;
    private String lastname;

}

Il suffit de créer une interface

@Mapper
public interface PersonMapper {
    PersonDto personToPersonDTO(Person entity);
    Person epersonDTOtoPerson(PersonDto dto);
}

MapStruct générera une classe d'implémentation. Ne surtout pas hésiter à la regarder pour voir le code généré pour éviter des surprises.

Mappage avec des noms différents de champs

public class Person{
    private String firstname;
    private String lastname;
    private int age;
}

public class PersonDto{
    private String name;
    private String lastname;

}

Il suffit de créer une interface et de spécifier les noms de source et de destination.

@Mapper
public interface PersonMapper {
    @Mapping(target="lastname", source"firstname")
    PersonDto personToPersonDTO(Person entity);

    @Mapping(target="firstname", source="name")
    Person epersonDTOtoPerson(PersonDto dto);
}

Mappage d'une valeur par défaut

 Si un champ est null, il est possible d'assigner une valeur par défaut.


@Mapper
public interface PersonMapper {
    @Mapping(target="lastname", source"firstname", defaultValue="bob")
    PersonDto personToPersonDTO(Person entity);
 }

Mettre à jour un objet existant

@Mapper
public interface PersonMapper {

    void updatePersonFromDto(
PersonDto dto, @MappingTarget Person entity);
}


Il suffit d'ajouter l'annotation MappingTarget à l'objet existant (l'entité devant être mis à jour).

Une multitude de possibilités existe, regardez la documentation

J'ai longtemps hésité à utiliser ce type de produit, car pour des cas plus complexes il peut être plus rapide de programmer soit même les affectations que de bien configurer l'outil et vérifier par la suite si le code généré correspondant à nos attentes.

mercredi 1 janvier 2020

Générer un rapport grâce à Thymeleaf et Open HTML to PDF

Une multitude de produit existe pour générer des rapports dans différents formats. Jaspert Report, iText. Certain sont plus bas niveau tel que PDFbox. Le moteur de template Thymeleaf permet de générer des pages web. Ensuite il est possible d'utiliser une librarie tierce tel que Open Html To pdf pour générer un pdf.

C'est la manière la plus simple et rapide que j'ai trouvé pour générer un pdf.

Spring boot sera utilisé, par défaut lorsqu'il est utilisé avec Thymeleaf, une configuration est généré afin de pouvoir généré des pages web.

Une autre configuration doit être créé pour être indépendant de celle-ci.

@Component
public class PdfGeneratorUtil<T> {

    @Autowired
    private TemplateEngine templateEngine;

    public byte[] process(String templateName, String templateExtension, List<T> listT, String contextVariableName) throws Exception {
        Context ctx = new Context();
        ctx.setVariable(contextVariableName, listT);
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        String processedHtml = templateEngine.process("fragments/html-reports/" + templateName + "." + templateExtension, ctx);

        PdfRendererBuilder builder = new PdfRendererBuilder();
        builder.useFastMode();
        builder.withHtmlContent(processedHtml, "");

        builder.toStream(output);
        builder.run();
        return output.toByteArray();
    }
}

Au niveau du contrôleur

public ResponseEntity<byte[]> getPdfReport(Model model) throws Exception {

        List<User> users = new ArrayList<>();

        users.add(new User("Yvan", "Dubois"));
        users.add(new User("Yvon", "Couler"));
        users.add(new User("Ytord", "Lamope"));

        byte[] content = pdfGeneratorUtil.process("usersReport", "html", users, "users");

        return preparePdfReport(content);
    }


Cette méthode permet de télécharger le pdf généré.

private ResponseEntity<byte[]> preparePdfReport(byte[] content) throws IOException {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("application/pdf"));
        String fileName = "report.pdf";

        headers.add("Content-Disposition", "inline;filename=" + fileName);
        headers.setCacheControl("no-cache, must-revalidate, post-check=0, pre-check=0");
        ResponseEntity<byte[]> response = new ResponseEntity<>(content, headers, HttpStatus.OK);
        return response;
    }


Cette portion de code permet d'assigner une liste d'objets à une variable dans un template Thymeleaf.  La classe PdfRendererBuilder prendra cette page et génèrera un pdf.

La mise en page peut être définie dans le template Thymeleaf dans la section css.

@page
{
    size: letter portrait;
    margin-left: 10px;
    margin-right:15px;
}


.new-page{
    page-break-after:always;
}


Avec des possibilités de condition dans Thymeleaf et le css, il est aisé d'arriver d'allure professionel.