Translate

Mostrando las entradas con la etiqueta Spring Boot. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Spring Boot. Mostrar todas las entradas

miércoles, 2 de julio de 2025

¿Qué es Spring Data JPA? Parte 2

 


Veamos un ejemplo de entidad de jpa: 

📄 Persona.java


package com.ejemplo.demo.entidad;

import jakarta.persistence.*;


@Entity

public class Persona {

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;

    private String nombre;

    private String email;


    // Constructores

    public Persona() {}

    public Persona(String nombre, String email) {

        this.nombre = nombre;

        this.email = email;

    }


    // Getters y setters

    public Long getId() { return id; }

    public String getNombre() { return nombre; }

    public void setNombre(String nombre) { this.nombre = nombre; }

    public String getEmail() { return email; }

    public void setEmail(String email) { this.email = email; }

}


@Entity: Marca esta clase como una entidad JPA, lo que significa que será persistida en una tabla de la base de datos. El nombre de la tabla por defecto será el mismo que el nombre de la clase (persona en minúsculas).

@Id: Indica que el atributo id es la clave primaria de la entidad. Cada fila tendrá un valor único en esta columna.

@GeneratedValue(strategy = GenerationType.IDENTITY):  Especifica que el valor del campo id será generado automáticamente por la base de datos (usualmente como autoincremental).  Existen otras estrategias (AUTO, SEQUENCE, TABLE), pero IDENTITY es simple y efectiva para bases H2 o MySQL.


Repositorio

📄 PersonaRepository.java


package com.ejemplo.demo.repositorio;

import com.ejemplo.demo.entidad.Persona;

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


public interface PersonaRepository extends JpaRepository<Persona, Long> {

}


JpaRepository<Persona, Long>:  Es una interfaz de Spring Data que nos proporciona métodos CRUD listos para usar: findAll(), save(), deleteById(), etc.

Persona: el tipo de entidad

Long: el tipo de la clave primaria (id)

No se necesita ninguna anotación porque Spring escanea esta interfaz y genera una implementación automática.


Servicio

📄 PersonaService.java


@Service

public class PersonaService {

    // ...

}


@Service:  Marca esta clase como un componente de servicio, lo que permite que Spring la detecte y gestione como parte del contexto de la aplicación (inyección de dependencias, ciclo de vida, etc.).

Controlador

📄 PersonaController.java


@RestController

@RequestMapping("/personas")

public class PersonaController {

    // ...

}


    • @RestController:  Marca la clase como un controlador REST. Es equivalente a usar @Controller + @ResponseBody para cada método.

    • @RequestMapping("/personas"):  Define la ruta base de la API. Todos los métodos se expondrán bajo /personas.

    • @GetMapping, @PostMapping, @DeleteMapping: Indican qué método HTTP debe usarse para cada operación.

    • @RequestBody:  Indica que los datos del cuerpo del request (formato JSON) deben ser convertidos a un objeto Java.

    • @PathVariable: Extrae una variable de la URL, por ejemplo /personas/1 extrae el id = 1.


Archivo application.properties

spring.datasource.url=jdbc:h2:mem:testdb

spring.jpa.hibernate.ddl-auto=update

spring.h2.console.enabled=true


spring.jpa.hibernate.ddl-auto=update:  Le indica a JPA que genere automáticamente las tablas según nuestras entidades. Si la tabla no existe, la crea.


martes, 1 de julio de 2025

¿Qué es Spring Data JPA?


JPA (Java Persistence API) es una especificación estándar de Java para el mapeo objeto-relacional (ORM), que permite interactuar con bases de datos relacionales utilizando objetos Java.


Con JPA se pueden:

    • Definir clases Java como entidades persistentes (@Entity).

    • Especificar relaciones entre entidades (uno a muchos, muchos a muchos, etc.).

    • Realizar operaciones como insertar, actualizar, eliminar y consultar datos sin escribir SQL explícito.


JPA no es una implementación, sino una interfaz estándar. Las implementaciones más comunes son:

    • Hibernate (la más usada)

    • EclipseLink

    • OpenJPA


Spring Data JPA es un módulo de Spring que simplifica el uso de JPA:

    • Permite definir interfaces de repositorio sin necesidad de implementar los métodos CRUD.

    • Integra la capa de persistencia con el resto del ecosistema de Spring (inyección de dependencias, control transaccional, validaciones, etc.).

    • Facilita la creación de consultas con nombres de método (findByNombre, findByEmailContaining, etc.).

    • Soporta JPQL, SQL nativo y consultas dinámicas.


Spring Data JPA es una abstracción sobre JPA que reduce drásticamente el código necesario para acceder a la base de datos.


Ventajas de usar JPA con Spring

    • Menos código repetitivo: no es necesario implementar manualmente métodos CRUD.

    • Integración total con Spring: todo se gestiona mediante inyección de dependencias y configuración automática.

    • Consultas declarativas: se pueden crear consultas complejas usando el nombre del método.

    • Abstracción del SQL: permite trabajar con objetos sin escribir sentencias SQL directamente.

    • Gestión automática del contexto de persistencia: Spring gestiona transacciones, apertura y cierre de conexiones.

    • Compatible con múltiples bases de datos: MySQL, PostgreSQL, H2, Oracle, SQL Server, etc.


En el siguiente post vamos con algo más práctico. 


viernes, 10 de enero de 2025

Programación Reactiva con Spring WebFlux y Cassandra


Vamos a crear un proyecto webflux utilizando Cassndra. 

Primero creamos un proyecto Spring Boot y agregamos las dependencias necesarias:

  • spring-boot-starter-webflux
  • spring-boot-starter-data-cassandra-reactive


Si utilizamos maven el archivo pom.xml tendria estas dependencias :


<dependencies>

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-webflux</artifactId>

    </dependency>

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>

    </dependency>

    <dependency>

        <groupId>com.datastax.oss</groupId>

        <artifactId>java-driver-core</artifactId>

    </dependency>

</dependencies>

Si usamos gradle seria algo así : 

        implementation("org.springframework.boot:spring-boot-starter-data-cassandra-reactive")

implementation("org.springframework.boot:spring-boot-starter-webflux")


Agregamos la configuración de Cassandra en application.yml:


spring:

  data:

    cassandra:

      contact-points: [localhost]

      port: 9042

      keyspace-name: demo_keyspace

      schema-action: create-if-not-exists

o en el properties: 

spring.cassandra.contact-points=127.0.0.1

spring.cassandra.port=9042

spring.cassandra.keyspace-name=demo_keyspace

spring.cassandra.schema-action= create-if-not-exists

spring.cassandra.local-datacenter=datacenter1


Ahora vamos a definir una entidad para guardar y recuperar:


import org.springframework.data.annotation.Id;

import org.springframework.data.cassandra.core.mapping.PrimaryKey;

import org.springframework.data.cassandra.core.mapping.Table;


@Table("products")

public class Product {

    @Id

    @PrimaryKey

    private String id;

    private String name;

    private double price;


    // Getters y setters

}


Crea un repositorio usando ReactiveCassandraRepository:


import org.springframework.data.cassandra.repository.ReactiveCassandraRepository;

import org.springframework.stereotype.Repository;


@Repository

public interface ProductRepository extends ReactiveCassandraRepository<Product, String> {

}


Ahora hacemos el servicio: 


import org.springframework.stereotype.Service;

import reactor.core.publisher.Flux;

import reactor.core.publisher.Mono;


@Service

public class ProductService {


    private final ProductRepository productRepository;


    public ProductService(ProductRepository productRepository) {

        this.productRepository = productRepository;

    }


    public Flux<Product> getAllProducts() {

        return productRepository.findAll();

    }


    public Mono<Product> getProductById(String id) {

        return productRepository.findById(id);

    }


    public Mono<Product> createProduct(Product product) {

        return productRepository.save(product);

    }


    public Mono<Void> deleteProduct(String id) {

        return productRepository.deleteById(id);

    }

}


Ahora creamos un controlador REST con WebFlux:


import org.springframework.web.bind.annotation.*;

import reactor.core.publisher.Flux;

import reactor.core.publisher.Mono;


@RestController

@RequestMapping("/products")

public class ProductController {


    private final ProductService productService;


    public ProductController(ProductService productService) {

        this.productService = productService;

    }


    @GetMapping

    public Flux<Product> getAllProducts() {

        return productService.getAllProducts();

    }


    @GetMapping("/{id}")

    public Mono<Product> getProductById(@PathVariable String id) {

        return productService.getProductById(id);

    }


    @PostMapping

    public Mono<Product> createProduct(@RequestBody Product product) {

        return productService.createProduct(product);

    }


    @DeleteMapping("/{id}")

    public Mono<Void> deleteProduct(@PathVariable String id) {

        return productService.deleteProduct(id);

    }

}


Por ultimo tenemos que agregar las anotaciones @EnableReactiveCassandraRepositories y @Push a nuestro application : 


@SpringBootApplication

@EnableReactiveCassandraRepositories

@Push

class ApplicationDemo : AppShellConfigurator


fun main(args: Array<String>) {

runApplication<ApplicationDemo>(*args)

}


Y ahora podemos probar nuestros servicios. 

viernes, 21 de junio de 2024

Hola mundo en Vaadin


Vamos a hacer un hola mundo en Vaadin. Yo voy a dar por sobreentendido que sabemos crear un proyecto con spring. Si no lo sabes es tan fácil como ir a https://start.spring.io/ y de ahi vas configurando las cosas y agregamos el framework vaadin. 

Luego creamos un nuevo paquete como por ejemplo : com.example.vaadin.ui. Dentro del paquete, creamos una nueva clase MainView.java


package com.example.vaadin.ui;


import com.vaadin.flow.component.button.Button;

import com.vaadin.flow.component.notification.Notification;

import com.vaadin.flow.component.orderedlayout.VerticalLayout;

import com.vaadin.flow.router.Route;


@Route("")

public class MainView extends VerticalLayout {


    public MainView() {

        Button button = new Button("Haz clic aquí",

                event -> Notification.show("¡Hola, Mundo!"));

        add(button);

    }

}


Y listo!! Vamos a localhost:8080 para chequear nuestro "Hola mundo". 

martes, 18 de junio de 2024

Inyectar condicionalmente un bean con profiles


Tengo 2 beans que implementan una interfaz, por ejemplo un repositorio jpa con una base de datos y otro con archivos o en memoria, ponele... Y quiero algunas veces utilizar un bean y otras otro dependiendo de una configuración, esto se puede resolver de 2 maneras. Se puedes utilizar la anotación @Conditional o @Profile.

Veamos ejemplo utilizando profile: 


public interface MyService {

    void performService();

}


@Service

@Profile("serviceA")

public class ServiceA implements MyService {

    @Override

    public void performService() {

        System.out.println("Service A implementation");

    }

}


@Service

@Profile("serviceB")

public class ServiceB implements MyService {

    @Override

    public void performService() {

        System.out.println("Service B implementation");

    }

}


Tenemos que configurar el perfil activo en application.properties:


# Para usar ServiceA
spring.profiles.active=serviceA

# Para usar ServiceB
#spring.profiles.active=serviceB


Y inyectamos el bean:


@RestController
public class MyController {

    private final MyService myService;

    @Autowired
    public MyController(MyService myService) {
        this.myService = myService;
    }

    @GetMapping("/perform")
    public String perform() {
        myService.performService();
        return "Service performed";
    }
}

Y listo!!

sábado, 18 de marzo de 2023

Libros gratuitos

 

Download FREE IT Guides!

 

Linux Bible, 10th Edition ($36.00 Value) FREE for a Limited Time

Linux Bible, 10th Edition is the ultimate hands-on Linux user guide, whether you're a true beginner or a more advanced user navigating recent changes. this updated tenth edition covers the latest versions of Red Hat Enterprise Linux

 
 

Teach Yourself VISUALLY Windows 11 ($19.00 Value) FREE for a Limited Time

Teach Yourself VISUALLY Windows 11 collects all the resources you need to master the day-to-day use of Microsoft’s new operating system and delivers them in a single resource. Fully illustrated, step-by-step

 
 

RESTful Architecture Cheatsheet

The web has become an integral part of our daily lives, and with it, web applications have also become increasingly popular. However, as the number of web applications has grown, so has the complexity of managing them

 
 

Modern API Development with Spring and Spring Boot ($33.99 Value) FREE for a Limited Time

Developers need to know how to adapt to these modern API design principles. Apps are now developed with APIs that enable ease of integration for the cloud environment and distributed systems.

 

lunes, 1 de agosto de 2022

Log con Aop de Spring boot


Supongamos que queremos logear cada cosa que pasa por cada método de nuestros objetos, para hacer esto podemos utilizar aop. 

Veamos un ejemplo: 

@Aspect

@Configuration

@Order(0)

class LogingAspect {

    private val logger = LogFactory.getLog(LoggingAspect::class.java)


    @Pointcut("execution(public * com.paquete..*(..))")

    fun allControllersMethods() {

    }


    @Around("allControllersMethods()")

    fun profileAllMethods(pjp: ProceedingJoinPoint): Any {

        val method: Method = (pjp.signature as MethodSignature).method


        logger.info("Started ${method.name}")


        try {

            return if (pjp.args != null) {

                pjp.proceed(pjp.args)

            } else {

                pjp.proceed()

            }

        } catch (ex: Exception) {

            logger.error("Error in ${method.name}", ex)

            throw ex

        } finally {

            logger.info( "Finished ${method.name}")

        }

    }

}


Y Listo!!

lunes, 25 de julio de 2022

Interceptor de Retrofit

                                                                                                                                                                                                                                                                        

Supongamos que tenemos que enviar en todas las llamadas a web services, un valor o un token o cualquier cosa. 

Lo que podemos hacer con retrofit es un interceptor, este lo declaramos una vez y para todas las llamadas enviarán los valores. Veamos un ejemplo: 

@Component

class RetrofitClient {


    @Value("\${service.url}")

    private lateinit var baseUrl: String


    private lateinit var retrofit: Retrofit


    private lateinit var objectMapper: ObjectMapper


    @PostConstruct

    fun init() {

        objectMapper = ObjectMapper()

            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

            .registerModule(KotlinModule())


        val client = OkHttpClient.Builder().addInterceptor(EjemploInterceptor()).build()


        retrofit = Retrofit.Builder()

            .baseUrl(baseUrl)

            .client(client)

            .addConverterFactory(JacksonConverterFactory.create(objectMapper))

            .build()

    }


    fun <T> buildService(service: Class<T>): T {

        return retrofit.create(service)

    }


}


class EjemploInterceptor : Interceptor {


    companion object {

        private const val EJEMPLO = "ejemplo"

    }


    override fun intercept(chain: Interceptor.Chain): Response = chain.run {

        proceed(

            request()

                .newBuilder()

                .addHeader(EJEMPLO, "valor a agregar")

                .build()

        )

    }

}


En este ejemplo en todas las llamadas enviarán el valor agregado en el head. Y listo! 

lunes, 18 de julio de 2022

Apis en diferentes lenguajes y frameworks en la plataforma Java


Estoy haciendo una aplicación con n microservicios en n tipos de tecnologías y lenguajes todos en la plataforma Java. 

Los de n tipos de tecnologías es mentiroso solo 2 framework use por ahora Spring boot y micronaut y a decir verdad use un 80% de spring boot pero quiero escribir un post tipo draft, por las dudas si me canso y nunca termino. 

Por ende, vamos a ver como me fue con Spring boot y diferentes lenguajes y luego hablo de micronaut. 

Java: Bueno, esta es la tupla más utilizada y creo que ustedes saben que anda muy bien. No sé si puedo agregar algo más que no sepan. 

Kotlin: Me encanto, por varias razones me parece mejor que java. Kotlin es un lenguaje, muy bueno y muy compatible con java lo que lo hace una opción super fácil de usar y no se sufre nada con la compatibilidad con spring boot. 

Groovy: Igual que Java, no encuentro una buena razón para elegir este lenguaje, esta bueno, pero el tipado dinamico, no ofrece mayor ventaja y si lo usamos con tipando estático, es similar a java o kotlin. 

Scala: Un infierno de incompatibilidad, no le recomiendo utilizar spring boot con scala, tenes que estar siempre cambiando tipos, de tipos de Scala a Java y de Java a Scala. No es una opción práctica...

Clojure: Otro infierno, no solo por los tipos, sino tambien por la generación de clases con anotaciones. Muy trabajoso, al final cuando te funciona una Api, no te acordas para que la querías. 

Micronaut lo utilice muy poco con Groovy y Java y me fue muy bien, casi casi puedo decir que me gusta más que spring boot. Y ahora sigo con Micronaut y Quarkus. 

sábado, 9 de julio de 2022

Hacer un test de integración de un dao que guarda en Redis con Testcontainers y Spring boot


Me quedo relargo el titulo, pero la idea sería:

Tenemos una aplicación de spring boot y spring data que guarda información en Redis con un Dao y queremos probar este dao, ¿como lo hacemos?. Podemos usar testcontainers. 

Testcontainers es un framework que levanta una imagen de docker para que diferentes tecnologías esten accesibles para nuestros tests. 

Para utilizarlo debemos agregar esta dependencia en gradle o maven :


testImplementation "org.testcontainers:testcontainers:1.17.2"


Y en este ejemplo vamos a utilizar kotlin, pero funciona tambien con Java: 


@RunWith(SpringJUnit4ClassRunner::class)

@SpringBootTest(classes = [Application::class])

class EjemploRepositoryTest {


    @Autowired

    lateinit var ejemploRepository: EjemploRepository


    companion object {

        

        init {

            val redis : GenericContainer<Nothing>  = GenericContainer<Nothing>(DockerImageName.parse("redis:5.0.3-alpine"))

                .withExposedPorts(6379)


            redis.start()


            System.setProperty("redis.host", "${redis.host}")

            System.setProperty("redis.port", "${redis.firstMappedPort}")

        }

    }


    @Test

    fun `save and find all ejemplos`() {

        val count = ejemploRepository.findAll().count()

        val ejemplo = crearUnEjemplo() //este metodo te crea un ejemplo :D


        ejemploRepository.save(ejemplo)

        val ejemplos = ejemplo.findAll()


        Assert.assertNotNull(ejemplos)

        Assert.assertEquals(count + 1, ejemplos.count())

    }


}

En el código anterior falta el metodo crear ejemplo que va a crear el objeto para probar, no lo agrego porque no lo veo necesario. Por supuesto, el objeto ejemplo debe seguir con las notaciones de Spring data. 

Y listo! 

martes, 21 de junio de 2022

Como chequear si un registro esta siendo cacheado por Hibernate en una aplicación Spring boot.


Necesitaba ver si un registro estaba siendo cacheado por hibernate es decir JPA, en una aplicación spring boot. 

Hice lo que cualquiera hubiera hecho, puse show-sql en true, corrí un test que busca 2 veces el registro y me fije en el log que no hubiera 2 selects. Fácil pero poco científico, estaría bueno hacer un test que pruebe esto, y bueno hice esto : 


@RunWith(SpringJUnit4ClassRunner::class)

@SpringBootTest(classes = [Application::class])

class EjemploRepositoryTest {


    @Autowired

    lateinit var ejemploRepository: EjemploRepository


    @Autowired

    lateinit var entityManagerFactory: EntityManagerFactory


    @Test

    fun `find a Ejemplo from Empty cache`() {

        val ejemplo= getEjemplo()

        val cache = entityManagerFactory.cache

        Assert.assertFalse(cache.contains(Ejemplo::class.java, ejemplo.id))

    }


    @Test

    fun `find a Ejemplo from cache`() {

        val ejemplo= getEjemplo()

        saveAndFind(ejemplo)


        val cache = entityManagerFactory.cache


        Assert.assertTrue(cache.contains(Ejemplo::class.java, ejemplo.id))

    }


    private fun saveAndFind(ejemplo: Ejemplo): Ejemplo{

        ejemploRepository.save(ejemplo)

        return ejemploRepository.findById(ejemplo.id).get()

    }


    private fun getEjemplo(): Ejemplo{

        return  ... //Aca construimos un ejemplo 

    }


}

Y listo!!

sábado, 18 de junio de 2022

Crear un proyecto Spring boot, con Leiningen y Clojure


La idea es crear un proyecto spring boot, con Clojure y Leiningen. (ya lo dice el titulo)

Primero hacemos un proyecto hello word con Leiningen (ojo tienen que tener Leiningen instalado, esta bueno instalarlo con sdkman, así :  sdk install leiningen) 

lein new app ejemplo

Luego agregamos las dependencias de spring boot al proyecto, para que quede algo así: 

(defproject github "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.10.3"],
                 [org.springframework.boot/spring-boot-starter-web "2.6.7"],
                 [org.springframework.boot/spring-boot-configuration-processor "2.6.7"]]
  :main com.assembly.example.core
  :aot :all
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all
                       :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})

Ojo, tambien hay que agregar :aot y :all 

Ahora hacemos el Application, yo modifique el hello wold que vino por default, pero si lo hacen tengan cuidado de ponerlo en un paquete. 

(ns com.assembly.example.core
  (:import (org.springframework.boot SpringApplication))
  (:gen-class
    :name ^{org.springframework.boot.autoconfigure.SpringBootApplication []
            org.springframework.context.annotation.ComponentScan {:basePackages ["com.assembly.example"]}}
    com.assembly.example.core.Application
    :main true)
  )

(defn -main []
  (SpringApplication/run (Class/forName "com.assembly.example.core.Application") (into-array String '()))
  )

Y ahora hacemos un endpoint, en este ejemplo voy a hacer un hola mundo : 

(ns com.assembly.exanple.controller.greet

  (:import (org.springframework.web.bind.annotation PathVariable RequestMapping RequestMethod RestController)

           (org.springframework.http ResponseEntity))

  )


(gen-class

  :name ^{RestController {}

          RequestMapping {:value ["/v1/greet"]}} com.assembly.exanple.controller.greet.GreeterEndPoint

  :methods [[^{RequestMapping {:value ["/hello"]

                               :method [RequestMethod/GET]}} sayHello [] Object ]

            [^{RequestMapping {:value ["/helloto/{name}"]

                               :method [RequestMethod/GET]}} sayHelloTo [^{PathVariable {:value "name"}} String] Object ]

            ]

  :state injected

  :init init

)


(defn -init

  "Initialize the class by setting the state to an empty map, which can be populated with injected dependencies."

  []

  [[] (atom {})])


(defn -sayHello

  [this]

  (ResponseEntity/ok "Hello"))


(defn -sayHelloTo

  [this name]

  (ResponseEntity/ok (str "Hello " name)))


Y listo, si corremos el main, vamos a levantar el tomcat embebido de spring y vamos a : 

http://localhost:8080/v1/greet/hello

y veremos como nos saluda spring boot con clojure!! 

viernes, 3 de junio de 2022

Crear un proyecto Spring boot, con Scala y Sbt



La idea es crear un proyecto spring boot, con scala y sbt. (ya lo dice el titulo)

Primero hacemos un proyecto hello word con sbt (ojo tienen que tener sbt instalado, esta bueno instalarlo con sdkman) 

sbt new scala/hello-world.g8

Luego, tenemos que agregar nuestra dependencia web de spring boot en build.sbt : 

// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web

libraryDependencies += "org.springframework.boot" % "spring-boot-starter-web" % "2.6.7"

libraryDependencies += "org.springframework.boot" % "spring-boot-configuration-processor" % "2.6.7"


Deberían de modificar bastante el buld.sbt en mi caso me quedo así :


scalaVersion := "2.13.8"

organization := "com.miCompania"
name := "ejemplo"
version := "1.0"


libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "2.1.1"

// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web
libraryDependencies += "org.springframework.boot" % "spring-boot-starter-web" % "2.6.7"
libraryDependencies += "org.springframework.boot" % "spring-boot-configuration-processor" % "2.6.7"

Ahora lo que debemos de hacer es crear un paquete y mover el main a ese paquete para luego modificarlo de tal forma que llame a run de spring boot : 

package com.miCompania

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


@SpringBootApplication
class Application

object Main extends App {
  SpringApplication.run(classOf[Application], args:_*)
}

Ojo, tienen que tener el main en un paquete sino no anda. 

Por ultimo hacemos nuestro endpoint, en este ejemplo voy a hacer un hola mundo, comun. Pero en este punto podemos utilizar todas las ventajas de spring boot : 

package com.assembly.endpoint

import org.springframework.http.ResponseEntity

import org.springframework.web.bind.annotation.{PathVariable, RequestMapping, RequestMethod, RestController}


@RestController

@RequestMapping(Array("/v1"))

class GreeterEndPoint {


  @RequestMapping(value=Array("/hello/{name}"), method = Array(RequestMethod.GET))

  def sayHello(@PathVariable(name = "name") name:String): ResponseEntity[String] =

    ResponseEntity.ok(s"Hello ${name}")

}


Lo puse en otro paquete, esto solo para que quede más ordenado, y listo!!! 

miércoles, 1 de junio de 2022

Cache con Redis y Spring boot


Vamos ha hacer una pequeña cache para unos servicios en spring boot. 

Primero es necesario tener instalado Redis, y si ...

Luego agregamos esta dependencia a nuestro proyecto (uso gradle) : 

implementation("org.springframework.boot:spring-boot-starter-cache")

implementation("org.springframework.boot:spring-boot-starter-data-redis")


Luego configuramos en nuestro cache con esta clase : 

 package com.uap.demo.config

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.cache.RedisCacheConfiguration
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair
import java.time.Duration

@Configuration
class CacheConfig {

    @Bean
    fun cacheConfiguration(): RedisCacheConfiguration? {
        return RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(60))
            .disableCachingNullValues()
            .serializeValuesWith(SerializationPair.fromSerializer(GenericJackson2JsonRedisSerializer()))
    }
}

Yo le puse 60 minutos de vida a mis datos dado que no cambian tan frecuente. 

Ahora en el Application agregamos @EnableCaching, algo así : 

@SpringBootApplication
@EnableCaching
class DemoApplication

fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}

Por ultimo a nuestro servicio le decimos que vamos a cachear los datos, en este ejemplo tengo un servicio X : 

@Service
class XServiceImpl : XService {

    @Cacheable(value = ["xxxxx"])
    override fun x(parametro: String): List<String> {
          ...
    }
}

Lo que va a ocurrir es que cuando llamemos al servicio con un parametro, se va generar una entrada en Redis, y la próxima vez que llamemos este servicio usará la cache. 

Y listo!!! 

lunes, 21 de marzo de 2022

Apache Isis

 


Apache Isis permite el desarrollo de aplicaciones basadas en domain-driven al generar automáticamente una interfaz de usuario para sus aplicaciones Spring Boot. Eso significa:

  • Productividad: la aplicación es principalmente lógica comercial.
  • Descubrimiento: los expertos en el dominio comercial y los técnicos pueden trabajar de la mano para comprender el espacio del problema y luego el espacio de la solución.
  • Facilidad de uso: los usuarios comerciales encontrarán que la aplicación es fácil de aprender, ya que los conceptos comerciales están al frente y al centro.
  • Arquitectura limpia: el marco garantiza la separación de responsabilidades dentro de la arquitectura interna de su aplicación
  • Integración simple: publica automáticamente un evento de dominio genérico para integraciones asíncronas y una API REST para integración de sincronizas.
  • Y debido a que Apache Isis es de código abierto, se basa en estándares industriales/de facto como Spring Boot.


viernes, 4 de marzo de 2022

Escribiendo clientes Eureka con Feign


Una alternativa a la clase RestTemplate habilitada para Spring Ribbon es la biblioteca cliente Feign de Netflix. La biblioteca Feign adopta un enfoque diferente para llamar a un servicio REST al hacer que el desarrollador primero defina una interfaz Java y luego anote esa interfaz con las anotaciones de Spring Cloud para mapear qué servicio basado en Eureka invocará Ribbon.

Spring Cloud generará dinámicamente una clase de proxy que se utilizará para invocar el servicio REST de destino. No se escribe ningún código para llamar al servicio que no sea una definición de interfaz.

Para habilitar el uso del cliente Feign se debe agregar una anotación, @EnableFeignClients, a la clase Application.java

Luego de habilitar el cliente de Feign, veamos una definición de interfaz de cliente de Feign que se puede usar para llama:


/*Package and import left off for conciseness*/

@FeignClient("ejemploService")

public interface EjemploFeignClient {

 @RequestMapping(method= RequestMethod.GET,  value="/v1/ejemplo/{ejemploId}",
 consumes="application/json")
Ejemplo getEjemplo( @PathVariable("ejemploId") String ejemploId);

}

La gente mayor recordarán un framework que trabajaba de forma similar llamado cxf. 

En la anotación @FeignClient le pasámos el nombre de la identificación del servicio que deseamos que represente la interfaz. A continuación, defimos un método, getEjemplo el cual retorna un objeto Ejemplo.

Como se puede ver la definición de la clase usa anotaciones de las cual ya estamos familiarizados. 

Para usar la clase EjemploFeignClient, todo lo que necesitamos hacer es injectarla y usarla. El código de Feign Client se encargará de todo el trabajo de codificación.

Cuando utiliza la clase Spring RestTemplate estándar, todos los códigos de estado HTTP de las llamadas de servicio se devolverán a través del método getStatusCode() de la clase ResponseEntity. Con Feign Client, cualquier código de estado HTTP 4xx – 5xx devuelto por el servicio al que se llama se asignará a una FeignException. FeignException contendrá un cuerpo JSON que se puede analizar para el mensaje de error específico. Feign le brinda la capacidad de escribir una clase de decodificación de errores que asignará el error a una clase de excepción personalizada. 

Dejo link : https://github.com/OpenFeign/feign


lunes, 28 de febrero de 2022

Descubrir un servicio publicado con Eureka con Spring boot, parte 3


Seguimos con el post anterior.

Veamos un ejemplo de cómo usar un RestTemplate que es Ribbonaware. Este es uno de los mecanismos más comunes para interactuar con Ribbon a través de Spring. Para usar una clase RestTemplate con un balanceador de carga, debe definir un método de construcción de bean RestTemplate con una anotación de Spring Cloud llamada @LoadBalanced. 

El siguiente código muestra el método getRestTemplate() que creará el bean Spring RestTemplate respaldado por Ribbon.

package com.thoughtmechanix.licenses;

//...Most of import statements have been removed for consiceness

import org.springframework.cloud.client.loadbalancer.LoadBalanced;

import org.springframework.context.annotation.Bean;

import org.springframework.web.client.RestTemplate;

@SpringBootApplication

@EnableDiscoveryClient

@EnableFeignClients

public class Application {

  @LoadBalanced
  @Bean
  public RestTemplate getRestTemplate(){
    return new RestTemplate();
  }

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

Ahora que la definición del bean para la clase RestTemplate respaldada por Ribbon está definida, cada vez que desee usar el bean RestTemplate para llamar a un servicio, solo necesita conéctelo automáticamente a la clase que lo usa.

El uso de la clase RestTemplate con balanceador de carga se comporta prácticamente como una clase Spring RestTemplate estándar, excepto por una pequeña diferencia en cómo se define la URL para el servicio de destino. En lugar de utilizar la ubicación física del servicio en la llamada RestTemplate, creará la URL de destino utilizando el ID de servicio de Eureka del servicio al que desea llamar.

Veamos esta diferencia mirando el siguiente código.

/*Package and import definitions left off for conciseness*/

@Component

public class OrganizationRestTemplateClient {

  @Autowired
  RestTemplate restTemplate;

  public Organization getOrganization(String organizationId){
    ResponseEntity<Organization> restExchange =
    restTemplate.exchange("http://organizationservice/v1/organizations/{organizationId}",
                   HttpMethod.GET, null, Organization.class, organizationId);
    return restExchange.getBody();
  }
}

Este código debería parecerse un poco al ejemplo anterior, excepto por dos diferencias clave. Primero, Spring Cloud DiscoveryClient no está a la vista. En segundo lugar, la URL que se utiliza en la llamada restTemplate.exchange() debería parecerte extraña:

restTemplate.exchange("http://organizationservice/v1/organizations/{organizationId}",
                   HttpMethod.GET, null, Organization.class, organizationId);

El nombre del servidor en la URL coincide con el ID de la aplicación de la clave de servicio de la organización con la que registró el servicio de la organización en Eureka:

http://{identificación de la aplicación}/v1/organizaciones/{identificación de la organización}

El RestTemplate habilitado para Ribbon analizará la URL que se le pasó y usará lo que se le pase como el nombre del servidor como clave para consultar a Ribbon por una instancia de un servicio. La ubicación real del servicio y el puerto están completamente abstraídos del desarrollador.

Además, al usar la clase RestTemplate, Ribbon equilibrará la carga por turnos de todas las solicitudes entre todas las instancias de servicio.


sábado, 26 de febrero de 2022

Descubrir un servicio publicado con Eureka con Spring boot, parte 2


Seguimos con el post anterior. Pero para no quedar tan colgado retomemos este código : 

/*Packages and imports removed for conciseness*/

@Component

public class OrganizationDiscoveryClient {


  @Autowired

   private DiscoveryClient discoveryClient;


   public Organization getOrganization(String organizationId) {

     RestTemplate restTemplate = new RestTemplate();

     List<ServiceInstance> instances = discoveryClient.getInstances("organizationservice");

     if (instances.size()==0) return null;

        String serviceUri = String.format("%s/v1/organizations/%s", instances.get(0).getUri().toString(),

            organizationId);

        ResponseEntity< Organization > restExchange = restTemplate.exchange(serviceUri, 

                                                                                                     HttpMethod.GET,

                                                                                                     null, Organization.class, organizationId);

      return restExchange.getBody();

   }

}

Solo debemos usar Discovery-Client directamente cuando el servicio necesita consultar Ribbon para comprender qué servicios e instancias de servicio están registrados con él. Hay varios problemas con este código, incluidos los siguientes:

No se está aprovechando el balanceador de carga del lado del cliente de Ribbon: al llamar a Discovery-Client directamente, obtenemos una lista de servicios, pero tambien responsabilidad elegir qué instancias de servicio a invocar.

Se está haciendo demasiado trabajo: en este momento, debe crear la URL que se utilizará para llamar a su servicio. Es algo pequeño, pero cada pieza de código que puede evitar escribir es una pieza menos de código que tiene que depurar.

Es posible que los desarrolladores observadores de Spring hayan notado que está instanciando directamente la clase RestTemplate en el código. Esto es la antítesis de las invocaciones normales de Spring REST, ya que normalmente Spring Framework inyectaría RestTemplate en la clase que lo usa a través de la anotación @Autowired.

Ha creado una instancia de la clase RestTemplate porque una vez que haya habilitado Spring DiscoveryClient en la clase de aplicación a través de la anotación @EnableDiscovery-Client, todas las RestTemplates administradas por Spring Framework tendrán un interceptor habilitado para Ribbon. Al instanciar directamente la clase RestTemplate le permite evitar este comportamiento.

En resumen, existen mejores mecanismos para llamar a un servicio respaldado por Ribbon.

lunes, 21 de febrero de 2022

Descubrir un servicio publicado con Eureka con Spring boot


Ya tenemos el servicio registrado en Eureka. Ahora necesitamos llamarlo sin tener conocimiento directo de la ubicación. De tal manera que este ultimo buscará la ubicación física mediante Eureka.

Para nuestros propósitos, vamos a ver tres bibliotecas de clientes de Spring/Netflix diferentes en las que un consumidor de servicios puede interactuar con Ribbon. Estas bibliotecas pasarán del nivel más bajo de abstracción para interactuar con Ribbon al más alto.

Las bibliotecas que exploraremos incluyen

  •  Spring Discovery 
  •  Spring Discovery  con RestTemplate habilitado 
  •  Netflix Feign

Spring Discovery Client ofrece el nivel más bajo de acceso a Ribbon y los servicios registrados en él. Usando Discovery Client, puede consultar todos los servicios registrados con el cliente y sus URL correspondientes.

Veamos un ejemplo simple del uso de DiscoveryClient para recuperar una de las direcciones URL de un servicio y luego llamaremos al servicio mediante una clase RestTemplate estándar. Para comenzar a usar DiscoveryClient, primero debe anotar la clase Application.java con la anotación @EnableDiscoveryClient:

@SpringBootApplication

@EnableDiscoveryClient

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

Veamos un ejemplo de un descubrimiento de un cliente : 

/*Packages and imports removed for conciseness*/
@Component
public class OrganizationDiscoveryClient {

  @Autowired
   private DiscoveryClient discoveryClient;

   public Organization getOrganization(String organizationId) {
     RestTemplate restTemplate = new RestTemplate();
     List<ServiceInstance> instances = discoveryClient.getInstances("organizationservice");
     if (instances.size()==0) return null;
        String serviceUri = String.format("%s/v1/organizations/%s", instances.get(0).getUri().toString(),
            organizationId);
        ResponseEntity< Organization > restExchange = restTemplate.exchange(serviceUri, 
                                                                                                     HttpMethod.GET,
                                                                                                     null, Organization.class, organizationId);
      return restExchange.getBody();
   }
}

DiscoveryClient es la clase que usamos para interactuar con Ribbon. Para recuperar todas las instancias de los servicios de la organización registrados con Eureka, se puede usar el método getInstances(), pasando la clave del servicio que está buscando, para recuperar una lista de objetos ServiceInstance.

La clase ServiceInstance se utiliza para contener información sobre una instancia específica de un servicio, incluido su nombre de host, puerto y URI.

Tomamos la primera clase ServiceInstance de su lista para crear una URL de destino que luego se puede usar para llamar a su servicio. Una vez que tenga una URL de destino, puede usar un Spring RestTemplate estándar para llamar al servicio y recuperar datos.

En post posteriores veremos otras opciones. 

lunes, 7 de febrero de 2022

Registrar un servicio Spring boot en Eureka

 


En el post anterior levantamos un servidor Eureka, ahora vamos registrar un servicio. 

Lo primero que debe hacer es agregar la dependencia Spring Eureka al archivo pom.xml del servicio que queremos registrar :

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-eureka</artifactId>

</dependency>

El artefacto spring-cloud-starter-eureka contiene los archivos jar que Spring Cloud usará para interactuar con su servicio Eureka.

Después de configurar su archivo pom.xml, debe indicarle a Spring Boot que registre el servicio en el servidor Eureka. Este registro se realice tenemos que configurarlo con el archivo src/main/java/resources/application.yml 

spring:
  application:
    name: myService
  profiles:
    active:
       default
    cloud:
      config:
        enabled: true
eureka:
  instance:
    preferIpAddress: true
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

Todo servicio registrado en Eureka tendrá dos componentes asociados: el ID de la aplicación y el ID de la instancia. El ID de la aplicación se utiliza para representar una instancia de servicio de grupo. En un microservicio basado en Spring-Boot, el ID de la aplicación siempre será el valor establecido por la propiedad spring.application.name. Para su servicio de organización, su spring.application.name se llama creativamente servicio de organización. El ID de la instancia será un número aleatorio destinado a representar una sola instancia de servicio.

La segunda parte de la configuración proporciona cómo y dónde debe registrarse el servicio con el servicio Eureka. La propiedad eureka.instance.preferIpAddress le dice a Eureka que desea registrar la dirección IP del servicio en Eureka en lugar de su nombre de host.

El atributo eureka.client.registerWithEureka es el disparador para decirle al servicio de la organización que se registre en Eureka. El eureka.client.fetchRegistry se utiliza para indicarle al cliente Spring Eureka que obtenga una copia local del registro. Establecer este atributo en true, almacenará en caché el registro localmente en lugar de llamar al servicio Eureka con cada búsqueda. Cada 30 segundos, el software del cliente volverá a ponerse en contacto con el servicio de Eureka para cualquier cambio en el registro.

El último atributo, el atributo eureka.serviceUrl.defaultZone, contiene una lista separada por comas de los servicios de Eureka que el cliente usará para resolver la ubicación del servicio. Para este ejemplo, solo tendrá un servicio Eureka.

Se puede usar la API REST de Eureka para ver el contenido del registro. para ver todos los
instancias de un servicio, presione el siguiente punto final GET:

http://<servicio de eureka>:8761/eureka/apps/<ID DE APLICACIÓN>

Por ejemplo, para ver el servicio de organización en el registro, puede llamar a 

http://localhost:8761/eureka/apps/myservice.

El formato predeterminado que devuelve el servicio Eureka es XML.