Informatica2 PDF
Informatica2 PDF
Informatica2 PDF
Crear proyecto:
DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
mvn eclipse:eclipse
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>
Teclear de nuevo:
mvn eclipse:eclipse
Compilar:
mvn compile
Ejecutar:
Crear JAR:
mvn package
Ejecutar:
<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;
<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">
</beans>
App.java
package com.codemonkey;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
Compilar:
mvn compile
Ejecutar:
Link: http://www.java2s.com/Tutorials/Java/Spring/0030__Spring_HelloWorld.htm
Grails framework
$ grails –version
$ grails
$ cd holamundo
$ grails
def index() {
render "La copa de Grails!"
}
}
grails-app/conf/application.yml
Escribir:
server:
contextPath: /holamundo
grails> run-app
http://localhost:8080/holamundo/
http://localhost:8080/holamundo/index
Links:
http://docs.grails.org/latest/guide/single.html
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 $ grais
2 | Created grails-app/domain/csv/Book.groovy
3 | Created src/test/groovy/csv/BookSpec.groovy
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 {
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...
4 | Created grails-app/controllers/csv/BookController.groovy
| Created src/test/groovy/csv/BookControllerSpec.groovy
5
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
19 response.contentType = "${csvMimeType};charset=${encoding}";
21 filename=${filename}"
22
lines.each { String line ->
23
outs << "${line}\n"
24
}
25
26
outs.flush()
27
outs.close()
28 }
29
30 @Override
32 csvMimeType =
36 }
37
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.
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.
In the last tutorial we saw how to use Pageable parameter in Spring MVC
Example
Entity
@Entity
private @Id
@GeneratedValue
Long id;
.............
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
EntityManagerFactory entityManagerFactory) {
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
ApplicationContext applicationContext;
@Bean
return templateEngine;
@Bean
new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(this.applicationContext);
templateResolver.setPrefix("/WEB-INF/views/");
templateResolver.setSuffix(".html");
return templateResolver;
@Bean
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
@Autowired
@GetMapping("/employees")
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%;}
.pagination-div{user-select: none;}
.pagination-div span{border-radius:3px;border:1px solid #999;
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>
<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>
<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:
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
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:
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.
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.
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:
# va-a-escampar.txt
nothing added to commit but untracked files present (use "git add" to track)
$ git status
# On branch master
# Initial commit
#
# Changes to be committed:
Para comitear el estado actual del archivo se ejecuta la orden git commit -m “mensaje
personal”:
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.
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
$ git log
commit 38b86f5625453732e442c127f1d4678ec8550a12
Va a escampar
$ git status
# On branch master
# modified: va-a-escampar.txt
no changes added to commit (use "git add" and/or "git commit -a")
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
--- a/va-a-escampar.txt
+++ b/va-a-escampar.txt
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).
# On branch master
# modified: va-a-escampar.txt
no changes added to commit (use "git add" and/or "git commit -a")
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
quiere la respuesta
$ git diff
+++ b/va-a-escampar.txt
+Llega la batalla
+y contra él estalla
+quiere la respuesta
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 diff
Ahora no hay ninguna salida, porque el archivo actualmente en edición es igual al que
guardamos en la zona de preparación.
--- a/va-a-escampar.txt
+++ b/va-a-escampar.txt
+Llega la batalla
+y contra él estalla
+quiere la respuesta
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 status
# On branch master
$ git log
commit 8f1eec1836a9ace8a2cbab7e2c3341efa5c3a537
commit 6ae57e3d91a7a526a257df081d83a5b9be4e6d28
commit 38b86f5625453732e442c127f1d4678ec8550a12
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:
--- a/va-a-escampar.txt
+++ b/va-a-escampar.txt
+Llega la batalla
+y contra él estalla
+quiere la respuesta
--- a/va-a-escampar.txt
+++ b/va-a-escampar.txt
+Llega la batalla
+y contra él estalla
+quiere la respuesta
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:
--- a/va-a-escampar.txt
+++ b/va-a-escampar.txt
+Llega la batalla
+y contra él estalla
Para no tipear cadenas de 40 números, Git permite usar los primeros de la cadena, con
el mismo resultado:
no va a escampar nada
$ 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
# 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:
$ cat va-a-escampar.txt
Va a escampar
Llega la batalla
y contra él estalla
quiere la respuesta
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:
ayuda:
del comando mismo: sin esos guiones, Git trataría de usar el nombre
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
$ git status
# On branch master
# Untracked files:
# 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:
# .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 status
# On branch master
a.dat
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:
# On branch master
# Ignored files:
# a.dat
# b.dat
# c.dat
# results/
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)
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...
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.
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):
$ git remote -v
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):
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
$ git status
# On branch master
No problem
Ahora sí volvemos a intentar subir los archivos que faltan hacia origin (el repo remoto,
solo, triste y abandonado):
To https://github.com/eldiegoefe/emacs.git
“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:
From https://github.com/eldiegoefe/emacs
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'
'-u'. Vamos a dejar eso para después, pero es casi seguro que se
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)
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]$ cd emacs
On branch master
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.
On branch master
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).
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:
From https://github.com/eldiegoefe/emacs
Updating 2c817b6..f94cdfa
Fast-forward
archivo-vacio.txt | 0
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.
No te vayas sin comentar algo del post, de las noticias del día, del clima, que se yo...
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:
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.
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: 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.
From https://github.com/eldiegoefe/emacs
Auto-merging 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:
<<<<<<< HEAD
=======
>>>>>>> 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:
# On branch master
# Unmerged paths:
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:
# On branch master
# Changes to be committed:
# modified: manifiesto.txt
# On branch master
To https://github.com/eldiegoefe/emacs.git
Esta vez pudimos subir todo exitosamente. Ahora vamos a ver qué le pasa al otro
usuario bioingenierodiegol cuando quiere actualizar su repo local:
From https://github.com/eldiegoefe/emacs
Updating efe8a9a..4f17908
Fast-forward
manifiesto.txt | 3 ++-
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.
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
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.
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.
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
@Id @GeneratedValue
package com.okta.developer.demo;
import org.springframework.data.jpa.repository.JpaRepository;
import
org.springframework.data.rest.core.annotation.RepositoryRestR
esource;
@RepositoryRestResource
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
SpringApplication.run(DemoApplication.class, args);
@Bean
ApplicationRunner init(CarRepository repository) {
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)
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 {
this.repository = repository;
@GetMapping("/cool-cars")
return repository.findAll().stream()
.filter(this::isCool)
.collect(Collectors.toList());
!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
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 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
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
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.
@Injectable()
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.
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [CarService],
bootstrap: [AppComponent]
})
ng g c car-list
cars: Array<any>;
ngOnInit() {
this.carService.getAll().subscribe(data => {
this.cars = data;
});
}
}
<h2>Car List</h2>
{{car.name}}
</div>
<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.
import org.springframework.web.bind.annotation.CrossOrigin;
...
@GetMapping("/cool-cars")
@CrossOrigin(origins = "http://localhost:4200")
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")
Restart the server, refresh the client, and you should see the list of cars in your
browser.
@NgModule({
...
imports: [
BrowserModule,
HttpClientModule,
BrowserAnimationsModule,
MatButtonModule,
MatCardModule,
MatInputModule,
MatListModule,
MatToolbarModule
],
...
})
<mat-toolbar color="primary">
<span>Welcome to {{title}}!</span>
</mat-toolbar>
<app-car-list></app-car-list>
<mat-card>
<mat-card-header>Car List</mat-card-header>
<mat-card-content>
<mat-list>
</mat-list-item>
</mat-list>
</mat-card-content>
</mat-card>
@import "~@angular/material/prebuilt-themes/pink-
bluegrey.css";
@import 'https://fonts.googleapis.com/icon?family=Material+Icons';
body {
margin: 0;
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 'rxjs/add/operator/map';
@Injectable()
giphyApi =
'//api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&limit=1&q=';
get(searchTerm) {
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
});
@NgModule({
...
bootstrap: [AppComponent]
})
cars: Array<any>;
ngOnInit() {
this.carService.getAll().subscribe(data => {
this.cars = data;
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.
ng g c car-edit
getAll(): Observable<any> {
get(id: string) {
if (car['href']) {
} else {
return result;
remove(href: string) {
return this.http.delete(href);
<mat-card>
<mat-card-header>Car List</mat-card-header>
<mat-card-content>
<mat-list>
<h3 mat-line>
</h3>
</mat-list-item>
</mat-list>
</mat-card-content>
<button mat-fab color="primary" [routerLink]="['/car-
add']">Add</button>
</mat-card>
path: 'car-list',
component: CarListComponent
},
path: 'car-add',
component: CarEditComponent
},
path: 'car-edit/:id',
component: CarEditComponent
];
@NgModule({
...
imports: [
...
FormsModule,
RouterModule.forRoot(appRoutes)
],
...
})
@Component({
selector: 'app-car-edit',
templateUrl: './car-edit.component.html',
styleUrls: ['./car-edit.component.css']
})
sub: Subscription;
ngOnInit() {
const id = params['id'];
if (id) {
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();
}
remove(href) {
this.carService.remove(href).subscribe(result => {
this.gotoList();
<mat-card>
<mat-card-header>
</mat-card-header>
<mat-card-content>
<mat-form-field>
</mat-form-field>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button color="primary" type="submit"
[disabled]="!carForm.form.valid">Save</button>
*ngIf="car.href" type="button">Delete</button>
</mat-card-actions>
<mat-card-footer>
<div class="giphy">
</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;
<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).
<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.
security:
oauth2:
client:
access-token-uri:
https://{yourOktaDomain}/oauth2/default/v1/token
user-authorization-uri:
https://{yourOktaDomain}/oauth2/default/v1/authorize
client-id: {clientId}
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.
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';
...
path: 'implicit/callback',
component: OktaCallbackComponent
];
@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.
import 'rxjs/add/observable/fromPromise';
@Injectable()
return Observable.fromPromise(this.handleAccess(request,
next));
request = request.clone({
setHeaders: {
});
return next.handle(request).toPromise();
@NgModule({
...
...
})
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>
(click)="oktaAuth.logout()">Logout
</button>
</mat-toolbar>
<mat-card *ngIf="!isAuthenticated">
<mat-card-content>
(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;
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
title = 'app';
isAuthenticated: boolean;
async ngOnInit() {
this.isAuthenticated = await
this.oktaAuth.isAuthenticated();
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
path: 'home',
component: HomeComponent
},
...
<mat-card>
<mat-card-content>
(click)="oktaAuth.loginRedirect()">Login
</button>
[routerLink]="['/car-list']">Car List
</button>
</mat-card-content>
</mat-card>
isAuthenticated: boolean;
async ngOnInit() {
this.isAuthenticated = await
this.oktaAuth.isAuthenticated();
this.oktaAuth.$authenticationState.subscribe(
);
<mat-toolbar color="primary">
<span>Welcome to {{title}}!</span>
<span class="toolbar-spacer"></span>
(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.
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")
config.setAllowCredentials(true);
config.setAllowedOrigins(
Collections.singletonList("http://localhost:4200"));
config.setAllowedMethods(Collections.singletonList("*"));
config.setAllowedHeaders(Collections.singletonList("*"));
source.registerCorsConfiguration("/**", config);
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
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.
Server-side 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
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:
2. Packaging Structure
<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>
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
Change the above configuration such as JDBC URL, username and password as per your
environment.
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 {
public Employee() {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@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>{
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
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
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.
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.
ng --version
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.
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
3. Modules
FormsModule
HttpClientModule
AppRoutingModule.
In this next step, we will generate these components, classes, and services using Angular
CLI.
- ng g s employee
– ng g c create-employee
– ng g c employee-details
– ng g c employee-list
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.
Use NPM to download Bootstrap & JQuery. Bootstrap and jQuery will be installed into the
node_modules folder.
...
"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 -
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 -
@Injectable({
providedIn: 'root'
})
export class EmployeeService {
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.
@Component({
selector: "app-employee-list",
templateUrl: "./employee-list.component.html",
styleUrls: ["./employee-list.component.css"]
})
export class EmployeeListComponent implements OnInit {
employees: Observable<Employee[]>;
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
CreateEmployeeComponent - create-employee.component.ts
CreateEmployeeComponent is used to create and handle a new employee form data. Add
the following code to it -
@Component({
selector: 'app-create-employee',
templateUrl: './create-employee.component.html',
styleUrls: ['./create-employee.component.css']
})
export class CreateEmployeeComponent implements OnInit {
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();
}
}
<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>
<div [hidden]="!submitted">
<h4>You submitted successfully!</h4>
</div>
EmployeeDetailsComponent- employee-details.component.ts
@Component({
selector: 'app-employee-details',
templateUrl: './employee-details.component.html',
styleUrls: ['./employee-details.component.css']
})
export class EmployeeDetailsComponent implements OnInit {
ngOnInit() {
}
}
<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>
<hr/>
</div>
AppRoutingModule - app-routing.module.ts
@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.
@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
<div class="container">
<h2>{{title}}</h2>
<hr>
</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.
As we already started Spring boot application. Now let's run this Angular 7 application.
Let's run the above developed Angular App with a command: ng serve
Output
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).
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.
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.
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.
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.
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.
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))
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.
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:
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):
products = Set(Product)
set_sql_debug(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:
Product.select().order_by(desc(Product.price))[:3]
Product.select(lambda p: p.quantity == 0)
Product.select().order_by(lambda p:
desc(sum(p.order_items.quantity))).first()
# Products that have never been ordered
Customer.select().order_by(lambda c:
desc(sum(c.orders.total_price)))[:3]
# The same query with the LEFT JOIN instead of NOT EXISTS
for p in c.orders.items.product
If the expression can be calculated in Python, Pony will pass the result of the
calculation as a parameter into the query:
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",
If you need to use a SQL function, you can use the raw_sql() function in order to
include this SQL fragment:
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:
Pony adds the DISCTINCT keyword only when there could be potential duplicates.
Let’s consider a couple of examples.
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:
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:
If you need to count persons with the same name, you’d better use an aggregate query:
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:
This query can contain duplicates, so Pony eliminates them using DISTINCT:
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:
And in this case Pony will not add the DISTINCT keyword to SQL query.
To summarize:
avg()
abs()
exists()
len()
max()
min()
count()
concat()
group_concat()
random()
raw_sql()
select()
sum()
getattr()
Examples:
ON "c"."id" = "order-1"."customer"
WHERE "o"."customer" IN (
SELECT "c"."id"
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'
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))
print(q.get_sql())
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.
x = 25
You can change the content of the raw_sql() function dynamically and still use
parameters inside:
x = 1
You can use various types inside the raw SQL query:
x = date(1990, 1, 1)
x = 10
y = 15
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:
print(names)
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:
print(dates)
print(dates)
x = 25
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
x = '123'
y = 'John'
== raw_sql("UPPER($y || '123')"))
The same parameter names can be used several times with different types and values:
x = 10
y = 31
x = date(1980, 1, 1)
y = 'j'
persons = q[:]
x = 9
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)'))
x = 1000
y = 500
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
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
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.
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
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’.
val s = "abc" + 1
println(s + "def")
val s = "abc"
println("$s.length is ${s.length}") // prints "abc.length is 3"
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).
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
https://fabrica-software.blogspot.com/2018/12/sintaxis-kotlin-declaracion-de-variables.html
https://fabrica-software.blogspot.com/2018/12/curso-kotlin-operadores.html
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: +=
// 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.
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.
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: +
// Ejemplo de suma entre 2 enteros.
var x: Int = 4
var y: Int = 6
var suma: Int
suma = x + y // Resultado: 10.
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.
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 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
Operador negación: !
// Ejemplo de negación !:
var x: Boolean = false
var negacion: Boolean
negacion = !x // Resultado: true
Operadores de Comparación:
Expresión Descripción
Función Descripción
1 0 0
0 1 0
1 1 1
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.
X Y Resultado XOR
1 0 1
0 1 1
1 1 0
var x: Int = 6
var resultado: Int
resultado = (x.inv() + 1) // calculado: inverso de 6 + 1, resulta
do: -6
Expresión Traducción:
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.
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: +=
// 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.
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.
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: +
// Ejemplo de suma entre 2 enteros.
var x: Int = 4
var y: Int = 6
var suma: Int
suma = x + y // Resultado: 10.
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.
Operadores Lógicos:
Existen 3 operadores lógicos en Kotlin:
Operador de Disyunción: ||
// Ejemplo de disyunciones ||:
var x: Boolean = false
var y: Boolean = false
var disyuncion: Boolean
disyuncion = x || y // Resultado: false
Operador negación: !
// Ejemplo de negación !:
var x: Boolean = false
var negacion: Boolean
negacion = !x // Resultado: true
Expresión Descripción
Función Descripción
X Y Resultado AND
1 0 0
0 1 0
1 1 1
1 0 1
0 1 1
1 1 1
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.
var x: Int = 6
var resultado: Int
resultado = (x.inv() + 1) // calculado: inverso de 6 + 1, resulta
do: -6
Expresión Traducción:
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.