Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Informatica2 PDF

Download as pdf or txt
Download as pdf or txt
You are on page 1of 200

Crear proyecto Spring usando Maven

Crear proyecto:

mvn archetype:generate -DgroupId=com.codemonkey -DartifactId=HolaMundo -

DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Ahora dentro del directorio (HolaMundo) teclear:

mvn eclipse:eclipse

Agregar esto en el pom.xml:

<!-- Spring framework -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring</artifactId>

<version>2.5.6</version>

</dependency>

Teclear de nuevo:

mvn eclipse:eclipse

Compilar:

mvn compile

Ejecutar:

mvn exec:java -Dexec.mainClass="com.codemonkey.App"

Crear JAR:

mvn package

Ejecutar:

java -jar App-0.0.1-SNAPSHOT.jar


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.apac
he.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.java2s.common</groupId>
<artifactId>Java2sExamples</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Java2sExamples</name>
<url>http://maven.apache.org</url>
<dependencies>
<!-- <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency> -->
<!-- Spring framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>
</dependencies>
</project>

Teclear de nuevo:
mvn eclipse:eclipse

HolaMundo.java

package com.codemonkey;
public class HolaMundo {
private String name;

public void setName(String name) {


this.name = name;
}

public void printHola() {


System.out.println("Spring 3 : Hola! " + name);
}
}

Crear un archivo XML llamado Spring-Module.xml en src/main/resources

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

<bean id="holaBean" class="com.codemonkey.HolaMundo">


<property name="name" value="Fernando" />
</bean>

</beans>

App.java
package com.codemonkey;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {


public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"Spring-Module.xml");
HolaMundo obj = (HolaMundo) context.getBean("holaBean");
obj.printHola();
}
}

Compilar:

mvn compile

Ejecutar:

mvn exec:java –Dexec.mainClass=”com.codemonkey.App”

Link: http://www.java2s.com/Tutorials/Java/Spring/0030__Spring_HelloWorld.htm

Grails framework

$ sdk install grails

$ grails –version

$ grails

grails> créate-app holamundo

$ cd holamundo

$ grails

grails> créate-controller hola


package holamundo
class HolaController {

def index() {
render "La copa de Grails!"
}
}

grails-app/conf/application.yml
Escribir:
server:
contextPath: /holamundo

grails> run-app

Abrir navegador en:

http://localhost:8080/holamundo/

http://localhost:8080/holamundo/index

Links:

http://docs.grails.org/latest/guide/single.html

Grails Programmer : How to


output CSV from a Grails 3
Controller

1 $ grails -version
2 Grails Version: 3.1.11
3 | Groovy Version: 2.4.7
4 | JVM Version: 1.8.0_45
Create an app with the default web profile.
1 grails create-app csv

2 Application created at /Users/groovycalamari/Documents/tests/csv

Enter the grails console


1 $ cd csv

2 $ grais

Create a domain class


1 grails> create-domain-class Book

2 | Created grails-app/domain/csv/Book.groovy

3 | Created src/test/groovy/csv/BookSpec.groovy

Add a couple of properties to the domain class


1
package cvs
2

3 class Book {
4

5 String title

6 String author

8 static constraints = {

}
9
}
10
In grails-app/init/BootStrap.groovy add a couple of domain class
instances. The BootStrap init closure runs when the app starts.
1 import csv.*

3 class BootStrap {

4 def init = { servletContext ->

5 new Book(title: 'Groovy for Domain-Specific Languages', author: 'Fergal Dearle')

new Book(title: 'Programming Groovy 2: Dynamic Productivity for the Java Develop
6 Subramaniam').save()
7 }
8 def destroy = {

9 }

10 }

Create a controller
1 $ grails
2 Enter a command name to run. Use TAB for completion:e...

3 grails> create-controller Book

4 | Created grails-app/controllers/csv/BookController.groovy

| Created src/test/groovy/csv/BookControllerSpec.groovy
5

This is the controller content:


package csv
1

2
import grails.config.Config
3
import grails.core.support.GrailsConfigurationAware
4

5
import static org.springframework.http.HttpStatus.OK
6
7 class BookController implements GrailsConfigurationAware {

9 String csvMimeType

10
String encoding
11

12
def index() {
13
final String filename = 'book.csv'
14
def lines = Book.findAll().collect { [it.title, it.author].join(';')
15
} as List<String>
16

17 def outs = response.outputStream


18 response.status = OK.value()

19 response.contentType = "${csvMimeType};charset=${encoding}";

20 response.setHeader "Content-disposition", "attachment;

21 filename=${filename}"

22
lines.each { String line ->
23
outs << "${line}\n"
24
}
25

26
outs.flush()
27
outs.close()
28 }
29

30 @Override

31 void setConfiguration(Config co) {

32 csvMimeType =

33 co.getProperty('grails.mime.types.csv', String, 'text/csv')

encoding = co.getProperty('grails.converters.encoding', String, 'UTF-8')


34
35 }

36 }

37

Several things about the above code.

A) I will recommend to put the logic fetching the lines in a Service.


B) I am using the mime type and encoding defined in
application.yml. Learn more about retrieving config values.
C) If you want the file to download you need to setup the Content-
disposition header.

If we run the app and call the controller we will download a CSV
file as this:

CSV is probably the best format to export your data from a Grails
App. A CSV file is easy to import in Excel. I was tired of my clients
asking me how to import a CSV in Excel. I wrote a post; in Spanish
though.

Do you like to read about Grails / Groovy development? If


the answer is yes, subscribe to Groovy Calamari. A weekly curated
email newsletter about the Groovy ecosystem.
Links:

http://sergiodelamo.es/grails-tips-how-to-output-csv-from-a-grails-3-controller/
¿Cómo importar un archivo CSV
en Excel?
Las siguientes capturas de pantalla muestra como importar un
archivo CSV en Excel 2010. CSV es el acrónimo en inglés
de Comma Separated Value. En español se suele utilizar el
termino archivo separado por comas.

1. El primer paso es descargar el archivo CSV en un directorio


fácilmente localizable de nuestro ordenador.
2. Ejecutar Excel
3. Crear un documento Nuevo.
4. Pulsar la pestaña Datos y luego el botón “Obtener Datos
Externos -> Desde Texto”.
5. En el campo “Origen del archivo” se pide la codificación del
mismo. Lo habitual es que sea UTF8.
6. Pulsar “Siguiente”.
7. En la siguiente pantalla nos piden que establezcamos los separadores que se usan para
delimitar los datos. Lo habitual es que los datos vengan separados por coma por lo que
tendremos que marcar la opción “Coma”.
8. Pulsar Siguiente
En la siguiente pantalla se nos da la opción de designar un formato para cada columna que se
va a importar a Excel. Dependiendo del uso que le vayamos a dar a los datos quizás nos
interese delimitar alguna columna como valor numérico o quizás el valor “Genérico por defecto
sea suficiente”.
Al pulsar finalizar los datos se importarían a nuestra hoja de cálculo

Spring Data JPA - Pagination With Thymeleaf


View
[Updated: Jun 12, 2018, Created: Jun 12, 2018]

In the last tutorial we saw how to use Pageable parameter in Spring MVC

controller methods. Following example shows how to use Spring Data

pagination with Thymeleaf view. We are also going to use @PageableDefault


annotation to change the default page size.

Example

Entity
@Entity

public class Employee {

private @Id

@GeneratedValue

Long id;

private String name;

private String dept;

private int salary;

.............

Repository
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long>

JavaConfig
@ComponentScan

@Configuration
@EnableWebMvc

@EnableJpaRepositories

@EnableSpringDataWebSupport
public class AppConfig {

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

LocalContainerEntityManagerFactoryBean factory =

new LocalContainerEntityManagerFactoryBean();

factory.setPersistenceProviderClass(HibernatePersistenceProvider.class);

return factory;

@Bean

public PlatformTransactionManager transactionManager(

EntityManagerFactory entityManagerFactory) {

JpaTransactionManager txManager = new JpaTransactionManager();

txManager.setEntityManagerFactory(entityManagerFactory);

return txManager;

//thymeleaf view configurations


@Autowired

ApplicationContext applicationContext;

@Bean

public SpringTemplateEngine templateEngine() {

SpringTemplateEngine templateEngine = new SpringTemplateEngine();


templateEngine.setTemplateResolver(templateResolver());

return templateEngine;

@Bean

public SpringResourceTemplateResolver templateResolver() {


SpringResourceTemplateResolver templateResolver =

new SpringResourceTemplateResolver();

templateResolver.setApplicationContext(this.applicationContext);
templateResolver.setPrefix("/WEB-INF/views/");
templateResolver.setSuffix(".html");

return templateResolver;

@Bean

public ViewResolver viewResolver() {

ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();

viewResolver.setTemplateEngine(templateEngine());

return viewResolver;

Spring MVC controller


@Controller

public class EmployeeController {

@Autowired

private EmployeeRepository repository;

@GetMapping("/employees")

public String getEmployees(@PageableDefault(size = 10) Pageable pageable,


Model model) {

Page<Employee> page = repository.findAll(pageable);

model.addAttribute("page", page);

return "employee-page";

}
Thymeleaf view
src/main/webapp/WEB-INF/views/employee-page.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"

xmlns:th="http://www.thymeleaf.org">
<head>

<style>

table{width:100%;}

table td, table th { border: 1px solid grey;}

table th { background: #eee;}

.pagination-div{user-select: none;}
.pagination-div span{border-radius:3px;border:1px solid #999;

padding:5px;margin:10px 0px 0px 10px;display:inline-block}

span.selected{background:#ccf;}
</style>

</head>

<body>

<h2>Employees</h2>

<table>

<tr><th>Id</th>

<th>Name</th>

<th>Department</th>

<th>Salary</th>

</tr>

<tr th:each="employee : ${page.content}">

<td th:text="${employee.id}"></td>
<td th:text="${employee.name}"></td>

<td th:text="${employee.dept}"></td>

<td th:text="${employee.salary}"></td>
</tr>
</table>

<div class="pagination-div">

<span th:if="${page.hasPrevious()}">

<a th:href="@{/employees(page=${page.number-1},size=${page.size})}">

Previous</a>

</span>

<th:block th:each="i: ${#numbers.sequence(0, page.totalPages - 1)}">

<span th:if="${page.number == i}" class="selected">[[${i}+1]]</span>

<span th:unless="${page.number == i}">

<a th:href="@{/employees(page=${i},size=${page.size})}">[[${i}+1]]</a>

</span>

</th:block>

<span th:if="${page.hasNext()}">

<a th:href="@{/employees(page=${page.number+1},size=${page.size})}">Next</a>

</span>

</div>

</body>

</html>

Running
To try examples, run embedded tomcat (configured in pom.xml of example project below):

mvn tomcat7:run-war

Output
localhost:8080/employees
Navigating to pages by clicking page links:
Example Project
Dependencies and Technologies Used:

 spring-data-jpa 2.0.7.RELEASE: Spring Data module for JPA repositories.


Uses org.springframework:spring-context version 5.0.6.RELEASE
 spring-webmvc 5.0.6.RELEASE: Spring Web MVC.
 javax.servlet-api 3.0.1 Java Servlet API
 hibernate-core 5.3.1.Final: Hibernate's core ORM functionality.
Implements javax.persistence:javax.persistence-api version 2.2
 h2 1.4.197: H2 Database Engine.
 thymeleaf-spring5 3.0.9.RELEASE: Modern server-side Java template engine for both we
 jackson-databind 2.9.5: General data-binding functionality for Jackson: works on core st
 JDK 1.8
 Maven 3.3.9

https://www.logicbig.com/tutorials/spring-framework/spring-data/pagination-with-
thymeleaf.html

Descarga:

https://www.logicbig.com/tutorials/spring-framework/spring-data/pagination-with-
thymeleaf/spring-data-jpa-pagination-with-thymeleaf.zip

Tutorial sobre control de


versiones (parte 1)

En el sitio de Software Carpentry hay un buen tutorial sobre el uso de sistemas


de control de versiones. Estos apuntes son prácticamente la traducción de
esas instrucciones.

Indice del Tutorial


o Parte 1. Cómo armar un repositorio local
o Parte 2. Cómo subir el repositorio local al remoto
o Parte 3. Cómo colaborar en un mismo repositorio remoto
o Parte 4. Cómo resolver conflictos

Para ver las versiones (en inglés) en las cuales se basa este tutorial, podés visitar la
página de Software Carpentry
Configuración
La primera vez que se usa Git en una máquina hay que configurar al menos el nombre
del usuario y su email. También puede elegirse el editor por defecto. Son comandos que
se ejecutan una sola vez. Por ejemplo:

$ git config --global user.name "Vlad Dracula"

$ git config --global user.email "vlad@tran.sylvan.ia"

$ git config --global color.ui "auto"

$ git config --global core.editor "emacs"

La tercer opcion es para que los mensajes con los cuales responde Git en el terminal
estén coloreados. La cláusula “—global” indica que esta configuración no se limita a un
proyecto particular, sino que será utilizado en todos los proyectos (repositorios) de
esa computadora.

Para verificar el nombre de usuario se usa git config user.name.

Inicialización
Se puede comenzar a trabajar con un proyecto usando el siguiente comando desde el
subdirectorio en el cual se alojarán los archivos del proyecto:

$ git init

De esta manera se crea un subdirectorio oculto (.git) donde se irá alojando todo el
historial de archivos, en todas sus versiones, a medida que se vayan produciendo.

Agregar un archivo al proyecto


Creamos un archivo de texto con cualquier editor (no necesariamente el elegido en la
configuración) y lo almacenamos en el directorio del proyecto. Por ejemplo una canción
de La Vela Puerca, en el archivo va-a-escampar.txt:
Va a escampar

Hoy asume lo que venga

sea para bien, o todo mal.

y aunque pierda lo que tenga

se va a morder para aguantar

Si hacemos “git status” el sistema nos responde que reconoce la existencia de un nuevo
archivo en el proyecto, que todavía el sistema de control de versiones no incorporó.

$ git status

# On branch master

# Initial commit

# Untracked files:

# (use "git add <file>..." to include in what will be committed)

# va-a-escampar.txt

nothing added to commit but untracked files present (use "git add" to track)

Para agregar el archivo al proyecto se ejecuta:

$ git add va-a-escampar.txt

Y se puede pedir el nuevo informe de situación con:

$ git status

# On branch master

# Initial commit

#
# Changes to be committed:

# (use "git rm --cached <file>..." to unstage)

# new file: va-a-escampar.txt

Comitear es un verbo muy extraño


Ahora Git ya sabe que debe ir registrando la evolución del archivo va-a-escampar.txt,
pero todavía no ha grabado ninguna información sobre este archivo en su base de datos
(y si ya hubiese información de antes, aún no habría guardado los cambios realizados
desde el último commit). En inglés, grabar el estado actual de uno o más archivos es
hacer un “commit”. En castellano commit puede traducirse como cometer, hacer,
encomendar, perpetrar… Ninguna de las cuales sirve para describir lo que sucede, así
que hablaremos de “comitear”.

Para comitear el estado actual del archivo se ejecuta la orden git commit -m “mensaje
personal”:

$ git commit -m "Empezando a transcribir la letra de La Vela"

[master (root-commit) 38b86f5] Empezando a transcribir la letra de La Vela

1 file changed, 6 insertions(+)

create mode 100644 va-a-escampar.txt

Git guarda entonces un copia permanente de todos los archivos que están en la base de
datos (los que fueron agregados con “git add”) dentro del directorio oculto .git. Esta
copia permanente es llamada una revisión, y su identificador en el ejemplo fue 38b86f5.

De omitir el mensaje en el comando git commit (si no hubiese aparecido -m “mensaje”),


se hubiese abierto el editor configurado al principio, para poder escribir el mensaje.

Volvemos a ver la situación actual del proyecto con “git status” y con “git log” podemos
ver el historial de cambios (en orden cronológico inverso), el cual incluye el identificador
completo de la revisión, el autor, la fecha y el mensaje de Git al responder al commit:
$ git status

# On branch master

nothing to commit, working directory clean

$ git log

commit 38b86f5625453732e442c127f1d4678ec8550a12

Author: eldiegoefe <eldiegoefe@gmail.com>

Date: Mon Oct 6 15:35:07 2014 -0300

Empezando a transcribir la letra de La Vela

Cambios (o agregados) en un archivo


¿Dónde se guardan los cambios?

En el directorio del proyecto sigue habiendo un solo archivo. Toda la información


extra se almacena en el subdirectorio oculto .git, de modo que el sistema de
archivos se ve limpio y se evita la posibilidad de borrar accidentalmente cosas
(como versiones viejas del mismo archivo).

Si agregamos una segunda estrofa al archivo de texto, pasa a verse así:

Va a escampar

Hoy asume lo que venga

sea para bien, o todo mal.

y aunque pierda lo que tenga

se va a morder para aguantar

Hoy que claro ve las cosas

que ayer no vio, ni va a exigir


Sobre su pena se posa

quiere entender para seguir

Al pedir el status del proyecto veremos:

$ git status

# On branch master

# Changes not staged for commit:

# (use "git add <file>..." to update what will be committed)

# (use "git checkout -- <file>..." to discard changes in working directory)

# modified: va-a-escampar.txt

no changes added to commit (use "git add" and/or "git commit -a")

La última linea es la frase clave: “no se agregaron cambios al commit”. El archivo ha


cambiado pero no le hemos dicho aún a Git que queremos guardar esos cambios (lo
haremos con git add).

Comparación de versiones
Antes de agregar estos cambios podemos revisar nuestro trabajo usando git diff, que nos
muestra las diferencias entre el estado actual del archivo y la última versión comiteada.

$ git diff

diff --git a/va-a-escampar.txt b/va-a-escampar.txt

index 97ab7b0..db818ec 100644

--- a/va-a-escampar.txt

+++ b/va-a-escampar.txt

@@ -4,3 +4,8 @@ Hoy asume lo que venga

sea para bien, o todo mal.

y aunque pierda lo que tenga


se va a morder para aguantar

+Hoy que claro ve las cosas

+que ayer no vio, ni va a exigir

+Sobre su pena se posa

+quiere entender para seguir

La salida es críptica porque es en realidad una serie de comandos para que un editor de
textos pueda reconstruir un archivo a partir del otro.

1. La primera linea muestra que Git produce una salida similar al comando
Unix diff comparando la versión antigua y nueva del archivo.
2. La segunda linea muestra la revisión exacta de cada archivo que está siendo comparado
(97ab7b0 y db818ec, etiquetas únicas generadas por la computadora para
esas revisiones).
3. La linea restante muestra finalmente las lineas en las que aparecen las diferencias
(marcando con “+” las lineas que se agregaron).

Cómo comitear los cambios


Si hacemos un commit de los cambios:

$ git commit -m "Agregada la segunda estrofa"

# On branch master

# Changes not staged for commit:

# (use "git add <file>..." to update what will be committed)

# (use "git checkout -- <file>..." to discard changes in working directory)

# modified: va-a-escampar.txt

no changes added to commit (use "git add" and/or "git commit -a")

Ups. No se comitearon los cambios porque faltó agregarlos antes.


Entonces, haremos:
$ git add va-a-escampar.txt

$ git commit -m "Agregada la segunda estrofa"

[master 6ae57e3] Agregada la segunda estrofa

1 file changed, 5 insertions(+)

Git insiste en que agreguemos los archivos al conjunto de los que querramos comitear
antes de realmente hacerlo, porque quizás no querramos comitear todo junto. Por
ejemplo, supongamos que estamos agregando algunas citas a un documento. Podríamos
querer agregarlas sin comitear el trabajo que estamos haciendo en las conclusiones (que
todavía no terminamos). NOTA PERSONAL: ¡no entiendo! ¿De qué me sirve agregar
los cambios si no los “comiteo”?

RESPUESTA: Para permitirlo, Git tiene una zona de almacenamiento especial donde
hace un seguimiento de cosas que fueron agregadas al último conjunto de cambios, pero
que aún no fueron comiteadas. Con git add se colocan las novedades en este area, y
luego git commit las copia al espacio de almacenamiento de largo plazo.

Veamos cómo nuestros cambios a un archivo se mueven desde nuestro editor hacia
la staging area(zona de preparación o también area de ensayo) y hacia el
almacenamiento de largo plazo. Primero, agregamos otra estrofa:

Llega la batalla

y contra él estalla

algún día va a escampar.

y como sale de esta

quiere la respuesta

sabe que no es escapar.

Y analizamos las diferencias con git diff:

$ git diff

diff --git a/va-a-escampar.txt b/va-a-escampar.txt

index db818ec..0c33091 100644


--- a/va-a-escampar.txt

+++ b/va-a-escampar.txt

@@ -9,3 +9,10 @@ Hoy que claro ve las cosas

que ayer no vio, ni va a exigir

Sobre su pena se posa

quiere entender para seguir

+Llega la batalla

+y contra él estalla

+algún día va a escampar.

+y como sale de esta

+quiere la respuesta

+sabe que no es escapar.

Git identifica las diferencias entre el archivo y la versión intermedia (pero no comiteada),
guardada en la staging area (la voy a llamar zona de preparación). Si agregamos estos
cambios al almacenamiento intermedio, veremos lo siguiente:

$ git add va-a-escampar.txt

$ git diff

Ahora no hay ninguna salida, porque el archivo actualmente en edición es igual al que
guardamos en la zona de preparación.

Sin embargo, si hacemos:

$ git diff --staged

diff --git a/va-a-escampar.txt b/va-a-escampar.txt

index db818ec..0c33091 100644

--- a/va-a-escampar.txt

+++ b/va-a-escampar.txt

@@ -9,3 +9,10 @@ Hoy que claro ve las cosas


que ayer no vio, ni va a exigir

Sobre su pena se posa

quiere entender para seguir

+Llega la batalla

+y contra él estalla

+algún día va a escampar.

+y como sale de esta

+quiere la respuesta

+sabe que no es escapar.

Ahora nos está mostrando las diferencias entre el último cambio comiteado (en el
almacenamiento de largo plazo) y lo que hay en la zona de preparación. Guardemos
estos cambios:

$ git commit -m "Tercera estrofa agregada"

[master 8f1eec1] Tercera estrofa agregada

1 file changed, 7 insertions(+)

Vemos cómo quedó:

$ git status

# On branch master

nothing to commit, working directory clean

Y podemos examinar la historia de lo que fue sucediendo hasta ahora:

$ git log

commit 8f1eec1836a9ace8a2cbab7e2c3341efa5c3a537

Author: eldiegoefe <eldiegoefe@gmail.com>

Date: Mon Oct 6 19:55:10 2014 -0300


Tercera estrofa agregada

commit 6ae57e3d91a7a526a257df081d83a5b9be4e6d28

Author: eldiegoefe <eldiegoefe@gmail.com>

Date: Mon Oct 6 16:54:40 2014 -0300

Agregada la segunda estrofa

commit 38b86f5625453732e442c127f1d4678ec8550a12

Author: eldiegoefe <eldiegoefe@gmail.com>

Date: Mon Oct 6 15:35:07 2014 -0300

Empezando a transcribir la letra de La Vela

Resumiendo, cuando queremos hacer cambios en nuestro repositorio, primero tenemos


que agregar los cambios a la zona de preparación (con git add), y luego comitear los
cambios ensayados al repositorio (con git commit).

Explorando el historial
Para ver lo que cambiamos, usamos git diff también, pero para refiriéndonos a versiones
viejas con la notación HEAD~1, HEAD~2, etc:

$ git diff HEAD~1 va-a-escampar.txt

diff --git a/va-a-escampar.txt b/va-a-escampar.txt

index db818ec..0c33091 100644

--- a/va-a-escampar.txt

+++ b/va-a-escampar.txt

@@ -9,3 +9,10 @@ Hoy que claro ve las cosas

que ayer no vio, ni va a exigir

Sobre su pena se posa


quiere entender para seguir

+Llega la batalla

+y contra él estalla

+algún día va a escampar.

+y como sale de esta

+quiere la respuesta

+sabe que no es escapar.

$ git diff HEAD~2 va-a-escampar.txt

diff --git a/va-a-escampar.txt b/va-a-escampar.txt

index 97ab7b0..0c33091 100644

--- a/va-a-escampar.txt

+++ b/va-a-escampar.txt

@@ -4,3 +4,15 @@ Hoy asume lo que venga

sea para bien, o todo mal.

y aunque pierda lo que tenga

se va a morder para aguantar

+Hoy que claro ve las cosas

+que ayer no vio, ni va a exigir

+Sobre su pena se posa

+quiere entender para seguir

+Llega la batalla

+y contra él estalla

+algún día va a escampar.

+y como sale de esta

+quiere la respuesta

+sabe que no es escapar.


De esta manera construimos una cadena de revisiones. El extremo más reciente de la
cadena es HEAD; podemos referirnos a revisiones previas usando la notación ~, de
manera que HEAD~1 (se pronuncia “head menos uno”) significa “la revisión previa”,
mientras que HEAD~123 vuelve 123 revisiones hacia atrás, desde donde nos
encontramos en la actualidad.

También nos podemos referir a las revisiones usando las cadenas largas de dígitos y
letras que Git muestra en los logs. Estos son IDs únicos para los cambios, “únicos”
significa realmente únicos: cada cambio a cualquier conjunto de archivos en cualquier
máquina tiene un identificador de 40 caracteres. Nuestro primer commit nos dio
el ID 38b86f5625453732e442c127f1d4678ec8550a12, así que probemos esto:

$ git diff 38b86f5625453732e442c127f1d4678ec8550a12 va-a-escampar.txt

diff --git a/va-a-escampar.txt b/va-a-escampar.txt

index 97ab7b0..0c33091 100644

--- a/va-a-escampar.txt

+++ b/va-a-escampar.txt

@@ -4,3 +4,15 @@ Hoy asume lo que venga

sea para bien, o todo mal.

y aunque pierda lo que tenga

se va a morder para aguantar

+Hoy que claro ve las cosas

+que ayer no vio, ni va a exigir

+Sobre su pena se posa

+quiere entender para seguir

+Llega la batalla

+y contra él estalla

+algún día va a escampar.

+y como sale de esta


+quiere la respuesta

+sabe que no es escapar.

Para no tipear cadenas de 40 números, Git permite usar los primeros de la cadena, con
el mismo resultado:

$ git diff 38b86f5 va-a-escampar.txt

Recuperar versiones antiguas


De acuerdo: podemos grabar cambios a los archivos y ver qué hemos cambiado. Pero
¿cómo restauramos versiones viejas de las cosas? Supongamos que sobreescribimos
accidentalmente nuestro archivo va-a-escampar.txt, que pasa a contener sólo la
siguiente linea:

no va a escampar nada

El comando cat muestra el contenido del archivo:

$ cat va-a-escampar.txt

no va a escampar nada

Con git status nos enteramos que el archivo ha cambiado, pero esos cambios aún no
pasaron a la zona de preparación (staging area).

$ git status

# On branch master

# Changes not staged for commit:

# (use "git add <file>..." to update what will be committed)

# (use "git checkout -- <file>..." to discard changes in working directory)

# modified: va-a-escampar.txt

no changes added to commit (use "git add" and/or "git commit -a")
Podemos volver atrás, dejando las cosas como estaban antes, usando git checkout:

$ git checkout HEAD va-a-escampar.txt

$ cat va-a-escampar.txt

Va a escampar

Hoy asume lo que venga

sea para bien, o todo mal.

y aunque pierda lo que tenga

se va a morder para aguantar

Hoy que claro ve las cosas

que ayer no vio, ni va a exigir

Sobre su pena se posa

quiere entender para seguir

Llega la batalla

y contra él estalla

algún día va a escampar.

y como sale de esta

quiere la respuesta

sabe que no es escapar.

Con git checkout se recupera una versión anterior de un archivo. En este caso, le estamos
pidiendo a Git que recupere la versión del archivo guardada en HEAD, que fue la última
revisión guardada. Si quisiéramos ir más atrás podríamos usar un identificador
de revisión:

$ git checkout 38b86f5 va-a-escampar.txt


Es importante recordar que debemos usar el número de revisión que identifica el estado
del repositorio antes del cambio que estamos tratando de revertir. Un error común es
usar el número de revisión del commit en el cual hicimos el cambio del cual estamos
tratando de deshacernos. En el ejemplo de abajo queremos recobrar el estado
inmediatamente anterior al commit más reciente (HEAD~1), que es la revisión
6ae57e3d9 (en la figura -realizada con otro ejemplo- corresponde a la revision f22b25e):

El diagrama siguiento ilustra el modo en que puede verse la historia de un archivo


(moviéndose hacia atrás desde HEAD, la versión más recientemente comiteada):

Simplificando un caso común

Si mirás cuidadosamente la salida de *git status*, vas a ver esta

ayuda:

(use "git checkout -- <file>..." to discard changes in working directory)

Tal como afirma, *git checkout* sin un identificador de versión

restaura los archivos al estado guardado en HEAD. El guión doble -- es

necesario para separar los nombres de los archivos siendo recuperados

del comando mismo: sin esos guiones, Git trataría de usar el nombre

del archivo como identificador de la revisión a la cual se desea volver.

El hecho de que los archivos puedan recuperarse o revertirse uno por uno tiende a
cambiar el modo en que la gente organiza su trabajo. Si todo estuviese en un solo gran
archivo, entonces es dificil (si no imposible) deshacer los cambios a la introducción sin
también deshacer los cambios hechos a continuación a la conclusión. Por otra parte, si
la introducción y la conclusión estuviesen en archivos separados, entonces sería más
fácil moverse hacia atrás y hacia adelante en el tiempo.
Ignorando cosas
Vamos a ver qué hacer con los archivos del mismo directorio que no queremos incluir
en el sistema de control de versiones. Agregamos algunos archivos vacíos: a.dat

$ mkdir results

$ touch a.dat b.dat c.dat results/a.out results/b.out

Y vemos qué dice Git:

$ git status

# On branch master

# Untracked files:

# (use "git add <file>..." to include in what will be committed)

# a.dat

# b.dat

# c.dat

# results/

nothing added to commit but untracked files present (use "git add"

to track)

Colocar estos archivos bajo control de versiones sería un desperdicio de espacio en disco.
Peor aún, tenerlos listados nos distraería de los cambios que realmente importan, de
manera que debemos decirle a Git que los ignore, creando un archivo en el directorio
raiz del proyecto. Al archivo lo llamamos .gitignore.

Dentro de .gitignore colocamos los patrones para ignorar archivos, en este caso los
archivos que terminen en .dat y los archivos que se encuentren en el
subdirectorio results (si cualquiera de estos archivos hubiese estado siendo parte del
control de versiones, Git continuará considerándolo a pesar de figurar en .gitignore). Al
listar el contenido de .gitignore deberíamos obtener esto:
$ cat .gitignore

*.dat

results/

Si pedimos el status del proyecto, la salida se verá mucho más limpia que antes:

$ git status

# On branch master

# Untracked files:

# (use "git add <file>..." to include in what will be committed)

# .gitignore

nothing added to commit but untracked files present (use "git add" to track)

La única cosa que Git advierte ahora es el archivo .gitignore recientemente creado.
Podríamos creer que no querríamos incorporarlo al control de versiones, pero todos con
quienes compartimos el repositorio probablemente querrían ignorar las mismas cosas
que estamos ignorando nosotros. Así que agregamos y comiteamos este archivo (oculto,
pues empieza con un punto):

$ git add .gitignore

$ git commit -m "Agregado del archivo de ignorancias"

$ git status

# On branch master

nothing to commit, working directory clean

Como un extra, al usar .gitignore evitamos agregar accidentalmente archivos que no


queremos al repositorio.

$ git add a.dat

The following paths are ignored by one of your .gitignore files:

a.dat

Use -f if you really want to add them.


fatal: no files added

Si queremos realmente pasar por alto los seteos de .gitignore, podemos usar git add -
f para forzar a Git a agregar algo. Siempre podemos ver la situación de los archivos
ignorados si quisiéramos:

$ git status --ignored

# On branch master

# Ignored files:

# (use "git add -f <file>..." to include in what will be committed)

# a.dat

# b.dat

# c.dat

# results/

nothing to commit, working directory clean

Claves
Usar git config para configurar un usuario, dirección de email, editor y otras
preferencias (todas estas cosas son válidas para una máquina)

Con git init se inicializa un repositorio

Con git status se muestra la situación de un repositorio

Los archivos puedes almacenarse en el directorio de trabajo (que los usuarios


ven), la zona de preparación o staging area (desde donde se realizará el
próximo commit) y el repositorio local (donde las instantáneas son
almacenadas permanentemente).
Con git add se agregan archivos a la zona de preparación.

Con git commit se crea una instantanea de la zona de preparación en el


repositorio local.

Siempre escribir un mensaje al comitear cambios (con git commit -m


“mensaje”).

Con git diff se muestran las diferencias entre versiones.

Con git checkout se recuperan viejas versiones de un archivo.

El archivo .gitignore indica a Git los archivos a ser ignorados por el sistema de
control de versiones.

No te vayas sin comentar algo del post, de las noticias del día, del clima, que se yo...

Tutorial sobre control de


versiones (parte 2)
Indice del Tutorial
o Parte 1. Cómo armar un repositorio local
o Parte 2. Cómo subir el repositorio local al remoto
o Parte 3. Cómo colaborar en un mismo repositorio remoto
o Parte 4. Cómo resolver conflictos

Para ver las versiones (en inglés) en las cuales se basa este tutorial, podés visitar la
página de Software Carpentry
Armar un proyecto (repositorio) en GitHub
Para subir el repositorio local a GitHub es indispensable tener una cuenta allí (que es
gratis, así que a relajarse y disfrutar). Es bastante intuitivo el modo de crear un
repositorio. Pero pongo un par de imágenes, a modo de muestra.
En la pantalla anterior hay que hacer click sobre el botón verde que dice “+ New
repository“, que nos deposita en la imagen de abajo.

Solamente ponemos el nombre y la descripción del repositorio. Los repositorios


privados son pagos, así que por lo general uno elige “Public“. No hace falta inicializarlo
con un readme, ni agregar un.gitignore, ni seleccionar una licencia. Basta con apretar
el botón verde de “Create repository” y chan, ya tenemos repositorio sin tener que
acercarnos a la farmacia.

La imagen que sigue es para mostrar que la dirección del nuevo repositorio, aún vacío,
pero listo para clonarse sin parecerse a la oveja Dolly, se encuentra sobre el panel de la
izquierda, donde dice “HTTPSclone URL”. Copiamos de ahí esa dirección y nos
dirigimos con presteza y gráciles movimientos al terminal, que debería estar ubicado
(controlar con pwd) en el directorio de nuestro repositorio local. Escribimos (en mi caso
estoy trabajando sobre un nuevo repositorio para alojar la configuración de mi Emacs):

Setear el repositorio remoto desde nuestro


repo local
En la siguiente orden le estamos diciendo a Git que agregue como repositorio remoto
la URL que le pasamos, y además a esa dirección le asignamos el alias “origin” (se suele
utilizar origin por convención, pero podríamos haberlo llamado “farodelfindelmundo“).
Podemos chequearlo con git remote -v.
$ git remote add origin https://github.com/eldiegoefe/emacs.git

$ git remote -v

origin https://github.com/eldiegoefe/emacs.git (fetch)

origin https://github.com/eldiegoefe/emacs.git (push)

Subir repo local al remoto


Ahora es superfacil subir los archivos de nuestro repositorio local hacia el repositorio
remoto (en github.com):

$ git push origin master

Cuando te dicen “master” es porque sos grosso, sabelo. Además “master” es la única
rama (branch) que por el momento tenemos en el recientemente creado repositorio
remoto y vacío (se me cae un lagrimón). También es la única rama que tenemos en el
repo local (lo podemos chequear con git branch). Al ejecutar el git push el sistema les va
a pedir primero el nombre de usuario y luego la contraseña (que se hicieron en github,
porque hacia allí está subiendo los archivos).

¿Problemas?
Si acaso diera un error, como me sucedió en el ejemplo que les estoy relatando, es porque
el repositorio remoto en vez de estar vacío tiene algún contenido. Debemos entonces,
antes de subir cosas, bajar ese contenido que no tenemos en el repositorio local (en mi
caso el archivo con la licencia), con la orden “git pull” (git push empuja desde el local
hacia el remoto, git pull tira desde el remoto hacia el local):

$ git pull origin master

warning: no common commits

remote: Counting objects: 3, done.

remote: Compressing objects: 100% (2/2), done.

remote: Total 3 (delta 0), reused 0 (delta 0)

Unpacking objects: 100% (3/3), done.


From https://github.com/eldiegoefe/emacs

* branch master -> FETCH_HEAD

* [new branch] master -> origin/master

Merge made by the 'recursive' strategy.

LICENSE | 675 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


+++++++++++++++++++++++

1 file changed, 675 insertions(+)

create mode 100644 LICENSE

Cuando se agregan cosas (al repo local) hay que comitearlas, como de costumbre. Por
eso, al ejecutar el comando anterior el sistema pide un mensaje de “commit” y luego da
el resultado que muestro (por ahora es un jeroglífico, pero tiene sentido, eh). En mi caso,
el archivo LICENCE que sólo estaba en el remoto, ahora está también en el local, lo cual
puede verse haciendo:

$ ls

custom.el LICENSE preload

Podemos chequear el estado del repo local:

$ git status

# On branch master

nothing to commit, working directory clean

No problem
Ahora sí volvemos a intentar subir los archivos que faltan hacia origin (el repo remoto,
solo, triste y abandonado):

$ git push origin master

Counting objects: 12, done.

Delta compression using up to 4 threads.

Compressing objects: 100% (8/8), done.

Writing objects: 100% (11/11), 2.88 KiB | 0 bytes/s, done.


Total 11 (delta 2), reused 0 (delta 0)

To https://github.com/eldiegoefe/emacs.git

e23c676..2c817b6 master -> master

“Joyines”, diría el Tano. Si ahora hiciésemos un pull desde el remoto, no debería pasar
nada porque ambos repos son iguales. Veamos:

git pull origin master

From https://github.com/eldiegoefe/emacs

* branch master -> FETCH_HEAD

Already up-to-date.

En cambio, si alguien actualizó el remoto con algún archivo nuevo, o modificó los
existentes, al hacer el pull se incorporarían al repo local esos cambios (lo cual siempre
hay que hacer antes de hacer un push hacia el remoto).

La bandera '-u'

Es común encontrar que git push se acompañe de la bandera

'-u'. Vamos a dejar eso para después, pero es casi seguro que se

usa para fijar origen y destino, de manera que un git push

desnudo, funcionará después con las ramas origen y destino que

acompañaban al -u (que es algo de --upstream nosecuanto). Pero

estoy tocando de oido, mejor veamos más adelante.

Nos vemos en la parte 3.

No te vayas sin comentar algo del post, de las noticias del día, del clima, que se yo..
Tutorial sobre control de
versiones (parte 3)

Indice del Tutorial


o Parte 1. Cómo armar un repositorio local
o Parte 2. Cómo subir el repositorio local al remoto
o Parte 3. Cómo colaborar en un mismo repositorio remoto
o Parte 4. Cómo resolver conflictos

Para ver las versiones (en inglés) en las cuales se basa este tutorial, podés visitar la
página de Software Carpentry

Probando el cooperativismo
Vamos a practicar cómo se realiza una colaboración a través de un repositorio en Github.
Para ello nada mejor que colaborar con uno mismo. Lo que hago es trabajar con dos
cuentas en Github, corriendo un usuario en mi PC de escritorio y el otro dentro de una
máquina virtual dentro de la misma PC (aunque también lo podría hacer desde una
notebook, o desde otra sesión de esta misma computadora, pero decir que uno corre una
máquina virtual con Linux suena mucho más god-level). No voy a detenerme a explicar
nada sobre las máquinas virtuales porque además de ser demasiado sencillo también es
off-topic, y no quiero offtopiquearme. ¡Cómprense un amiguito y chau!

Vamos a seguir haciendo las pruebas usando el repositorio emacs con el que estaba
nerdeando/tinkereando/boludeando (ahhhh, la flexibilidad del lenguaje) en la parte 2.
Voy a darle permiso de acceso (no-carnal) a un tercero (bioingenieroDiegol, mi otra
cuenta). Con este propósito me dirijo a la página del repositorio en Github, y selecciono
Settings/Collaborators. Una vez allí agrego a bioingenieroDiegol.
Clonación
Luego me cambio a la PC virtual y allí ya vestido como bioingenieroDiegol voy a clonar
el repositorio en esa virtuosa computadora:

[bioingenierodiegol]$ git clone https://github.com/eldiegoefe/emacs.git

[bioingenierodiegol]$ cd emacs

[bioingenierodiegol]$ git status

On branch master

Your branch is up-to-date with 'origin/master'.

nothing to commit, working directory clean

Ahora tengo los mismos archivos en la compu de bioingenieroDiegol que en el


repositorio de Github. Además sucede algo muy curioso, sin haber hecho git remote
add origin https://github.com/eldiegoefe/emacs.git vamos a tener:

[bioingenierodiegol]$ git remote -v

origin https://github.com/eldiegoefe/emacs.git (fetch)

origin https://github.com/eldiegoefe/emacs.git (push)

Vemos que la url y el alias estaban en el repositorio. Es por convención que se usa el
nombre de origincomo alias. Cuando más adelante quiera hacer el git push voy a usar
directamente git push origin master y va a funcionar.

Cambios hechos por el colaborador


A continuación hago algunos cambios en los archivos locales
de bioingenieroDiegol (para esta prueba puedo agregar un archivo nuevo o modificar
los existentes) y luego voy a tratar de subirlos al repo remoto (que sigue siendo
propiedad de eldiegoefe). Para esta última tarea es que hube agregado (¡caramba!)
a bioingenieroDiegol a la lista de colaboradores (porque para la clonación no
necesitaba permisos).
[bioingenierodiegol]$ touch archivo-vacio.txt

[bioingenierodiegol]$ git add archivo-vacio.txt

[bioingenierodiegol]$ git commit -m "Agrego archivo-vacio.txt"

[bioingenierodiegol]$ git status

On branch master

Your branch is ahead of 'origin/master' by 1 commit.

(use "git push" to publish your local commits)

nothing to commit, working directory clean

Git es muy comunicativo y nos dice lo que sucede y cómo tenemos que continuar.
Nuestra rama masterlocal está adelantada respecto de la rama master que se encuentra
en origin (origin/master) por 1 commit (el que acabamos de realizar).

El colaborador sube sus cambios a Github


Tenemos que hacer un push para llevar los cambios locales de bioingenieroDiegol al
remoto. No hay nada que comitear (ni nada que festejar, aún). Vamos a hacerle caso a
Git, vean lo que sucede tras ungit push origin master hecho por bioingenieroDiegol (no
se confundan con el prompt de esa consola, que es manjaro-efe%):

Ningún error, todo bien (vean que tuve que responder con el username y password del
colaborador bioingenieroDiegol, bah, el password no se ve…).
El dueño original del repositorio actualiza su
repo local
Finalmente el dueño original del repositorio (yo, eldiegoefe) quiere actualizar su
repositorio local con los cambios hechos por todos los contribuyentes (en este caso
sólo bioingenieroDiegol). Para ello hace un pull desde el remoto:

[eldiegoefe]$ git pull origin master

remote: Counting objects: 2, done.

remote: Compressing objects: 100% (1/1), done.

remote: Total 2 (delta 1), reused 2 (delta 1)

Unpacking objects: 100% (2/2), done.

From https://github.com/eldiegoefe/emacs

* branch master -> FETCH_HEAD

2c817b6..f94cdfa master -> origin/master

Updating 2c817b6..f94cdfa

Fast-forward

archivo-vacio.txt | 0

1 file changed, 0 insertions(+), 0 deletions(-)

create mode 100644 archivo-vacio.txt

El sistema le indica que se actualizó, que un archivo cambió (insertions y deletions son
cambios en el interior de los archivos, como no hubo ningun cambio entonces quedan
en cero). El método por el cual se hizo la fusión (merge) entre el remoto y el local
fue Fast-forward y no hubo ningún conflicto. ¡Fiesta!

Claves
Un repositorio local Git puede estar conectado a uno o más repositorios remotos.
Se puede usar el protocolo HTTPS para conectarse al repositorio remoto (hasta
que uno aprenda a lidiar con el protocolo SSH, que es más seguro).

Con git push se copian los cambios del repositorio local hacia el remoto

Con git pull se copian los cambios desde el repositorio remoto hacia el
repositorio local.

Con git clone se copia un repositorio remoto para crear un repositorio local con
un remote automáticamente llamado origin.

¡basta de hacer chistes malos!

No te vayas sin comentar algo del post, de las noticias del día, del clima, que se yo...

Tutorial sobre control de


versiones (parte 4)

Indice del Tutorial


o Parte 1. Cómo armar un repositorio local
o Parte 2. Cómo subir el repositorio local al remoto
o Parte 3. Cómo colaborar en un mismo repositorio remoto
o Parte 4. Cómo resolver conflictos

Para ver las versiones (en inglés) en las cuales se basa este tutorial, podés visitar la
página de Software Carpentry
Cuándo aparecen los conflictos
El sistema de control de versiones permite que la gente trabaje en paralelo editando sus
programas en código fuente. En realidad, se puede usar para cualquier tipo de archivo
con texto plano (me parece fantástico para informes, relatos, blogs como este, etc).

Trabajar en paralelo implica que en algún momento dos personas se van a pisar y van a
modificar una misma porción de texto. Esto podría pasarle incluso a una sola persona:
si trabajamos un mismo fragmento en la computadora de escritorio en casa, en una
notebook y también en una PC en el laburo, podríamos haber hecho diferentes cambios
en cada copia. El control de versiones nos ayuda a manipular esos conflictos dándonos
herramientas para resolver esos cambios superpuestos.

Un conflicto impostado
Para ver cómo se resuelven estos conflictos vamos a subir al repositorio emacs que
posee eldiegoefe el archivo manifiesto.txt con el siguiente texto:

Un fantasma recorre Europa: el fantasma del comunismo. Todas las

fuerzas de la vieja Europa se han unido en santa cruzada para acosar a

ese fantasma: el Papa y el zar, Metternich y Guizot, los radicales

franceses y los polizontes alemanes.

A continuación vamos a hacer que los dos usuarios con los que trabajamos en la parte
3 (eldiegoefe y bioingenierodiegol) realicen cambios de este archivo en sus
repositorios locales.

El usuario bioingenierodiegol agregar una linea a su versión local de manifiesto.txt:

Un fantasma recorre Europa: el fantasma del comunismo. Todas las

fuerzas de la vieja Europa se han unido en santa cruzada para acosar a

ese fantasma: el Papa y el zar, Metternich y Guizot, los radicales

franceses y los polizontes alemanes. Marx se la come.


Mientras tanto el usuario eldiegoefe agrega otra oración, un poco menos revisionista:

Un fantasma recorre Europa: el fantasma del comunismo. Todas las

fuerzas de la vieja Europa se han unido en santa cruzada para acosar a

ese fantasma: el Papa y el zar, Metternich y Guizot, los radicales

franceses y los polizontes alemanes. Marx es re-grosso, vieja.

Por cosas de la vida, bioingenierodiegol hace su secuencia 1. git add


manifiesto.txt, 2. git commit -m “Agregue linea ‘Marx se la come’” y 3. git
push origin master de modo que cuando termina de hacerlo, en el repositorio remoto
(en Github) el archivo manifiesto.txt habla de la gastronómica vida sexual de Marx.

Experimentando la frustración del militante


El respetuoso militante eldiegoefe también modifica su copia local de manifiesto.txt,
hace su git add y su git commit pero veamos qué sucede cuando intenta el git push:

[eldiegoefe]$ git push origin master

To https://github.com/eldiegoefe/emacs.git

! [rejected] master -> master (fetch first)

error: failed to push some refs to 'https://github.com/eldiegoefe/emacs.git'

hint: Updates were rejected because the remote contains work that you do

hint: not have locally. This is usually caused by another repository pushing

hint: to the same ref. You may want to first integrate the remote changes

hint: (e.g., 'git pull ...') before pushing again.

hint: See the 'Note about fast-forwards' in 'git push --help' for details.

El sistema nos da un error, pero también nos dice el motivo por el cual fue rechazado
el push: el remoto contiene trabajo que no poseemos localmente. Esto es frecuentemente
causado por otro usuario actualizando el repositorio en Github en el tiempo que
transcurre entre el momento que hicimos la última sincronización con el repositorio
remoto y el tiempo en el que intentamos el git push. En su mensaje, como el sistema
sabe de esta posibilidad, sugiere primero integrar los cambios del remoto con un git
pull antes de volver a intentar nuestro git push.

Comenzando a solucionar la cuestión


Es decir que Git detecta que los cambios hechos en una copia se superponen con los
hechos en la otra copia, y nos impide pisar o sobreescribir el trabajo. Vamos entonces a
hacer el git pull desde Github y fundir esos cambios remotos en nuestra copia local.

[eldiegoefe]$ git pull origin master

remote: Counting objects: 3, done.

remote: Compressing objects: 100% (1/1), done.

remote: Total 3 (delta 2), reused 3 (delta 2)

Unpacking objects: 100% (3/3), done.

From https://github.com/eldiegoefe/emacs

* branch master -> FETCH_HEAD

142c683..efe8a9a master -> origin/master

Auto-merging manifiesto.txt

CONFLICT (content): Merge conflict in manifiesto.txt

Automatic merge failed; fix conflicts and then commit the result.

Ahora Git nos avisa que hay un conflicto y marca el archivo donde éste se produce
(manifiesto.txt). Veamos ahora el contenido de este archivo con cualquier editor:

Un fantasma recorre Europa: el fantasma del comunismo. Todas las

<<<<<<< HEAD

fuerzas de la vieja Europa se han unido en santa cruzada para acosar a

ese fantasma: el Papa y el zar, Metternich y Guizot, los radicales

franceses y los polizontes alemanes. Marx es re-grosso, vieja.

=======

fuerzas de la vieja Europa se han unido en santa cruzada para


acosar a ese fantasma: el Papa y el zar, Metternich y Guizot, los

radicales franceses y los polizontes alemanes. Marx se la come.

>>>>>>> efe8a9a434f1a2609a16660f7d78c82fadad7d7c

Vemos que Git ha modificado el archivo local colocando marcas para separar las dos
versiones. Veo que ambas versiones no solamente difieren en la oración que habla de
Marx, sino también en el contenido de las últimas tres lineas (no empiezan y terminan
con las mismas palabras). ¡No es facil engañar a Git!

Las marcas son <<<<<<< HEAD, el separador ======= (que divide los cambios
conflictivos en las dos versiones) y >>>>>>>
efe8a9a434f1a2609a16660f7d78c82fadad7d7c. Lo que está junto alHEAD es el
contenido local, mientras lo que está tras el separador (antes del identificador de la
revisión que acabamos de bajar) es el contenido agregado remotamente.

Solución en proceso
Nos corresponde editar este archivo para remover las marcas y reconciliar los cambios.
Podemos hacer lo que nos plazca: mantener los cambios que hicimos en el repositorio
local, mantener los cambios hechos en el repositorio remoto, escribir algo nuevo que
reemplace a ambos, o eliminar completamente ambos cambios. Hagamos una mezcla:

Un fantasma recorre Europa: el fantasma del comunismo. Todas las

fuerzas de la vieja Europa se han unido en santa cruzada para

acosar a ese fantasma: el Papa y el zar, Metternich y Guizot, los

radicales franceses y los polizontes alemanes. Marx es re-grosso,

pero Engels es más grosso todavía.

Ahora eldiegoefe pide enterarse de cómo está la situación:

[eldiegoefe]$ git status

# On branch master

# You have unmerged paths.


# (fix conflicts and run "git commit")

# Unmerged paths:

# (use "git add <file>..." to mark resolution)

# both modified: manifiesto.txt

no changes added to commit (use "git add" and/or "git commit -a")

Tenemos que agregar los cambios y comitearlos antes de volver a intentar el push:

[eldiegoefe]$ git add manifiesto.txt

[eldiegoefe]$ git status

# On branch master

# All conflicts fixed but you are still merging.

# (use "git commit" to conclude merge)

# Changes to be committed:

# modified: manifiesto.txt

[eldiegoefe]$ git commit -m "Conflicto arreglado en manifiesto.txt"

[master 4f17908] Conflicto arreglado en manifiesto.txt

[eldiegoefe]$ git status

# On branch master

nothing to commit, working directory clean


Listo el pollo
Ahora sí intentamos el push:

[eldiegoefe]$ git push origin master

Counting objects: 10, done.

Delta compression using up to 4 threads.

Compressing objects: 100% (6/6), done.

Writing objects: 100% (6/6), 665 bytes | 0 bytes/s, done.

Total 6 (delta 4), reused 0 (delta 0)

To https://github.com/eldiegoefe/emacs.git

efe8a9a..4f17908 master -> master

Esta vez pudimos subir todo exitosamente. Ahora vamos a ver qué le pasa al otro
usuario bioingenierodiegol cuando quiere actualizar su repo local:

[bioingenierodiegol] $ git pull origin master

remote: Counting objects: 6, done.

remote: Compressing objects: 100% (2/2), done.

Unpacking objects: 100% (6/6), done.

remote: Total 6 (delta 4), reused 6 (delta 4)

From https://github.com/eldiegoefe/emacs

* branch master -> FETCH_HEAD

efe8a9a..4f17908 master -> origin/master

Updating efe8a9a..4f17908

Fast-forward

manifiesto.txt | 3 ++-

1 file changed, 2 insertions(+), 1 deletion(-)

Sin problemas se ha cerrado el círculo, ambos usuarios tienen el mismo contenido en el


archivo que había generado el conflicto:
Un fantasma recorre Europa: el fantasma del comunismo. Todas las

fuerzas de la vieja Europa se han unido en santa cruzada para

acosar a ese fantasma: el Papa y el zar, Metternich y Guizot, los

radicales franceses y los polizontes alemanes. Marx es re-grosso,

pero Engels es más grosso todavía.

El usuario bioingenierodiegol no tuvo necesidad de hacer la fusión (merge) porque Git


sabe que algún otro ya lo hizo.

Los usuarios tienden a dividir sus programas y textos en múltiples archivos (en vez de
meter todo en un mismo archivo enorme) porque esto facilita lo que acabamos de ver:
habilidad del sistema de control de versiones para fusionar cambios conflictivos.

Hay también otro beneficio: siempre que hay conflictos repetidos en un archivo en
particular, el sistema de control de versiones está esencialmente tratando de decirle a
sus usuarios que deberían clarificar quién es el responsable de cada cosa, o encontrar un
modo de dividir el trabajo de modo diferente.

Claves
Los conflictos ocurren cuando dos o más personas cambian el mismo archivo al
mismo tiempo.

El sistema de control de versiones no permite que la gente sobreescriba


ciegamente los cambios realizados por otras personas. En cambio, resalta los
conflictos para que los puedan resolver.

No te vayas sin comentar algo del post, de las noticias del día, del clima, que se yo...
Links

https://eldiegoefe.github.io/tutorial-sobre-control-de-versiones-parte-1.html

https://eldiegoefe.github.io/tutorial-sobre-control-de-versiones-parte-2.html

https://eldiegoefe.github.io/tutorial-sobre-control-de-versiones-parte-3.html

https://eldiegoefe.github.io/tutorial-sobre-control-de-versiones-parte-4.html

Angular + Spring boot

Build a Basic CRUD App


with Angular 5.0 and Spring
Boot 2.0
https://developer.okta.com/blog/2017/12/04/basic-crud-angular-and-spring-boot

Technology moves fast these days. It can be challenging to keep up with the latest
trends as well as new releases of your favorite projects. I’m here to help! Spring
Boot and Angular are two of my favorite projects, so I figured I’d write y’all a guide
to show you how to build and secure a basic app using their latest and greatest
releases.

In Spring Boot, the most significant change in 2.0 is its new web framework: Spring
WebFlux. In Angular 5.0, we get a new HttpClient on the table. This class
replaces Http, and is a bit easier to use, with less boilerplate code. Today, I’m not
going to explore Spring WebFlux, because we still have some work to do before
we can support in with the Okta Spring Boot Starter.
The good news is our Angular SDK works well with Angular 5, so I’ll be showing
how to use it in this blog post. Speaking of Angular, did you know that Angular
has one of the most dramatic increases in questions on Stack Overflow? You might
think this means a lot of people have issues with Angular. I like to think that there’s
a lot of people using it, and developers often have questions when using a new
technology. It’s a definite sign of a healthy community. You rarely see a lot of
questions on Stack Overflow for a dying technology.

This article describes how to build a simple CRUD application that displays a list of
cool cars. It’ll allow you to edit the list, and it’ll show an animated gif
from GIPHY that matches the car’s name. You’ll also learn how to secure your
application using Okta’s Spring Boot starter and Angular SDK.

You will need Java 8 and Node.js 8 installed to complete this tutorial.

Build an API with Spring Boot


2.0
To get started with Spring Boot 2.0, head on over to start.spring.io and create a
new project that uses Java, Spring Boot version 2.0.1, and options to create a
simple API: JPA, H2, Rest Repositories, Lombok, and Web. In this example, I’ve
added Actuator as well, since it’s a very cool feature of Spring Boot.

Create a directory to hold your server and client applications. I called mine okta-
spring-boot-2-angular-5-example, but you can call yours whatever you
like. If you’d rather just see the app running than write code, you can see the
example on GitHub, or clone and run locally using the commands below.

git clone https://github.com/oktadeveloper/okta-spring-boot-2-angular-


5-example.git

cd okta-spring-boot-2-angular-5-example/client && npm install


&& ng serve &

cd ../server && ./mvnw spring-boot:run

After downloading demo.zip from start.spring.io, expand it and copy


the demo directory to your app-holder directory. Rename demo to server. Open
the project in your favorite IDE and create a Car.java file in
the src/main/java/com/okta/developer/demo directory. You can use
Lombok’s annotations to reduce boilerplate code.

package com.okta.developer.demo;
import lombok.*;

import javax.persistence.Id;

import javax.persistence.GeneratedValue;

import javax.persistence.Entity;

@Entity

@Getter @Setter

@NoArgsConstructor

@ToString @EqualsAndHashCode

public class Car {

@Id @GeneratedValue

private Long id;

private @NonNull String name;

Create a CarRepository class to perform CRUD (create, read, update, and


delete) on the Car entity.

package com.okta.developer.demo;

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

import
org.springframework.data.rest.core.annotation.RepositoryRestR
esource;
@RepositoryRestResource

interface CarRepository extends JpaRepository<Car, Long> {

Add an ApplicationRunner bean to the DemoApplication class


(in src/main/java/com/okta/developer/demo/DemoApplication.java)
and use it to add some default data to the database.

package com.okta.developer.demo;

import org.springframework.boot.ApplicationRunner;

import org.springframework.boot.SpringApplication;

import
org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.annotation.Bean;

import java.util.stream.Stream;

@SpringBootApplication

public class DemoApplication {

public static void main(String[] args) {

SpringApplication.run(DemoApplication.class, args);

@Bean
ApplicationRunner init(CarRepository repository) {

return args -> {

Stream.of("Ferrari", "Jaguar", "Porsche",


"Lamborghini", "Bugatti",

"AMC Gremlin", "Triumph Stag", "Ford


Pinto", "Yugo GV").forEach(name -> {

Car car = new Car();

car.setName(name);

repository.save(car);

});

repository.findAll().forEach(System.out::println);

};

If you start your app (using ./mvnw spring-boot:run) after adding this code,
you’ll see the list of cars displayed in your console on startup.

Car(id=1, name=Ferrari)

Car(id=2, name=Jaguar)

Car(id=3, name=Porsche)

Car(id=4, name=Lamborghini)

Car(id=5, name=Bugatti)

Car(id=6, name=AMC Gremlin)

Car(id=7, name=Triumph Stag)

Car(id=8, name=Ford Pinto)


Car(id=9, name=Yugo GV)

Add a CoolCarController class


(in src/main/java/com/okta/developer/demo/CoolCarController.jav
a) that returns a list of cool cars to display in the Angular client.

package com.okta.developer.demo;

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

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

import java.util.Collection;

import java.util.stream.Collectors;

@RestController

class CoolCarController {

private CarRepository repository;

public CoolCarController(CarRepository repository) {

this.repository = repository;

@GetMapping("/cool-cars")

public Collection<Car> coolCars() {

return repository.findAll().stream()

.filter(this::isCool)
.collect(Collectors.toList());

private boolean isCool(Car car) {

return !car.getName().equals("AMC Gremlin") &&

!car.getName().equals("Triumph Stag") &&

!car.getName().equals("Ford Pinto") &&

!car.getName().equals("Yugo GV");

If you restart your server app and hit localhost:8080/cool-cars with your
browser, or a command-line client, you should see the filtered list of cars.

http localhost:8080/cool-cars

HTTP/1.1 200

Content-Type: application/json;charset=UTF-8

Date: Mon, 05 Mar 2018 12:31:32 GMT

Transfer-Encoding: chunked

"id": 1,

"name": "Ferrari"

},

{
"id": 2,

"name": "Jaguar"

},

"id": 3,

"name": "Porsche"

},

"id": 4,

"name": "Lamborghini"

},

"id": 5,

"name": "Bugatti"

Create a Client with Angular CLI


Angular CLI is a command-line utility that can generate an Angular project for you.
Not only can it create new projects, but it can also generate code. It’s a convenient
tool because it also offers commands that will build and optimize your project for
production. It uses webpack under the covers for building. If you want to learn
more about webpack, I recommend webpack.academy.

You can learn the basics of Angular CLI at https://cli.angular.io.


Install the latest version of Angular CLI, which is version 1.7.4.

npm install -g @angular/cli@1.7.4

Create a new project in the umbrella directory you created. Again, mine is
named okta-spring-boot-2-angular-5-example.

ng new client

After the client is created, navigate into its directory and install Angular Material.

cd client

npm install --save-exact @angular/material@5.2.4


@angular/cdk@5.2.4

You’ll use Angular Material’s components to make the UI look better, especially on
mobile phones. If you’d like to learn more about Angular Material,
see https://material.angular.io. It has extensive documentation on its various
components and how to use them.
Build a Car List Page
Use Angular CLI to generate a car service that can talk to the Cool Cars API.

ng g s car

Move the generated files into the client/src/app/shared/car directory.

mkdir -p src/app/shared/car

mv src/app/car.service.* src/app/shared/car/.

Update the code in car.service.ts to fetch the list of cars from the server.

import { Injectable } from '@angular/core';

import { HttpClient } from '@angular/common/http';


import { Observable } from 'rxjs/Observable';

@Injectable()

export class CarService {

constructor(private http: HttpClient) {

getAll(): Observable<any> {

return this.http.get('//localhost:8080/cool-cars');

TIP: If you’re using using Angular 6+, you can make this code work by
installing rxjs-compat with npm i rxjs-compat.

Add this service as a provider in src/app/app.module.ts. While you’re in


there, import HttpClientModule too.

import { CarService } from './shared/car/car.service';

import { HttpClientModule } from '@angular/common/http';

@NgModule({

declarations: [

AppComponent

],
imports: [

BrowserModule,

HttpClientModule

],

providers: [CarService],

bootstrap: [AppComponent]

})

Generate a car-list component to display the list of cars.

ng g c car-list

Update client/src/app/car-list/car-list.component.ts to use


the CarService to fetch the list and set the values in a local cars variable.

import { CarService } from '../shared/car/car.service';

export class CarListComponent implements OnInit {

cars: Array<any>;

constructor(private carService: CarService) { }

ngOnInit() {

this.carService.getAll().subscribe(data => {

this.cars = data;

});

}
}

Update client/src/app/car-list/car-list.component.html to show


the list of cars.

<h2>Car List</h2>

<div *ngFor="let car of cars">

{{car.name}}

</div>

Update client/src/app/app.component.html to have the app-car-


list element.

<div style="text-align:center">

<h1>Welcome to {{title}}!</h1>

</div>

<app-car-list></app-car-list>

Start the client application using ng serve. Open your favorite browser
to http://localhost:4200. You won’t see the car list just yet, and if you open your
developer console, you’ll see why.
This error happens because you haven’t enabled CORS (Cross-Origin Resource
Sharing) on the server.

Enable CORS on the Server


To enable CORS on the server, add a @CrossOrigin annotation to
the CoolCarController (in server/src/main/java/com/okta/develope
r/demo/CoolCarController.java).

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

...

@GetMapping("/cool-cars")

@CrossOrigin(origins = "http://localhost:4200")

public Collection<Car> coolCars() {

return repository.findAll().stream()

.filter(this::isCool)
.collect(Collectors.toList());

Also, add it to CarRepository so you can communicate with its endpoints when
adding/deleting/editing.

@RepositoryRestResource

@CrossOrigin(origins = "http://localhost:4200")

interface CarRepository extends JpaRepository<Car, Long> {

Restart the server, refresh the client, and you should see the list of cars in your
browser.

Add Angular Material


You’ve already installed Angular Material, to use its components, you simply need
to import them. Open client/src/app/app.module.ts and add imports for
animations, and Material’s toolbar, buttons, inputs, lists, and card layout.

import { MatButtonModule, MatCardModule, MatInputModule,


MatListModule, MatToolbarModule } from '@angular/material';

import { BrowserAnimationsModule } from '@angular/platform-


browser/animations';

@NgModule({

...

imports: [

BrowserModule,

HttpClientModule,
BrowserAnimationsModule,

MatButtonModule,

MatCardModule,

MatInputModule,

MatListModule,

MatToolbarModule

],

...

})

Update client/src/app/app.component.html to use the toolbar component.

<mat-toolbar color="primary">

<span>Welcome to {{title}}!</span>

</mat-toolbar>

<app-car-list></app-car-list>

Update client/src/app/car-list/car-list.component.html to use the


card layout and list component.

<mat-card>

<mat-card-header>Car List</mat-card-header>

<mat-card-content>

<mat-list>

<mat-list-item *ngFor="let car of cars">

<img mat-list-avatar src="{{car.giphyUrl}}"


alt="{{car.name}}">
<h3 mat-line>{{car.name}}</h3>

</mat-list-item>

</mat-list>

</mat-card-content>

</mat-card>

Modify client/src/styles.css to specify the theme and icons.

@import "~@angular/material/prebuilt-themes/pink-
bluegrey.css";

@import 'https://fonts.googleapis.com/icon?family=Material+Icons';

body {

margin: 0;

font-family: Roboto, sans-serif;

If you run your client with ng serve and navigate to http://localhost:4200, you’ll
see the list of cars, but no images associated with them.
Add Animated GIFs with Giphy
To add a giphyUrl property to cars,
create client/src/app/shared/giphy/giphy.service.ts and populate it
with the code below.

import { Injectable } from '@angular/core';

import { HttpClient } from '@angular/common/http';

import 'rxjs/add/operator/map';

@Injectable()

export class GiphyService {


// Public beta key: https://github.com/Giphy/GiphyAPI#public-beta-
key

giphyApi =
'//api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&limit=1&q=';

constructor(public http: HttpClient) {

get(searchTerm) {

const apiLink = this.giphyApi + searchTerm;

return this.http.get(apiLink).map((response: any) => {

if (response.data.length > 0) {

return response.data[0].images.original.url;

} else {

return
'https://media.giphy.com/media/YaOxRsmrv9IeA/giphy.gif'; // dancing
cat for 404

});

Add GiphyService as a provider in client/src/app/app.module.ts.

import { GiphyService } from './shared/giphy/giphy.service';

@NgModule({
...

providers: [CarService, GiphyService],

bootstrap: [AppComponent]

})

Update the code in client/src/app/car-list/car-list.component.ts to


set the giphyUrl property on each car.

import { GiphyService } from '../shared/giphy/giphy.service';

export class CarListComponent implements OnInit {

cars: Array<any>;

constructor(private carService: CarService, private


giphyService: GiphyService) { }

ngOnInit() {

this.carService.getAll().subscribe(data => {

this.cars = data;

for (const car of this.cars) {

this.giphyService.get(car.name).subscribe(url =>
car.giphyUrl = url);

});

}
Now your browser should show you the list of car names, along with an avatar
image beside them.

Add an Edit Feature


Having a list of car names and images is cool, but it’s a lot more fun when you can
interact with it! To add an edit feature, start by generating a car-edit component.

ng g c car-edit

Update client/src/app/shared/car/car.service.ts to have methods for


adding, removing, and updating cars. These methods talk to the endpoints
provided by the CarRepository and
the @RepositoryRestResource annotation.

import { Injectable } from '@angular/core';

import { HttpClient } from '@angular/common/http';

import { Observable } from 'rxjs/Observable';


@Injectable()

export class CarService {

public API = '//localhost:8080';

public CAR_API = this.API + '/cars';

constructor(private http: HttpClient) {

getAll(): Observable<any> {

return this.http.get(this.API + '/cool-cars');

get(id: string) {

return this.http.get(this.CAR_API + '/' + id);

save(car: any): Observable<any> {

let result: Observable<Object>;

if (car['href']) {

result = this.http.put(car.href, car);

} else {

result = this.http.post(this.CAR_API, car);


}

return result;

remove(href: string) {

return this.http.delete(href);

In client/src/app/car-list/car-list.component.html, add a link to


the edit component. Also, add a button at the bottom to add a new car.

<mat-card>

<mat-card-header>Car List</mat-card-header>

<mat-card-content>

<mat-list>

<mat-list-item *ngFor="let car of cars">

<img mat-list-avatar src="{{car.giphyUrl}}"


alt="{{car.name}}">

<h3 mat-line>

<a mat-button [routerLink]="['/car-edit',


car.id]">{{car.name}}</a>

</h3>

</mat-list-item>

</mat-list>

</mat-card-content>
<button mat-fab color="primary" [routerLink]="['/car-
add']">Add</button>

</mat-card>

In client/src/app/app.module.ts, add routes and import


the FormsModule.

import { FormsModule } from '@angular/forms';

import { RouterModule, Routes } from '@angular/router';

const appRoutes: Routes = [

{ path: '', redirectTo: '/car-list', pathMatch: 'full' },

path: 'car-list',

component: CarListComponent

},

path: 'car-add',

component: CarEditComponent

},

path: 'car-edit/:id',

component: CarEditComponent

];
@NgModule({

...

imports: [

...

FormsModule,

RouterModule.forRoot(appRoutes)

],

...

})

Modify client/src/app/car-edit/car-edit.component.ts to fetch a


car’s information from the id passed on the URL, and to add methods for saving
and deleting.

import { Component, OnDestroy, OnInit } from '@angular/core';

import { Subscription } from 'rxjs/Subscription';

import { ActivatedRoute, Router } from '@angular/router';

import { CarService } from '../shared/car/car.service';

import { GiphyService } from '../shared/giphy/giphy.service';

import { NgForm } from '@angular/forms';

@Component({

selector: 'app-car-edit',

templateUrl: './car-edit.component.html',

styleUrls: ['./car-edit.component.css']
})

export class CarEditComponent implements OnInit, OnDestroy {

car: any = {};

sub: Subscription;

constructor(private route: ActivatedRoute,

private router: Router,

private carService: CarService,

private giphyService: GiphyService) {

ngOnInit() {

this.sub = this.route.params.subscribe(params => {

const id = params['id'];

if (id) {

this.carService.get(id).subscribe((car: any) => {

if (car) {

this.car = car;

this.car.href = car._links.self.href;

this.giphyService.get(car.name).subscribe(url =>
car.giphyUrl = url);

} else {
console.log(`Car with id '${id}' not found,
returning to list`);

this.gotoList();

});

});

ngOnDestroy() {

this.sub.unsubscribe();

gotoList() {

this.router.navigate(['/car-list']);

save(form: NgForm) {

this.carService.save(form).subscribe(result => {

this.gotoList();

}, error => console.error(error));

}
remove(href) {

this.carService.remove(href).subscribe(result => {

this.gotoList();

}, error => console.error(error));

Update the HTML in client/src/app/car-edit/car-


edit.component.html to have a form with the car’s name, as well as to display
the image from Giphy.

<mat-card>

<form #carForm="ngForm" (ngSubmit)="save(carForm.value)">

<mat-card-header>

<mat-card-title><h2>{{car.name ? 'Edit' : 'Add'}}


Car</h2></mat-card-title>

</mat-card-header>

<mat-card-content>

<input type="hidden" name="href"


[(ngModel)]="car.href">

<mat-form-field>

<input matInput placeholder="Car Name"


[(ngModel)]="car.name"

required name="name" #name>

</mat-form-field>

</mat-card-content>

<mat-card-actions>
<button mat-raised-button color="primary" type="submit"

[disabled]="!carForm.form.valid">Save</button>

<button mat-raised-button color="secondary"


(click)="remove(car.href)"

*ngIf="car.href" type="button">Delete</button>

<a mat-button routerLink="/car-list">Cancel</a>

</mat-card-actions>

<mat-card-footer>

<div class="giphy">

<img src="{{car.giphyUrl}}" alt="{{car.name}}">

</div>

</mat-card-footer>

</form>

</mat-card>

Put a little padding around the image by adding the following CSS
to client/src/app/car-edit/car-edit.component.css.

.giphy {

margin: 10px;

Modify client/src/app/app.component.html and replace <app-car-


list></app-car-list> with <router-outlet></router-outlet>. This
change is necessary or routing between components won’t work.

<mat-toolbar color="primary">

<span>Welcome to {{title}}!</span>

</mat-toolbar>
<router-outlet></router-outlet>

After you make all these changes, you should be able to add, edit, or delete any
cars. Below is a screenshot that shows the list with the add button.

The following screenshot shows what it looks like to edit a car that you’ve added.
Add Authentication with Okta
Add authentication with Okta is a nifty feature you can add to this application.
Knowing who the person is can come in handy if you want to add auditing, or
personalize your application (with a rating feature for example).

Spring Security + OAuth 2.0


On the server side, you can lock things down with Spring Security and its OAuth
2.0 support. Open server/pom.xml and add the following dependencies.

<dependency>

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

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

</dependency>

<dependency>

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

<artifactId>spring-security-oauth2-
autoconfigure</artifactId>

<version>2.0.0.RELEASE</version>

</dependency>

Now you need to configure the server to use Okta for authentication. You’ll need to
create an OIDC app in Okta for that.
Create an OIDC App in Okta
Log in to your Okta Developer account (or sign up if you don’t have an account)
and navigate to Applications > Add Application. Click Single-Page App,
click Next, and give the app a name you’ll remember. Change all instances
of localhost:8080 to localhost:4200 and click Done.

Create server/src/main/resources/application.yml and copy the client


ID into it. While you’re in there, fill in the rest of the necessary values to match your
Okta domain.

security:

oauth2:

client:

access-token-uri:
https://{yourOktaDomain}/oauth2/default/v1/token

user-authorization-uri:
https://{yourOktaDomain}/oauth2/default/v1/authorize

client-id: {clientId}

scope: openid profile email

resource:

user-info-uri:
https://{yourOktaDomain}/oauth2/default/v1/userinfo

token-info-uri:
https://{yourOktaDomain}/oauth2/default/v1/introspect

prefer-token-info: false

Update server/src/main/java/com/okta/developer/demo/DemoApplic
ation.java to enable it as a resource server.

import
org.springframework.security.oauth2.config.annotation.web.con
figuration.EnableResourceServer;
@EnableResourceServer

@SpringBootApplication

After making these changes, you should be able to restart your app and see
access denied when you try to navigate to http://localhost:8080.

It’s nice that your server is locked down, but now you need to configure your client
to talk to it. This is where Okta’s Angular support comes in handy.

Okta’s Angular Support


The Okta Angular SDK is a wrapper around Okta Auth JS, which builds on top of
OIDC. More information about Okta’s Angular library can be found on npmjs.com.
To install it, run the following command in the client directory:

npm install @okta/okta-angular@1.0.0

In client/src/app/app.module.ts, add a config variable with the settings


for your OIDC app.

const config = {

issuer: 'https://{yourOktaDomain}/oauth2/default',

redirectUri: 'http://localhost:4200/implicit/callback',

clientId: '{clientId}'

};

In this same file, you’ll also need to add a new route for the redirectUri that
points to the OktaCallbackComponent.
import { OktaCallbackComponent, OktaAuthModule } from
'@okta/okta-angular';

const appRoutes: Routes = [

...

path: 'implicit/callback',

component: OktaCallbackComponent

];

Next, initialize and import the OktaAuthModule.

@NgModule({

...

imports: [

...

OktaAuthModule.initAuth(config)

],

...

})

These are the three steps you need to set up an Angular app to use Okta. To make
it easy to add a bearer token to HTTP requests, you can use
an HttpInterceptor.

Create client/src/app/shared/okta/auth.interceptor.ts and add the


following code to it.

import { Injectable } from '@angular/core';


import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest
} from '@angular/common/http';

import { Observable } from 'rxjs/Observable';

import 'rxjs/add/observable/fromPromise';

import { OktaAuthService } from '@okta/okta-angular';

@Injectable()

export class AuthInterceptor implements HttpInterceptor {

constructor(private oktaAuth: OktaAuthService) {

intercept(request: HttpRequest<any>, next: HttpHandler):


Observable<HttpEvent<any>> {

return Observable.fromPromise(this.handleAccess(request,
next));

private async handleAccess(request: HttpRequest<any>, next:


HttpHandler): Promise<HttpEvent<any>> {

// Only add to known domains since we don't want to send


our tokens to just anyone.

// Also, Giphy's API fails when the request includes a


token.

if (request.urlWithParams.indexOf('localhost') > -1) {


const accessToken = await
this.oktaAuth.getAccessToken();

request = request.clone({

setHeaders: {

Authorization: 'Bearer ' + accessToken

});

return next.handle(request).toPromise();

To register this interceptor, add it as a provider


in client/src/app/app.module.ts.

import { HTTP_INTERCEPTORS } from '@angular/common/http';

import { AuthInterceptor } from


'./shared/okta/auth.interceptor';

@NgModule({

...

providers: [CarService, GiphyService,

{provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor,


multi: true}],

...

})
Modify client/src/app/app.component.html to have login and logout
buttons.

<mat-toolbar color="primary">

<span>Welcome to {{title}}!</span>

<span class="toolbar-spacer"></span>

<button mat-raised-button color="accent"


*ngIf="isAuthenticated"

(click)="oktaAuth.logout()">Logout

</button>

</mat-toolbar>

<mat-card *ngIf="!isAuthenticated">

<mat-card-content>

<button mat-raised-button color="accent"

(click)="oktaAuth.loginRedirect()">Login

</button>

</mat-card-content>

</mat-card>

<router-outlet></router-outlet>

You might notice there’s a span with a toolbar-spacer class. To make that
work as expected, update client/src/app/app.component.css to have the
following class.

.toolbar-spacer {

flex: 1 1 auto;
}

There’s also a reference to isAuthenticated for checking authenticated status.


To make this work, add it as a dependency to the constructor
in client/src/app/app.component.ts and add an initializer method that sets
the variable.

import { Component, OnInit } from '@angular/core';

import { OktaAuthService } from '@okta/okta-angular';

@Component({

selector: 'app-root',

templateUrl: './app.component.html',

styleUrls: ['./app.component.css']

})

export class AppComponent implements OnInit {

title = 'app';

isAuthenticated: boolean;

constructor(private oktaAuth: OktaAuthService) {

async ngOnInit() {

this.isAuthenticated = await
this.oktaAuth.isAuthenticated();

// Subscribe to authentication state changes

this.oktaAuth.$authenticationState.subscribe(
(isAuthenticated: boolean) => this.isAuthenticated =
isAuthenticated

);

Now if you restart your client, you should see a login button.

Notice that this shows elements from the car-list component. To fix this, you can
create a home component and make it the default route.

ng g c home

Modify client/src/app/app.module.ts to update the routes.

const appRoutes: Routes = [

{path: '', redirectTo: '/home', pathMatch: 'full'},

path: 'home',

component: HomeComponent

},
...

Move the HTML for the Login button


from app.component.html to client/src/app/home/home.component.ht
ml.

<mat-card>

<mat-card-content>

<button mat-raised-button color="accent"


*ngIf="!isAuthenticated"

(click)="oktaAuth.loginRedirect()">Login

</button>

<button mat-raised-button color="accent"


*ngIf="isAuthenticated"

[routerLink]="['/car-list']">Car List

</button>

</mat-card-content>

</mat-card>

Add oktaAuth as a dependency


in client/src/app/home/home.component.ts and set it up to
initialize/change the isAuthenticated variable.

import { OktaAuthService } from '@okta/okta-angular';

export class HomeComponent implements OnInit {

isAuthenticated: boolean;

constructor(private oktaAuth: OktaAuthService) {


}

async ngOnInit() {

this.isAuthenticated = await
this.oktaAuth.isAuthenticated();

// Subscribe to authentication state changes

this.oktaAuth.$authenticationState.subscribe(

(isAuthenticated: boolean) => this.isAuthenticated =


isAuthenticated

);

Update client/src/app/app.component.html, so the Logout button


redirects back to home when it’s clicked.

<mat-toolbar color="primary">

<span>Welcome to {{title}}!</span>

<span class="toolbar-spacer"></span>

<button mat-raised-button color="accent"


*ngIf="isAuthenticated"

(click)="oktaAuth.logout()"
[routerLink]="['/home']">Logout

</button>

</mat-toolbar>

<router-outlet></router-outlet>
Now you should be able to open your browser to http://localhost:4200 and click on
the Login button. If you’ve configured everything correctly, you’ll be redirected to
Okta to log in.

Enter the credentials you used to sign up for an account, and you should be
redirected back to your app. However, your list of cars won’t load because of a
CORS error. This happens because Spring’s @CrossOrigin doesn’t work well
with Spring Security.

To fix this, add a bean to DemoApplication.java that handles CORS.

import
org.springframework.boot.web.servlet.FilterRegistrationBean;

import org.springframework.core.Ordered;

import org.springframework.web.cors.CorsConfiguration;

import
org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import org.springframework.web.filter.CorsFilter;

import java.util.Collections;
...

@Bean

@SuppressWarnings("unchecked")

public FilterRegistrationBean simpleCorsFilter() {

UrlBasedCorsConfigurationSource source = new


UrlBasedCorsConfigurationSource();

CorsConfiguration config = new CorsConfiguration();

config.setAllowCredentials(true);

config.setAllowedOrigins(

Collections.singletonList("http://localhost:4200"));

config.setAllowedMethods(Collections.singletonList("*"));

config.setAllowedHeaders(Collections.singletonList("*"));

source.registerCorsConfiguration("/**", config);

FilterRegistrationBean bean = new


FilterRegistrationBean(new CorsFilter(source));

bean.setOrder(Ordered.HIGHEST_PRECEDENCE);

return bean;

Restart your server and celebrate when it all works! 🎉


You can see the full source code for the application developed in this tutorial on
GitHub at https://github.com/oktadeveloper/okta-spring-boot-2-angular-5-example.

Spring Boot 2 + Angular 7 CRUD Example Tutorial

Link: https://www.javaguides.net/2019/02/spring-boot-2-angular-7-crud-example-
tutorial.html?fbclid=IwAR3dtxbT_pKqVJL8g_XlBH55JxdkH2tfa66mWmKvx1Ls_TcU6J87bvHAi5M

In this tutorial, we will learn how to develop a CRUD (Create, Read, Update, Delete) Web
Application using Angular 7 as a front-end and Spring boot 2 restful API as a backend.

If you are looking for Angular 6 with spring boot 2 integration example then check
out Spring Boot + Angular 6 CRUD Example article.
You can download the source code of this tutorial from my GitHub repository at end of
this tutorial.

What we will build?

Basically, we will create two projects:

1. springboot2-jpa-crud-example: This project is used to develop CRUD RESTFul


APIs for a simple Employee Management System using Spring Boot 2, JPA and
MySQL as a database.
2. angular7-springboot-client: This project is used to develop single page
application using Angular 7 as front-end technology. This Angular 7 application
consumes CRUD Restful APIs developed and exposed by a springboot2-jpa-crud-
example project.
Let me list out tools and technologies used to develop these two applications.

Tools and technologies used

Server-side technologies

 Spring Boot - 2.0.4.RELEASE


 JDK - 1.8 or later
 Spring Framework - 5.0.8 RELEASE
 Hibernate - 5.2.17.Final
 Spring Data JPA - 2+

Front end technologies

 Angular 7.2
 Bootstrap 4
 npm- 6.4.1
 JQuery

Tools

 Maven - 3.2+
 IDE - Eclipse or Spring Tool Suite (STS)
 Visual Studio 2017
 Angular CLI

Spring Boot CRUD Rest APIs

Let's first we will build a CRUD RESTFul APIs for a Simple Employee Management
System using Spring Boot 2 JPA and MySQL. Later we will consume these Rest APIs using
Angular 7 client application. Following are five REST APIs (Controller handler methods) are
created for Employee resource.
1. Creating and Importing a Project

There are many ways to create a Spring Boot application. The simplest way is to use Spring
Initializrat http://start.spring.io/, which is an online Spring Boot application generator.
Look at the above diagram, we have specified the following details:

 Generate: Maven Project


 Java Version: 1.8 (Default)
 Spring Boot:2.0.4
 Group: net.guides.springboot2
 Artifact: springboot2-jpa-crud-example
 Name: springboot2-jpa-crud-example
 Description: Rest API for a Simple Employee Management Application
 Package Name : net.guides.springboot2.springboot2jpacrudexample
 Packaging: jar (This is the default value)
 Dependencies: Web, JPA, MySQL, DevTools
Once, all the details are entered, click on Generate Project button will generate a spring boot project
and downloads it. Next, Unzip the downloaded zip file and import it into your favorite IDE.

2. Packaging Structure

Following is the packing structure of our Employee Management System -

3. The pom.xml File

<?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>net.guides.springboot2</groupId>
<artifactId>springboot2-jpa-crud-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>springboot2-jpa-crud-example</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.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.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</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>

4. Configuring MySQL Database

Configure application.properties to connect to your MySQL database. Let's open an


application.properties file and add the following database configuration to it. Also,
note that we have added MySQL dependency to pom.xml so spring boot will auto-
configure all database related beans and configurations internally.

spring.datasource.url = jdbc:mysql://localhost:3306/users_database?useSSL=false
spring.datasource.username = root
spring.datasource.password = root

## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect =
org.hibernate.dialect.MySQL5InnoDBDialect

# Hibernate ddl auto (create, create-drop, validate, update)


spring.jpa.hibernate.ddl-auto = update

Change the above configuration such as JDBC URL, username and password as per your
environment.

5. Create JPA Entity - Employee.java

package net.guides.springboot2.springboot2jpacrudexample.model;

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

@Entity
@Table(name = "employees")
public class Employee {

private long id;


private String firstName;
private String lastName;
private String emailId;

public Employee() {

public Employee(String firstName, String lastName, String emailId) {


this.firstName = firstName;
this.lastName = lastName;
this.emailId = emailId;
}

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}

@Column(name = "first_name", nullable = false)


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

@Column(name = "last_name", nullable = false)


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

@Column(name = "email_address", nullable = false)


public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}

@Override
public String toString() {
return "Employee [id=" + id + ", firstName=" + firstName + ",
lastName=" + lastName + ", emailId=" + emailId
+ "]";
}

}
6. Create Spring Data Repository - EmployeeRepository.java

package net.guides.springboot2.springboot2jpacrudexample.repository;

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

import net.guides.springboot2.springboot2jpacrudexample.model.Employee;

@Repository
public interface EmployeeRepository extends JpaRepository<Employee,
Long>{

7. Create Spring Rest Controller - EmployeeController.java

package net.guides.springboot2.springboot2jpacrudexample.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import
net.guides.springboot2.springboot2jpacrudexample.exception.ResourceNotFou
ndException;
import net.guides.springboot2.springboot2jpacrudexample.model.Employee;
import
net.guides.springboot2.springboot2jpacrudexample.repository.EmployeeRepos
itory;

@RestController
@RequestMapping("/api/v1")
public class EmployeeController {
@Autowired
private EmployeeRepository employeeRepository;

@GetMapping("/employees")
public List<Employee> getAllEmployees() {
return employeeRepository.findAll();
}

@GetMapping("/employees/{id}")
public ResponseEntity<Employee> getEmployeeById(@PathVariable(value =
"id") Long employeeId)
throws ResourceNotFoundException {
Employee employee = employeeRepository.findById(employeeId)
.orElseThrow(() -> new ResourceNotFoundException("Employee not
found for this id :: " + employeeId));
return ResponseEntity.ok().body(employee);
}

@PostMapping("/employees")
public Employee createEmployee(@Valid @RequestBody Employee employee)
{
return employeeRepository.save(employee);
}

@PutMapping("/employees/{id}")
public ResponseEntity<Employee> updateEmployee(@PathVariable(value =
"id") Long employeeId,
@Valid @RequestBody Employee employeeDetails) throws
ResourceNotFoundException {
Employee employee = employeeRepository.findById(employeeId)
.orElseThrow(() -> new ResourceNotFoundException("Employee not
found for this id :: " + employeeId));

employee.setEmailId(employeeDetails.getEmailId());
employee.setLastName(employeeDetails.getLastName());
employee.setFirstName(employeeDetails.getFirstName());
final Employee updatedEmployee =
employeeRepository.save(employee);
return ResponseEntity.ok(updatedEmployee);
}

@DeleteMapping("/employees/{id}")
public Map<String, Boolean> deleteEmployee(@PathVariable(value =
"id") Long employeeId)
throws ResourceNotFoundException {
Employee employee = employeeRepository.findById(employeeId)
.orElseThrow(() -> new ResourceNotFoundException("Employee not
found for this id :: " + employeeId));

employeeRepository.delete(employee);
Map<String, Boolean> response = new HashMap<>();
response.put("deleted", Boolean.TRUE);
return response;
}
}
8. Running Application

This spring boot application has an entry point Java class


called SpringBootCrudRestApplication.java with the public static void main(String[] args) method,
which you can run to start the application.

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);
}
}

The main() method uses Spring Boot’s SpringApplication.run() method to launch an application.
Or you can start spring boot application via command line using mvn spring-boot:run command.

This completes the development of Spring boot CRUD Rest APIs. Now we will develop client
application using Angular 7.

Angular 7 Client Application

Let's develop a step by step CRUD (Create, Read, Update, Delete) Web Application using
Angular 7 which consume above CRUD rest APIs.

Good to know Angular 7 release notes and new features at Version 7 of Angular — CLI
Prompts, Virtual Scroll, Drag and Drop and more.

I assume that you have installed Node.js. Now, we need to check the Node.js and NPM
versions. Open the terminal or Node command line then type this commands.

node -v
v8.12.0
npm -v
6.4.1

That's the Node.js and NPM version that we are using. Now, you can go to the main steps.
Install or Update Angular 7 CLI and Create Application
To install or update Angular 7 CLI, type this command in the Terminal or Node Command
Line.

npm install -g @angular/cli

Now, you have the latest version of Angular CLI.

ng --version

Angular CLI: 7.0.1


Node: 8.12.0
OS: darwin x64
Angular:
...

Package Version
------------------------------------------------------
@angular-devkit/architect 0.10.1
@angular-devkit/core 7.0.1
@angular-devkit/schematics 7.0.1
@schematics/angular 7.0.1
@schematics/update 0.10.1
rxjs 6.3.3
typescript 3.1.3

Next, create a new Angular 7 Web Application using this Angular CLI command.

The Angular CLI is a command-line interface tool that you use to initialize, develop,
scaffold, and maintain Angular applications.

If you are new to Angular CLI then check out official documentation
at https://cli.angular.io.

Create Angular 7 client application using Angular CLI

Let's use below command to generate an Angular 7 Client application. We name this
project as "angular7-springboot-client".

ng new angular7-springboot-client
Components, Services, and Modules

Let's list out what are components, service, and modules we are going to create in this
application. We will use Angular CLI to generate components, services because Angular
CLI follows best practices and saves much of time.

1. Components

 create-employee
 employee-list
 employee-details
2. Services

 employee.service.ts - Service for Http Client methods

3. Modules

 FormsModule
 HttpClientModule
 AppRoutingModule.

4. Employee Class (Typescript class)

 employee.ts: class Employee (id, firstName, lastName, emailId)

In this next step, we will generate these components, classes, and services using Angular
CLI.

Create Service & Components


Let's auto-generate service and components using Angular CLI. Change your project
directory to angular7-springboot-client\src\app and run the following commands:

- ng g s employee
– ng g c create-employee
– ng g c employee-details
– ng g c employee-list

Here is complete command and output for your reference:

C:\angular7\angular7-springboot-client\src\app>ng g s employee
CREATE src/app/employee.service.spec.ts (343 bytes)
CREATE src/app/employee.service.ts (137 bytes)

C:\angular7\angular7-springboot-client\src\app>ng g c create-employee
CREATE src/app/create-employee/create-employee.component.html (34 bytes)
CREATE src/app/create-employee/create-employee.component.spec.ts (685
bytes)
CREATE src/app/create-employee/create-employee.component.ts (304 bytes)
CREATE src/app/create-employee/create-employee.component.css (0 bytes)
UPDATE src/app/app.module.ts (509 bytes)

C:\angular7\angular7-springboot-client\src\app>ng g c employee-details
CREATE src/app/employee-details/employee-details.component.html (35
bytes)
CREATE src/app/employee-details/employee-details.component.spec.ts (692
bytes)
CREATE src/app/employee-details/employee-details.component.ts (308 bytes)
CREATE src/app/employee-details/employee-details.component.css (0 bytes)
UPDATE src/app/app.module.ts (629 bytes)

C:\angular7\angular7-springboot-client\src\app>ng g c employee-list
CREATE src/app/employee-list/employee-list.component.html (32 bytes)
CREATE src/app/employee-list/employee-list.component.spec.ts (671 bytes)
CREATE src/app/employee-list/employee-list.component.ts (296 bytes)
CREATE src/app/employee-list/employee-list.component.css (0 bytes)
UPDATE src/app/app.module.ts (737 bytes)

We will use Bootstrap 4 for styling our application so let's integrate bootstrap 4 with
Angular 7.

Integrate Bootstrap with Angular

Use NPM to download Bootstrap & JQuery. Bootstrap and jQuery will be installed into the
node_modules folder.

npm install bootstrap jquery --save

Configure installed Bootstrap & JQuery in an angular.json file:

...

"styles": [
"src/styles.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
"node_modules/jquery/dist/jquery.min.js",
"node_modules/bootstrap/dist/js/bootstrap.min.js"
]

...

Let's discuss each of the above generate components and service files and we will
customize it as per our requirement.

package.json
This file Configures npm package dependencies that are available to all projects in the
workspace.
Note that angular version 7.2.0 in dependencies section in below file.

{
"name": "angular7-springboot-client",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config proxy.conf.json",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~7.2.0",
"@angular/common": "~7.2.0",
"@angular/compiler": "~7.2.0",
"@angular/core": "~7.2.0",
"@angular/forms": "~7.2.0",
"@angular/platform-browser": "~7.2.0",
"@angular/platform-browser-dynamic": "~7.2.0",
"@angular/router": "~7.2.0",
"bootstrap": "^4.2.1",
"core-js": "^2.5.4",
"jquery": "^3.3.1",
"rxjs": "~6.3.3",
"tslib": "^1.9.0",
"zone.js": "~0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.12.0",
"@angular/cli": "~7.2.1",
"@angular/compiler-cli": "~7.2.0",
"@angular/language-service": "~7.2.0",
"@types/node": "~8.9.4",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~3.1.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"typescript": "~3.2.2"
}
}
Create Employee class - employee.ts
Before defining the EmployeeListComponent, let’s define an Employee class for working
with employees. create a new file employee.ts inside src/app folder and add the
following code to it -

export class Employee {


id: number;
firstName: string;
lastName: string;
emailId: string;
active: boolean;
}

EmployeeService - employee-list.component.ts

The EmployeeService will be used to get the data from backend by calling spring boot
APIs. Update the employee.service.ts file inside src/app directory with the following
code to it -

import { Injectable } from '@angular/core';


import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
providedIn: 'root'
})
export class EmployeeService {

private baseUrl = '/api/v1/employees';

constructor(private http: HttpClient) { }

getEmployee(id: number): Observable<Object> {


return this.http.get(`${this.baseUrl}/${id}`);
}

createEmployee(employee: Object): Observable<Object> {


return this.http.post(`${this.baseUrl}`, employee);
}

updateEmployee(id: number, value: any): Observable<Object> {


return this.http.put(`${this.baseUrl}/${id}`, value);
}

deleteEmployee(id: number): Observable<any> {


return this.http.delete(`${this.baseUrl}/${id}`, { responseType:
'text' });
}
getEmployeesList(): Observable<any> {
return this.http.get(`${this.baseUrl}`);
}
}

EmployeeListComponent - employee-list.component.ts

Let's update the EmployeeListComponent component which will be used to display a list
of employee, create a new employee, and delete an employee.

Update/remove the content of employee-list.component.ts inside src/app directory


and add the following code to it -

import { Observable } from "rxjs";


import { EmployeeService } from "./../employee.service";
import { Employee } from "./../employee";
import { Component, OnInit } from "@angular/core";

@Component({
selector: "app-employee-list",
templateUrl: "./employee-list.component.html",
styleUrls: ["./employee-list.component.css"]
})
export class EmployeeListComponent implements OnInit {
employees: Observable<Employee[]>;

constructor(private employeeService: EmployeeService) {}

ngOnInit() {
this.reloadData();
}

reloadData() {
this.employees = this.employeeService.getEmployeesList();
}

deleteEmployee(id: number) {
this.employeeService.deleteEmployee(id)
.subscribe(
data => {
console.log(data);
this.reloadData();
},
error => console.log(error));
}
}
Create a template for EmployeeListComponent - employee-list.component.html

Update employee-list.component.html file with the following code to it -

<div class="panel panel-default">


<div class="panel-heading">
<h1>Employees</h1>
</div>
<div class="panel-body">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Email</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let employee of employees | async">
<td>{{employee.firstName}}</td>
<td>{{employee.lastName}}</td>
<td>{{employee.emailId}}</td>
<td><button
(click)="deleteEmployee(employee.id)">Delete</button></td>
</tr>
</tbody>
</table>
</div>
</div>

CreateEmployeeComponent - create-employee.component.ts

CreateEmployeeComponent is used to create and handle a new employee form data. Add
the following code to it -

import { EmployeeService } from './../employee.service';


import { Employee } from './../employee';
import { Component, OnInit } from '@angular/core';

@Component({
selector: 'app-create-employee',
templateUrl: './create-employee.component.html',
styleUrls: ['./create-employee.component.css']
})
export class CreateEmployeeComponent implements OnInit {

employee: Employee = new Employee();


submitted = false;
constructor(private employeeService: EmployeeService) { }

ngOnInit() {
}

newEmployee(): void {
this.submitted = false;
this.employee = new Employee();
}

save() {
this.employeeService.createEmployee(this.employee)
.subscribe(data => console.log(data), error => console.log(error));
this.employee = new Employee();
}

onSubmit() {
this.submitted = true;
this.save();
}
}

Create a template for EmployeeCreateComponent create-


employee.component.html

<h3>Create Employee</h3>
<div [hidden]="submitted" style="width: 400px;">
<form (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="name">First Name</label>
<input type="text" class="form-control" id="firstName"
required [(ngModel)]="employee.firstName" name="firstName">
</div>

<div class="form-group">
<label for="name">Last Name</label>
<input type="text" class="form-control" id="lastName"
required [(ngModel)]="employee.lastName" name="lastName">
</div>

<div class="form-group">
<label for="name">First Name</label>
<input type="text" class="form-control" id="emailId" required
[(ngModel)]="employee.emailId" name="emailId">
</div>

<button type="submit" class="btn btn-success">Submit</button>


</form>
</div>

<div [hidden]="!submitted">
<h4>You submitted successfully!</h4>
</div>

EmployeeDetailsComponent- employee-details.component.ts

This component shows details of employee -

import { Employee } from './../employee';


import { Component, OnInit, Input } from '@angular/core';
import { EmployeeService } from '../employee.service';
import { EmployeeListComponent } from '../employee-list/employee-
list.component';

@Component({
selector: 'app-employee-details',
templateUrl: './employee-details.component.html',
styleUrls: ['./employee-details.component.css']
})
export class EmployeeDetailsComponent implements OnInit {

@Input() employee: Employee;

constructor(private employeeService: EmployeeService, private


listComponent: EmployeeListComponent) { }

ngOnInit() {
}
}

Create a template for EmployeeDetailsComponent employee-


details.component.html

<div *ngIf="employee">
<div>
<label>Name: </label> {{employee.firstName}}
</div>
<div>
<label>Age: </label> {{employee.lastName}}
</div>
<div>
<label>Active: </label> {{employee.emailId}}
</div>
<div>
<label>Active: </label> {{employee.active}}
</div>
<span class="button is-small btn-primary" *ngIf='employee.active'
(click)='updateActive(false)'>Inactive</span>

<span class="button is-small btn-primary" *ngIf='!employee.active'


(click)='updateActive(true)'>Active</span>

<span class="button is-small btn-danger"


(click)='deleteEmployee()'>Delete</span>

<hr/>
</div>

AppRoutingModule - app-routing.module.ts

import { EmployeeDetailsComponent } from './employee-details/employee-


details.component';
import { CreateEmployeeComponent } from './create-employee/create-
employee.component';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { EmployeeListComponent } from './employee-list/employee-
list.component';

const routes: Routes = [


{ path: '', redirectTo: 'employee', pathMatch: 'full' },
{ path: 'employees', component: EmployeeListComponent },
{ path: 'add', component: CreateEmployeeComponent },
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

AppComponent - app/app.component.ts

Defines the logic for the app's root component, named AppComponent. The view
associated with this root component becomes the root of the view hierarchy as you add
components and services to your app.

import { Component } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Angular 7 + Spring Boot 2 + Spring Data JPA + MySQL + CRUD
Tutorial';
}

app/app.component.html

Defines the HTML template associated with the root AppComponent.

<div class="container">
<h2>{{title}}</h2>
<hr>

<nav class="navbar navbar-expand-sm bg-dark navbar-dark">


<!-- Links -->
<ul class="navbar-nav">
<li class="nav-item">
<a routerLink="employees" class="btn btn-primary active"
role="button" routerLinkActive="active">Employees</a>
</li>
<li class="nav-item" style="margin-left: 10px;">
<a routerLink="add" class="btn btn-primary active"
role="button" routerLinkActive="active">Add</a>
</li>
</ul>

</nav>
<router-outlet></router-outlet>
</div>

app/app.module.ts

Defines the root module, named AppModule, that tells Angular how to assemble the
application. Initially declares only the AppComponent. As you add more components to the
app, they must be declared here.

import { BrowserModule } from '@angular/platform-browser';


import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CreateEmployeeComponent } from './create-employee/create-
employee.component';
import { EmployeeDetailsComponent } from './employee-details/employee-
details.component';
import { EmployeeListComponent } from './employee-list/employee-
list.component';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [
AppComponent,
CreateEmployeeComponent,
EmployeeDetailsComponent,
EmployeeListComponent
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

As we already started Spring boot application. Now let's run this Angular 7 application.

Running Angular 7 Application

Let's run the above developed Angular App with a command: ng serve

Output

Open browser for URL http://URLalhost:4200/:


Employee List Page
Add Employee Page

Delete Employee

Note that update employee functionality is not implemented so you can take as exercise
and try to implement yourself (Tip: Add an update button to the employee list page and
based on employee id you can implement update functionality. Note that Rest API is
already created for update employee functionality).

The source code of this article available


on my GitHub repository
at https://github.com/RameshMF/angular7-springboot-crud-tutorial
https://www.fullstackpython.com/object-relational-mappers-orms.html

Object-relational mappers
(ORMs)
An object-relational mapper (ORM) is a code library that automates the
transfer of data stored in relational databases tables into objects that are
more commonly used in application code.

Why are ORMs useful?


ORMs provide a high-level abstraction upon a relational database that
allows a developer to write Python code instead of SQL to create, read,
update and delete data and schemas in their database. Developers can use
the programming language they are comfortable with to work with a
database instead of writing SQL statements or stored procedures.
For example, without an ORM a developer would write the following SQL
statement to retrieve every row in the USERS table where
the zip_code column is 94107:
SELECT * FROM USERS WHERE zip_code=94107;

The equivalent Django ORM query would instead look like the following
Python code:

# obtain everyone in the 94107 zip code and assign to users variable

users = Users.objects.filter(zip_code=94107)

The ability to write Python code instead of SQL can speed up web
application development, especially at the beginning of a project. The
potential development speed boost comes from not having to switch from
Python code into writing declarative paradigm SQL statements. While
some software developers may not mind switching back and forth
between languages, it's typically easier to knock out a prototype or start a
web application using a single programming language.
ORMs also make it theoretically possible to switch an application between
various relational databases. For example, a developer could
use SQLite for local development and MySQL in production. A production
application could be switched from MySQL to PostgreSQL with minimal
code modifications.
In practice however, it's best to use the same database for local
development as is used in production. Otherwise unexpected errors could
hit in production that were not seen in a local development environment.
Also, it's rare that a project would switch from one database in production
to another one unless there was a pressing reason.
While you're learning about ORMs you should also read up
on deployment and check out the application dependencies page.

Do I have to use an ORM for my web


application?
Python ORM libraries are not required for accessing relational databases.
In fact, the low-level access is typically provided by another library called
a database connector, such aspsycopg (for PostgreSQL) or MySQL-
python (for MySQL). Take a look at the table below which shows how
ORMs can work with different web frameworks and connectors and
relational databases.
The above table shows for example that SQLAlchemy can work with
varying web frameworks and database connectors. Developers can also
use ORMs without a web framework, such as when creating a data
analysis tool or a batch script without a user interface.

What are the downsides of using an ORM?


There are numerous downsides of ORMs, including

1. Impedance mismatch
2. Potential for reduced performance
3. Shifting complexity from the database into the application code

Impedance mismatch
The phrase "impedance mismatch" is commonly used in conjunction with
ORMs. Impedance mismatch is a catch-all term for the difficulties that
occur when moving data between relational tables and application
objects. The gist is that the way a developer uses objects is different from
how data is stored and joined in relational tables.
This article on ORM impedance mismatch does a solid job of explaing
what the concept is at a high level and provides diagrams to visualize why
the problem occurs.

Potential for reduced performance


One of the concerns that's associated with any higher-level abstraction or
framework is potential for reduced performance. With ORMs, the
performance hit comes from the translation of application code into a
corresponding SQL statement which may not be tuned properly.
ORMs are also often easy to try but difficult to master. For example, a
beginner using Django might not know about
the select_related() function and how it can improve some queries' foreign
key relationship performance. There are dozens of performance tips and
tricks for every ORM. It's possible that investing time in learning those
quirks may be better spent just learning SQL and how to write stored
procedures.
There's a lot of hand-waving "may or may not" and "potential for" in this
section. In large projects ORMs are good enough for roughly 80-90% of
use cases but in 10-20% of a project's database interactions there can be
major performance improvements by having a knowledgeable database
administrator write tuned SQL statements to replace the ORM's
generated SQL code.

Shifting complexity from the database into the app code


The code for working with an application's data has to live somewhere.
Before ORMs were common, database stored procedures were used to
encapsulate the database logic. With an ORM, the data manipulation code
instead lives within the application's Python codebase. The addition of
data handling logic in the codebase generally isn't an issue with a sound
application design, but it does increase the total amount of Python code
instead of splitting code between the application and the database stored
procedures.
Python ORM Implementations
There are numerous ORM implementations written in Python, including

1. SQLAlchemy
2. Peewee
3. The Django ORM
4. PonyORM
5. SQLObject
6. Tortoise ORM

There are other ORMs, such as Canonical's Storm, but most of them do
not appear to currently be under active development. Learn more about
the major active ORMs below.

Django's ORM
The Django web framework comes with its own built-in object-relational
mapping module, generally referred to as "the Django ORM" or "Django's
ORM".
Django's ORM works well for simple and medium-complexity database
operations. However, there are often complaints that the ORM makes
complex queries much more complicated than writing straight SQL or
using SQLAlchemy.
It is technically possible to drop down to SQL but it ties the queries to a
specific database implementation. The ORM is coupled closely with
Django so replacing the default ORM with SQLAlchemy is currently a
hack workaround. Note though it is possible that swappable ORM
backends will be possible in the future as it is now possible to change
thetemplate engine for rendering output in Django.
Since the majority of Django projects are tied to the default ORM, it is
best to read up on advanced use cases and tools for doing your best work
within the existing framework.

SQLAlchemy ORM
SQLAlchemy is a well-regarded Python ORM because it gets the
abstraction level "just right" and seems to make complex database queries
easier to write than the Django ORM in most cases. There is an entire
page on SQLAlchemy that you should read if you want to learn more
about using the library.

Peewee ORM
Peewee is a Python ORM implementation that is written to be "simpler,
smaller and more hackable" than SQLAlchemy. Read the full Peewee
page for more information on the Python ORM implementation.

Pony
Pony ORM is another Python ORM available as open source, under the
Apache 2.0 license.

SQLObject ORM
SQLObject is an ORM that has been under active open source
development for over 14 years, since before 2003.

Schema migrations
Schema migrations, for example when you need to add a new column to
an existing table in your database, are not technically part of ORMs.
However, since ORMs typically lead to a hands-off approach to the
database (at the developers peril in many cases), libraries to perform
schema migrations often go hand-in-hand with Python ORM usage on
web application projects.
Database schema migrations are a complex topic and deserve their own
page. For now, we'll lump schema migration resources under ORM links
below.

General ORM resources


 This detailed overview of ORMs is a generic description of how ORMs
work and how to use them.
 This example GitHub project implements the same Flask application with
several different ORMs: SQLAlchemy, Peewee, MongoEngine, stdnet and
PonyORM.
 Martin Fowler addresses the ORM hate in an essay about how ORMs are
often misused but that they do provide benefits to developers.
 The Rise and Fall of Object Relational Mapping is a talk on the history of
ORMs that doesn't shy away from some controversy. Overall I found the
critique of conceptual ideas worth the time it took to read the
presentation slides and companion text.
 If you're confused about the difference between a connector, such as
MySQL-python and an ORM like SQLAlchemy, read this StackOverflow
answer on the topic.
 What ORMs have taught me: just learn SQL is another angle in the ORM
versus embedded SQL / stored procedures debate. The author's
conclusion is that while working with ORMs such as SQLAlchemy and
Hibernate (a Java-based ORM) can save time up front there are issues as
a project evolves such as partial objects and schema redundancies. I think
the author makes some valid points that some ORMs can be a shaky
foundation for extremely complicated database-backed applications.
However, I disagree with the overriding conclusion to eschew ORMs in
favor of stored procedures. Stored procedures have their own issues and
there are no perfect solutions, but I personally prefer using an ORM at the
start of almost every project even if it later needs to be replaced with
direct SQL queries.
 The Vietnam of Computer Science provides the perspective from Ted
Neward, the originator of the phrase "Object/relational mapping is the
Vietnam of Computer Science" that he first spoke about in 2004. The gist
of the argument against ORMs is captured in Ted's quote that an ORM
"represents a quagmire which starts well, gets more complicated as time
passes, and before long entraps its users in a commitment that has no
clear demarcation point, no clear win conditions, and no clear exit
strategy." There are follow up posts on Coding Horror and another one
from Ted entitled thoughts on Vietnam commentary.
 Turning the Tables: How to Get Along with your Object-Relational
Mapper coins the funny but insightful phrase "database denial" to
describe how some ORMs provide a usage model that can cause more
issues than they solve over straight SQL queries. The post then goes into
much more detail about the problems that can arise and how to mitigate
or avoid them.
SQLAlchemy and Peewee resources
A comprehensive list of SQLAlchemy and Peewee ORM resources can be
found on their respective pages.

Django ORM links


A curated list of resources can be found on the dedicated Django ORM
resources page.

Pony ORM resources


All Pony ORM resources are listed on the dedicated Pony ORM page.

SQLObject resources
SQLObject has been around for a long time as an open source project but
unfortunately there are not that many tutorials for it. The following talks
and posts will get you started. If you take an interest in the project and
write additional resources, file an issue ticket so we can get them added to
this list.
 This post on Object-Relational Mapping with SQLObject explains the
concept behind ORMs and shows the Python code for how they can be
used.
 Ian Bicking presented on SQLObject back in 2004 with a talk
on SQLObject and Database Programming in Python.
 Connecting databases to Python with SQLObject is an older post but still
relevant with getting started basics.

PonyORM
https://docs.ponyorm.org/queries.html

Queries
Pony provides a very convenient way to query the database using the generator
expression syntax. Pony allows programmers to work with objects which are stored in
a database as if they were stored in memory, using native Python syntax. It makes
development much easier.

For writing queries you can use Python generator expressions or lambdas.

Using Python generator expressions


Pony allows to use generator expressions as a very natural way of writing database
queries. Pony provides select() function which accepts Python generator, translates
it to SQL and returns objects from the database. The process of the translation is
described in this StackOverflow question.

Here is an example of a query:

query = select(c for c in Customer

if sum(o.total_price for o in c.orders) > 1000)

or, with attribute lifting:

query = select(c for c in Customer

if sum(c.orders.total_price) > 1000)

You can apply filter() function to query

query2 = query.filter(lambda person: person.age > 18)

Also you can make new query based on another query:

query3 = select(customer.name for customer in query2

if customer.country == 'Canada')

select() function returns an instance of a Query class, and you can then call
the Query object methods for getting the result, for example:

customer_name = query3.first()
From query you can return entity, attribute or tuple of arbitrary expressions

select((c, sum(c.orders.total_price))

for c in Customer if sum(c.orders.total_price) > 1000)

Using lambda functions


Instead of using a generator, you can write queries using the lambda function:

Customer.select(lambda c: sum(c.orders.price) > 1000)

From the point of the translation the query into SQL there is no difference, if you use
a generator or a lambda. The only difference is that using the lambda you can only
return entity instances - there is no way to return a list of specific entity attributes or a
list of tuples.

Pony ORM functions used to query the


database
See the Queries and functions part of the API Reference for details.

Pony query examples


For demonstrating Pony queries let’s use the example from the Pony ORM
distribution. You can try these queries yourself in the interactive mode and see the
generated SQL. For this purpose import the example module this way:

>>> from pony.orm.examples.estore import *

This module offers a simplified data model of a eCommerce online store. Here is
the ER Diagram of the data model:
Here are the entity definitions:

from decimal import Decimal

from datetime import datetime

from pony.converting import str2datetime

from pony.orm import *

db = Database()

class Customer(db.Entity):
email = Required(str, unique=True)

password = Required(str)

name = Required(str)

country = Required(str)

address = Required(str)

cart_items = Set('CartItem')

orders = Set('Order')

class Product(db.Entity):

id = PrimaryKey(int, auto=True)

name = Required(str)

categories = Set('Category')

description = Optional(str)

picture = Optional(buffer)

price = Required(Decimal)

quantity = Required(int)

cart_items = Set('CartItem')

order_items = Set('OrderItem')

class CartItem(db.Entity):

quantity = Required(int)

customer = Required(Customer)

product = Required(Product)
class OrderItem(db.Entity):

quantity = Required(int)

price = Required(Decimal)

order = Required('Order')

product = Required(Product)

PrimaryKey(order, product)

class Order(db.Entity):

id = PrimaryKey(int, auto=True)

state = Required(str)

date_created = Required(datetime)

date_shipped = Optional(datetime)

date_delivered = Optional(datetime)

total_price = Required(Decimal)

customer = Required(Customer)

items = Set(OrderItem)

class Category(db.Entity):

name = Required(str, unique=True)

products = Set(Product)

set_sql_debug(True)

db.bind('sqlite', 'estore.sqlite', create_db=True)

db.generate_mapping(create_tables=True)
When you import this example, it will create the SQLite database in the file
‘estore.sqlite’ and fill it with some test data. Below you can see some query examples:

# All USA customers

Customer.select(lambda c: c.country == 'USA')

# The number of customers for each country

select((c.country, count(c)) for c in Customer)

# Max product price

max(p.price for p in Product)

# Max SSD price

max(p.price for p in Product

for cat in p.categories if cat.name == 'Solid State


Drives')

# Three most expensive products

Product.select().order_by(desc(Product.price))[:3]

# Out of stock products

Product.select(lambda p: p.quantity == 0)

# Most popular product

Product.select().order_by(lambda p:
desc(sum(p.order_items.quantity))).first()
# Products that have never been ordered

Product.select(lambda p: not p.order_items)

# Customers who made several orders

Customer.select(lambda c: count(c.orders) > 1)

# Three most valuable customers

Customer.select().order_by(lambda c:
desc(sum(c.orders.total_price)))[:3]

# Customers whose orders were shipped

Customer.select(lambda c: SHIPPED in c.orders.state)

# Customers with no orders

Customer.select(lambda c: not c.orders)

# The same query with the LEFT JOIN instead of NOT EXISTS

left_join(c for c in Customer for o in c.orders if o is None)

# Customers which ordered several different tablets

select(c for c in Customer

for p in c.orders.items.product

if 'Tablets' in p.categories.name and count(p) > 1)


You can find more queries in the pony.orm.examples.estore module.

Query object methods


See the Query result part of the API Reference for details.

Using date and time in queries


You can perform arithmetic operations with the datetime and timedelta in queries.

If the expression can be calculated in Python, Pony will pass the result of the
calculation as a parameter into the query:

select(o for o in Order if o.date_created >= datetime.now() -


timedelta(days=3))[:]

SELECT "o"."id", "o"."state", "o"."date_created", "o"."date_shipped",

"o"."date_delivered", "o"."total_price", "o"."customer"

FROM "Order" "o"

WHERE "o"."date_created" >= ?

If the operation needs to be performed with the attribute, we cannot calculate it


beforehand. That is why such expression will be translated into SQL:

select(o for o in Order if o.date_created + timedelta(days=3) >=


datetime.now())[:]

SELECT "o"."id", "o"."state", "o"."date_created", "o"."date_shipped",

"o"."date_delivered", "o"."total_price", "o"."customer"

FROM "Order" "o"

WHERE datetime("o"."date_created", '+3 days') >= ?

The SQL generated by Pony will vary depending on the database. Above is the
example for SQLite. Here is the same query, translated into PostgreSQL:
SELECT "o"."id", "o"."state", "o"."date_created", "o"."date_shipped",

"o"."date_delivered", "o"."total_price", "o"."customer"

FROM "order" "o"

WHERE ("o"."date_created" + INTERVAL '72:0:0' DAY TO SECOND) >=


%(p1)s

If you need to use a SQL function, you can use the raw_sql() function in order to
include this SQL fragment:

select(m for m in DBVoteMessage if m.date >= raw_sql("NOW() - '1


minute'::INTERVAL"))

With Pony you can use the datetime attributes, such as month, hour, etc. Depending
on the database, it will be translated into different SQL, which extracts the value for
this attribute. In this example we get the monthattribute:

select(o for o in Order if o.date_created.month == 12)

Here is the result of the translation for SQLite:

SELECT "o"."id", "o"."state", "o"."date_created", "o"."date_shipped",

"o"."date_delivered", "o"."total_price", "o"."customer"

FROM "Order" "o"

WHERE cast(substr("o"."date_created", 6, 2) as integer) = 12

And for PostgreSQL:

SELECT "o"."id", "o"."state", "o"."date_created", "o"."date_shipped",

"o"."date_delivered", "o"."total_price", "o"."customer"

FROM "order" "o"

WHERE EXTRACT(MONTH FROM "o"."date_created") = 12


Automatic DISTINCT
Pony tries to avoid duplicates in a query result by automatically adding
the DISTINCT SQL keyword where it is necessary, because useful queries with
duplicates are very rare. When someone wants to retrieve objects with a specific
criteria, they typically don’t expect that the same object will be returned more than
once. Also, avoiding duplicates makes the query result more predictable: you don’t
need to filter duplicates out of a query result.

Pony adds the DISCTINCT keyword only when there could be potential duplicates.
Let’s consider a couple of examples.

1. Retrieving objects with a criteria:

Person.select(lambda p: p.age > 20 and p.name == 'John')

In this example, the query doesn’t return duplicates, because the result contains the
primary key column of a Person. Since duplicates are not possible here, there is no
need in the DISTINCT keyword, and Pony doesn’t add it:

SELECT "p"."id", "p"."name", "p"."age"

FROM "Person" "p"

WHERE "p"."age" > 20

AND "p"."name" = 'John'

2. Retrieving object attributes:

select(p.name for p in Person)

The result of this query returns not objects, but its attribute. This query result can
contain duplicates, so Pony will add DISTINCT to this query:

SELECT DISTINCT "p"."name"

FROM "Person" "p"


The result of a such query typically used for a dropdown list, where duplicates are not
expected. It is not easy to come up with a real use-case when you want to have
duplicates here.

If you need to count persons with the same name, you’d better use an aggregate query:

select((p.name, count(p)) for p in Person)

But if it is absolutely necessary to get all person’s names, including duplicates, you can
do so by using the Query.without_distinct() method:

select(p.name for p in Person).without_distinct()

3. Retrieving objects using joins:

select(p for p in Person for c in p.cars if c.make in ("Toyota",


"Honda"))

This query can contain duplicates, so Pony eliminates them using DISTINCT:

SELECT DISTINCT "p"."id", "p"."name", "p"."age"

FROM "Person" "p", "Car" "c"

WHERE "c"."make" IN ('Toyota', 'Honda')

AND "p"."id" = "c"."owner"

Without using DISTINCT the duplicates are possible, because the query uses two
tables (Person and Car), but only one table is used in the SELECT section. The query
above returns only persons (and not their cars), and therefore it is typically not
desirable to get the same person in the result more than once. We believe that
without duplicates the result looks more intuitive.

But if for some reason you don’t need to exclude duplicates, you always can
add without_distinct() to the query:

select(p for p in Person for c in p.cars

if c.make in ("Toyota", "Honda")).without_distinct()


The user probably would like to see the Person objects duplicates if the query result
contains cars owned by each person. In this case the Pony query would be different:

select((p, c) for p in Person for c in p.cars if c.make in ("Toyota",


"Honda"))

And in this case Pony will not add the DISTINCT keyword to SQL query.

To summarize:

1. The principle “all queries do not return duplicates by default” is easy to


understand and doesn’t lead to surprises.
2. Such behavior is what most users want in most cases.
3. Pony doesn’t add DISTINCT when a query is not supposed to have duplicates.
4. The query method without_distinct() can be used for forcing Pony do not
eliminate duplicates.

Functions which can be used inside a query


Here is the list of functions that can be used inside a generator query:

 avg()
 abs()
 exists()
 len()
 max()
 min()
 count()
 concat()
 group_concat()
 random()
 raw_sql()
 select()
 sum()
 getattr()

Examples:

select(avg(c.orders.total_price) for c in Customer)


SELECT AVG("order-1"."total_price")

FROM "Customer" "c"

LEFT JOIN "Order" "order-1"

ON "c"."id" = "order-1"."customer"

select(o for o in Order if o.customer in

select(c for c in Customer if c.name.startswith('A')))[:]

SELECT "o"."id", "o"."state", "o"."date_created", "o"."date_shipped",

"o"."date_delivered", "o"."total_price", "o"."customer"

FROM "Order" "o"

WHERE "o"."customer" IN (

SELECT "c"."id"

FROM "Customer" "c"

WHERE "c"."name" LIKE 'A%'

Using getattr()
getattr() is a built-in Python function, that can be used for getting the attribute value.

Example:

attr_name = 'name'

param_value = 'John'

select(c for c in Customer if getattr(c, attr_name) == param_value)


Using raw SQL
Pony allows using raw SQL in your queries. There are two options on how you can
use raw SQL:

1. Use the raw_sql() function in order to write only a part of a generator or


lambda query using raw SQL.
2. Write a complete SQL query using
the Entity.select_by_sql() or Entity.get_by_sql() methods.

Using the raw_sql() function


Let’s explore examples of using the raw_sql() function. Here is the schema and
initial data that we’ll use for our examples:

from datetime import date

from pony.orm import *

db = Database('sqlite', ':memory:')

class Person(db.Entity):

id = PrimaryKey(int)

name = Required(str)

age = Required(int)

dob = Required(date)

db.generate_mapping(create_tables=True)

with db_session:
Person(id=1, name='John', age=30, dob=date(1986, 1, 1))

Person(id=2, name='Mike', age=32, dob=date(1984, 5, 20))

Person(id=3, name='Mary', age=20, dob=date(1996, 2, 15))

The raw_sql() result can be treated as a logical expression:

select(p for p in Person if raw_sql('abs("p"."age") > 25'))

The raw_sql() result can be used for a comparison:

q = Person.select(lambda x: raw_sql('abs("x"."age")') > 25)

print(q.get_sql())

SELECT "x"."id", "x"."name", "x"."age", "x"."dob"

FROM "Person" "x"

WHERE abs("x"."age") > 25

Also, in the example above we use raw_sql() in a lambda query and print out the
resulting SQL. As you can see the raw SQL part becomes a part of the whole query.

The raw_sql() can accept $parameters:

x = 25

select(p for p in Person if raw_sql('abs("p"."age") > $x'))

You can change the content of the raw_sql() function dynamically and still use
parameters inside:

x = 1

s = 'p.id > $x'

select(p for p in Person if raw_sql(s))

Another way of using dynamic raw SQL content:


x = 1

cond = raw_sql('p.id > $x')

select(p for p in Person if cond)

You can use various types inside the raw SQL query:

x = date(1990, 1, 1)

select(p for p in Person if raw_sql('p.dob < $x'))

Parameters inside the raw SQL part can be combined:

x = 10

y = 15

select(p for p in Person if raw_sql('p.age > $(x + y)'))

You can even call Python functions inside:

select(p for p in Person if raw_sql('p.dob < $date.today()'))

The raw_sql() function can be used not only in the condition part, but also in the
part which returns the result of the query:

names = select(raw_sql('UPPER(p.name)') for p in Person)[:]

print(names)

['JOHN', 'MIKE', 'MARY']

But when you return data using the raw_sql() function, you might need to specify
the type of the result, because Pony has no idea on what the result type is:

dates = select(raw_sql('(p.dob)') for p in Person)[:]

print(dates)

['1985-01-01', '1983-05-20', '1995-02-15']


If you want to get the result as a list of dates, you need to specify the result_type:

dates = select(raw_sql('(p.dob)', result_type=date) for p in


Person)[:]

print(dates)

[datetime.date(1986, 1, 1), datetime.date(1984, 5, 20),


datetime.date(1996, 2, 15)]

The raw_sql() function can be used in a Query.filter() too:

x = 25

select(p for p in Person).filter(lambda p: p.age > raw_sql('$x'))

It can be used inside the Query.filter() without lambda. In this case you have to
use the first letter of entity name in lower case as the alias:

x = 25

Person.select().filter(raw_sql('p.age > $x'))

You can use several raw_sql() expressions in a single query:

x = '123'

y = 'John'

Person.select(lambda p: raw_sql("UPPER(p.name) || $x")

== raw_sql("UPPER($y || '123')"))

The same parameter names can be used several times with different types and values:

x = 10

y = 31

q = select(p for p in Person if p.age > x and p.age < raw_sql('$y'))

x = date(1980, 1, 1)
y = 'j'

q = q.filter(lambda p: p.dob > x and


p.name.startswith(raw_sql('UPPER($y)')))

persons = q[:]

You can use raw_sql() in Query.order_by() section:

x = 9

Person.select().order_by(lambda p: raw_sql('SUBSTR(p.dob, $x)'))

Or without lambda, if you use the same alias, that you used in previous filters. In this
case we use the default alias - the first letter of the entity name:

x = 9

Person.select().order_by(raw_sql('SUBSTR(p.dob, $x)'))

Using the select_by_sql() and get_by_sql() methods


Although Pony can translate almost any condition written in Python to SQL,
sometimes the need arises to use raw SQL, for example - in order to call a stored
procedure or to use a dialect feature of a specific database system. In this case, Pony
allows the user to write a query in a raw SQL, by placing it inside the
function Entity.select_by_sql() or Entity.get_by_sql() as a string:

Product.select_by_sql("SELECT * FROM Products")

Unlike the method Entity.select(), the method Entity.select_by_sql() does


not return the Queryobject, but a list of entity instances.

Parameters are passed using the following syntax: “$name_variable” or “$(expression


in Python)”. For example:

x = 1000

y = 500

Product.select_by_sql("SELECT * FROM Product WHERE price > $x OR


price = $(y * 2)")
When Pony encounters a parameter within a raw SQL query, it gets the variable value
from the current frame (from globals and locals) or from the dictionaries which can
be passed as parameters:

Product.select_by_sql("SELECT * FROM Product WHERE price > $x OR


price = $(y * 2)",

globals={'x': 100}, locals={'y': 200})

Variables and more complex expressions specified after the $ sign, will be
automatically calculated and transferred into the query as parameters, which makes
SQL-injection impossible. Pony automatically replaces $x in the query string with “?”,
“%S” or with other paramstyle, used in your database.

If you need to use the $ sign in the query (for example, in the name of a system table),
you have to write two $signs in succession: $$.

https://www.fullstackpython.com/pony-orm.html

Curso de Kotlin: Tipos de Variables

https://fabrica-software.blogspot.com/2018/11/curso-kotlin-tipos-de-variables.html

Tabla de Contenido:
1. Introducción
1. Sintaxis para definir variables
2. Números
1. Definición de valores numéricos
2. Uso de _ en valores numéricos (Desde versión
1.1)
3. Representando valores numéricos que pueden
ser nulos
3. Caracteres (Char)
4. Cadenas de Texto (String)
1. Tipos de String y Definición de Valores
2. Expresiones en Strings (String Templates)
5. Variables Lógicas (Boolean)
6. Arrays
7. Conversión de Tipos
8. Fuentes y Enlaces de interés
Introducción:
En Kotlin, todo es un objeto, en el sentido en que podemos llamar
a funciones y propiedades en cada variable. Algunos de los tipos
pueden tener una representación interna especial – por ejemplo,
números, caracteres y lógicos (boolean), se pueden representar
como valores primitivos en el modo de ejecución (runtime) – Pero,
para los mortales parecen como clases ordinarias. En esta sección
vamos a describir los tipos básicos usados en Kotlin: núm eros,
caracteres, cadenas de texto, variables lógicos (booleans) y
arrays.

A p re n di en do lo s t ipo s bá si co s d e va riab l es en ko tl in
. . . - T ui te alo

Sintaxis para definir variables:


val Identificador[: TipoVariable[?]] = Valor ;
var Identificador[[: TipoVariable[?]] [= Valor]];
Vamos a explicar estas sentencias:
val, son variables de solo lectura, como constantes en Java, es
decir, no se puede modificar su valor.
var, son variables mutables, es decir, que se puede modificar su
valor.
Identificador, es el nombre que queramos asignar a la variable.
: TipoVariable, opcional, es el tipo de variable que estamos
asignando, numero, carácter, boleano, etc.
?, opcional, sirve para hacer referencia de números u otro tipo de
variable que pueden contener nulos.
= Valor, asignación de valor a la hora de declarar una variable (
opcional en los var ).

l as va ri ab le s va l so n de so lo le c tu ra , la s va ri ab le s v ar
so n d e l ec tu ra y es cr itu ra . . . - Tu it ea lo

Números:
Kotlin maneja los números de una forma muy parecida a Java, pero
no exactamente igual. Por ejemplo, no existen conversiones
implícitas de números, y los literales son ligeramente diferentes
en algunos casos.

Estos son los tipos de variables que representan a números en


Kotlin (similares a Java):
Tipo Tamaño en Bits

Double 64

Float 32

Long 64

Int 32

Short 16

Byte 8

Ko tl i n ma ne ja lo s núm e ro s d e u na f o rma m uy pa re ci da
a Jav a , p e ro no e x ac tam e nt e i gua l. . . - T ui tea lo

Definición de valores numéricos:


Existen diferentes tipos de literales para def inir valores
numéricos:
 Decimales: 1234
o Long, se etiquetan con una L mayuscula: 123L
 Hexadecimales: 0x0F
 Binarios: 0b00001011
NOTA: Para los octales, no están soportados los literales de este
tipo.
 Doubles, por defecto: 123.5, 123.5e10 ( 123.5 elevado a 10
).
 Floats, se etiquetan con una f minúscula o F
mayúscula: 123.5f o 123.5F
Uso de _ en valores numéricos (Desde versión 1.1):
Se pueden utilizar las barras bajas _ para hacer los valores
numéricos más legibles y comprensibles:
val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L // (Etiquetado como v
ariable Long).
val socialSecurityNumber = 999_99_9999L // (Etiquetado como varia
ble Long).
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

Se pueden utilizar las barras bajas _ para


hacer los valores numéricos más legibles y
comprensibles... - Tuitealo
Representando valores numéricos que pueden ser nulos:
En Kotlin, se pueden representar valores numéricos que pueden
hacer referencia a valores nulos. Para ello, se utiliza el signo
interrogación ?. En estos casos, se dice que los números están
encapsulados (boxed). Ej: val a: Int? = 1

Caracteres (Char):
Los caracteres se representan como Char. No pueden ser tratados
directamente como números. La definición del valor de un
carácter va entrecomillado, con comilla simple: ‘H’.

Se pueden utilizar caracteres especiales utilizando delante


una contra barra \. Los caracteres especiales que están
soportados actualmente son:

\t, \b, \n, \r, \', \", \\ y \$

Para utilizar otro carácter especial no incluido, se puede utilizar


el código Unicode con la contra barra. Por ejemplo: '\u0026' que
sería el signo ampersan &.
Cadenas de Texto (String):
Las cadenas de texto se representan como String, a partir de ahora
nos referiremos a las cadenas de texto como Strings.

Los strings son inmutables, es decir, que su valor no se puede


cambiar. Los elementos de un string son caracteres y se puede
acceder a ellos a través del operador de indexado: str[i]. Un string
se puede iterar con un bucle for, que veremos más adelante.

Se pueden concatenar strings con el operador +. También funciona


a la hora de concatenar strings con valores de otros tipos
(Números, Caracteres, etc). OJO, esto funciona, siempre y cuando,
el primer elemento de la concatenación sea un string, si no, al
ejecutar la aplicación, recibiremos una excepción (más adelante
veremos el tratamiento de excepciones).

val s = "abc" + 1
println(s + "def")

No obstante, en la mayoría de los casos, usar expresiones en


string o strings crudas (raw strings) es preferible a la
concatenación de string.

Los Strings son inmutables, no pueden


cambiar su contenido y están compuestos por
caracteres... - Tuitealo
Tipos de String y Definición de Valores:
Kotlin tiene dos opciones a la hora de definir valores a los strings:
 Escaped strings: Son cadenas en las que debe contener
caracteres de escape en ella. Son muy parecidas a los strings de
java.
val s = "Hello, world!\n"

El carácter de escape es la contra barra, como vimos en los tipos


Char, para definir caracteres especiales, en este caso \n, new line.
 Raw Strings: Son cadenas que pueden contener líneas de
texto y otros caracteres, sin caracteres de escape. Estos strings
se definen con triple doble comillas: """.
val text = """
for (c in "foo")
print(c)
"""

Expresiones en Strings (String Templates):


En Kotlin, los strings pueden contener “template expressions”
que son piezas de código que son evaluadas y cuyo resultado es
concatenado en el string. Con mis propias palabras: Son modos
de imprimir dentro de una cadena de texto valores de variables,
operaciones matemáticas o llamadas a funciones dentro de ellas.

Una “template expression” empieza con el signo del


dólar $ seguida de:
 El nombre de una variable. Ej: “i = $i”
 Una operación matemática o la llamada a un método entre
llaves ${expresión}.
val i = 10
println("i = $i") // prints "i = 10"

val s = "abc"
println("$s.length is ${s.length}") // prints "abc.length is 3"

Estas expresiones se pueden usar en los dos tipos de strings


mencionados anteriormente (Escaped Strings y Raw Strings). Si
se necesita representar el signo dólar $ en una raw string, se debe
usar el siguiente literal (ya que no se pueden usar caracteres de
escape):

val price = """


${'$'}9.99
"""

L a m ejo r f o rma d e co n cat e nar S tr i ng s es co n


e x pr es io n es q ue co m ie n za n co n e l si gno $ . .. - Tu it ea lo

Variables Lógicas (Boolean):


Las variables de tipo lógico se representan como Boolean y solo
tienen dos valores posibles, verdadero y falso ( true or false).

Las variables booleanas también se pueden encapsular


(boxed) si se necesita una referencia de valor nulo.

Operadores que se pueden utilizar con variables booleanas:


|| - Des-conjunción, se puede traducir como: O. X es verdadero
“O” Y es verdadero.
&& - Conjunción, se puede traducir como: Y. X es verdadero “Y”
Z es verdadero.
! - Negación.

En próximos artículos hablaremos de los operadores más en


detalle.

Arrays:
Los arrays en Kotlin se representan como la clase Array, que tiene
funciones get y set (que tornan entre corchetes [] por convención)
y la propiedad size entre otras funciones que son de utilidad. Los
arrays en Kotlin no pueden variar de tipo (de un array de
enteros a Otro cualquiera).

class Array<T> private constructor() {


val size: Int
operator fun get(index: Int): T
operator fun set(index: Int, value: T): Unit

operator fun iterator(): Iterator<T>


// ...
}

Para crear un array, podemos usar la función arrayOf() y pasando


los valores de los items, entonces con arrayOf(1, 2, 3) creamos el
array siguiente: [1, 2, 3]. También se puede crear un array con
valores nulos con la función arrayOfNulls() indicando el tamaño
del array, que será rellenado con valores nulos.

Otra opción es usar el constructor Array, que obtiene el tamaño


del array. Esta función puede devolver el valor inicial de cada
elemento dando el indice del mismo.
// Creates an Array<String> with values ["0", "1", "4", "9", "16"
]
val asc = Array(5, { i -> (i * i).toString() })
asc.forEach { println(it) }

Kotlin también tiene clases especiales para representar arrays de


tipos primitivos como: ByteArray, ShortArray, IntArray, etc.

L o s a r ray s no pu e de n c amb iar de t ipo .. . - Tu it ea lo

Conversión de Tipos:
Como apuntábamos antes, en Kotlin no existe la posibilidad de
convertir tipos de forma implícita. También comentamos que en
Kotlin todo son objetos y las variables también tienen métodos.
Entre estos métodos, tenemos los necesarios para poder hacer
conversiones de tipos:
 toByte(): Byte
 toShort(): Short
 toInt(): Int
 toLong(): Long
 toFloat(): Float
 toDouble(): Double
 toChar(): Char

Fuentes y Enlaces de interés:


Documentación oficial de Kotlin con códigos de ejemplo
ejecutables:
https://kotlinlang.org/docs/reference/basic-types.html

Testea tu código Kotlin en la pagina oficial Online:


https://play.kotlinlang.org/

Referencia completa de Kotlin (PDF de la página oficial):


https://kotlinlang.org/docs/kotlin-docs.pdf

Tabla de caracteres unicode en castellano:


https://unicode-table.com/es/

Sintaxis Kotlin: Declaración de Variables

https://fabrica-software.blogspot.com/2018/12/sintaxis-kotlin-declaracion-de-variables.html

Aprende la sintaxis básica de


la declaración de variables en kotlin.

val Identificador[: TipoVariable[?]] = Valor;


var Identificador[[: TipoVariable[?]] [= Valor]];

Vamos a explicar estas sentencias:


val, son variables de solo lectura, como constantes en Java, es
decir, no se puede modificar su valor.
var, son variables mutables, es decir, que se puede modificar su
valor.
Identificador, es el nombre que queramos asignar a la variable.
: TipoVariable, opcional, es el tipo de variable que estamos
asignando, numero, carácter, boleano, etc.
?, opcional, sirve para hacer referencia de números u otro tipo de
variable que pueden contener nulos.
= Valor, asignación de valor a la hora de declarar una variable (
opcional en los var ).

https://fabrica-software.blogspot.com/2018/12/curso-kotlin-operadores.html

Curso Kotlin: Operadores

Introducción a los Operadores:

En este articulo sobre los operadores en kotlin, vamos a ver los


diferentes tipos de operadores existentes: operadores de
asignación, operadores aritméticos, operadores
lógicos, operadores de comparación, operadores de igualdad
y desigualdad, etc. Antes de continuar, ¿Que es un
operador? Un operador es un símbolo o función que determina
el tipo de operación o relaciónentre, el o los, operandos de
una expresión.

Tabla de Contenido:
1. Introducción a los Operadores
2. Operadores de Asignación
1. Ejemplos Operadores de Asignación
3. Operadores Aritméticos
1. Ejemplos Operadores Aritméticos
4. Operadores Lógicos
1. Ejemplos Operadores Lógicos
5. Operadores de Comparación
1. Ejemplos Operadores de Comparación
6. Operadores a nivel de Bit
1. Ejemplos Operadores a nivel de Bit
7. Operadores de Igualdad y Desigualdad
8. Conclusión
9. Referencias

Operadores de Asignación:
Los operadores de asignación sirven para, como su nombre
indica, asignar valores a variables. Entre estos operadores,
también encontramos los operadores de
aumento o decremento de valores.

Operador Descripción

Operador de Asignación. También se usa para asignar valores por d


=
funciones.

Operador de asignación con incremento o suma de operandos. Tam


+=
concatenar strings, aunque como vimos, lo mejor es usar plantillas

-= Operador de asignación con decremento o resta de operandos.

*= Operador de asignación cuyo resultado es la multiplicación de oper

/= Operador de asignación cuyo resultado es la división de operandos.

%= Operador de asignación cuyo resultado es el resto de la división de

Ejemplos Operadores de Asignación:

Para ver como funcionan los operadores de Asignación, vamos


a poner un par de ejemplos:

Operador de asignación: +=
// Ejemplos de asignación +=
var x: Int = 2
x += x // es igual a: x = x + x. Resultado: 4.

var x: Int = 1
x += x // es igual a: x = x + x. Resultado: 2.

// Ejemplos de asignación += con Strings.


var x: String = "Hola"
x += " Mundo" // es igual a: x = x + " Mundo". Resultado: "Hola M
undo".

Operador de asignación: -=
// Ejemplos de asignación -=
var x: Int = 1
x -= x // es igual a: x = x - x. Resultado: 0.

var x: Int = 4
x -= 2 // es igual a: x = x - 2. Resultado: 2.

Operador de asignación: *=
// Ejemplos de asignación *=
var x: Int = 1
x *= 5 // es igual a: x = x * 5. Resultado: 5.

var x: Int = 4
x *= x // es igual a: x = x * x, o, x elevado a 2. Resultado: 16.

Operador de asignación: /=
// Ejemplos de asignación /=
var x: Int = 4
x /= 2 // es igual a: x = x / 2. Resultado: 2.

var x: Int = 16
x /= x // es igual a: x = x / x. Resultado: 1.

Operador de asignación: %=
// Ejemplos de asignación %=
var x: Int = 4
x %= 2 // es igual a: x = x % 2. Resultado: 0.

var x: Float = 4.5F


x %= 2 // es igual a: x = x % 2. Resultado: 0.5

Operadores Aritméticos:
Los operadores aritméticos sirven para realizar operaciones
matemáticas entre operandos numéricos. El operador
aritmético +, como vimos en el el articulo sobre tipos de
variables, cadenas de texto (string), también sirve para
concatenar strings, aunque es preferible utilizar las plantillas de
strings.

Operador Descripción

Operador Aritmético + Suma de operandos o signo positivo. Tambi


+
concatenación en Strings.

- Operador Aritmético - Resta de operandos o signo negativo.

Operador Aritmético * Multiplicación de operandos. También se us


*
parametro vararg.

/ Operador Aritmético / División de operandos.

% Operador Aritmético % Resto de la división de operandos.

Ejemplos Operadores Aritméticos:

Vamos a incluir unos ejemplos de uso:

Operador aritmético: +
// Ejemplo de suma entre 2 enteros.
var x: Int = 4
var y: Int = 6
var suma: Int
suma = x + y // Resultado: 10.

// Ejemplo de concatenación de dos String:


var cadena1: String = "Hola"
var cadena2: String = "Mundo"
var concat: String
concat = cadena1 + " " + cadena2 // Resultado: "Hola Mundo".

Operador aritmético: -
// Ejemplo de resta entre 2 enteros.
var x: Int = 4
var y: Int = 6
var resta: Int
resta = x - y // Resultado: -2.

Operador aritmético: *
// Ejemplo de multiplicación entre 2 enteros.
var x: Int = 4
var y: Int = 6
var multiplicacion: Int
multiplicacion = x * y // Resultado: 24.

Operador aritmético: /
// Ejemplo de división entre 2 enteros.
var x: Int = 4
var y: Int = 6
var division: Int
division = x / y // Resultado: 0. La variable division es entero,
por eso no recoge los decimales.

// Ejemplo de división entre 2 Floats.


var x: Float = 4F
var y: Float = 6F
var division: Float
division = x / y // Resultado: 0.6666667 Ahora si vemos el result
ado con los decimales.

Operador aritmético: %
// Ejemplo de resto de la división entre 2 enteros.
var x: Int = 4
var y: Int = 6
var resto: Int
resto = x % y // Resultado: 4.

Operadores Lógicos:
Existen 3 operadores lógicos en Kotlin:
 Operador de conjunción && , traducido como y. También
se puede usar la palabra clave and .
 Operador de disyunción || , traducido como o. También se
puede usar la palabra clave or .
 Operador de negación ! , traducido como no.
Ejemplos Operadores Lógicos:
Veamos unos ejemplos completos, con todas las posibilidades de
uso, de estos operadores lógicos:

Operador de Conjunción: &&


// Ejemplo de conjunciones &&:
var x: Boolean = false
var y: Boolean = false
var conjuncion: Boolean
conjuncion = x && y // Resultado: false

var x: Boolean = false


var y: Boolean = true
var conjuncion: Boolean
conjuncion = x && y // Resultado: false

var x: Boolean = true


var y: Boolean = false
var conjuncion: Boolean
conjuncion = x && y // Resultado: fa lse

var x: Boolean = true


var y: Boolean = true
var conjuncion: Boolean
conjuncion = x && y // Resultado: true

Como podemos observar, en la conjunción, se deben cumplir (


Deben ser verdaderos ) los dos operandos.

Operador de Disyunción: ||
// Ejemplo de disyunciones ||:
var x: Boolean = false
var y: Boolean = false
var disyuncion: Boolean
disyuncion = x || y // Resultado: false
var x: Boolean = false
var y: Boolean = true
var disyuncion: Boolean
disyuncion = x || y // Resultado: true

var x: Boolean = true


var y: Boolean = false
var disyuncion: Boolean
disyuncion = x || y // Resultado: true

var x: Boolean = true


var y: Boolean = true
var disyuncion: Boolean
disyuncion = x || y // Resultado: true

En las disyunciones, a diferencia de las conjunciones, con que se


cumpla un operando nos resulta verdadero.

Operador negación: !
// Ejemplo de negación !:
var x: Boolean = false
var negacion: Boolean
negacion = !x // Resultado: true

var x: Boolean = true


var negacion: Boolean
negacion = !x // Resultado: false

Con la negación, invertimos el valor booleano de una variable,


como se puede ver en los ejemplos.

Operadores de Comparación:

Los operadores de comparación nos sirven para comparar 2


operandos. En kotlin, todas las comparaciones se traducen en
llamadas al método compareTo() , en los tipos no primitivos, el
cual retorna un entero Int .

Expresión Traducido a Descripci

A<B A.compareTo(B) < 0 Operador de comparación A menor


Expresión Traducido a Descripci

A>B A.compareTo(B) > 0 Operador de comparación A mayor

A <= B A.compareTo(B) <= 0 Operador de comparación A menor

A >= B A.compareTo(B) >= 0 Operador de comparación A mayor

Para las variables de tipo Float o Double , cuando son


tipificadas como tal, se pueden realizar comparaciones con
rangos. A continuación, mostramos las expresiones de
comparación de rangos para las variables de
tipo Float o Double :

Expresión Descripción

Operador de comparación de rango, donde X está entre A (inicio d


X in A..B
rango).

Operador de comparación de rango, donde X NO está entre A (inic


X !in A..B
rango).

Ejemplos Operadores de Comparación:


Operador de comparación: <
// Ejemplo operador de comparación <
var x: Int = 4
var y: Int = 6
var comparacion: Boolean
comparacion = x < y // Calculado: 4 < 6, resultado: true.

var x: Float = 4.1F


var y: Float = 4F
var comparacion: Boolean
comparacion = x < y // Calculado: 4.1 < 4, resultado: false.

var x: Char = 'A'


var y: Char = 'a'
var comparacion: Boolean
comparacion = x < y // Calculado: ('A' = 65) < ('a' = 97), result
ado: true.
// El ejemplo anterior se podría tipificar mejor, del siguiente m
odo:
var x: Int = 'A'.toInt()
var y: Int = 'a'.toInt()
var comparacion: Boolean
comparacion = x < y // Calculado: 65 < 97, resultado: true.

Operador de comparación: >


// Ejemplo operador de comparación >
var x: Int = 4
var y: Int = 6
var comparacion: Boolean
comparacion = x > y // Calculado: 4 > 6, resultado: false.

var x: Float = 4.1F


var y: Float = 4F
var comparacion: Boolean
comparacion = x > y // Calculado: 4.1 > 4, resultado: true.

var x: Char = 'A'


var y: Char = 'a'
var comparacion: Boolean
comparacion = x > y // Calculado: ('A' = 65) > ('a' = 97), result
ado: false.

// El ejemplo anterior se podría tipificar mejor, del siguiente m


odo:
var x: Int = 'A'.toInt()
var y: Int = 'a'.toInt()
var comparacion: Boolean
comparacion = x > y // Calculado: 65 > 97, resultado: false.

Operador de comparación: <=


// Ejemplo operador de comparación &lt=;
var x: Int = 4
var y: Int = 6
var comparacion: Boolean
comparacion = x <= y // Calculado: 4 <= 6, resultado: true.

var x: Float = 4.1F


var y: Float = 4F
var comparacion: Boolean
comparacion = x <= y // Calculado: 4.1 <= 4, resultado: false.

var x: Char = 'A'


var y: Char = 'a'
var comparacion: Boolean
comparacion = x <= y // Calculado: ('A' = 65) <= ('a' = 97), resu
ltado: true.

// El ejemplo anterior se podría tipificar mejor, del siguiente m


odo:
var x: Int = 'A'.toInt()
var y: Int = 'a'.toInt()
var comparacion: Boolean
comparacion = x <= y // Calculado: 65 <= 97, resultado: true.

Operador de comparación: >=


// Ejemplo operador de comparación >=
var x: Int = 4
var y: Int = 6
var comparacion: Boolean
comparacion = x >= y // Calculado: 4 >= 6, resultado: false.

var x: Float = 4.1F


var y: Float = 4F
var comparacion: Boolean
comparacion = x >= y // Calculado: 4.1 >= 4, resultado: true.

var x: Char = 'A'


var y: Char = 'a'
var comparacion: Boolean
comparacion = x >= y // Calculado: ('A' = 65) >= ('a' = 97), resu
ltado: false.

// El ejemplo anterior se podría tipificar mejor, del siguiente m


odo:
var x: Int = 'A'.toInt()
var y: Int = 'a'.toInt()
var comparacion: Boolean
comparacion = x >= y // Calculado: 65 >= 97, resultado: false.

Operadores a nivel de Bit:


Kotlin soporta operaciones a nivel de Bit, sobre variables
tipo Int y Long únicamente. Sobre estos operadores, no
existen caracteres especiales, pero si funciones que pueden ser
invocadas entre dos operandos.

A continuación, veremos la tabla con estos operadores y una


descripción de su funcionamiento:

Función Descripción

Función de operador a nivel de Bits shl, desplazamiento hacia la izq


shl(bits)
signo (+ positivo o - negativo).
Función Descripción

Función de operador a nivel de Bits shr, desplazamiento hacia la de


shr(bits)
signo (+ positivo o - negativo).

Función de operador a nivel de Bits ushr, desplazamiento hacia la d


ushr(bits)
signo (+ positivo o - negativo).

and(bits) Función de operador a nivel de Bits and (y).

or(bits) Función de operador a nivel de Bits or (o).

xor(bits) Función de operador a nivel de Bits xor (o exclusivo).

inv() Función de operador a nivel de Bits inv (inversión).

Ejemplos Operadores a nivel de Bit:


Vamos a ver unos ejemplos de uso para estas funciones que, nos
hacen de operadores a nivel de bit:

Operador a nivel de Bit: shl(bits)


// Ejemplo "operadores" a nivel de Bit shl(bits):
var x: Int = 2
var desplazamiento: Int = 4
var resultado: Int
resultado = x shl desplazamiento // calculado: 2 shl 4, resultado
: 32
/* calculo, paso a paso, del resultado:
* 2 en binario: 10
* Bits de Desplazamiento: 4 (desplazamiento de x hacia la izquie
rda, rellenando con 4 ceros delante.).
* resultado en binario de desplazar 2, 4 posiciones a la izquier
da: 100000
* resultado en decimal: 32
*/

Operador a nivel de Bit: shr(bits)


// Ejemplo "operadores" a nivel de Bit shr(bits)
var x: Int = -32
var desplazamiento: Int = 2
var resultado: Int
resultado = x shr desplazamiento // calculado: -32 shr 2, resulta
do: -8
/* calculo, paso a paso, del resultado:
* -32 en binario: 100000, con el bit de signo en 1 (negativo), e
l bit de signo positivo es 0.
* Bits de Desplazamiento: 2 (desplazamiento de x hacia la derech
a, rellenando con 2 ceros detrás).
* resultado en bi nario de desplazar -32, 2 posiciones a la izqui
erda: 001000 (quitamos los 2 últimos dígitos de la derecha).
* resultado en decimal: -8
*/

Operador a nivel de Bit: ushr(bits)


// Ejemplo "operadores" a nivel de Bit ushr(bits)
var x: Int = 30
var desplazamiento: Int = 2
var resultado: Int
resultado = x ushr desplazamiento // calculado: 30 ushr 2, result
ado: 7
/* calculo, paso a paso, del resultado:
* 30 en binario: 11110
* Bits de Desplazamiento: 2 (desplazamiento de x hacia la derech
a, rellenando con 2 ceros detrás).
* resultado en binario de desplazar 30, 2 posiciones a la izquie
rda: 00111 (quitamos los 2 últimos dígitos de la derecha).
* resultado en decimal: 7
*/

Operador a nivel de Bit: and(bits)


// Ejemplo "operadores" a nivel de Bit a nd(bits)
var x: Int = 5
var y: Int = 3
var resultado: Int
resultado = x and y // calculado: 5 y 3, resultado: 1.

Vamos a explicar en detalle como funciona el


operador and(bits) :
El operador and bit a bit, toma dos números enteros y realiza la
operación and lógica en cada par correspondiente de bits. El
resultado en cada posición es 1 si el bit correspondiente de los
dos operandos es 1, y 0 de lo contrario. Os pongo una tabla,
con el calculo anterior x = 5 e y = 3:
X Y Resultado AND

1 0 0

0 1 0

1 1 1

Como podemos ver, el resultado de operar AND entre 5 y 3 en


binario es 001, que se traduce en decimal como 1.

Operador a nivel de Bit: or(bits)


// Ejemplo "operadores" a nivel de Bit or(bits)
var x: Int = 5
var y: Int = 3
var resultado: Int
resultado = x or y // calculado: 5 y 3, resultado: 7.

Vamos a explicar en detalle como funciona el


operador or(bits) :
Una operación or bit a bit, toma dos números enteros y realiza la
operación or inclusivo en cada par correspondiente de bits. El
resultado en cada posición es 1 si el bit correspondiente de
cualquiera de los dos operandos es 1, y 0 si ambos bits son 0 .
Os pongo una tabla, con el calculo anterior x = 5 e y = 3:

X Y Resultado OR

1 0 1

0 1 1

1 1 1
Como podemos ver, el resultado de operar OR entre 5 y 3 en
binario es 111, que se traduce en decimal como 7.

Operador a nivel de Bit: xor(bits)


// Ejemplo "operadores" a nivel de Bit xor(bits)
var x: Int = 5
var y: Int = 3
var resultado: Int
resultado = x xor y // calculado: 5 y 3, resultado: 6.

Vamos a explicar en detalle como funciona el


operador xor(bits) :
Una operación xor bit a bit, toma dos números enteros y realiza
la operación or exclusivo en cada par correspondiente de bits. El
resultado en cada posición es 1 si el par de bits son diferentes y
cero si el par de bits son iguales. Os pongo una tabla, con el
calculo anterior x = 5 e y = 3:

X Y Resultado XOR

1 0 1

0 1 1

1 1 0

Como podemos ver, el resultado de operar XOR entre 5 y 3 en


binario es 110, que se traduce en decimal como 6.

Operador a nivel de Bit: inv()


// Ejemplo "operadores" a nivel de Bit inv()
var x: Int = -6
var resultado: Int
resultado = (x.inv() + 1) // calculado: inverso de -6 + 1, result
ado: 6

var x: Int = 6
var resultado: Int
resultado = (x.inv() + 1) // calculado: inverso de 6 + 1, resulta
do: -6

* En la función de invertir bit a bit, no estoy 100% seguro de


como opera. Pero añadiendo ese + 1, obtendremos el mismo
número introducido con el signo (positivo o negativo) invertido.

Operadores de Igualdad y Desigualdad:


Estos operadores de igualdad y desigualdad, nos
permite comprobar si los operandos son equivalentes (para
variables no primitivas, se traduce en la llamada al
método equals() ).
Los operadores de igualdad y desigualdad son los siguientes:

Expresión Traducción:

x == y x?.equals(y) ?: (y === null) Operador de ig

x != y !(x?.equals(y) ?: (y === null)) Operador de d

La operación == se traduce en una expresión compleja que,


también cubre los valores nulos null . null == null es siempre
verdadero ( true ) y x == null para una variable x que no es
nula, el resultado será siempre falso ( false ) y no invocará la
función x.equals() .

También podemos realizar operaciones de igualdad y


desigualdad de referencias. La igualdad de referencias se
representa con el operador === y la desigualdad de referencias
se representa con el operador !== . La expresión x === y ,
resulta verdadero ( true ) si, y solo si, x e y apuntan al mismo
objeto.

Para valores que se representan en tiempo de ejecución como


variables primitivas (por ejemplo, Int ), el operador === se
traduce al operador == y para la desigualdad, !== pasa lo
mismo, se traduce a != .

Conclusión:
En este articulo, hemos revisado los operadores,
y funciones que trabajan como operadores, disponibles
en kotlin. Si a la hora de leer este artículo te surge alguna duda,
falta algún operador, conoces la forma de operar de la función
inv(), etc. puedes dejar un comentario. Si te ha gustado el
articulo, compártelo con tus amigos y/o en tus redes sociales.

Curso Kotlin: Operadores

Introducción a los Operadores:

En este articulo sobre los operadores en kotlin, vamos a ver los


diferentes tipos de operadores existentes: operadores de
asignación, operadores aritméticos, operadores
lógicos, operadores de comparación, operadores de igualdad
y desigualdad, etc. Antes de continuar, ¿Que es un
operador? Un operador es un símbolo o función que determina
el tipo de operación o relaciónentre, el o los, operandos de
una expresión.

Tabla de Contenido:
1. Introducción a los Operadores
2. Operadores de Asignación
1. Ejemplos Operadores de Asignación
3. Operadores Aritméticos
1. Ejemplos Operadores Aritméticos
4. Operadores Lógicos
1. Ejemplos Operadores Lógicos
5. Operadores de Comparación
1. Ejemplos Operadores de Comparación
6. Operadores a nivel de Bit
1. Ejemplos Operadores a nivel de Bit
7. Operadores de Igualdad y Desigualdad
8. Conclusión
9. Referencias

Operadores de Asignación:
Los operadores de asignación sirven para, como su nombre
indica, asignar valores a variables. Entre estos operadores,
también encontramos los operadores de
aumento o decremento de valores.

Operador Descripción

Operador de Asignación. También se usa para asignar valores por d


=
funciones.

Operador de asignación con incremento o suma de operandos. Tam


+=
concatenar strings, aunque como vimos, lo mejor es usar plantillas

-= Operador de asignación con decremento o resta de operandos.

*= Operador de asignación cuyo resultado es la multiplicación de oper


Operador Descripción

/= Operador de asignación cuyo resultado es la división de operandos.

%= Operador de asignación cuyo resultado es el resto de la división de

Ejemplos Operadores de Asignación:

Para ver como funcionan los operadores de Asignación, vamos


a poner un par de ejemplos:

Operador de asignación: +=
// Ejemplos de asignación +=
var x: Int = 2
x += x // es igual a: x = x + x. Resultado: 4.

var x: Int = 1
x += x // es igual a: x = x + x. Resultado: 2.

// Ejemplos de asignación += con Strings.


var x: String = "Hola"
x += " Mundo" // es igual a: x = x + " Mundo". Resultado: "Hola M
undo".

Operador de asignación: -=
// Ejemplos de asignación -=
var x: Int = 1
x -= x // es igual a: x = x - x. Resultado: 0.

var x: Int = 4
x -= 2 // es igual a: x = x - 2. Resultado: 2.

Operador de asignación: *=
// Ejemplos de asignación *=
var x: Int = 1
x *= 5 // es igual a: x = x * 5. Resultado: 5.

var x: Int = 4
x *= x // es igual a: x = x * x, o, x elevado a 2. Resultado: 16.
Operador de asignación: /=
// Ejemplos de asignación /=
var x: Int = 4
x /= 2 // es igual a: x = x / 2. Resultado: 2.

var x: Int = 16
x /= x // es igual a: x = x / x. Resultado: 1.

Operador de asignación: %=
// Ejemplos de asignación %=
var x: Int = 4
x %= 2 // es igual a: x = x % 2. Resultado: 0.

var x: Float = 4.5F


x %= 2 // es igual a: x = x % 2. Resultado: 0.5

Operadores Aritméticos:
Los operadores aritméticos sirven para realizar operaciones
matemáticas entre operandos numéricos. El operador
aritmético +, como vimos en el el articulo sobre tipos de
variables, cadenas de texto (string), también sirve para
concatenar strings, aunque es preferible utilizar las plantillas de
strings.

Operador Descripción

Operador Aritmético + Suma de operandos o signo positivo. Tambi


+
concatenación en Strings.

- Operador Aritmético - Resta de operandos o signo negativo.

Operador Aritmético * Multiplicación de operandos. También se us


*
parametro vararg.

/ Operador Aritmético / División de operandos.

% Operador Aritmético % Resto de la división de operandos.


Ejemplos Operadores Aritméticos:

Vamos a incluir unos ejemplos de uso:

Operador aritmético: +
// Ejemplo de suma entre 2 enteros.
var x: Int = 4
var y: Int = 6
var suma: Int
suma = x + y // Resultado: 10.

// Ejemplo de concatenación de dos String:


var cadena1: String = "Hola"
var cadena2: String = "Mundo"
var concat: String
concat = cadena1 + " " + cadena2 // Resultado: "Hola Mundo".

Operador aritmético: -
// Ejemplo de resta entre 2 enteros.
var x: Int = 4
var y: Int = 6
var resta: Int
resta = x - y // Resultado: -2.

Operador aritmético: *
// Ejemplo de multiplicación entre 2 enteros.
var x: Int = 4
var y: Int = 6
var multiplicacion: Int
multiplicacion = x * y // Resultado: 24.

Operador aritmético: /
// Ejemplo de división entre 2 enteros.
var x: Int = 4
var y: Int = 6
var division: Int
division = x / y // Resultado: 0. La variable division es entero,
por eso no recoge los decimales.

// Ejemplo de división entre 2 Floats.


var x: Float = 4F
var y: Float = 6F
var division: Float
division = x / y // Resultado: 0.6666667 Ahora si vemos el result
ado con los decimales.
Operador aritmético: %
// Ejemplo de resto de la división entre 2 enteros.
var x: Int = 4
var y: Int = 6
var resto: Int
resto = x % y // Resultado: 4.

Operadores Lógicos:
Existen 3 operadores lógicos en Kotlin:

 Operador de conjunción && , traducido como y. También


se puede usar la palabra clave and .
 Operador de disyunción || , traducido como o. También se
puede usar la palabra clave or .
 Operador de negación ! , traducido como no.
Ejemplos Operadores Lógicos:
Veamos unos ejemplos completos, con todas las posibilidades de
uso, de estos operadores lógicos:

Operador de Conjunción: &&


// Ejemplo de conjunciones &&:
var x: Boolean = false
var y: Boolean = false
var conjuncion: Boolean
conjuncion = x && y // Resultado: false

var x: Boolean = false


var y: Boolean = true
var conjuncion: Boolean
conjuncion = x && y // Resultado: false

var x: Boolean = true


var y: Boolean = false
var conjuncion: Boolean
conjuncion = x && y // Resultado: fa lse

var x: Boolean = true


var y: Boolean = true
var conjuncion: Boolean
conjuncion = x && y // Resultado: true

Como podemos observar, en la conjunción, se deben cumplir (


Deben ser verdaderos ) los dos operandos.

Operador de Disyunción: ||
// Ejemplo de disyunciones ||:
var x: Boolean = false
var y: Boolean = false
var disyuncion: Boolean
disyuncion = x || y // Resultado: false

var x: Boolean = false


var y: Boolean = true
var disyuncion: Boolean
disyuncion = x || y // Resultado: true

var x: Boolean = true


var y: Boolean = false
var disyuncion: Boolean
disyuncion = x || y // Resultado: true

var x: Boolean = true


var y: Boolean = true
var disyuncion: Boolean
disyuncion = x || y // Resultado: true

En las disyunciones, a diferencia de las conjunciones, con que se


cumpla un operando nos resulta verdadero.

Operador negación: !
// Ejemplo de negación !:
var x: Boolean = false
var negacion: Boolean
negacion = !x // Resultado: true

var x: Boolean = true


var negacion: Boolean
negacion = !x // Resultado: false

Con la negación, invertimos el valor booleano de una variable,


como se puede ver en los ejemplos.
Operadores de Comparación:

Los operadores de comparación nos sirven para comparar 2


operandos. En kotlin, todas las comparaciones se traducen en
llamadas al método compareTo() , en los tipos no primitivos, el
cual retorna un entero Int .

Expresión Traducido a Descripci

A<B A.compareTo(B) < 0 Operador de comparación A menor

A>B A.compareTo(B) > 0 Operador de comparación A mayor

A <= B A.compareTo(B) <= 0 Operador de comparación A menor

A >= B A.compareTo(B) >= 0 Operador de comparación A mayor

Para las variables de tipo Float o Double , cuando son


tipificadas como tal, se pueden realizar comparaciones con
rangos. A continuación, mostramos las expresiones de
comparación de rangos para las variables de
tipo Float o Double :

Expresión Descripción

Operador de comparación de rango, donde X está entre A (inicio d


X in A..B
rango).

Operador de comparación de rango, donde X NO está entre A (inic


X !in A..B
rango).

Ejemplos Operadores de Comparación:


Operador de comparación: <
// Ejemplo operador de comparación <
var x: Int = 4
var y: Int = 6
var comparacion: Boolean
comparacion = x < y // Calculado: 4 < 6, resultado: true.

var x: Float = 4.1F


var y: Float = 4F
var comparacion: Boolean
comparacion = x < y // Calculado: 4.1 < 4, resultado: false.

var x: Char = 'A'


var y: Char = 'a'
var comparacion: Boolean
comparacion = x < y // Calculado: ('A' = 65) < ('a' = 97), result
ado: true.

// El ejemplo anterior se podría tipificar mejor, del siguiente m


odo:
var x: Int = 'A'.toInt()
var y: Int = 'a'.toInt()
var comparacion: Boolean
comparacion = x < y // Calculado: 65 < 97, resultado: true.

Operador de comparación: >


// Ejemplo operador de comparación >
var x: Int = 4
var y: Int = 6
var comparacion: Boolean
comparacion = x > y // Calculado: 4 > 6, resultado: false.

var x: Float = 4.1F


var y: Float = 4F
var comparacion: Boolean
comparacion = x > y // Calculado: 4.1 > 4, resultado: true.

var x: Char = 'A'


var y: Char = 'a'
var comparacion: Boolean
comparacion = x > y // Calculado: ('A' = 65) > (' a' = 97), result
ado: false.

// El ejemplo anterior se podría tipificar mejor, del siguiente m


odo:
var x: Int = 'A'.toInt()
var y: Int = 'a'.toInt()
var comparacion: Boolean
comparacion = x > y // Calculado: 65 > 97, resultado: false.

Operador de comparación: <=


// Ejemplo operador de comparación &lt=;
var x: Int = 4
var y: Int = 6
var comparacion: Boolean
comparacion = x <= y // Calculado: 4 <= 6, resultado: true.

var x: Float = 4.1F


var y: Float = 4F
var comparacion: Boolean
comparacion = x <= y // Calculado: 4.1 <= 4, resultado: false.

var x: Char = 'A'


var y: Char = 'a'
var comparacion: Boolean
comparacion = x <= y // Calculado: ('A' = 65) <= ('a' = 97), resu
ltado: true.

// El ejemplo anterior se podría tipificar mejor, del siguiente m


odo:
var x: Int = 'A'.toInt()
var y: Int = 'a'.toInt()
var comparacion: Boolean
comparacion = x <= y // Calculado: 65 <= 97, resultado: true.

Operador de comparación: >=


// Ejemplo operador de comparación >=
var x: Int = 4
var y: Int = 6
var comparacion: Boolean
comparacion = x >= y // Calculado: 4 >= 6, resultado: false.

var x: Float = 4.1F


var y: Float = 4F
var comparacion: Boolean
comparacion = x >= y // Calculado: 4.1 >= 4, resultado: true.

var x: Char = 'A'


var y: Char = 'a'
var comparacion: Boolean
comparacion = x >= y // Calculado: ('A' = 65) >= ('a' = 97), resu
ltado: false.

// El ejemplo anterior se podría tipificar mejor, del siguiente m


odo:
var x: Int = 'A'.toInt()
var y: Int = 'a'.toInt()
var comparacion: Boolean
comparacion = x >= y // Calcula do: 65 >= 97, resultado: false.

Operadores a nivel de Bit:


Kotlin soporta operaciones a nivel de Bit, sobre variables
tipo Int y Long únicamente. Sobre estos operadores, no
existen caracteres especiales, pero si funciones que pueden ser
invocadas entre dos operandos.
A continuación, veremos la tabla con estos operadores y una
descripción de su funcionamiento:

Función Descripción

Función de operador a nivel de Bits shl, desplazamiento hacia la izq


shl(bits)
signo (+ positivo o - negativo).

Función de operador a nivel de Bits shr, desplazamiento hacia la de


shr(bits)
signo (+ positivo o - negativo).

Función de operador a nivel de Bits ushr, desplazamiento hacia la d


ushr(bits)
signo (+ positivo o - negativo).

and(bits) Función de operador a nivel de Bits and (y).

or(bits) Función de operador a nivel de Bits or (o).

xor(bits) Función de operador a nivel de Bits xor (o exclusivo).

inv() Función de operador a nivel de Bits inv (inversión).

Ejemplos Operadores a nivel de Bit:


Vamos a ver unos ejemplos de uso para estas fu nciones que, nos
hacen de operadores a nivel de bit:

Operador a nivel de Bit: shl(bits)


// Ejemplo "operadores" a nivel de Bit shl(bits):
var x: Int = 2
var desplazamiento: Int = 4
var resultado: Int
resultado = x shl desplazamiento // calculado: 2 shl 4, resultado
: 32
/* calculo, paso a paso, del resultado:
* 2 en binario: 10
* Bits de Desplazamiento: 4 (desplazamiento de x hacia la izquie
rda, rellenando con 4 ceros delante.).
* resultado en binario de desplazar 2, 4 posiciones a la izquier
da: 100000
* resultado en decimal: 32
*/

Operador a nivel de Bit: shr(bits)


// Ejemplo "operadores" a nivel de Bit shr(bits)
var x: Int = -32
var desplazamiento: Int = 2
var resultado: Int
resultado = x shr desplazamiento // calculado: -32 shr 2, resulta
do: -8
/* calculo, paso a paso, del resultado:
* -32 en binario: 100000, con el bit de signo en 1 (negativo), e
l bit de signo positivo es 0.
* Bits de Desplazamiento: 2 (desplazamiento de x hacia la derech
a, rellenando con 2 ceros detrás).
* resultado en binario de desplazar -32, 2 posiciones a la izqui
erda: 001000 (quitamos los 2 últimos dígitos de la derecha).
* resultado en decimal: -8
*/

Operador a nivel de Bit: ushr(bits)


// Ejemplo "operadores" a nivel de Bit ushr(bits)
var x: Int = 30
var desplazamiento: Int = 2
var resultado: Int
resultado = x ushr desplazamiento // calculado: 30 ushr 2, result
ado: 7
/* calculo, paso a paso, del resultado:
* 30 en binario: 11110
* Bits de Desplazamiento: 2 (desplazamiento de x hacia la derech
a, rellenando con 2 ceros detrás).
* resultado en binario de desplazar 30, 2 posiciones a la izquie
rda: 00111 (quitamos los 2 últimos dígitos de la derecha).
* resultado en decimal: 7
*/

Operador a nivel de Bit: and(bits)


// Ejemplo "operadores" a nivel de Bit a nd(bits)
var x: Int = 5
var y: Int = 3
var resultado: Int
resultado = x and y // calculado: 5 y 3, resultado: 1.

Vamos a explicar en detalle como funciona el


operador and(bits) :
El operador and bit a bit, toma dos números enteros y realiza la
operación and lógica en cada par correspondiente de bits. El
resultado en cada posición es 1 si el bit correspondiente de los
dos operandos es 1, y 0 de lo contrario. Os pongo una tabla,
con el calculo anterior x = 5 e y = 3:

X Y Resultado AND

1 0 0

0 1 0

1 1 1

Como podemos ver, el resultado de operar AND entre 5 y 3 en


binario es 001, que se traduce en decimal como 1.

Operador a nivel de Bit: or(bits)


// Ejemplo "operadores" a nivel de Bit or(bits)
var x: Int = 5
var y: Int = 3
var resultado: Int
resultado = x or y // calculado: 5 y 3, resultado: 7.

Vamos a explicar en detalle como funciona el


operador or(bits) :
Una operación or bit a bit, toma dos números enteros y realiza la
operación or inclusivo en cada par correspondiente de bits. El
resultado en cada posición es 1 si el bit correspondiente de
cualquiera de los dos operandos es 1, y 0 si ambos bits son 0 .
Os pongo una tabla, con el calculo anterior x = 5 e y = 3:
X Y Resultado OR

1 0 1

0 1 1

1 1 1

Como podemos ver, el resultado de operar OR entre 5 y 3 en


binario es 111, que se traduce en decimal como 7.

Operador a nivel de Bit: xor(bits)


// Ejemplo "operadores" a nivel de Bit xor(bits)
var x: Int = 5
var y: Int = 3
var resultado: Int
resultado = x xor y // calculado: 5 y 3, resultado: 6.

Vamos a explicar en detalle como funciona el


operador xor(bits) :
Una operación xor bit a bit, toma dos números enteros y realiza
la operación or exclusivo en cada par correspondiente de bits. El
resultado en cada posición es 1 si el par de bits son diferentes y
cero si el par de bits son iguales. Os pongo una tabla, con el
calculo anterior x = 5 e y = 3:

X Y Resultado XOR

1 0 1

0 1 1

1 1 0
Como podemos ver, el resultado de operar XOR entre 5 y 3 en
binario es 110, que se traduce en decimal como 6.

Operador a nivel de Bit: inv()


// Ejemplo "operadores" a nivel de Bit inv()
var x: Int = -6
var resultado: Int
resultado = (x.inv() + 1) // calculado: inverso de -6 + 1, result
ado: 6

var x: Int = 6
var resultado: Int
resultado = (x.inv() + 1) // calculado: inverso de 6 + 1, resulta
do: -6

* En la función de invertir bit a bit, no estoy 100% seguro de


como opera. Pero añadiendo ese + 1, obtendremos el mismo
número introducido con el signo (positivo o negativo) invertido.

Operadores de Igualdad y Desigualdad:


Estos operadores de igualdad y desigualdad, nos
permite comprobar si los operandos son equivalentes (para
variables no primitivas, se traduce en la llamada al
método equals() ).
Los operadores de igualdad y desigualdad son los siguientes:

Expresión Traducción:

x == y x?.equals(y) ?: (y === null) Operador de ig

x != y !(x?.equals(y) ?: (y === null)) Operador de d

La operación == se traduce en una expresión compleja que,


también cubre los valores nulos null . null == null es siempre
verdadero ( true ) y x == null para una variable x que no es
nula, el resultado será siempre falso ( false ) y no invocará la
función x.equals() .

También podemos realizar operaciones de igualdad y


desigualdad de referencias. La igualdad de referencias se
representa con el operador === y la desigualdad de referencias
se representa con el operador !== . La expresión x === y ,
resulta verdadero ( true ) si, y solo si, x e y apuntan al mismo
objeto.

Para valores que se representan en tiempo de ejecución como


variables primitivas (por ejemplo, Int ), el operador === se
traduce al operador == y para la desigualdad, !== pasa lo
mismo, se traduce a != .

Conclusión:
En este articulo, hemos revisado los operadores,
y funciones que trabajan como operadores, disponibles
en kotlin. Si a la hora de leer este artículo te surge alguna duda,
falta algún operador, conoces la forma de operar de la función
inv(), etc. puedes dejar un comentario. Si te ha gustado el
articulo, compártelo con tus amigos y/o en tus redes sociales.

You might also like