vendredi 15 juin 2018

Datatable et la pagination de Spring Data

Datatable est un composant JQuery qui fournit divers fonctionalités: pagination, trie, filtre concernant un tableau (grid).  C'est un des plus évolué dans son domaine.

Voici l'initialisation du datatable en Javascript. La partie en gras concernant le passage des paramètres côté client au côté serveur.

var samplingsTable = $('#samplingsTable').DataTable({
    'bLengthChange': false, //hide 'show entries dropdown
    'processing': true,
    'serverSide': true,
    'pagingType': 'simple_numbers',
    'dom': 'Bfrtip',
    'ajax': {
        'type': 'get',
        'url': url,
        'data': function(d) {
            var current = $('#samplingsTable').DataTable();
            d.page = (current != undefined) ? current.page.info().page : 0;
            d.size = (current != undefined) ? current.page.info().length : 5;
            d.sort = d.columns[d.order[0].column].data + ',' + d.order[0].dir;
            d.search = d.search.value;
        }
    },
    'columns': [
      {'data': 'compositeId'}, 
      {'data': 'buildDate'}, 
      {'data': 'productTypesName'}, 
      {'data': 'productsName'}, 
      {'data': 'machineName'}
     ]
});

Niveau serveur, il faut faire le pont du côté serveur à client. Le paramètre draw est un champ utilisé par Datatable.

@GetMapping("samplings")
@ResponseBody
public Map<String, Object> getSamplings(Pageable pageable,
            @RequestParam("draw") Integer draw,

            @RequestParam(value = "search", defaultValue = "") String search) {

             Map<String, Object> data = new HashMap<>();

             Page<SamplingsDto> newPage  = samplingsService.get(pageable);

             data.put("data", newPage.getContent());
             data.put("draw", draw);
             data.put("recordsTotal", newPage.getTotalElements());
             data.put("recordsFiltered", newPage.getTotalElements());

             return data;
}

Il y a d'autre façon de procéder. tel que tout faire sur le serveur ou le client. La façon présenté ici est hybride.

Triage de clé composite avec Spring Data

Une clé primaire est très souvent lié à un champs. Il est possible d'en composer une à l'aide de plusieurs champs. Dans notre sous nous utliserons spring boot 2, jpa avec l'implémentation hibernate et postgres sql. Il est possible que ça fonctionne avec d'autre base de donnée et d'autre implémentation de JPA. La clé primaire sera composé de deux champs. Soit un Id issue d'une séquence et de l'année en cours.

@Entity
@IdClass(SamplingsPK.class)
public class Samplings {

    @Id
    private Integer year;

    @Id
    @GeneratedValue
    private Integer id;
}

public class SamplingsPK implements Serializable {

    private int year;
    private Integer id;

    public SamplingsPK(int year, Integer id) {
        this.id = id;
        this.year=year;
    }

    private SamplingsPK(){
        
    } 

    @PrePersist
    public void prePersist() {
        year = LocalDate.now().getYear();
    }

}

Puisque nous utilisons une clé composé, au niveau du repository, il faut spécifié le type.

@Repository
public interface SamplingsRepository extends JpaRepository<Samplings, SamplingsPK> {
}

Lorsque vous utiliser le paging dans Spring Data, il faut mentionner les champs de la clé composé.

Pageable pageable = PageRequest.of(1, 1, Sort.Direction.ASC, "year","id");

samplingsRepository.finAll(pageable);

Nous avons vue comment utiliser une clé composé  lors de l'utilisation de la pagination avec Spring data. 

samedi 30 décembre 2017

Spring Data: Sécuriser le trie

Spring Data permet de simplifier différentes opérations telles que les requêtes, les tries et la pagination.

L'objet Pageable contient tous le nécessaire pour supporter un grid html. Les champs à trier, l'ordre du trie, le nombre de donnée maximale à afficher...

Spring Data n'est que pour la partie serveur. Si aucune vérification n'est effectuée du côté du serveur et que le champs est inexistant ou si vous utilisez un dto avec une structure différente de votre bean, une exception sera lancée.

@GetMapping(value = "/members")
public Page<MemberDto> getPagingMembers(String search, Pageable pageRequest) {
    return memberService.getMembers(search, pageRequest);
}

@GetMapping(value = "/members")
public Page<MemberDto> getPagingMembers(String search, Pageable pageRequest) {
    Pageable newPageRequest = BindingPageRequestHelpler.binding(BindingPageRequestHelpler.memberPageRequestSort(), pageRequest);
    return memberService.getMembers(search, newPageRequest);
}
 
Un moyen simple est decréer une classe qui remplacera le nom du champsreçu du côté web avec celui qui lui est lié du côté serveur.

Un dto nommé memberDto pourrait avoir un champ id et un champ name.
Le bean aura un champs memberId et un champ name;

public class BindingPageRequestHelpler {

    public static Map<String, String> memberPageRequestSort() {
        return Collections.unmodifiableMap(Stream.of(
            new SimpleEntry<>("Id", "memberId"),
            new SimpleEntry<>("name", "name"))
            .collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue())));
    }

    public static Pageable binding(Map<String, String> pageRequestSort, Pageable pageRequest) {

        Sort sort = pageRequest.getSort();
        PageRequest newPageRequest = null;
        List<Order> newOrders = new ArrayList<>();

        for (Iterator<Order> orderIterator = sort.iterator(); orderIterator.hasNext();) {
            Order order = orderIterator.next();
            String column = pageRequestSort.get(order.getProperty());
            if (column != null) {
                Order newOrder = new Order(order.getDirection(), column);
                newOrders.add(newOrder);
            }
            Sort newSorts = new Sort(newOrders);
            newPageRequest = new PageRequest(pageRequest.getPageNumber(), pageRequest.getPageSize(), newSorts);
        }
        return newPageRequest;
    }
}
Cette classe permet rapidement d'éviter de se rendre jusqu'à la couche DAO pour avoir une erreur.

Spring Microservices in Action

Spring Microservices in Action est un livre qui s'adresse aux gens désirant se lancer dans le développement de microservice à l'aide des technologies de Spring. Il couvre les différentes sujets reliés au microservice: sécurité, load balancing, circuit breaker, log... Lorsque plus d'une librairie est utilisable au sein du framework Spring, l'auteur explique les avantages et inconvénients de chacune.

Le framework spring cloud utilise des librarie déjà éprouvé tel que celle de netflix. Peu de configuration est nécessaire.

L'auteur utilise l'exemple du passage d'une application monolithique à une architecture microsrevice tout au long du livre.

J'ai trouvé que le livre se lisait assez bien. L'auteur tout le sujet des microservice en général et il permet de se lancer en la création d'un système avec les bases apprises. Énormément d'annotations sont employés au sein de Spring. Une fois les concepts bien compris, il est possible de créer une application rapidement.

L'auteur utilise des images docker, il est donc possible de passer aisément d'un chapitre à un autre si on maitrise déjà la matière.

Je recommende fortement ce livre à tous ceux désirant se lancer dans les microservices à l'ade du framework Spring.

jeudi 19 octobre 2017

Utilisation d'annotation @Value dans Spring

Lorsque vous utilisez un fichier properties par exemple

message.text=bonjour
display.message=true

il est possible d'accéder à ces valeurs en utilisant l'annotation @Value

@Value("${message.text:default value}") 
private String messageText;


@Value("${display.message:true}") 
private boolean displayMessage;

lundi 5 décembre 2016

Télécharger un fichier sécurisé avec spring

Nous allons montrer comment accéder à un document sécurisé.

Lorsque l'utilisateur cliquera sur un bouton, il y aura appel sur le serveur qui génèrera un PDF.
Le fichier ouvrira dans un autre onglet.

   $("#memberContractPrintReport").on('click', function (e) {
        openDocument(getHostName() + "/report/document/contracts");
    });


   function openDocument(urlServer) {
        var win = window.open('_blank');
        downloadFile(urlServer, function (blob) {
            var url = URL.createObjectURL(blob);
            win.location = url;
        });
    }

    function downloadFile(url, success) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.setRequestHeader("Authorization", "Basic " + $.cookie('norcoption-authorization'));
        xhr.responseType = "blob";
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                if (success)
                    success(xhr.response);
            }
        };
        xhr.send(null);
    }

Maintenant la partie Java

   @GetMapping(value = "/report/document/contracts", produces = "application/pdf")
    public ResponseEntity<byte[]> getContractReport() throws IOException {
        byte[] content = reportService.generateContractReport();
        return prepareReport(content);
    }

    private ResponseEntity<byte[]> prepareReport(byte[] content) throws IOException {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("application/pdf"));
        String filename = "report.pdf";
        headers.setContentDispositionFormData(filename, filename);
        headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
        ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(content, headers, HttpStatus.OK);
        return response;
    }

Bootstrap-Table et le paging avec spring.

Nous allons voir comment mettre en place un bootstrap table avec le paging de spring data.
La pagination se fait du côté serveur

La première partie consiste à créer le tableau

  <table id="memberReportTableResult"
               style="min-height:100"
               data-search="true"
               data-classes="table table-no-bordered"
               data-height="600"
               data-pagination="true"
               data-side-pagination="server"
               data-sort-name="memberId"
               data-sort-order="desc">
        <thead>
            <tr>
                <th data-field="memberId" data-sortable="true">#</th>
                <th data-field="name" data-sortable="true">Nom</th>
                <th data-field="birthdate" data-formatter="localDateFormatter">Date de naissance</th>
                <th data-field="address" >Adresse</th>
                <th data-field="cityId" data-formatter="cityFormatter">Ville</th>
                <th data-field="postalCode" >Code postal</th>
                <th data-field="man" data-formatter="sexeFormatter" data-width="50px">Sexe</th>
                <th data-field="email">Courriel</th>
                <th data-field="phone1">Tél</th>
            </tr>
        </thead>
    </table>

Ensuite il faut effectuer l'initialisation en Javascript. C'est dans les fonctions de responseHandler et de queryParams qu'on lie les valeurs du composants table à ceux du paging.

$("#memberReportTableResult").bootstrapTable({
url: "http://localhost:8080/members",
queryParamsType: "",
ajaxOptions: {headers: {"Authorization": "Basic " + $.cookie('authorization')}},
responseHandler: function (res) {
return {
rows: res.content,
total: res.totalElements,
pageNumber: res.number,
pageSize: res.size
};
},
queryParams: function (params) {
return {
search: params.searchText,
page: params.pageNumber - 1,
size: params.pageSize,
sort: params.sortName + "," + params.sortOrder
};
}

});

Maintenant voyons la partie java

  @GetMapping(value = "/members")
    public Page<MemberDto> getPagingMembers(String search, Pageable pageRequest) {
        return memberService.getMembers(search, pageRequest);
    }

Nous avons pu voir rapidement comment faire le pont entre le paging de spring et le bootstrap table. Il suffit de relier les noms adéquats entre eux.