martes, 5 de diciembre de 2017

API Rest con PHP y MySQL para el manejo de imágenes

Las cargas de imágenes son una cosa que siempre se torna bastante complicada y manejar esto a través de una API Rest es aún más retador. En este artículo vamos a ver cómo insertar imágenes y demás datos en una tabla de MySQL a través de servicios web de tipo REST que permitirán ejecutar las diferentes operaciones sobre la base de datos. Como consideraciones iniciales, es importante mencionar que debamos revisar antes el siguiente artículo:
http://blog.rolandopalermo.com/2017/12/slim-composer-php-rest-api-install.html.

También tendremos que configurar nuestro servidor Apache para poder tener URL más amigables. Para esto modificar los archivos:

C:\xampp\apache\conf\extra\httpd-vhosts.conf
C:\Windows\System32\drivers\etc\hosts

Los cuales deberán quedarnos con el siguiente contenido:

Archivo httpd-vhosts.conf
NameVirtualHost *:80

<VirtualHost *:80>
    DocumentRoot "C:/xampp/htdocs/rest-api/public"
    ServerName rest-api
</VirtualHost>

<VirtualHost *:80>
    DocumentRoot "C:/xampp/htdocs/rest-api-demo/public"
    ServerName rest-api-demo
</VirtualHost>
Archivo hosts
127.0.0.1 rest-api-demo

Recuerden que a lo largo de todo el artículo haré referencia a la ruta C:/xampp que es donde tengo instalado a mi servidor Xampp.

Como primera parte, tendremos una base de datos con una tabla para gestionar usuarios. El script es el siguiente:
CREATE DATABASE IF NOT EXISTS rest_api_base;
USE rest_api_base;

--
-- Definition of table `user`
--

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id_user` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `avatar` blob NOT NULL,
  PRIMARY KEY (`id_user`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
Ahora la clase PHP para conectarnos a la base de datos:
<?php

class DB {

    private $db_host = 'localhost';
    private $db_user = 'root';
    private $db_pass = '123456';
    private $db_name = 'rest_api_base';

    public function connect() {
        $mysql_connect_str = "mysql:host=$this->db_host;dbname=$this->db_name";
        $dbConnection = new PDO($mysql_connect_str, $this->db_user, $this->db_pass);
        $dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        return $dbConnection;
    }

}
Y la clase que mapea a la tabla users de la base de datos.
<?php

class User {

    public $idUser;
    public $name;
    public $avatar;

    function __construct($idUser, $name, $avatar) {
        $this->idUser = $idUser;
        $this->name = $name;
        $this->avatar = $avatar;
    }

}
También las clase DAO donde se implementarán las operaciones sobre la base de datos.
<?php

require '../src/model/user.php';

class UserDAO {

    public function findAll() {
        $sql = "SELECT * FROM user;";
        try {
            $db = new DB();
            $db = $db->connect();
            if (is_null($db)) {
                echo '{"error": true, "message": "text": "No se pudo conectar a la base de datos."}';
            } else {
                $stmt = $db->query($sql);
                $usuarios = $stmt->fetchAll();
                $arr_usuarios = array();  //array to parse jason from
                $db = null;
                foreach ($usuarios as $row) {
                    $usr = new User($row[0], $row[1], base64_encode($row[2]));
                    $arr_usuarios[] = $usr;
                }
                echo '{"error": false, "data": ' . json_encode($arr_usuarios) . '}';
            }
        } catch (PDOException $ex) {
            echo '{"error": true, "message": "' . $ex->getMessage() . '"}}';
        }
    }

    public function findOne($idUser) {
        $sql = "SELECT * FROM user WHERE id_user=?;";
        try {
            $db = new DB();
            $db = $db->connect();
            if (is_null($db)) {
                echo '{"error": true, "message": "text": "No se pudo conectar a la base de datos."}';
            } else {
                $stmt = $db->prepare($sql);
                $array = array($idUser);
                $stmt->execute($array);
                $usuarios = $stmt->fetchAll();
                $arr_usuarios = array();  //array to parse jason from
                foreach ($usuarios as $row) {
                    $usr = new User($row[0], $row[1], base64_encode($row[2]));
                    $arr_usuarios[] = $usr;
                }
                echo '{"error": false, "data": ' . json_encode($arr_usuarios) . '}';
            }
        } catch (PDOException $ex) {
            echo '{"error": true, "message": "' . $ex->getMessage() . '"}}';
        }
    }

    public function put($user) {
        $sql = "INSERT INTO user(name, avatar) VALUES (?,?)";
        try {
            $db = new DB();
            $db = $db->connect();
            if (is_null($db)) {
                echo '{"error": true, "message": "text": "No se pudo conectar a la base de datos."}';
            } else {
                $stmt = $db->prepare($sql);
                $array = array($user->name, base64_decode($user->avatar));
                $stmt->execute($array);
                $id_producto_insertado = $db->lastInsertId();
                $db = null;
                echo '{"error": false, "data": ' . $id_producto_insertado . '}';
            }
        } catch (PDOException $ex) {
            echo '{"error": true, "message": "' . $ex->getMessage() . '"}}';
        }
    }
    
    public function update($user) {
        $sql = "UPDATE user SET name=?, avatar=? WHERE id_user=?";
        try {
            $db = new DB();
            $db = $db->connect();
            if (is_null($db)) {
                echo '{"error": true, "message": "text": "No se pudo conectar a la base de datos."}';
            } else {
                $stmt = $db->prepare($sql);
                $array = array($user->name, base64_decode($user->avatar),$user->idUser);
                $stmt->execute($array);
//                $id_producto_insertado = $db->lastInsertId();
                $db = null;
                echo '{"error": false, "data": "Usuario actualizado"}';
            }
        } catch (PDOException $ex) {
            echo '{"error": true, "message": "' . $ex->getMessage() . '"}}';
        }
    }

}
Ahora, como mencioné al inicio del artículo, utilizaremos el Framework Slim cuya instalación la expliqué en el post del cual hice referencia al inicio de este artículo. En esta clase se definirán las rutas y los métodos a través de los cuales se accederá a las distintas opciones de nuestra API.
<?php

use PsrHttpMessageServerRequestInterface as Request;
use PsrHttpMessageResponseInterface as Response;

//$app_productos = new SlimApp;

require '../src/dao/user_dao.php';

$app_dapp->get('/api/users/findall', function(Request $request, Response $response) {
    $dao = new UserDAO();
    $dao->findAll();
});

$app_dapp->get('/api/users/find/{idUser}', function(Request $request, Response $response) {
    $dao = new UserDAO();
    $dao->findOne($request->getAttribute('idUser'));
});

$app_dapp->post('/api/users/put', function(Request $request, Response $response) {
    $dao = new UserDAO();
    $user = new User(null, $request->getParam('name'), $request->getParam('avatar'));
    $dao->put($user);
});

$app_dapp->post('/api/users/update', function(Request $request, Response $response) {
    $dao = new UserDAO();
    $user = new User($request->getParam('idUser'), $request->getParam('name'), $request->getParam('avatar'));
    $dao->update($user);
});
Si todo ha marchado bien, podremos acceder desde cualquier cliente y visualizar la información que se solicite. Por ejemplo, lanzado una petición a través de POSTMAN a la opción de listar los usuarios, tendremos lo siguiente:



En este caso, las imágenes fueron tratas en su codificación de Base64. Ahora, si queremos más de cerca nuestra API en acción, podemos utilizar POSTMAN para insertar una imagen:



También podemos escribir un sencillo cliente en PHP. El código sería el siguiente:
<?php

$url = "http://rest-api-demo/api/users/findall";
$client = curl_init($url);
curl_setopt($client, CURLOPT_RETURNTRANSFER, 1);
$curl_response = curl_exec($client);
//echo $curl_response;
$obj_response = json_decode($curl_response);
$array_data = $obj_response->data;
//header("Content-type: image/gif");
foreach($array_data as $item) {
 echo $item->name . '<br>';
 echo '<img width="150" src="data:image/gif;base64,' . $item->avatar . '" />';
 echo '<br>';
}
Y deberíamos tener algo similar a esto:



El proyecto completo lo pueden encontrar en Github y lo pueden descargar del siguiente enlace:
https://github.com/rolandopalermo/rest-api-demo

Espero que este artículo les sea de utilidad y no olviden seguirme a través de Facebook.

lunes, 4 de diciembre de 2017

String split with Transact-SQL

In spite of The STRING_SPLIT function is now available under compatibility level 130, knowing how to implement this function is something that is definitely worthwhile.
CREATE FUNCTION [dbo].[fnSplitString] 
( 
    @string NVARCHAR(MAX), 
    @delimiter CHAR(1) 
) 
RETURNS @output TABLE(splitdata NVARCHAR(MAX) 
) 
BEGIN 
    DECLARE @start INT, @end INT 
    SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) 
    WHILE @start < LEN(@string) + 1 BEGIN 
        IF @end = 0  
            SET @end = LEN(@string) + 1
       
        INSERT INTO @output (splitdata)  
        VALUES(SUBSTRING(@string, @start, @end - @start)) 
        SET @start = @end + 1 
        SET @end = CHARINDEX(@delimiter, @string, @start)
        
    END 
    RETURN 
END
GO
How to use:
DECLARE
    @_txt_tmp  NVARCHAR(MAX),
    @full_string NVARCHAR(MAX) = "Hello World From Transact-SQL"
DECLARE
    @cursor_for_spliting CURSOR
    SET @cursor_for_spliting = CURSOR FOR SELECT splitdata FROM [dbo].[fnSplitString](@full_string,'')
    OPEN @cursor_for_spliting FETCH NEXT FROM @cursor_for_spliting INTO @_txt_tmp
    WHILE @@FETCH_STATUS = 0
    BEGIN
        -- Do whatever with @_txt_tmp
    FETCH NEXT FROM @cursor_for_spliting INTO @_txt_tmp
END;
CLOSE @cursor_for_spliting ;
DEALLOCATE @cursor_for_spliting;

sábado, 2 de diciembre de 2017

Creando una REST Api para PHP con el Framework Slim 3

Slim es un framework para crear microservicios REST utilizando PHP. En este post se va a mostrar la manera cómo instalar Slim utilizando una herramienta para la gestión de dependencias en PHP llamada Composer. Resumiento, para poder seguir este artículo, necesitaremos lo siguiente:
Una vez tengamos instaladas todas estas herramientas, procederemos a descargar Slim vía Composer. Para esto, primero debemos crear nuestra carpeta de trabajo llamada rest-api dentro del directo C:\xampp\htdocs (La ruta dependerá de dónde hayan instalado Xampp pero se recomiendo no utilizar nombres muy largos), situarnos en la carpeta utilizando un CDM y descargar la versión 3 del Framework Slim.
C:\xampp\htdocs\rest-api>cd C:\xampp\htdocs\rest-api
C:\xampp\htdocs\rest-api>composer require slim/slim "^3.0"
Ahora utilizaremos SublimeText para abrir la carpeta, en donde podremos visualizar todos los archivos de dependencias que se nos ha creado. Dentro de la carpeta rest-api crearemos dos subcarpetas: public y src. La siguiente imagen ilustra lo descrito anteriormente:



Ahora crearemos un archivo en la carpeta public llamada index.php cuyo contenido será el siguiente:
<?php
use PsrHttpMessageServerRequestInterface as Request;
use PsrHttpMessageResponseInterface as Response;

require '../vendor/autoload.php';

$app = new SlimApp;
$app->get('/hello/{name}', function (Request $request, Response $response) {
    $name = $request->getAttribute('name');
    $response->getBody()->write("Hello, $name");

    return $response;
});
$app->run();
El código anterior es un ejemplo sencillo de un servicio Rest que recibe como parámetro un nombre y retorna un saludo. Y ahora, para poder verificar el funcionamiento de nuestra api podemos ingresar a la siguiente URL: http://localhost/rest-api/public/index.php/hello/rolando. En este punto quiero hacer algunas anotaciones, en primer lugar la ruta depende de la carpeta que hayan creado, en mi caso dicha carpeta se llama rest-api. Es importante también no olvidar que debemos escribir la ruta completa incluído el nombre del archivo index.php sino obtendremos de respuesta un error 404.

Espero que este post les sea de ayuda para iniciarse en el mundo de los Microservicios con PHP. No se olviden seguirme a través de mis redes sociales de Facebook haciendo clic aquí.

martes, 28 de noviembre de 2017

Diferencia entre git push -u y git push

En el post de hoy vamos a explicar las diferencias entre los comandos git push -u origin master y git push origin master. Aunque ambos comandos sirven para enviar nuestros cambios a un repositorio remoto, la bandera -u permite también agregar referencias ascendentes de seguimiento; en otras palabras, a partir de este punto, Git sabrá hacia dónde enviar y de dónde obtener los cambios sin la necesidad de utilizar argumentos en los comandos git push y git pull.

Si no establecemos el upstream para el seguimiento, cuando invoquemos al comando git pull, se nos presentará un error indicándonos que no existe información de seguimiento para la rama actual. La siguiente imagen ilustra este escenario.



Por otra parte, si ejecutamos el comando con la bandera -u, a partir de ese momento se creará una referencia de trazabilidad que nos permitirá invocar, por ejemplo, al comando git pull sin la necesidad de argumentos.

sábado, 25 de noviembre de 2017

Crear dominio en Servidor Glassfish

Un dominio Glassfish es un conjunto de instancias o espacio de nombres administrativos que se pueden administrar de forma conjunta. Este mismo proporciona una configuración preconfigurada para todas las aplicaciones del usuario o cliente, en este caso se pueden editar acorde a lo que necesitemos. Para crear un dominio, lo primero que debemos hacer es ubicarnos en la instalación de nuestro Glassfish y a continuación ejecutar el comando:
asadmin create-service domain1
Si todo se realizó correctamente, obtendremos el mensaje mostrado en la siguiente imagen:



Ahora inicimos nuestro Glassfish a través del comando:
asadmin start-domain


Si todo se ejecutó correctamente, al acceder a la ruta http://localhost:8080, podremos visualizar la consola de administración de nuestro contenedor de aplicaciones.
Para reiniciar nuestro dominio podemos usar el comando:
asadmin restart-domain domain1

miércoles, 22 de noviembre de 2017

RestController con Spring Boot

En este post vamos a revisar un ejemplo sencillo de lo que sería los servicios Rest utilizando Spring Framework. Las tecnologías requeridas para este proyecto son:
  • Spring Boot
  • H2
  • Maven
  • JDK 8
A continuación se muestra la estructura del proyecto Maven. En este proyecto, utilizaremos una base de datos H2 la cual podremos manipular a través de tres servicios Rest ya sea para insertar, consultar un registro o listar una tabla.



El archivo POM de dependencias Maven es el siguiente:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.rolandopalermo.git</groupId>
 <artifactId>base-api-rest-springboot</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>
 
 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.0.RELEASE</version>
    </parent>
    
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
    
 <dependencies>
  
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
 
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  
  <dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
  </dependency>
 </dependencies>
 <build>
  <finalName>base-api-rest-springboot</finalName>
 </build>
</project>
La clase principal utilizada para iniciar Spring Boot es la siguiente:
package com.rolandopalermo.rest.base;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

 public static void main(String[] args) {
  SpringApplication.run(Application.class, args);
 }

}
La siguiente clase representa el bean que será utilizado para manipular nuestra base de datos. Este bean utilizará anotaciones las cuales le permitirán validar la correcta estructura de campos en cualquier instancia así como también manipular este bean como objeto JSON.
package com.rolandopalermo.rest.base.model;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;

import org.hibernate.validator.constraints.NotBlank;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;

@Entity
public class User {

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

 @JsonProperty("user_name")
 @NotBlank
 private String userName;

 @JsonProperty("last_name")
 @NotBlank
 private String lastName;

 @JsonProperty("gender")
 @NotNull
 private Gender gender;

 @JsonProperty("age")
 @Min(value = 18)
 @Max(value = 150)
 @NotNull
 private Integer age;

 @JsonProperty("birth")
 @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy")
 @Past
 @NotNull
 private Date dateOfBirth;

 public Long getId() {
  return id;
 }

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

 public String getUserName() {
  return userName;
 }

 public void setUserName(String userName) {
  this.userName = userName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public Gender getGender() {
  return gender;
 }

 public void setGender(Gender gender) {
  this.gender = gender;
 }

 public Integer getAge() {
  return age;
 }

 public void setAge(Integer age) {
  this.age = age;
 }

 public Date getDateOfBirth() {
  return dateOfBirth;
 }

 public void setDateOfBirth(Date dateOfBirth) {
  this.dateOfBirth = dateOfBirth;
 }

}
package com.rolandopalermo.rest.base.model;

public enum Gender {
    MALE, FEMALE;
}
Esta clase se encargá de definir los métodos para acceder a base de datos. En este caso se reutilizará la clase genérica CrudRepository.
package com.rolandopalermo.rest.base.repository;

import org.springframework.data.repository.CrudRepository;

import com.rolandopalermo.rest.base.model.User;
 
public interface UserRepository extends CrudRepository<User, Long> {
 
}
La clase UserService conformará la capa de servicio y es en donde se invocará a los métodos definidos en la clase UserRepository que vendría siendo nuestra capa de datos.
package com.rolandopalermo.rest.base.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.rolandopalermo.rest.base.model.User;
import com.rolandopalermo.rest.base.repository.UserRepository;

@Service
public class UserService {

 @Autowired
 private UserRepository repository;

 public User get(long userId) {
  return repository.findOne(userId);
 }

 public List<User> list() {
  Iterable<User> users = repository.findAll();
  List<User> list = new ArrayList<User>();
  for(User user : users) {
   list.add(user);
  }
  return list;
 }

 public User create(User user) {
  return repository.save(user);
 }
}
La clase UserController expondrá los diversos métodos para manipular la tabla de Usuarios. Estos serán para insertar, consultar un registro o listarlos todos. Estos servicios serán expuestos a través de los métodos POST y GET.
package com.rolandopalermo.rest.base.controller;

import java.util.List;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.rolandopalermo.rest.base.model.User;
import com.rolandopalermo.rest.base.service.UserService;

@RestController
public class UserController {

 @Autowired
 private UserService userService;

 @RequestMapping(value = "/users", method = RequestMethod.GET)
 public ResponseEntity<User> list() {
  List<User> users = userService.list();
  return new ResponseEntity(users, HttpStatus.OK);
 }

 @RequestMapping(value = "/user", method = RequestMethod.GET)
 public ResponseEntity<User> userById(@RequestParam(value = "id") long id) {
  User user = userService.get(id);
  return new ResponseEntity(user, HttpStatus.OK);
 }

 @RequestMapping(value = "/create", method = RequestMethod.POST)
 public ResponseEntity<User> create(@Valid @RequestBody User user) {
  User userCreated = userService.create(user);
  return new ResponseEntity(userCreated, HttpStatus.CREATED);
 }

}
Al ejecutar el proyecto, tendremos lo siguiente:



Al enviar la siguiente petición utilizando POSTMAN, el resultado será el siguiente:
{
   "id":1,
   "user_name":"Juan",
   "last_name":"Perez",
   "gender":"MALE",
   "age":23,
   "birth":"12-10-1994"
}


Cuando consultemos los registros ingresados, obtendremos lo siguiente:
[
    {
        "id": 1,
        "user_name": "Juan",
        "last_name": "Perez",
        "gender": "MALE",
        "age": 23,
        "birth": "12-10-1994"
    },
    {
        "id": 2,
        "user_name": "Juan",
        "last_name": "De Arco",
        "gender": "FEMALE",
        "age": 23,
        "birth": "12-10-1994"
    }
]

martes, 14 de noviembre de 2017

Creating Web Services with PHP and SOAP

There are two basic approaches that are used to create web services from scratch. In this first of a two part series on web services I’ll talk about the 'top-down' approach. According with this approach, we need to produce the XML description of the service before it is implemented. So the service is fully described in terms of what it does, how it can be called and what result(s) it returns, but is not actually implemented. This (WSDL) specification is then used as a guide to writing the code that implements the service.

The project structure



The webservice will receive a string parameter and it will return the 'hello' string concatenated with the input parameter. For example, if the input is 'Andrea', then the output will be 'Hello Andrea'. In order to achieve this, we need to define the WSDL descriptor.

The descriptor.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="HelloWS"
      targetNamespace="http://ws.rolandopalermo.com/"
      xmlns:tns="http://ws.rolandopalermo.com/"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">

 <wsdl:types>
 </wsdl:types>
  
 <wsdl:message name="helloRequest">
  <wsdl:part name="name" type="xsd:string"></wsdl:part>
 </wsdl:message>
 
 <wsdl:message name="helloResponse">
  <wsdl:part name="result" type="xsd:string"></wsdl:part>
 </wsdl:message>
  
 <wsdl:portType name="HelloWS">
  <wsdl:operation name="hello">
            <wsdl:input message="tns:helloRequest"/>
            <wsdl:output message="tns:helloResponse"/>
        </wsdl:operation>
 </wsdl:portType>
  
 <wsdl:binding name="HelloWSPortBinding" type="tns:HelloWS">
  <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
  <wsdl:operation name="hello">
   <soap:operation soapAction="http://localhost/soap_demo/server.php"/>
   <wsdl:input>
    <soap:body use="literal"/>
   </wsdl:input>
   <wsdl:output>
    <soap:body use="literal"/>
   </wsdl:output>
  </wsdl:operation>
 </wsdl:binding>
  
 <wsdl:service name="HelloWS">
  <wsdl:port name="HelloWSPort" binding="tns:HelloWSPortBinding">
   <soap:address location="http://localhost/soap_demo/server.php"/>
  </wsdl:port>
 </wsdl:service>
 
</wsdl:definitions>
We also need to implement the hello method in a php file.

The server.php file
<?php
// turn off WSDL caching
ini_set("soap.wsdl_cache_enabled","0");

function hello($name)
{
 return 'Hello '.' '.$name;
}

// initialize SOAP Server
$server = new SoapServer("descriptor.wsdl",[]);
// register available functions
$server->addFunction('hello');
// start handling requests
$server->handle();
Now, we can import the WSDL file into SoapUI for example and the results should look like this:

The SOAP request.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.rolandopalermo.com/">
   <soapenv:Header/>
   <soapenv:Body>
      <ws:hello>
         <name>Andrea</name>
      </ws:hello>
   </soapenv:Body>
</soapenv:Envelope>
The SOAP response.
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Body>
      <SOAP-ENV:helloResponse>
         <result>Hello  Andrea</result>
      </SOAP-ENV:helloResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Do not forget to enable the SOAP extension in the php.ini file. To do this, you have to find extension=php_soap.dll in php.ini, remove the semicolon(;) and restart the server.

domingo, 9 de julio de 2017

Nuevo Esquema Offline de Documentos Electrónicos | SRI

Desde la publicación de la Resolución NAC-DGERCGC14-00790, el SRI puso en marcha un nuevo esquema para la emisión de comprobantes electrónicos: el esquema Off-line. Actualmente este esquema, con el propósito de que los emisores de comprobantes que lo deseen puedan realizar pruebas y ajustar sus servicios a este nuevo esquema, está disponible en las siguientes URL's:

Uno es para el ambiente de Pruebas en donde cada contribuyente certificará que su aplicación funcione correctamente con cada tipo de comprobante electrónico.

https://celcer.sri.gob.ec/comprobantes-electronicos-ws/RecepcionComprobantesOffline?wsdl
https://celcer.sri.gob.ec/comprobantes-electronicos-ws/AutorizacionComprobantesOffline?wsdl

El segundo es para el ambiente de Producción, al cual cada contribuyente deberá acceder una vez que ha realizado las pruebas y esté seguro que su aplicación funciona correctamente, las direcciones del los WS son las siguientes:

https://cel.sri.gob.ec/comprobantes-electronicos-ws/RecepcionComprobantesOffline?wsdl
https://cel.sri.gob.ec/comprobantes-electronicos-ws/AutorizacionComprobantesOffline?wsdl

Con el nuevo esquema Off-line, se puede generar un comprobante en tres sencillos pasos; ya que el cambio más significativo se encuentra en que los comprobantes serán enviados al mismo tiempo tanto al receptor como al SRI. Es decir, el mismo comprobante que recibe el SRI para su validación es el que recibirán sus socios de negocio.

Figura 1. Diferencias entre el esquema ONLINE y el nuevo esquema OFFLINE (Fuente: SRI)

En RP Consulting ya disponemos del nuevo esquema Off-line. Más de 20 clientes en Ecuador confirman la calidad de nuestro servicio de consultoría y capacitación en temas de facturación electrónica.

Te invitamos a ponerte en contacto con nosotros en el siguiente formulario para evaluar la integración de este nuevo esquema, ya sea a través de librerías, dll, apis o servicios Web. Contamos con los consultores más especializados en el tema.

Datos de contacto
Skype rolandopalermo
Email rolando.roc@gmail.com
Facebook fb.com/rolandopalermo
Licenciamiento No posee restricciones de licencia

lunes, 1 de mayo de 2017

HTML y Latex: MathJax

MathJax es un motor de visualización JavaScript de código abierto para la notación LaTeX, MathML y ​​AsciiMath que funciona en todos los navegadores modernos. Para utilizar este motor, basta con agregarel código que muestro a continuación:
<script type="text/x-mathjax-config">
   MathJax.Hub.Config({"HTML-CSS": { preferredFont: "TeX", availableFonts: ["STIX","TeX"], linebreaks: { automatic:true }, EqnChunk: (MathJax.Hub.Browser.isMobile ? 10 : 50) },
       tex2jax: { inlineMath: [ ["$", "$"], ["\\(","\\)"] ], displayMath: [ ["$$","$$"], ["\[", "\]"] ], processEscapes: true, ignoreClass: "tex2jax_ignore|dno" },
       TeX: { 
           noUndefined: { attributes: { mathcolor: "red", mathbackground: "#FFEEEE", mathsize: "90%" } }, Macros: { href: "{}" } },
       messageStyle: "none"
   });
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-AMS_HTML-full"></script>
Los resultados son tan asombrosos como los ejemplos que muestro a continuación:
<p>
When (a ne 0), there are two solutions to (ax^2 + bx + c = 0) and they are
$$x = {-b pm sqrt{b^2-4ac} over 2a}.$$
</p>
$$\text{When }(a \ne 0)\text{, there are two solutions to }(ax^2 + bx + c = 0)\text{ and they are}$$ $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$
<p>
$$sum_{i=0}^n i^2 = frac{(n^2+n)(2n+1)}{6}$$
</p>
$$\sum_{i=0}^n i^2 = \frac{(n^2+n)(2n+1)}{6}$$
<p>
$$sum_{i=0}^n i^2 = frac{(n^2+n)(2n+1)}{6}$$
</p>
$$\sum_{i=0}^n i^2 = \frac{(n^2+n)(2n+1)}{6}$$
<p>
$$
begin{array}{c|lcr}
n & text{Left} & text{Center} & text{Right} \
hline
1 & 0.24 & 1 & 125 \
2 & -1 & 189 & -8 \
3 & -20 & 2000 & 1+10i
end{array}
$$
</p>
$$ \begin{array}{c|lcr} n & \text{Left} & \text{Center} & \text{Right} \\ \hline 1 & 0.24 & 1 & 125 \\ 2 & -1 & 189 & -8 \\ 3 & -20 & 2000 & 1+10i \end{array} $$
<p>
$$underset{j=1}{overset{infty}{LARGEmathrm K}}frac{a_j}{b_j}=cfrac{a_1}{b_1+cfrac{a_2}{b_2+cfrac{a_3}{b_3+ddots}}}$$
</p>
$$\underset{j=1}{\overset{\infty}{\LARGE\mathrm K}}\frac{a_j}{b_j}=\cfrac{a_1}{b_1+\cfrac{a_2}{b_2+\cfrac{a_3}{b_3+\ddots}}}$$
<p>
$require{AMScd}$
begin{CD}
A @&gt;a&gt;&gt; B\
@V b V V= @VV c V\
C @&gt;&gt;d&gt; D
end{CD}
</p>
$\require{AMScd}$ \begin{CD} A @>a>> B\\ @V b V V= @VV c V\\ C @>>d> D \end{CD}
Un claro ejemplo donde podemos visualizar MathJax en todo su potencial es en math.stackexchange.com. Les invito a revisar el proyecto que definitivamente es de mucha ayuda a la publicación de resultados para la gente inmersa en la ciencia.

domingo, 30 de abril de 2017

Raphael JS Pie Chart and Spring MVC

Raphael JS is a lightweight JavaScript framework which allows you to draw vector graphics in a browser. In this post, I will introduce you a basic example in order to explain how to create a Pie Chart from a given set of data which will be provided by a Spring Controller and a Ajax calling.

Required Software to Run Example:
  • Java 7
  • Eclipse Mars
  • Apache Tomcat 8.0
  • Maven
  • Raphael JS
Project Structure in Netbeans



POM.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.rolandopalermo.web</groupId>
 <artifactId>spring-raphael</artifactId>
 <packaging>war</packaging>
 <version>0.0.1-SNAPSHOT</version>
 <name>spring-raphael Maven Webapp</name>
 <url>http://maven.apache.org</url>
 <dependencies>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>jstl</artifactId>
   <version>1.2</version>
  </dependency>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>3.8.1</version>
   <scope>test</scope>
  </dependency>
  <!-- Spring -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>4.3.5.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>4.3.5.RELEASE</version>
  </dependency>
  <!-- JSON -->
  <dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-core</artifactId>
   <version>2.7.3</version>
  </dependency>
  <dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.7.3</version>
  </dependency>
 </dependencies>
 <build>
  <finalName>spring-raphael</finalName>
 </build>
</project>
Pie.js
Raphael.fn.pieChart = function(cx, cy, r, values, labels, stroke) {
 var paper = this;
 var rad = Math.PI / 180;// degrees to radians
 var chart = this.set();
 var angle = 0, total = 0, start = 0;
 // Draw a sector
 function sector(cx, cy, r, startAngle, endAngle, params) {
  console.log(params.fill);
  var x1 = cx + r * Math.cos(-startAngle * rad), x2 = cx + r
    * Math.cos(-endAngle * rad), y1 = cy + r
    * Math.sin(-startAngle * rad), y2 = cy + r
    * Math.sin(-endAngle * rad);
  return paper.path(
    [ "M", cx, cy, "L", x1, y1, "A", r, r, 0,
      +(endAngle - startAngle > 180), 0, x2, y2, "z" ]).attr(
    params);
 }

 process = function(j) {
  var value = values[j], angleplus = 360 * value / total, popangle = angle
    + (angleplus / 2), color = Raphael.hsb(start, .75, 1), ms = 500, delta = 30, bcolor = Raphael
    .hsb(start, 1, 1), p = sector(cx, cy, r, angle, angle
    + angleplus, {
   fill : "90-" + bcolor + "-" + color,
   stroke : stroke,
   "stroke-width" : 3
  }), txt = paper.text(cx + (r + delta + 55) * Math.cos(-popangle * rad),
    cy + (r + delta + 25) * Math.sin(-popangle * rad), labels[j])
    .attr({
     fill : bcolor,
     stroke : "none",
     opacity : 0,
     "font-size" : 20
    });
  p.mouseover(function() {
   p.stop().animate({
    transform : "s1.1 1.1 " + cx + " " + cy
   }, ms, "elastic");
   txt.stop().animate({
    opacity : 1
   }, ms, "elastic");
  }).mouseout(function() {
   p.stop().animate({
    transform : ""
   }, ms, "elastic");
   txt.stop().animate({
    opacity : 0
   }, ms);
  });
  angle += angleplus;
  chart.push(p);
  chart.push(txt);
  start += .1;
 };
 for (var i = 0, ii = values.length; i < ii; i++) {
  total += values[i];
 }
 for (i = 0; i < ii; i++) {
  process(i);
 }
 return chart;
};

$(function() {
 $.ajax({
  method: "POST",
        url: 'get-full-report',
        dataType: 'json',
        crossDomain: true,
        beforeSend: function (xhr) {
            //Set authentication headers
        },
        success: function (data, textStatus, jqXHR) {
         if (data.error) {
                alert(data.message);
            } else {
             var values = [], labels = [];
             $.each(data, function(i, item) {
              values.push(parseInt(data[i].percentageOfUse, 10));
              labels.push(data[i].name);
             });
             Raphael("holder", 700, 700).pieChart(350, 350, 200, values, labels, "#fff");
            }
        },
        error: function (xhr) { // if error occured
            alert('No se pudo procesar la solicitud.');
        },
        complete: function () {
        },
        xhrFields: {
            withCredentials: true
        }
 });
});
Spring Controller
package com.rolandopalermo.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.rolandopalermo.web.bean.Language;
import com.rolandopalermo.web.util.ReportUtil;
import com.rolandopalermo.web.util.ResponseList;

@Controller
public class HomeController {

 @RequestMapping(value = { "/", "/home" }, method = RequestMethod.GET)
 public ModelAndView home() {
  ModelAndView model = new ModelAndView();
  model.setViewName("index");
  return model;
 }

 @RequestMapping(value = { "get-full-report" }, method = RequestMethod.POST)
 @CrossOrigin(origins = "*", allowCredentials = "true")
 @ResponseBody
 public String getReport() {
  String jsonResponse = "";
  ResponseList<Language> list = ReportUtil.generateReport();
  if (list == null || list.isEmpty()) {
   jsonResponse = "{"error" : true, "message": "No available data"}";
  } else {
   jsonResponse = list.toString();
  }
  return jsonResponse;
 }

}

And your output should look like:



Download the full source code from Github and enjoy it! https://github.com/rolandopalermo/spring-raphael

lunes, 24 de abril de 2017

How to use Jquery datatable in Spring MVC

Creating a table data along with features like pagination, filtering the records, sorting, no of records to show etc. is a challenger job for developers because it involves lot of designs and coding. But now it becomes very easy to develop with the help of jQuery datatable plugin. In this post we will show how to use jQuery datatable in Spring MVC 4 application.

Required Software to Run Example:
  • Java 7
  • Netbeans
  • Maven
  • MySQL
Project Structure in Netbeans



POM.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.rolandopalermo.web</groupId>
    <artifactId>spring-datatable</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>spring-datatable</name>

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
        
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>
        
        <!-- Jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.7.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.7.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${endorsed.dir}</outputDirectory>
                            <silent>true</silent>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>javax</groupId>
                                    <artifactId>javaee-endorsed-api</artifactId>
                                    <version>7.0</version>
                                    <type>jar</type>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
POJO Class used in Example
package com.rolandopalermo.domain;

import com.rolandopalermo.util.EmployeeStatus;
import com.rolandopalermo.util.JsonConverter;
import java.io.Serializable;

/**
 *
 * @author rolan
 */
public class Employee implements Serializable {

    private int id;
    private String name;
    private String surname;
    private int age;
    private String address;
    private EmployeeStatus status;

    public Employee(int id, String name, String surname, int age, String address, EmployeeStatus status) {
        this.id = id;
        this.name = name;
        this.surname = surname;
        this.age = age;
        this.address = address;
        this.status = status;
    }

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
    
    public EmployeeStatus getStatus() {
        return status;
    }

    public void setStatus(EmployeeStatus status) {
        this.status = status;
    }

    @Override
    public String toString() {
        String json = JsonConverter.INSTANCE.asJsonString(this);
        return json;
    }

}
Spring MVC Controller
package com.rolandopalermo.controller;

import com.rolandopalermo.domain.Employee;
import com.rolandopalermo.test.DummyDAO;
import com.rolandopalermo.util.ResponseList;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

/**
 *
 * @author rolan
 */
@Controller
public class HomeController {

    @RequestMapping(value = {"/", "/home"}, method = RequestMethod.GET)
    public ModelAndView home() {
        ModelAndView model = new ModelAndView();
        model.setViewName("home");
        return model;
    }

    @RequestMapping(value = {"/get-all-employees"}, method = RequestMethod.POST)
    @CrossOrigin(origins = "*", allowCredentials = "true")
    @ResponseBody
    public String getAllEmployees(HttpServletRequest request, HttpServletResponse response) {
        String json;
        try {
            ResponseList<Employee> lstInvoices = DummyDAO.getAllEmployees();
            json = lstInvoices.toString();
        } catch (Exception e) {
            json = "{"error" : true, "message": "" + e.getMessage() + "."}";
        }
        return json;
    }
}
Javascript
$(document).ready(function () {
    $.ajax({
        method: "POST",
        url: 'get-all-employees',
        dataType: 'json',
        crossDomain: true,
        beforeSend: function (xhr) {
            //Set authentication headers
        },
        success: function (data, textStatus, jqXHR) {
            if (data.error) {
                alert(data.message);
            } else {
                var selector = $("#applications");
                selector.html("");
                selector.append(JSON.stringify(data));
                data = $.parseJSON(selector.text());
                console.log(data);
                table = $("#employees").DataTable({
                    data: data,
                    scrollX: true,
                    scrollCollapse: true,
                    fixedColumns: true,
                    columns: [{
                            data: "id",
                            visible: false
                        }, {
                            data: "name"
                        }, {
                            data: "surname"
                        }, {
                            data: "age"
                        }, {
                            data: "address"
                        }, {
                            data: "status",
                            render: function (value) {
                                switch (value) {
                                    case "ACTIVE":
                                        return '<span class=active></span>';
                                    case "PENDING":
                                        return '<span class=pending></span>';
                                    case "INACTIVE":
                                        return '<span class=inactive></span>';
                                    case "DELETED":
                                        return '<span class=deleted></span>';
                                    default:
                                        return '<span class=active></span>';
                                }
                            }
                        }
                    ]
                });
            }
        },
        error: function (xhr) { // if error occured
            alert('No se pudo procesar la solicitud.');
        },
        complete: function () {
        },
        xhrFields: {
            withCredentials: true
        }
    });
});
WebAppInitializer
<%-- 
    Document   : view
    Created on : Jan 20, 2017, 7:15:31 PM
    Author     : Rolando
--%>

<%@ page language="java" pageEncoding="UTF-8" contentType="text/html;charset=UTF-8" %>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<script type="text/javascript" src="<c:url value='/static/app/third-party/js/jquery-1.12.4.js' />"></script>
<script type="text/javascript" src="<c:url value='/static/app/third-party/datatables/js/jquery.dataTables.min.js' />"></script>
<script type="text/javascript" src="<c:url value='/static/app/third-party/datatables/js/dataTables.buttons.min.js' />"></script>
<script type="text/javascript" src="<c:url value='/static/app/js/home.js' />"></script>
<link href="<c:url value='/static/app/third-party/datatables/css/jquery.dataTables.min.css'/>" rel="stylesheet"/>
<link href="<c:url value='/static/app/third-party/datatables/css/buttons.dataTables.min.css'/>" rel="stylesheet"/>
<link href="<c:url value='/static/app/css/master.css'/>" rel="stylesheet"/>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>How to use Jquery datatable in Spring MVC</title>
    </head>
    <body>
        <div id="applications" style="display: none"></div>
        <table id="employees" cellspacing="0" width="100%">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Name</th>
                    <th>Surname</th>
                    <th>Age</th>
                    <th>Address</th>
                    <th>Status</th>
                </tr>
            </thead>
            <tbody>
            </tbody>
        </table>
    </body>
</html>
And your output should looks like:



Download the full source code from Github and enjoy it! https://github.com/rolandopalermo/spring-datatable

domingo, 23 de abril de 2017

Spring 4 and MyBatis Java Full Annotations

In this post, we will provide MyBatis 3 annotations example with a simple application example. These annotations are declared in interface on methods for select, insert, update and delete operation. Now this interface will act as Mapper for SQL queries. We need to register mapper interface in MyBatis configuration XML. To map POJO and table columns, MyBatis provides Results annotation which works same as Resultmap tag in XML mapper. @Results helps to get query result by @Select annotation.

Required Software to Run Example:
  • Java 7
  • Netbeans
  • Maven
  • MySQL
Project Structure in Netbeans



Table Schema
CREATE TABLE `mybatis-spring`.`product` (
  `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NOT NULL,
  `price` DOUBLE NOT NULL,
  `description` TEXT NOT NULL,
  PRIMARY KEY (`id`)
)
ENGINE = InnoDB;
POM.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.rolandopalermo.web</groupId>
    <artifactId>spring-mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>spring-mybatis</name>

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <mysql.connector.version>5.1.31</mysql.connector.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
        
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.2.1.RELEASE</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
        
        <!-- Mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.2.2</version>
        </dependency>
        
        <!-- MySQL -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.connector.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${endorsed.dir}</outputDirectory>
                            <silent>true</silent>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>javax</groupId>
                                    <artifactId>javaee-endorsed-api</artifactId>
                                    <version>7.0</version>
                                    <type>jar</type>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
POJO Class used in Example
package com.rolandopalermo.web.domain;

/**
 *
 * @author rolan
 */
public class Product {

    private long id;
    private String name;
    private double price;
    private String description;

    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 getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

}
Mapper Interface Using MyBatis Annotation
package com.rolandopalermo.web.mapper;

import com.rolandopalermo.web.domain.Product;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

/**
 *
 * @author rolan
 */
public interface ProductMapper {

    @Results({
        @Result(property = "id", column = "id"),
        @Result(property = "name", column = "name"),
        @Result(property = "price", column = "price"),
        @Result(property = "description", column = "description")
    })
    @Select("SELECT id, name, price, description from product WHERE id = #{id}")
    Product selectProduct(int id);

    @Insert("INSERT into product(name, price, description) VALUES(#{name}, #{price}, #{description})")
    void insertProduct(Product product);

    @Update("UPDATE product SET name=#{name}, price =#{price}, description =#{description} WHERE id =#{id}")
    void updateProduct(Product product);

    @Delete("DELETE FROM product WHERE id =#{id}")
    void deleteProduct(int id);
    
    @Results({
        @Result(property = "id", column = "id"),
        @Result(property = "name", column = "name"),
        @Result(property = "price", column = "price"),
        @Result(property = "description", column = "description")
    })
    @Select("SELECT id, name, price, description from product")
    List<Product> selectAllProduct();

}
DataConfig
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
//import org.apache.commons.dbcp.BasicDataSource;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

/**
 *
 * @author Rolando
 */
@Configuration
@PropertySource("classpath:data.properties")
@MapperScan(basePackages = {"com.rolandopalermo.web.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory")
public class DataConfig {

    @Value("${mysql.driver}")
    private String driver;

    @Value("${mysql.url}")
    private String url;

    @Value("${mysql.user}")
    private String username;

    @Value("${mysql.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean
    public DataSourceTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }

}
MvcConfig
package com.rolandopalermo.web.config;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

/**
 *
 * @author Rolando
 */
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.rolandopalermo.web"})
public class MvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Bean
    public InternalResourceViewResolver jspViewResolver() {
        InternalResourceViewResolver bean = new InternalResourceViewResolver();
        bean.setPrefix("/WEB-INF/jsp/");
        bean.setSuffix(".jsp");
        return bean;
    }

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:messages");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }

}
WebAppInitializer
package com.rolandopalermo.web.config;

import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

/**
 *
 * @author Rolando
 */
public class WebAppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext container) {
        // Create the 'root' Spring application context
        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
//        rootContext.register(ServiceConfig.class, JPAConfig.class, SecurityConfig.class);
        rootContext.register(MvcConfig.class, DataConfig.class);
        // Manage the lifecycle of the root application context
        container.addListener(new ContextLoaderListener(rootContext));
        // Create the dispatcher servlet's Spring application context
        AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
        dispatcherServlet.register(MvcConfig.class);
        // Register and map the dispatcher servlet
        ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    }

}
Home.jsp
Now we will test our application.
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <h1>Products</h1>
        <c:forEach var="item" items="${products}">
            <c:out value="${item.name} ${item.price}"/>
            <br>
        </c:forEach>
    </body>
</html>
And your output should looks like:



Download the full source code from Github and enjoy it! https://github.com/rolandopalermo/spring-mybatis