domingo, 8 de octubre de 2017

Spring Data JPA using Hibernate and Java Configuration with Annotations

In this tutorial I’ll show you how to use Spring Data JPA to integrate a relational database (PostgreSQL in my example) into a Spring Boot application.
I’ll use these technologies and tools:
  • Spring Tool Suite (STS) 3.8.4.RELEASE
  • Java 8
  • Spring Boot 1.5.3.RELEASE
  • Maven 3.3.9
  • PostgreSQL 9.6.2

1. The Project Structure

The final folder structure of our project.
The folder structure of the Spring Data JPA project as seen in STS

2. Create a new Spring Boot project

If you’re using STS, you can easily create a starter project by either selecting File > New > Spring Starter Project from the main menu or right-clicking on the Package Explorer and select New > Spring Starter Project.
STS command to create a new Spring Starter Project
Dialog window to set parameters for the Spring Data JPA project
The list of dependencies of the Spring Data JPA project
In case you’re using another IDE like Eclipse, Netbeans or IntelliJ IDEA, you can create a new Maven project and add to the pom.xml file the dependencies listed in the next paragraph.

3. Maven Dependencies

spring-boot-starter-data-jpa is the fundamental dependency for our project, then we need the driver for whatever SQL database we have chosen, in this case postgresql.
  • pom.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.thomasvitale</groupId>
  6. <artifactId>Application</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. <packaging>jar</packaging>
  9. <name>Application</name>
  10. <description>Demo application for Spring Data JPA</description>
  11. <parent>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-starter-parent</artifactId>
  14. <version>1.5.3.RELEASE</version>
  15. <relativePath/> <!-- lookup parent from repository -->
  16. </parent>
  17. <properties>
  18. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  19. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  20. <java.version>1.8</java.version>
  21. </properties>
  22. <dependencies>
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-starter-data-jpa</artifactId>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.postgresql</groupId>
  29. <artifactId>postgresql</artifactId>
  30. <scope>runtime</scope>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.springframework.boot</groupId>
  34. <artifactId>spring-boot-starter-test</artifactId>
  35. <scope>test</scope>
  36. </dependency>
  37. </dependencies>
  38. <build>
  39. <plugins>
  40. <plugin>
  41. <groupId>org.springframework.boot</groupId>
  42. <artifactId>spring-boot-maven-plugin</artifactId>
  43. </plugin>
  44. </plugins>
  45. </build>
  46. </project>
<?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.thomasvitale</groupId>
  <artifactId>Application</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Application</name>
  <description>Demo application for Spring Data JPA</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.3.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </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.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <scope>runtime</scope>
    </dependency>
  
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>
I’m using PostgreSQL, but this example works fine also with other relational databases. For example, if you want to use MySQL, all you need to do is replace the postgresql dependency with the mysql one.
  1. <dependency>
  2. <groupId>mysql</groupId>
  3. <artifactId>mysql-connector-java</artifactId>
  4. <scope>runtime</scope>
  5. </dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
</dependency>

4. Spring Configuration using Java Annotations and Hibernate

Spring lets you use either Java configuration or XML configuration or a mix of the two. I’ll use a pure Java configuration. There are a few implementations of JPA, in this example Hibernate is used. Its properties are stored in a dedicated file.
  • JpaConfig.java
  1. package com.thomasvitale;
  2. import java.util.Properties;
  3. import javax.persistence.EntityManagerFactory;
  4. import javax.sql.DataSource;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.context.annotation.PropertySource;
  9. import org.springframework.core.env.Environment;
  10. import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
  11. import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  12. import org.springframework.jdbc.datasource.DriverManagerDataSource;
  13. import org.springframework.orm.jpa.JpaTransactionManager;
  14. import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
  15. import org.springframework.orm.jpa.vendor.Database;
  16. import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
  17. import org.springframework.transaction.PlatformTransactionManager;
  18. import org.springframework.transaction.annotation.EnableTransactionManagement;
  19. @Configuration
  20. @EnableTransactionManagement
  21. @EnableJpaRepositories(basePackages = "com.thomasvitale.repository")
  22. @PropertySource("classpath:jpa.properties")
  23. public class JpaConfig {
  24. @Autowired
  25. private Environment env;
  26. @Bean
  27. public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
  28. HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
  29. vendorAdapter.setDatabase(Database.POSTGRESQL);
  30. vendorAdapter.setGenerateDdl(true);
  31. LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
  32. em.setDataSource(dataSource());
  33. em.setPackagesToScan("com.thomasvitale.model");
  34. em.setJpaVendorAdapter(vendorAdapter);
  35. em.setJpaProperties(additionalProperties());
  36. return em;
  37. }
  38. @Bean
  39. public DataSource dataSource(){
  40. DriverManagerDataSource dataSource = new DriverManagerDataSource();
  41. dataSource.setDriverClassName(env.getProperty("hibernate.connection.driver_class"));
  42. dataSource.setUrl(env.getProperty("hibernate.connection.url"));
  43. dataSource.setUsername(env.getProperty("hibernate.connection.username"));
  44. dataSource.setPassword(env.getProperty("hibernate.connection.password"));
  45. return dataSource;
  46. }
  47. @Bean
  48. public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
  49. JpaTransactionManager transactionManager = new JpaTransactionManager();
  50. transactionManager.setEntityManagerFactory(emf);
  51. return transactionManager;
  52. }
  53. @Bean
  54. public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
  55. return new PersistenceExceptionTranslationPostProcessor();
  56. }
  57. Properties additionalProperties() {
  58. Properties properties = new Properties();
  59. properties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
  60. properties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
  61. properties.setProperty("hibernate.current_session_context_class", env.getProperty("hibernate.current_session_context_class"));
  62. return properties;
  63. }
  64. }
package com.thomasvitale;

import java.util.Properties;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement 
@EnableJpaRepositories(basePackages = "com.thomasvitale.repository")
@PropertySource("classpath:jpa.properties")
public class JpaConfig {
  
  @Autowired
  private Environment env;

  @Bean
  public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
    
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setDatabase(Database.POSTGRESQL);
    vendorAdapter.setGenerateDdl(true);
    
    LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setDataSource(dataSource());
    em.setPackagesToScan("com.thomasvitale.model");
    em.setJpaVendorAdapter(vendorAdapter);
    em.setJpaProperties(additionalProperties());
 
    return em;
  }
  
  @Bean
  public DataSource dataSource(){
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(env.getProperty("hibernate.connection.driver_class"));
    dataSource.setUrl(env.getProperty("hibernate.connection.url"));
    dataSource.setUsername(env.getProperty("hibernate.connection.username"));
    dataSource.setPassword(env.getProperty("hibernate.connection.password"));
    return dataSource;
  }
  
  @Bean
  public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(emf);
  
    return transactionManager;
  }
  
  @Bean
  public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
    return new PersistenceExceptionTranslationPostProcessor();
  }

  Properties additionalProperties() {
    Properties properties = new Properties();
    properties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
    properties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
    properties.setProperty("hibernate.current_session_context_class", env.getProperty("hibernate.current_session_context_class"));
    return properties;
  }

}
  • jpa.properties
  1. hibernate.connection.username=db-user
  2. hibernate.connection.password=db-password
  3. hibernate.connection.url=jdbc:postgresql://localhost:5432/app
  4. hibernate.connection.driver_class=org.postgresql.Driver
  5. hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
  6. hibernate.current_session_context_class=thread
  7. hibernate.hbm2ddl.auto=validate
  8. hibernate.show_sql=false
  9. hibernate.format_sql=false
hibernate.connection.username=db-user
hibernate.connection.password=db-password
hibernate.connection.url=jdbc:postgresql://localhost:5432/app
hibernate.connection.driver_class=org.postgresql.Driver
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.current_session_context_class=thread
hibernate.hbm2ddl.auto=validate
hibernate.show_sql=false
hibernate.format_sql=false
If you’re using MySQL, pay attention to replace line 36 of JpaConfig.java with this one: vendorAdapter.setDatabase(Database.MYSQL); and change the relative values in the jpa.properties file:
  1. hibernate.connection.url=jdbc:mysql://localhost:3306/app
  2. hibernate.connection.driver_class=com.mysql.jdbc.Driver
  3. hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.connection.url=jdbc:mysql://localhost:3306/app
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.dialect=org.hibernate.dialect.MySQLDialect

5. Person Model

For the Person entity, first of all I create a new table (persons) in my PostgreSQL database (called app).
  1. CREATE TABLE persons(
  2. id SERIAL,
  3. firstName VARCHAR(32),
  4. lastName VARCHAR(32),
  5. PRIMARY KEY(id)
  6. );
CREATE TABLE persons(
  id SERIAL,
  firstName VARCHAR(32),
  lastName VARCHAR(32),
  PRIMARY KEY(id)
);
Then I code the mapping object model. The @Table(name = "persons") annotation specifies in which table to save a Person object. The @GeneratedValue(strategy = GenerationType.IDENTITY) is quite general. In PostgreSQL I have defined the id attribute as SERIAL. In MySQL you probably want to use AUTO_INCREMENT. That’s just fine, it works with both of them.
  • Person.java
  1. package com.thomasvitale.model;
  2. import javax.persistence.Entity;
  3. import javax.persistence.GeneratedValue;
  4. import javax.persistence.GenerationType;
  5. import javax.persistence.Id;
  6. import javax.persistence.Table;
  7. @Entity
  8. @Table(name = "persons")
  9. public class Person {
  10. @Id
  11. @GeneratedValue(strategy = GenerationType.IDENTITY)
  12. private Integer id;
  13. private String firstName;
  14. private String lastName;
  15. public Person() {
  16. }
  17. public Person(String firstName, String lastName) {
  18. this.firstName = firstName;
  19. this.lastName = lastName;
  20. }
  21. public Integer getId() {
  22. return id;
  23. }
  24. public void setId(Integer id) {
  25. this.id = id;
  26. }
  27. public String getFirstName() {
  28. return firstName;
  29. }
  30. public void setFirstName(String firstName) {
  31. this.firstName = firstName;
  32. }
  33. public String getLastName() {
  34. return lastName;
  35. }
  36. public void setLastName(String lastName) {
  37. this.lastName = lastName;
  38. }
  39. @Override
  40. public String toString() {
  41. return firstName + " " + lastName;
  42. }
  43. }
package com.thomasvitale.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "persons")
public class Person {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;
  
  private String firstName;
  private String lastName;

  public Person() {
  }

  public Person(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  public Integer getId() {
    return id;
  }

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

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

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

  @Override
  public String toString() {
    return firstName + " " + lastName;
  }
}

6. Person Repository

Our PersonRepository extends the JpaRepository interface, that provides us with a lot of operations out-of-the-box, including the standard CRUD operations. You can also add custom operations like findByFirstName and findByLastName without writing any implementation: Spring Data JPA creates them for you!
  • PersonRepository.java
  1. package com.thomasvitale.repository;
  2. import java.util.List;
  3. import org.springframework.data.jpa.repository.JpaRepository;
  4. import org.springframework.stereotype.Repository;
  5. import com.thomasvitale.model.Person;
  6. @Repository
  7. public interface PersonRepository extends JpaRepository<Person, Integer> {
  8. public Person findByFirstName(String firstName);
  9. public List<Person> findByLastName(String lastName);
  10. }
package com.thomasvitale.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.thomasvitale.model.Person;

@Repository
public interface PersonRepository extends JpaRepository<Person, Integer> {
  
  public Person findByFirstName(String firstName);
  public List<Person> findByLastName(String lastName);
  
}

7. Demo

Let’s try some CRUD operations to test our application.
  1. package com.thomasvitale;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.boot.CommandLineRunner;
  4. import org.springframework.boot.SpringApplication;
  5. import org.springframework.boot.autoconfigure.SpringBootApplication;
  6. import com.thomasvitale.model.Person;
  7. import com.thomasvitale.repository.PersonRepository;
  8. @SpringBootApplication
  9. public class Application implements CommandLineRunner {
  10. @Autowired
  11. PersonRepository personRepository;
  12. public static void main(String[] args) {
  13. SpringApplication.run(Application.class, args);
  14. }
  15. @Override
  16. public void run(String... arg0) throws Exception {
  17. // Save three Person documents on PostgreSQL
  18. personRepository.save(new Person("Sheldon", "Cooper"));
  19. personRepository.save(new Person("Missy", "Cooper"));
  20. personRepository.save(new Person("Leonard", "Hofstadter"));
  21. // Get all the people
  22. System.out.println(">>> All the people in the database:");
  23. personRepository.findAll().forEach(System.out::println);
  24. // Get all the people with a specific last name
  25. System.out.println(">>> All people with last name = 'Cooper'");
  26. personRepository.findByLastName("Cooper").forEach(System.out::println);
  27. // Update an individual person
  28. Person person = personRepository.findByFirstName("Sheldon");
  29. person.setFirstName("Shelly");
  30. personRepository.save(person);
  31. // Delete all
  32. personRepository.deleteAll();
  33. }
  34. }
package com.thomasvitale;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.thomasvitale.model.Person;
import com.thomasvitale.repository.PersonRepository;

@SpringBootApplication
public class Application implements CommandLineRunner {
  
  @Autowired
  PersonRepository personRepository;

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

  @Override
  public void run(String... arg0) throws Exception {
    
      // Save three Person documents on PostgreSQL
      personRepository.save(new Person("Sheldon", "Cooper"));
      personRepository.save(new Person("Missy", "Cooper"));
      personRepository.save(new Person("Leonard", "Hofstadter"));
      
      // Get all the people
      System.out.println(">>> All the people in the database:");
      personRepository.findAll().forEach(System.out::println);
      
      // Get all the people with a specific last name
      System.out.println(">>> All people with last name = 'Cooper'");
      personRepository.findByLastName("Cooper").forEach(System.out::println);
      
      // Update an individual person
      Person person = personRepository.findByFirstName("Sheldon");
      person.setFirstName("Shelly");
      personRepository.save(person);
      
      // Delete all 
      personRepository.deleteAll();
    
  }
}

Resources

domingo, 10 de septiembre de 2017

Wildfly – Install PostgreSQL JDBC Driver as a Module

Instead of deploying a JDBC driver with the wildfly auto-deploy feature, the driver can be alternatively installed as an module. This is also necessary to be used with XA-Datasources. So it is a recommended way to install the driver as a module. The following section shows how to a jdbc driver module is created.

Creating a Module

To create a module:
  1. Go to WILDFLY_HOME/modules/system/layers/base/ and create the folder org/postgresql/main;
  2. copy the file postgresql jdbc driver jar file  to the new folderWILDFLY_HOME/modules/system/layers/base/org/postgresql/main
  3. create the file module.xml in the same folder with the following content:
        <?xml version="1.0" encoding="UTF-8"?>
        <module xmlns="urn:jboss:module:1.1" name="org.postgresql">
            <resources>
                <resource-root path="postgresql-9.4.1211.jar"/>
            </resources>
            <dependencies>
                <module name="javax.api"/>
                <module name="javax.transaction.api"/>
            </dependencies>
        </module>
The name of the driver file may vary, so make sure you declare exactly the same name in the resource-root tag. At this point, the module is not available yet. Youneed to reference the module as a driver in WildFly configuration with the following jboss-cli command:
[standalone@localhost:9990 /] /subsystem=datasources/jdbc-driver=postgresql:add(
    driver-name=postgresql,
    driver-module-name=org.postgresql,
    driver-class-name=org.postgresql.Driver
)
The command returns {“outcome” => “success”} in case of success. This command resulted in the following part in the configuration file:
    <datasources>
        {...}
        <drivers>
            {...}
            <driver name="postgresql" module="org.postgresql">
                <driver-class>org.postgresql.Driver</driver-class>
            </driver>
        </drivers>
    </datasources>
It makes the JDBC driver module available for the datasource creation. So a datasource definition can now be defined to use the postgresql driver in the following way:
...
 <datasource ....>
 <connection-url>jdbc:postgresql://localhost/....</connection-url>
 <driver>postgresql</driver>
...

lunes, 20 de febrero de 2017

UTL_MATCH : String Matching by Testing Levels of Similarity/Difference


The UTL_MATCH package was introduced in Oracle 10g Release 2, but first documented (and therefore supported) in Oracle 11g Release 2. It contains a variety of functions that are helpful for testing the level of similarity/difference between strings.
Related articles.

Setup

The examples in this article require the following table definition.
DROP TABLE match_tab;

CREATE TABLE match_tab (
  id   NUMBER,
  col1 VARCHAR2(15),
  col2 VARCHAR2(15),
  CONSTRAINT match_tab_pk PRIMARY  KEY (id)
);

INSERT INTO match_tab VALUES (1, 'Peter Parker', 'Pete Parker');
INSERT INTO match_tab VALUES (2, 'Peter Parker', 'peter parker');
INSERT INTO match_tab VALUES (3, 'Clark Kent', 'Claire Kent');
INSERT INTO match_tab VALUES (4, 'Wonder Woman', 'Ponder Woman');
INSERT INTO match_tab VALUES (5, 'Superman', 'Superman');
INSERT INTO match_tab VALUES (6, 'The Hulk', 'Iron Man');
COMMIT;

domingo, 16 de octubre de 2016

Adapter java

Adapter

Busca una manera estandarizada de adaptar un objeto a otro. Se utiliza para transformar una interfaz en otra, de tal modo que una clase que no pudiera utilizar la primera, haga uso de ella a través de la segunda.
Es conocido como Wrapper (al patrón Decorator también se lo llama Wrapper, con lo cual es nombre Wrapper muchas veces se presta a confusión).
Una clase Adapter implementa un interfaz que conoce a sus clientes y proporciona acceso a una instancia de una clase que no conoce a sus clientes, es decir convierte la interfaz de una clase en una interfaz que el cliente espera. Un objeto Adapter proporciona la funcionalidad prometida por un interfaz sin tener que conocer que clase es utilizada para implementar ese interfaz. Permite trabajar juntas a dos clases con interfaces incompatibles.

Este patrón se debe utilizar cuando:
  • Se quiere utilizar una clase que llame a un método a través de una interface, pero se busca utilizarlo con una clase que no implementa ese interface.
  • Se busca determinar dinámicamente que métodos de otros objetos llama un objeto.
  • No se quiere que el objeto llamado tenga conocimientos de la otra clase de objetos.
Este patrón convierte la interfaz de una clase en otra interfaz que el cliente espera. Esto permite a las clases trabajar juntas, lo que de otra manera no podrían hacerlo debido a sus interfaces incompatibles.
Por lo general, esta situación se da porque no es posible modificar la clase original, ya sea porque no se tiene el código fuente de la clase o porque la clase es una clase de propósito general, y es inapropiado para ella implementar un interface par un propósito específico. En resumen, este patrón debe ser aplicado cuando debo transformar una estructura a otra, pero sin tocar la original, ya sea porque no puedo o no quiero cambiarla.

Diagrama UML


Target: define la interfaz específica del dominio que Cliente usa.
Cliente: colabora con la conformación de objetos para la interfaz Target.
Adaptado: define una interfaz existente que necesita adaptarse
Adapter: adapta la interfaz de Adaptee a la interfaz Target
El Cliente llama a las operaciones sobre una instancia Adapter. De hecho, el adaptador llama a las operaciones de Adaptee que llevan a cabo el pedido.

Ejemplo

Vamos a plantear el siguiente escenario: nuestro código tiene una clase Persona (la llamamos PersonaVieja) que se utiliza a lo largo de todo el código y hemos importado un API que también necesita trabajar con una clase Persona (la llamamos PersonaNueva), que si bien son bastante similares tienen ciertas diferencias:
Nosotros trabajamos con los atributos nombre, apellido y fecha de nacimiento.
Sin embargo, la PersonaNueva tiene un solo atributo nombre (que es el nombre y apellido de la persona en cuestión) y la edad actual, en vez de la fecha de nacimiento.

Para esta situación lo ideal es utilizar el Adapter. Para ello primero crearemos las 2 clases de Persona y sus correspondientes interfaces.





 Ahora crearemos al Adapter.

Y se utiliza de esta manera:

Consecuencias
  • El cliente y las clases Adaptee permanecen independientes unas de las otras.
  • Puede hacer que un programa sea menos entendible.
  • Permite que un único Adapter trabaje con muchos Adaptees, es decir, el Adapter por sí mismo y las subclases (si es que la tiene). El Adapter también puede agregar funcionalidad a todos los Adaptees de una sola vez.

Temas a tener en cuenta.
Si bien el Adapter tiene una implementación relativamente sencilla, se puede llevar a cabo con varias técnicas:
1) Creando una nueva clase que será el Adaptador, que extienda del componente existente e implemente la interfaz obligatoria. De este modo tenemos la funcionalidad que queríamos y cumplimos la condición de implementar la interfaz.
2) Pasar una referencia a los objetos cliente como parámetro a los costructores de los objetos adapter o a uno de sus métodos. Esto permite al objeto adapter ser utilizado con cualquier instancia o posiblemente muchas instancias de la clase Adaptee. En este caso particular, el Adapter tiene una implementación casi idéntica al patrón Decorator.
3) Hacer la clase Adapter una clase interna de la clase Adaptee. Esto asume que tenemos acceso al código de dicha clase y que es permitido la modificación de la misma.
4) Utilizar sólo interfaces para la comunicación entre los objetos.

Las opciones más utilizadas son la 1 y la 4.

lunes, 23 de mayo de 2016

Docker 101 Tutorial

Docker 101 Tutorial

This is a realistic interactive tutorial for understanding the basics of Docker.
It requires you to successfully complete each minitask, so could be used as an interviewing tool for those that claim to know Docker.
It uses a complete and realistic shell to train you in.
Below is a video showing what it looks like to the user.