diff --git a/.gitignore b/.gitignore
index f6af9b8..7cb8db5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,4 @@ bin/
*.uml
*.uxf
*.umlcd
+target/
diff --git a/008_java_oo/java_util.md b/008_java_oo/java_util.md
index e6305a6..32e6a2d 100644
--- a/008_java_oo/java_util.md
+++ b/008_java_oo/java_util.md
@@ -434,9 +434,9 @@ anteceden y preceden
```mermaid
%%{init: {'theme': 'dark','themeVariables': {'clusterBkg': '#2b2f38'}}}%%
graph LR
- c1(cc1)-->c2(cc2)-->c1
- c2-->c3(cc3)-->c2
- c3-->c4(cc4)-->c3
+ c1((cc1))-->c2((cc2))-->c1
+ c2-->c3((cc3))-->c2
+ c3-->c4((cc4))-->c3
```
```java
@@ -605,18 +605,20 @@ public class TestRepasoArray {
```mermaid
%%{init: {'theme': 'dark','themeVariables': {'clusterBkg': '#2b2f38'}, 'flowchart': {'curve': 'monotoneX'}}}%%
-flowchart RL
- jln(java.lang.number)-->jld(java.lang.Double)--bytes-->8
- jln-->jlf(java.lang.Float)--bytes-->4
- jln-->jll(java.lang.Long)--bytes-->8
- jln-->jli(java.lang.Integer)--bytes-->4
- jln-->jls(java.lang.Short)--bytes-->2
- jln-->jlb(java.lang.Byte)--bytes-->1
- jln-->jlc(java.lang.Character)--bytes-->2
- jln-->jlbol(java.lang.Boolean)--bytes-->docs(JVM docs)
+flowchart LR
+ jln(java.lang.number)-->jld(java.lang.Double)-->b8
+ jln-->jlf(java.lang.Float)-->b4((4 bytes))
+ jln-->jll(java.lang.Long)-->b8((8 bytes))
+ jln-->jli(java.lang.Integer)-->b4
+ jln-->jls(java.lang.Short)-->b2((2 bytes))
+ jln-->jlb(java.lang.Byte)-->b1((1 byte))
+ jln-->jlc(java.lang.Character)-->b2
+ jln-->jlbol(java.lang.Boolean)-->docs{{JVM docs}}
click docs "https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-2.html#jvms-2.3.4" "Documentación Java" _blank
```
+
+
```java
public static void main(String[] args) {
Double numero_double = 33.0;
diff --git a/009_emprendimiento/README.md b/009_emprendimiento/README.md
index 2dda5ff..027539e 100644
--- a/009_emprendimiento/README.md
+++ b/009_emprendimiento/README.md
@@ -5,7 +5,7 @@
- [Curso](https://app.aluracursos.com/course/lean-startup-metodo-eficaz-idea-negocio-empresa),
Resumen [Lean Startup](./lean_startup.md)
- [Curso](https://app.aluracursos.com/course/emprendimiento-idea-plan-negocios),
-Resumen [Emprendimiento]()
+Resumen [Emprendimiento](./emprendimiento.md)
- [Curso](https://app.aluracursos.com/course/pitch-entrevistas-presentaciones-impactantes),
Resumen [Pitch para entrevistas]()
- Artículo
diff --git a/009_emprendimiento/apple_lcm.webp b/009_emprendimiento/apple_lcm.webp
new file mode 100644
index 0000000..7388d18
Binary files /dev/null and b/009_emprendimiento/apple_lcm.webp differ
diff --git a/009_emprendimiento/bmc_template.jpg b/009_emprendimiento/bmc_template.jpg
new file mode 100644
index 0000000..92b54bc
Binary files /dev/null and b/009_emprendimiento/bmc_template.jpg differ
diff --git a/009_emprendimiento/emprendimiento.md b/009_emprendimiento/emprendimiento.md
new file mode 100644
index 0000000..fd2e864
--- /dev/null
+++ b/009_emprendimiento/emprendimiento.md
@@ -0,0 +1,433 @@
+# Emprendimiento
+
+Curso
+[Emprendimiento](https://app.aluracursos.com/course/emprendimiento-idea-plan-negocios)
+
+## De la idea al plan de negocios
+
+### Agenda
+
+- Pilares del emprendimiento
+ - Qué es emprender
+ - Características de la mentalidad del emprendedor
+ - 3 fuerzas del negocio
+ - Emprendedor
+ - Oportunidades
+ - Recursos
+- Ideas y oportunidades
+ - Ideas vs oportunidades
+ - Atractividad del mercado
+ - Margen (precios y costes)
+ - Diferenciación estratégica
+- Modelo de negocio
+ - Propuesta de valor
+ - Business Model Canvas
+- Plan de negocios
+ - 9 elementos del plan
+
+## Emprendimiento
+
+> ***El proceso de crear algo nuevo asumiendo los riesgos y recompensas*** Robert
+Hisrich
+
+### Mindset emprendedor
+
+- Compromiso
+- Determinación
+- Perseverancia
+- Ambición (crecimiento)
+- Orientado a objetivos
+- Iniciativa y proactividad
+- Tolerancia a estés
+- Asumir riesgos calculados
+- Comparte riesgos
+- Buen manejo de errores y fracasos
+- Capacidad de aprendizaje
+- Construir equipos
+
+### Mindset anti-emprendedor
+
+- Sentimiento de invulnerabilidad
+- Infexibilidad
+- Antiautoridad
+- Impulsividad
+- Falta de control
+- Perfeccionismo
+- Creer saberlo todo
+- Querer trabajar poco
+
+### Startup
+
+- Nuevos mercados y productos
+- Tecnología
+- Escalable
+- Disruptiva
+- Incertidumbre
+- Debe probar su modelo de negocio
+- Puede transformase en ¿unicornio?
+
+**StartUp vs Negocio LifeStyle**
+
+### Intraemprendimiento
+
+Emprender dentro de una corporación tiene oportunidades más restrictivas
+
+- Representar intereses de un conjunto mayor
+- Core Business
+- Legado de la organización
+- Enfoque a corto plazo
+- Sin riesgos
+
+Innovación radical como oportunidad de intraemprendimiento
+
+En una startup todos son emprendedores
+
+## Ejemplo Bytebank
+
+- Una empresa - un producto
+- Tarjeta de crédito virtual 100%
+- Dos emprendedores
+- \+ 6 exfuncionarios del sector bancario
+- Compras online
+- Público objetivo: jóvenes de clase media
+- Autoservicio: Sin sucursales
+- Seguro contra extravío
+- Transparencia
+
+----
+
+## Pilares del emprendimiento
+
+1. **Fundadores**
+ - Emprendedor
+ - Equipo de fundación
+ - Experiencia y conocimiento
+ - Autoridad
+ - Gestión, habilidades y competencias
+ - Actitud, tenacidad, osadía
+2. **Oportunidades**
+ - Encontrar la oportunidad
+ - Necesidad/problema real
+ - Tamaño del mercado
+ - Crecimiento
+ - Momento correcto
+ - Fuerzas (externas e internas)
+ - Lucratividad y rentabilidad
+ - Clientes 'ángeles' (pioneros)
+3. **Recursos**
+ - Financieros
+ - Tecnología
+ - Producción
+ - Personas
+ - Sistemas
+ - Consejeros/consultores
+ - Controles de gestión
+
+
+
+| Guia del emprendedor ||
+| - | - |
+| **¿Mis metas están definidas?** | Aspiraciones personales
Tamaño y sostenibilidad del negocio
Tolerancia a riesgos |
+| **¿Mi estrategia es correcta?** | Definida y clara
Lucrativa y con alto potencial de crecimiento
Durabilidad |
+| **¿Puedo ejecutar la estrategia?** | Recursos
Infraestructura
Papel del fundador |
+
+
+
+| Modelo Flavio Augusto ||
+| - | - |
+| **¿Lo ves antes que todos?** | Como una información privilegiada
ej. equipo fútbol en USA |
+| **Coraje** | ¡Nadie te va a empujar, tienes que saltar! |
+| **Competencia** | Tú tienes que hacerlo realidad |
+
+
+
+|Fórmula Daniel Wainmann |||
+| - | - | - |
+| Suerte | Talento | Estrategia |
+| **10%** | **10%** | **80%** |
+
+
+
+> Referencias:
+> - The Questions Every Entrepreneurs Must Answer
+> *(Harvard Business Review) - Amar Bhide*
+> - Being a Sucessful Entrepreneur isn't Only About Having the Best Ideas
+> *(Harvard Business Review) - Andy Molinsky*
+> - Six Fundamentals Every Entrepreneur Needs
+> *(Harvard Business Review) - Bob Diener*
+> - TEDx Emprender e innovar en América Latina *Daniel Wainmann*
+
+## Ideas y oportunidades
+
+> **El problema debe ser más importante que la solución** Daniel Wainmann
+
+
+### Valor potencial
+
+**`Problema` + `Solucion con diferencial` + `Emprendedor`**
+
+..*"Las ideas son abuntantes, y las **soluciones escasas**"*..
+
+### Atractivad del mercado
+
+- Tamaño del mercado
+ - ¿Cuántos clientes potenciales posee tu empresa?
+ - ¿Cuánto genera esa industria actualmente en $?
+ - ¿Tu producto es algo realmente necesario?
+- Compradores preparados
+ - ¿Cuál es el nivel de preparación de tus compradores?
+- Homogeneidad de los compradores
+ - ¿Las necesidades de su público son las mismas?
+ - ¿Necesitas personalizar?
+- Verificar las reglas del segmento/area/rubro del país
+
+### Margen precio costo
+
+¿El margen líquido vale la pena?
+
+| Rubro | Margen |
+| - | - |
+| Minería | 7% |
+| Aviación | 11% |
+| Farmacia | 15% |
+| Entretenimiento | 13% |
+| Seguros | 8% |
+| Energía | 4% |
+| Supermercado | 2% |
+| Cigarrillos | 28% |
+| Software | 12% |
+
+| Tecnología | Margen |
+| - | - |
+| Baidu | 47.35% |
+| Adobe | 23% |
+| Microsoft | 21% |
+| AWS | 23% |
+| Apple | 24% |
+| Yahoo | 7% |
+
+
+### Escalabilidad e implementación
+
+[
](./viab_graph.png)
+[
](./esca_graph.png)
+
+### Recurrencia
+
+Ejs. de servicios por subscripción: Gimnasios, Netflix, Spotify, etc.
+
+### Competencia
+
+- ¿Como se mueve el mercado en este momento?
+- ¿Cuales son los competidores y sustitutos?
+- Mirar fuera de la "mesa de juego"
+
+### Diferencial
+
+**¿Que te diferencia?** Ejemplo marcas de vehículos
+
+- Precio
+ - Volkswagen
+ - Ford
+- Lujo
+ - Mercedes-Benz
+- "Lujo y performance"
+ - BMW
+- Calidad y precio
+ - Nissan
+- Seguridad
+ - Volvo
+- Performance y diseño
+ - Ferrari
+- Tecnología
+ - Tesla
+
+### Facilidad de divulgación
+
+¿De que forma se va a anunciar/comunicar con el cliente?
+
+### Equipo de trabajo
+
+Personas necesarias para que la empresa sea atractiva para inversionistas y
+consumidores
+
+> [Four VCs on Evaluating Opportunities ](https://hbswk.hbs.edu/item/four-vcs-on-evaluating-opportunities)
+*(Harvard Business School) - Lauren Barley*
+[Creating a Culture of Entrepreneurship](https://hbswk.hbs.edu/item/skills-and-behaviors-that-make-entrepreneurs-successful)
+*Richard Patton*
+
+## Propuesta de valor
+
+- Necesidad: ¿Que necesidades resuelve tu producto o servicio?
+- Segmentos de clientes: Edad, clase social, género, intereses, etc.
+- Posicionamento
+- ¿Mercado nuevo o existente?
+- ¿Tendrá complementos?
+- ¿Cual es el MVP?
+- Precios/Categorías de precios
+- Precio o Costo para el cliente para cambiar a tu producto/servicio
+
+### Tecnologías, operaciones y márgenes
+
+```mermaid
+%%{init: {'theme': 'dark','themeVariables': {'clusterBkg': '#2b2f38'}, 'flowchart': {'curve': 'monotoneY'}}}%%
+flowchart LR
+subgraph "Que actividades son necesarias para desarrollar el producto"
+MB["Marketing &
+Branding"]
+VN[Ventas]
+ET[Entregas]
+JR[Jurídico]
+CT[Contabilidad]
+DS[Desarrollo]
+RH["Recuros
+Humanos"]
+end
+```
+
+Propiedad intelectual - ¿Hay algo que sea patentable?
+
+```mermaid
+%%{init: {'theme': 'dark','themeVariables': {'clusterBkg': '#2b2f38'}, 'flowchart': {'curve': 'monotoneY'}}}%%
+flowchart LR
+subgraph "Donde y como tu producto puede ser adquirido"
+VN[Teléfono]
+ET[Aplicación]
+JR[Página web]
+DS[Supermercados]
+RH[WhatsApp]
+CT["Redes
+Sociales"]
+end
+```
+
+### Generar demanda
+
+```mermaid
+%%{init: {'theme': 'dark','themeVariables': {'clusterBkg': '#2b2f38'}, 'flowchart': {'curve': 'monotoneY'}}}%%
+flowchart LR
+subgraph "¿Como atraer clientes?"
+JR["Google adsense
+Social media ads"]
+VN[Outdoor]
+RH[Eventos]
+CT[Freemium]
+VR[Viralización]
+DS["Revistas
+Periodicos"]
+ET["Radio, TV
+podcast"]
+end
+```
+
+### Customer Lifetime Value
+
+¿Cual es el CVL esperado? Valor del tiempo de vida del cliente
+
+- **CLV** `=` Valor medio de compras de un cliente `x` compras por año `x` años de permanencia
+
+¿Cual es el costo de adquisición del cliente (CAC)?
+
+- **CAC** `=` Gastos `/` Nro. de clientes nuevos
+
+¿Existe una oportunidad de *Upsell*?
+
+Recurrencia, subscripción
+
+### Margen
+
+[
](./punto_muerto.png)
+
+- Margen de ganancia
+- Costos fijos
+- Punto de equilibrio (*breakeven*)
+
+### Ritmo de crecimiento
+
+¿El mercado está parando o en crecimiento, a que ritmo?
+
+### Capital de giro
+
+¿Cual es la necesidad de capital de giro del negocio?
+
+## Business Model Canvas
+
+[
](./bmc_template.jpg)
+
+### Ejms. BMC
+
+[
](./tesla_lcm.webp)
+[
](./google_lcm.webp)
+[
](./apple_lcm.webp)
+[
](./skype_lcm.webp)
+[
](./facebook_lcm.webp)
+
+## Plan de negocio
+
+*.."La idea parecía fantástica, pero los números dicen otra cosa"..*
+
+### Resumen ejecutivo
+
+La oportunidad de llamar la atencion del inversor
+
+- Conceptp del negocio
+- UVP (Unique Value Proposition)
+- Inversion necesaria
+- Clientes y mercado
+
+### Producto o servicio
+
+- Funcionalidades
+- Público objetivo
+- Fuentes de ingresos
+- Estado actual de desarrollo
+- ¿Existe algo similar en otro país o mercado?
+- ¿Como fidelizar al cliente?
+
+### Equipo
+
+- ¿Quienes son los fundadores?
+- Capacidad de cada fundador par el éxito
+- División de responsbilidades
+
+### Mercado
+
+- Tamaño del mercado
+- Potencial de crecimiento
+- Competencia
+- Puntos fuertes y débiles
+
+### Marketing y ventas
+
+- ¿Como promover?
+- ¿Como distribuir?
+- ¿Como fijar el precio?
+
+### Organización
+
+- Construcción de equipos
+- Plan de contratación
+- Tercerización
+- Aliados
+- Canales de ventas
+
+### Cronograma
+
+- Responsables
+- Plazos
+- Hitos (*milestones*)
+
+### Riesgos
+
+- El mejor y el peor escenario
+- Principales riesgos
+- Como mitigar los riesgos
+
+### Finazas
+
+- Estado de resultados
+- Flujo de caja
+- Tasación
+- Balance
+- Fuentes de financiación
+
diff --git a/009_emprendimiento/esca-graph.svg b/009_emprendimiento/esca-graph.svg
new file mode 100644
index 0000000..73460d5
--- /dev/null
+++ b/009_emprendimiento/esca-graph.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/009_emprendimiento/esca_graph.png b/009_emprendimiento/esca_graph.png
new file mode 100644
index 0000000..205abd9
Binary files /dev/null and b/009_emprendimiento/esca_graph.png differ
diff --git a/009_emprendimiento/facebook_lcm.webp b/009_emprendimiento/facebook_lcm.webp
new file mode 100644
index 0000000..2e2b586
Binary files /dev/null and b/009_emprendimiento/facebook_lcm.webp differ
diff --git a/009_emprendimiento/google_lcm.webp b/009_emprendimiento/google_lcm.webp
new file mode 100644
index 0000000..cbb5566
Binary files /dev/null and b/009_emprendimiento/google_lcm.webp differ
diff --git a/009_emprendimiento/lean_canvas_model_dark.webp b/009_emprendimiento/lean_canvas_model_dark.webp
new file mode 100644
index 0000000..0b14f19
Binary files /dev/null and b/009_emprendimiento/lean_canvas_model_dark.webp differ
diff --git a/009_emprendimiento/lean_canvas_model_template.webp b/009_emprendimiento/lean_canvas_model_template.webp
new file mode 100644
index 0000000..a3c46dd
Binary files /dev/null and b/009_emprendimiento/lean_canvas_model_template.webp differ
diff --git a/009_emprendimiento/punto_muerto.png b/009_emprendimiento/punto_muerto.png
new file mode 100644
index 0000000..ea86a55
Binary files /dev/null and b/009_emprendimiento/punto_muerto.png differ
diff --git a/009_emprendimiento/skype_lcm.webp b/009_emprendimiento/skype_lcm.webp
new file mode 100644
index 0000000..928748b
Binary files /dev/null and b/009_emprendimiento/skype_lcm.webp differ
diff --git a/009_emprendimiento/tesla_lcm.webp b/009_emprendimiento/tesla_lcm.webp
new file mode 100644
index 0000000..a5deca0
Binary files /dev/null and b/009_emprendimiento/tesla_lcm.webp differ
diff --git a/009_emprendimiento/viab-graph.svg b/009_emprendimiento/viab-graph.svg
new file mode 100644
index 0000000..54ae39b
--- /dev/null
+++ b/009_emprendimiento/viab-graph.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/009_emprendimiento/viab_graph.png b/009_emprendimiento/viab_graph.png
new file mode 100644
index 0000000..efa30f4
Binary files /dev/null and b/009_emprendimiento/viab_graph.png differ
diff --git a/010_spring_boot/README.md b/010_spring_boot/README.md
index b63123a..8d1f57c 100644
--- a/010_spring_boot/README.md
+++ b/010_spring_boot/README.md
@@ -4,4 +4,6 @@ Tablero en [trello](https://trello.com/b/gWJsG18e/g5-formaci%C3%B3n-spring-boot)
- [Curso](https://app.aluracursos.com/course/introduccion-sql-mysql-manipule-consulte-datos)
Java y [bases de datos](./base_de_datos.md)
-
+- Lectura [JDBC](https://www.aluracursos.com/blog/conociendo-el-jdbc)
+- [Curso](https://app.aluracursos.com/course/java-jdbc-trabajando-base-datos)
+Java y [JDBC](./jdbc.md)
diff --git a/010_spring_boot/base_de_datos.md b/010_spring_boot/base_de_datos.md
index 33a9073..f4dfda0 100644
--- a/010_spring_boot/base_de_datos.md
+++ b/010_spring_boot/base_de_datos.md
@@ -292,7 +292,7 @@ realizado su primera compra.
```sql
-- show databases;
-use jugos;
+USE jugos;
CREATE TABLE cliente(
dni VARCHAR(20),
nombre VARCHAR(150),
@@ -317,7 +317,7 @@ CREATE TABLE cliente(
#### Creación de tabla **`vendedor`**
```sql
-use jugos;
+USE jugos;
CREATE TABLE vendedor(
matricula VARCHAR(4),
nombre VARCHAR(100),
@@ -328,7 +328,7 @@ CREATE TABLE vendedor(
#### Elimnar tabla
```sql
-use jugos;
+USE jugos;
-- CREATE TABLE vendedor2(
-- matricula VARCHAR(4),
-- nombre VARCHAR(100),
diff --git a/010_spring_boot/java_jdbc/control-de-stock/pom.xml b/010_spring_boot/java_jdbc/control-de-stock/pom.xml
new file mode 100644
index 0000000..cb1dbd9
--- /dev/null
+++ b/010_spring_boot/java_jdbc/control-de-stock/pom.xml
@@ -0,0 +1,42 @@
+
+ 4.0.0
+ com.alura
+ control-de-stock
+ 0.0.1-SNAPSHOT
+ Control de Stock
+ Proyecto para trabajar con bases de datos con JDBC
+
+ 17
+
+
+
+ mysql
+ mysql-connector-java
+ 8.0.33
+
+
+ com.mchange
+ c3p0
+ 0.9.5.5
+
+
+ com.mchange
+ mchange-commons-java
+ 0.2.20
+
+
+
+
+
+ org.apache.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+ ${java.version}
+ ${java.version}
+ true
+
+
+
+
+
diff --git a/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/ControlDeStockMain.java b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/ControlDeStockMain.java
new file mode 100644
index 0000000..1cb926e
--- /dev/null
+++ b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/ControlDeStockMain.java
@@ -0,0 +1,14 @@
+package com.alura.jdbc;
+
+import javax.swing.JFrame;
+
+import com.alura.jdbc.view.ControlDeStockFrame;
+
+public class ControlDeStockMain {
+
+ public static void main(String[] args) {
+ ControlDeStockFrame produtoCategoriaFrame = new ControlDeStockFrame();
+ produtoCategoriaFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ }
+
+}
\ No newline at end of file
diff --git a/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/controller/CategoriaController.java b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/controller/CategoriaController.java
new file mode 100644
index 0000000..4a6bb94
--- /dev/null
+++ b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/controller/CategoriaController.java
@@ -0,0 +1,26 @@
+package com.alura.jdbc.controller;
+
+import java.util.List;
+
+import com.alura.jdbc.dao.CategoriaDAO;
+import com.alura.jdbc.factory.ConnectionFactory;
+import com.alura.jdbc.modelo.Categoria;
+
+public class CategoriaController {
+
+ private CategoriaDAO categoriaDAO;
+
+ public CategoriaController() {
+ var factory = new ConnectionFactory();
+ this.categoriaDAO = new CategoriaDAO(factory.recuperaConexion());
+ }
+
+ public List listar() {
+ return categoriaDAO.listar();
+ }
+
+ public List cargaReporte() {
+ return this.categoriaDAO.listarConProductos();
+ }
+
+}
diff --git a/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/controller/ProductoController.java b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/controller/ProductoController.java
new file mode 100644
index 0000000..44ac0d7
--- /dev/null
+++ b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/controller/ProductoController.java
@@ -0,0 +1,40 @@
+package com.alura.jdbc.controller;
+
+import java.util.List;
+
+import com.alura.jdbc.dao.ProductoDAO;
+import com.alura.jdbc.factory.ConnectionFactory;
+import com.alura.jdbc.modelo.Categoria;
+import com.alura.jdbc.modelo.Producto;
+
+public class ProductoController {
+
+ private ProductoDAO productoDAO;
+
+ public ProductoController() {
+ this.productoDAO = new ProductoDAO(new ConnectionFactory().recuperaConexion());
+ }
+
+ public int modificar(Producto producto) {
+ return productoDAO.modificar(producto);
+ }
+
+ public int eliminar(Integer id) {
+ return productoDAO.eliminar(id);
+ }
+
+ public List listar() {
+ return productoDAO.listar();
+ }
+
+ public List listar(Categoria categoria) {
+ return productoDAO.listar(categoria.getId());
+ }
+
+ public void guardar(Producto producto, Integer categoriaId) {
+ producto.setCategoriaId(categoriaId);
+ productoDAO.guardar(producto);
+ }
+
+
+}
\ No newline at end of file
diff --git a/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/dao/CategoriaDAO.java b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/dao/CategoriaDAO.java
new file mode 100644
index 0000000..5c96438
--- /dev/null
+++ b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/dao/CategoriaDAO.java
@@ -0,0 +1,135 @@
+package com.alura.jdbc.dao;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import com.alura.jdbc.modelo.Categoria;
+import com.alura.jdbc.modelo.Producto;
+
+public class CategoriaDAO {
+ private final Connection con;
+
+ public CategoriaDAO(Connection conexion) {
+ this.con = conexion;
+ }
+
+ public void guardar(Categoria categoria) {
+ try {
+ final PreparedStatement statement = con.prepareStatement(
+ "INSERT INTO categoria(nombre) VALUES(?)",
+ Statement.RETURN_GENERATED_KEYS);
+ try (statement) {
+ ejecutaRegistro(categoria, statement);
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void ejecutaRegistro(Categoria categoria, PreparedStatement statement)
+ throws SQLException {
+ statement.setString(1, categoria.getNombre());
+ statement.execute();
+ final ResultSet resultSet = statement.getGeneratedKeys();
+ try (resultSet) {
+ while (resultSet.next()) {
+ categoria.setId(resultSet.getInt(1));
+ System.out.println(String.format("Categoria agregada %s: ", categoria));
+ }
+ }
+ }
+
+ public List listar() {
+ List resultado = new ArrayList<>();
+ final String query = "SELECT ID, NOMBRE FROM categoria;";
+ System.out.println(query);
+ try {
+
+ final PreparedStatement statement = con.prepareStatement(query);
+ try (statement) {
+ statement.execute();
+ ResultSet resultSet = statement.getResultSet();
+ while (resultSet.next()) {
+ Categoria fila = new Categoria(
+ resultSet.getInt("ID"),
+ resultSet.getString("NOMBRE")
+ );
+ resultado.add(fila);
+ }
+ return resultado;
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public int modificar(Categoria categoria) {
+ try {
+ final String query = "UPDATE categoria SET NOMBRE=? WHERE ID=?;";
+ final PreparedStatement statement = con.prepareStatement(query);
+ try (statement) {
+ statement.setString(1, categoria.getNombre());
+ statement.setInt(2, categoria.getId());
+ statement.execute();
+ int resultado = statement.getUpdateCount();
+ return resultado;
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public int eliminar(Integer id) {
+ try {
+ final PreparedStatement statement = con.prepareStatement("DELETE FROM categoria WHERE ID=?;");
+ try (statement) {
+ statement.setInt(1, id);
+ statement.execute();
+ int resultado = statement.getUpdateCount();
+ return resultado;
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public List listarConProductos() {
+ List resultado = new ArrayList<>();
+ final String query = "SELECT C.ID, C.NOMBRE, P.ID, P.NOMBRE, P.CANTIDAD FROM categoria C "
+ + "INNER JOIN producto P ON C.ID = P.CATEGORIA_ID ";
+ System.out.println(query);
+ try {
+ final PreparedStatement statement = con.prepareStatement(query);
+ try (statement) {
+ statement.execute();
+ final ResultSet resultSet = statement.getResultSet();
+ try (resultSet){
+ while (resultSet.next()) {
+ Integer categoriaId = resultSet.getInt("C.ID");
+ String categoriaNombre = resultSet.getString("C.NOMBRE");
+ var categoria = resultado
+ .stream()
+ .filter(cat -> cat.getId().equals(categoriaId))
+ .findAny().orElseGet(() -> {
+ Categoria cat = new Categoria(categoriaId, categoriaNombre);
+ resultado.add(cat);
+ return cat;
+ });
+ Producto producto = new Producto(resultSet.getInt("P.ID"),
+ resultSet.getString("P.NOMBRE"),
+ resultSet.getInt("P.CANTIDAD"));
+ categoria.agregar(producto);
+ }
+ };
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ return resultado;
+ }
+
+}
diff --git a/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/dao/ProductoDAO.java b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/dao/ProductoDAO.java
new file mode 100644
index 0000000..6582262
--- /dev/null
+++ b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/dao/ProductoDAO.java
@@ -0,0 +1,144 @@
+package com.alura.jdbc.dao;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import com.alura.jdbc.modelo.Producto;
+
+public class ProductoDAO {
+ private final Connection con;
+
+ public ProductoDAO(Connection conexion) {
+ this.con = conexion;
+ }
+
+ public void guardar(Producto producto) {
+ try {
+ final PreparedStatement statement = con.prepareStatement(
+ "INSERT INTO producto(nombre, descripcion, cantidad, categoria_id) VALUES(?,?,?,?)",
+ Statement.RETURN_GENERATED_KEYS);
+ try (statement) {
+ ejecutaRegistro(producto, statement);
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void ejecutaRegistro(Producto producto, PreparedStatement statement)
+ throws SQLException {
+ statement.setString(1, producto.getNombre());
+ statement.setString(2, producto.getDescripcion());
+ statement.setInt(3, producto.getCantidad());
+ statement.setInt(4, producto.getCategoriaId());
+ statement.execute();
+ final ResultSet resultSet = statement.getGeneratedKeys();
+ try (resultSet) {
+ while (resultSet.next()) {
+ producto.setId(resultSet.getInt(1));
+ System.out.println(String.format("Producto insertado %s: ", producto));
+ }
+ }
+ }
+
+ public List listar() {
+ List resultado = new ArrayList<>();
+ try {
+ final PreparedStatement statement = con.prepareStatement(
+ "SELECT ID, NOMBRE, DESCRIPCION, CANTIDAD, CATEGORIA_ID FROM producto;");
+ try (statement) {
+ statement.execute();
+ ResultSet resultSet = statement.getResultSet();
+ while (resultSet.next()) {
+ Producto fila = new Producto(
+ resultSet.getInt("ID"),
+ resultSet.getString("NOMBRE"),
+ resultSet.getString("DESCRIPCION"),
+ resultSet.getInt("CANTIDAD"),
+ resultSet.getInt("CATEGORIA_ID")
+ );
+ resultado.add(fila);
+ }
+ return resultado;
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public int modificar(Producto producto) {
+ String query = "UPDATE producto SET NOMBRE=?, DESCRIPCION=?, CANTIDAD=? ";
+ if (producto.getCategoriaId() != null) {
+ query += ", CATEGORIA_ID=? WHERE ID=?;";
+ } else {
+ query += " WHERE ID=?;";
+ }
+
+ try {
+ final PreparedStatement statement = con.prepareStatement(query);
+ try (statement) {
+ statement.setString(1, producto.getNombre());
+ statement.setString(2, producto.getDescripcion());
+ statement.setInt(3, producto.getCantidad());
+ if (producto.getCategoriaId() != null) {
+ statement.setInt(4, producto.getCategoriaId());
+ statement.setInt(5, producto.getId());
+ } else {
+ statement.setInt(4, producto.getId());
+ }
+ //System.out.println(statement.toString());
+ statement.execute();
+ int resultado = statement.getUpdateCount();
+ return resultado;
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public int eliminar(Integer id) {
+ try {
+ final PreparedStatement statement = con.prepareStatement("DELETE FROM producto WHERE ID=?;");
+ try (statement) {
+ statement.setInt(1, id);
+ statement.execute();
+ int resultado = statement.getUpdateCount();
+ return resultado;
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public List listar(Integer id) {
+ List resultado = new ArrayList<>();
+ final String query = "SELECT ID, NOMBRE, DESCRIPCION, CANTIDAD, CATEGORIA_ID FROM producto WHERE categoria_id=?;";
+ //System.out.println(query);
+ try {
+ final PreparedStatement statement = con.prepareStatement(query);
+ statement.setInt(1, id);
+ try (statement) {
+ statement.execute();
+ ResultSet resultSet = statement.getResultSet();
+ while (resultSet.next()) {
+ Producto fila = new Producto(
+ resultSet.getInt("ID"),
+ resultSet.getString("NOMBRE"),
+ resultSet.getString("DESCRIPCION"),
+ resultSet.getInt("CANTIDAD"),
+ resultSet.getInt("CATEGORIA_ID")
+ );
+ resultado.add(fila);
+ }
+ return resultado;
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/factory/ConnectionFactory.java b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/factory/ConnectionFactory.java
new file mode 100644
index 0000000..87e4420
--- /dev/null
+++ b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/factory/ConnectionFactory.java
@@ -0,0 +1,37 @@
+package com.alura.jdbc.factory;
+
+import java.sql.Connection;
+import javax.sql.DataSource;
+
+import com.mchange.v2.c3p0.ComboPooledDataSource;
+
+public class ConnectionFactory {
+
+ private final static String driver = "jdbc:mysql://";
+ private final static String dbaddr = "192.168.0.8:3306/";
+ private final static String params = "?useTimeZone=true&serverTimeZone=UTC";
+ private final static String dbname = "control_de_stock";
+ private final static String dburl = driver+dbaddr+dbname+params;
+ private final static String dbuser = "alura";
+ private final static String dbpass = "alura";
+
+ private DataSource datasource;
+
+ public ConnectionFactory() {
+ var pooledDataSource = new ComboPooledDataSource();
+ pooledDataSource.setJdbcUrl(dburl);
+ pooledDataSource.setUser(dbuser);
+ pooledDataSource.setPassword(dbpass);
+ pooledDataSource.setMaxPoolSize(10);
+ this.datasource = pooledDataSource;
+ }
+
+ public Connection recuperaConexion() {
+ //return DriverManager.getConnection(dburl, dbuser, dbpass);
+ try {
+ return this.datasource.getConnection();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/modelo/Categoria.java b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/modelo/Categoria.java
new file mode 100644
index 0000000..5ca7ef9
--- /dev/null
+++ b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/modelo/Categoria.java
@@ -0,0 +1,44 @@
+package com.alura.jdbc.modelo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Categoria {
+
+ private Integer id;
+ private String nombre;
+ private List productos;
+
+ public Categoria(int id, String nombre) {
+ this.id = id;
+ this.nombre = nombre;
+ }
+
+ public String getNombre() {
+ return this.nombre;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public Integer getId() {
+ return this.id;
+ }
+
+ @Override
+ public String toString() {
+ return this.getNombre();
+ }
+
+ public void agregar(Producto producto) {
+ if (this.productos == null) {
+ this.productos = new ArrayList<>();
+ }
+ this.productos.add(producto);
+ }
+
+ public List getProductos() {
+ return this.productos;
+ }
+}
diff --git a/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/modelo/Producto.java b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/modelo/Producto.java
new file mode 100644
index 0000000..82faf06
--- /dev/null
+++ b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/modelo/Producto.java
@@ -0,0 +1,71 @@
+package com.alura.jdbc.modelo;
+
+public class Producto {
+ private Integer id;
+ private String nombre;
+ private String descripcion;
+ private Integer cantidad;
+ private Integer categoriaId;
+
+ public Producto(String nombre, String descripcion, Integer cantidad) {
+ this.nombre = nombre;
+ this.descripcion = descripcion;
+ this.cantidad = cantidad;
+ }
+
+ public Producto(Integer id, String nombre, String descripcion, Integer cantidad) {
+ this.id = id;
+ this.nombre = nombre;
+ this.descripcion = descripcion;
+ this.cantidad = cantidad;
+ }
+
+ public Producto(Integer id, String nombre, String descripcion, Integer cantidad, Integer categoriaId) {
+ this.id = id;
+ this.nombre = nombre;
+ this.descripcion = descripcion;
+ this.cantidad = cantidad;
+ this.categoriaId = categoriaId;
+ }
+
+ public Producto(Integer id, String nombre, Integer cantidad) {
+ this.id = id;
+ this.nombre = nombre;
+ this.cantidad = cantidad;
+ }
+
+ public String getNombre() {
+ return this.nombre;
+ }
+
+ public String getDescripcion() {
+ return this.descripcion;
+ }
+
+ public int getCantidad() {
+ return this.cantidad;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public Integer getId() {
+ return this.id;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{id: %d, nombre: %s, descripción: %s, cantidad: %d, categoria: %d}",
+ this.id, this.nombre, this.descripcion, this.cantidad, this.categoriaId);
+ }
+
+ public void setCategoriaId(Integer categoriaId) {
+ this.categoriaId = categoriaId;
+ }
+
+ public Integer getCategoriaId() {
+ return this.categoriaId;
+ }
+
+}
diff --git a/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/pruebas/PruebaConexion.java b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/pruebas/PruebaConexion.java
new file mode 100644
index 0000000..1e54b07
--- /dev/null
+++ b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/pruebas/PruebaConexion.java
@@ -0,0 +1,16 @@
+package com.alura.jdbc.pruebas;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import com.alura.jdbc.factory.ConnectionFactory;
+
+public class PruebaConexion {
+
+ public static void main(String[] args) throws SQLException {
+ Connection con = new ConnectionFactory().recuperaConexion();
+ System.out.println("Cerrando conexión");
+ con.close();
+ }
+
+}
diff --git a/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/pruebas/PruebaDelete.java b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/pruebas/PruebaDelete.java
new file mode 100644
index 0000000..c4e14a1
--- /dev/null
+++ b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/pruebas/PruebaDelete.java
@@ -0,0 +1,16 @@
+package com.alura.jdbc.pruebas;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import com.alura.jdbc.factory.ConnectionFactory;
+
+public class PruebaDelete {
+ public static void main(String[] args) throws SQLException {
+ Connection con = new ConnectionFactory().recuperaConexion();
+ Statement stmnt = con.createStatement();
+ stmnt.execute("DELETE FROM producto WHERE ID=99");
+ System.out.println(stmnt.getUpdateCount());
+ }
+}
diff --git a/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/pruebas/PruebaPoolDeConexiones.java b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/pruebas/PruebaPoolDeConexiones.java
new file mode 100644
index 0000000..b995991
--- /dev/null
+++ b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/pruebas/PruebaPoolDeConexiones.java
@@ -0,0 +1,19 @@
+package com.alura.jdbc.pruebas;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import com.alura.jdbc.factory.ConnectionFactory;
+
+public class PruebaPoolDeConexiones {
+
+ public static void main(String[] args) throws SQLException {
+ ConnectionFactory connectionFactory = new ConnectionFactory();
+ for (int i = 0; i < 20; i++) {
+ @SuppressWarnings("unused")
+ Connection con = connectionFactory.recuperaConexion();
+ System.out.println("Abriendo conexión nro: "+(i+1));
+ }
+ }
+
+}
diff --git a/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/view/ControlDeStockFrame.java b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/view/ControlDeStockFrame.java
new file mode 100644
index 0000000..751ea03
--- /dev/null
+++ b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/view/ControlDeStockFrame.java
@@ -0,0 +1,285 @@
+package com.alura.jdbc.view;
+
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Optional;
+
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.table.DefaultTableModel;
+
+import com.alura.jdbc.controller.CategoriaController;
+import com.alura.jdbc.controller.ProductoController;
+import com.alura.jdbc.modelo.Categoria;
+import com.alura.jdbc.modelo.Producto;
+
+public class ControlDeStockFrame extends JFrame {
+
+ private static final long serialVersionUID = 1L;
+
+ private JLabel labelNombre, labelDescripcion, labelCantidad, labelCategoria;
+ private JTextField textoNombre, textoDescripcion, textoCantidad;
+ private JComboBox comboCategoria;
+ private JButton botonGuardar, botonModificar, botonLimpiar, botonEliminar, botonReporte;
+ private JTable tabla;
+ private DefaultTableModel modelo;
+ private ProductoController productoController;
+ private CategoriaController categoriaController;
+
+ public ControlDeStockFrame() {
+ super("Productos");
+
+ this.categoriaController = new CategoriaController();
+ this.productoController = new ProductoController();
+
+ Container container = getContentPane();
+ setLayout(null);
+
+ configurarCamposDelFormulario(container);
+
+ configurarTablaDeContenido(container);
+
+ configurarAccionesDelFormulario();
+ }
+
+ private void configurarTablaDeContenido(Container container) {
+ tabla = new JTable();
+
+ modelo = (DefaultTableModel) tabla.getModel();
+ modelo.addColumn("Identificador del Producto");
+ modelo.addColumn("Nombre del Producto");
+ modelo.addColumn("Descripción del Producto");
+ modelo.addColumn("Cantidad");
+
+ cargarTabla();
+
+ tabla.setBounds(10, 205, 760, 280);
+
+ botonEliminar = new JButton("Eliminar");
+ botonModificar = new JButton("Modificar");
+ botonReporte = new JButton("Ver Reporte");
+ botonEliminar.setBounds(10, 500, 80, 20);
+ botonModificar.setBounds(100, 500, 80, 20);
+ botonReporte.setBounds(190, 500, 80, 20);
+
+ container.add(tabla);
+ container.add(botonEliminar);
+ container.add(botonModificar);
+ container.add(botonReporte);
+
+ setSize(800, 600);
+ setVisible(true);
+ setLocationRelativeTo(null);
+ }
+
+ private void configurarCamposDelFormulario(Container container) {
+ labelNombre = new JLabel("Nombre del Producto");
+ labelDescripcion = new JLabel("Descripción del Producto");
+ labelCantidad = new JLabel("Cantidad");
+ labelCategoria = new JLabel("Categoría del Producto");
+
+ labelNombre.setBounds(10, 10, 240, 15);
+ labelDescripcion.setBounds(10, 50, 240, 15);
+ labelCantidad.setBounds(10, 90, 240, 15);
+ labelCategoria.setBounds(10, 130, 240, 15);
+
+ labelNombre.setForeground(Color.BLACK);
+ labelDescripcion.setForeground(Color.BLACK);
+ labelCategoria.setForeground(Color.BLACK);
+
+ textoNombre = new JTextField();
+ textoDescripcion = new JTextField();
+ textoCantidad = new JTextField();
+ comboCategoria = new JComboBox<>();
+ comboCategoria.addItem(new Categoria(0, "Elige una categoría"));
+
+ var categorias = this.categoriaController.listar();
+ categorias.forEach(categoria -> comboCategoria.addItem(categoria));
+
+ textoNombre.setBounds(10, 25, 265, 20);
+ textoDescripcion.setBounds(10, 65, 265, 20);
+ textoCantidad.setBounds(10, 105, 265, 20);
+ comboCategoria.setBounds(10, 145, 265, 20);
+
+ botonGuardar = new JButton("Guardar");
+ botonLimpiar = new JButton("Limpiar");
+ botonGuardar.setBounds(10, 175, 80, 20);
+ botonLimpiar.setBounds(100, 175, 80, 20);
+
+ container.add(labelNombre);
+ container.add(labelDescripcion);
+ container.add(labelCantidad);
+ container.add(labelCategoria);
+ container.add(textoNombre);
+ container.add(textoDescripcion);
+ container.add(textoCantidad);
+ container.add(comboCategoria);
+ container.add(botonGuardar);
+ container.add(botonLimpiar);
+ }
+
+ private void configurarAccionesDelFormulario() {
+ botonGuardar.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ guardar();
+ limpiarTabla();
+ cargarTabla();
+ }
+ });
+
+ botonLimpiar.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ limpiarFormulario();
+ }
+ });
+
+ botonEliminar.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ eliminar();
+ limpiarTabla();
+ cargarTabla();
+ }
+ });
+
+ botonModificar.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ modificar();
+ limpiarTabla();
+ cargarTabla();
+ }
+ });
+
+ botonReporte.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ abrirReporte();
+ }
+ });
+ }
+
+ private void abrirReporte() {
+ new ReporteFrame(this);
+ }
+
+ private void limpiarTabla() {
+ modelo.getDataVector().clear();
+ }
+
+ private boolean tieneFilaElegida() {
+ return tabla.getSelectedRowCount() == 0 || tabla.getSelectedColumnCount() == 0;
+ }
+
+ private boolean tieneCatElegida() {
+ return !(comboCategoria.getSelectedItem().toString().equals(comboCategoria.getItemAt(0).toString()));
+ }
+
+ private void modificar() {
+ if (tieneFilaElegida()) {
+ JOptionPane.showMessageDialog(this, "Por favor, elije un item");
+ return;
+ }
+ if (tieneCatElegida()) {
+ Optional.ofNullable(modelo.getValueAt(tabla.getSelectedRow(), tabla.getSelectedColumn()))
+ .ifPresentOrElse(fila -> {
+ Producto producto = new Producto(
+ Integer.valueOf(modelo.getValueAt(tabla.getSelectedRow(), 0).toString()),
+ (String) modelo.getValueAt(tabla.getSelectedRow(), 1),
+ (String) modelo.getValueAt(tabla.getSelectedRow(), 2),
+ Integer.valueOf(modelo.getValueAt(tabla.getSelectedRow(), 3).toString()),
+ (Integer) ((Categoria) comboCategoria.getSelectedItem()).getId());
+ int cantidadActualizada;
+ cantidadActualizada = this.productoController.modificar(producto);
+ JOptionPane.showMessageDialog(this, cantidadActualizada+" Item actualizado con éxito!");
+ }, () -> JOptionPane.showMessageDialog(this, "Por favor, elije un item"));
+ } else {
+ Optional.ofNullable(modelo.getValueAt(tabla.getSelectedRow(), tabla.getSelectedColumn()))
+ .ifPresentOrElse(fila -> {
+ var producto = new Producto(
+ Integer.valueOf(modelo.getValueAt(tabla.getSelectedRow(), 0).toString()),
+ (String) modelo.getValueAt(tabla.getSelectedRow(), 1),
+ (String) modelo.getValueAt(tabla.getSelectedRow(), 2),
+ Integer.valueOf(modelo.getValueAt(tabla.getSelectedRow(), 3).toString()));
+ int cantidadActualizada;
+ cantidadActualizada = this.productoController.modificar(producto);
+ JOptionPane.showMessageDialog(this, cantidadActualizada+" Item actualizado con éxito!");
+ }, () -> JOptionPane.showMessageDialog(this, "Por favor, elije un item"));
+ }
+ }
+
+ private void eliminar() {
+ if (tieneFilaElegida()) {
+ JOptionPane.showMessageDialog(this, "Por favor, elije un item");
+ return;
+ }
+
+ Optional.ofNullable(modelo.getValueAt(tabla.getSelectedRow(), tabla.getSelectedColumn()))
+ .ifPresentOrElse(fila -> {
+ Integer id = Integer.valueOf(modelo.getValueAt(tabla.getSelectedRow(), 0).toString());
+
+ int cantidadEliminada;
+ cantidadEliminada = this.productoController.eliminar(id);
+ modelo.removeRow(tabla.getSelectedRow());
+
+ JOptionPane.showMessageDialog(this, cantidadEliminada+" Item eliminado con éxito!");
+ }, () -> JOptionPane.showMessageDialog(this, "Por favor, elije un item"));
+ }
+
+ private void cargarTabla() {
+ var productos = this.productoController.listar();
+ productos.forEach(producto -> modelo.addRow(
+ new Object[] {
+ producto.getId(),
+ producto.getNombre(),
+ producto.getDescripcion(),
+ producto.getCantidad(),
+ producto.getCategoriaId()
+ }
+ )
+ );
+ }
+
+ private void guardar() {
+ if (textoNombre.getText().isBlank() || textoDescripcion.getText().isBlank()) {
+ JOptionPane.showMessageDialog(this, "Los campos Nombre y Descripción son requeridos.");
+ return;
+ }
+
+ Integer cantidadInt;
+
+ try {
+ cantidadInt = Integer.parseInt(textoCantidad.getText());
+ } catch (NumberFormatException e) {
+ JOptionPane.showMessageDialog(this, String
+ .format("El campo cantidad debe ser numérico dentro del rango %d y %d.", 0, Integer.MAX_VALUE));
+ return;
+ }
+
+ //var producto = new HashMap();
+ //producto.put("NOMBRE",textoNombre.getText());
+ //producto.put("DESCRIPCION", textoDescripcion.getText());
+ //producto.put("CANTIDAD", String.valueOf(cantidadInt));
+ var producto = new Producto(textoNombre.getText(),
+ textoDescripcion.getText(),
+ cantidadInt);
+
+ var categoria = (Categoria) comboCategoria.getSelectedItem();
+
+ this.productoController.guardar(producto, categoria.getId());
+ JOptionPane.showMessageDialog(this, "Registrado con éxito!");
+ this.limpiarFormulario();
+ }
+
+ private void limpiarFormulario() {
+ this.textoNombre.setText("");
+ this.textoDescripcion.setText("");
+ this.textoCantidad.setText("");
+ this.comboCategoria.setSelectedIndex(0);
+ }
+
+}
diff --git a/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/view/ReporteFrame.java b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/view/ReporteFrame.java
new file mode 100644
index 0000000..88fabf5
--- /dev/null
+++ b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/view/ReporteFrame.java
@@ -0,0 +1,64 @@
+package com.alura.jdbc.view;
+
+import java.awt.Container;
+
+import javax.swing.JFrame;
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableModel;
+
+import com.alura.jdbc.controller.CategoriaController;
+//import com.alura.jdbc.controller.ProductoController;
+
+public class ReporteFrame extends JFrame {
+
+ private static final long serialVersionUID = 1L;
+
+ private JTable tablaReporte;
+ private DefaultTableModel modelo;
+
+ private CategoriaController categoriaController;
+ //private ProductoController productoController;
+
+ public ReporteFrame(ControlDeStockFrame controlDeStockFrame) {
+ super("Reporte de produtos del stock");
+
+ this.categoriaController = new CategoriaController();
+ //this.productoController = new ProductoController();
+
+ Container container = getContentPane();
+ setLayout(null);
+
+ tablaReporte = new JTable();
+ tablaReporte.setBounds(0, 0, 600, 400);
+ container.add(tablaReporte);
+
+ modelo = (DefaultTableModel) tablaReporte.getModel();
+ modelo.addColumn("");
+ modelo.addColumn("");
+ modelo.addColumn("");
+ modelo.addColumn("");
+
+ cargaReporte();
+
+ setSize(600, 400);
+ setVisible(true);
+ setLocationRelativeTo(controlDeStockFrame);
+ }
+
+ private void cargaReporte() {
+ var contenido = categoriaController.cargaReporte();
+ contenido.forEach(categoria -> {
+ modelo.addRow(new Object[] { categoria.getId(),
+ categoria.getNombre(),
+ "Artículo", "Stock" });
+ var productos = categoria.getProductos();
+ productos.forEach(producto ->
+ modelo.addRow(new Object[] { "",
+ producto.getId(),
+ producto.getNombre(),
+ producto.getCantidad() }
+ ));
+ });
+ }
+
+}
diff --git a/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/tests/PruebaConexion.java b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/tests/PruebaConexion.java
new file mode 100644
index 0000000..ccd0380
--- /dev/null
+++ b/010_spring_boot/java_jdbc/control-de-stock/src/main/java/com/alura/tests/PruebaConexion.java
@@ -0,0 +1,24 @@
+package com.alura.tests;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+public class PruebaConexion {
+
+ private final static String driver = "jdbc:mysql://";
+ private final static String dbaddr = "192.168.0.8:3306/";
+ private final static String params = "?useTimeZone=true&serverTimeZone=UTC";
+ private final static String dbname = "control_de_stock";
+ private final static String dburl = driver+dbaddr+dbname+params;
+ private final static String dbuser = "alura";
+ private final static String dbpass = "alura";
+
+ public static void main(String[] args) throws SQLException {
+ System.out.println("hola");
+ Connection con = DriverManager.getConnection(dburl, dbuser, dbpass);
+ con.close();
+ System.out.println("chao");
+ }
+
+}
diff --git a/010_spring_boot/jdbc.md b/010_spring_boot/jdbc.md
new file mode 100644
index 0000000..3ec8068
--- /dev/null
+++ b/010_spring_boot/jdbc.md
@@ -0,0 +1,677 @@
+# JDBC
+
+```sh
+mysql -u -h -p
+```
+
+```sql
+CREATE DATABASE control_de_stock;
+USE control_de_stock;
+
+CREATE TABLE producto(
+ id INT AUTO_INCREMENT,
+ nombre VARCHAR(50) NOT NULL,
+ descripcion VARCHAR(255),
+ cantidad INT NOT NULL DEFAULT 0,
+ PRIMARY KEY(id)
+)Engine=InnoDB;
+
+INSERT INTO producto(
+ nombre, descripcion, cantidad)
+ values('Mesa', 'Mesa de 4 lugares', 10);
+
+INSERT INTO producto(
+ nombre, descripcion, cantidad)
+ values( 'Celular', 'Celular Samsung', 50);
+
+SELECT * FROM producto;
+
++----+----------+-------------------+----------+
+| id | nombre | descripcion | cantidad |
++----+----------+-------------------+----------+
+| 1 | Mesa | Mesa de 4 lugares | 10 |
+| 2 | Celuclar | Celular Samsung | 50 |
++----+----------+-------------------+----------+
+```
+
+
+
+```mermaid
+%%{init: {'theme': 'dark','themeVariables': {'clusterBkg': '#2b2f38'}}}%%
+flowchart
+subgraph "Conexión a base de datos"
+DB1[(MySQL)]
+DB2[(SQL Server)]
+DB3[(MariaDB)]
+app1["Aplicación
+Java"]
+DV1[/"Driver MySQL
+MySqlConnector.getConnection()"\]
+DV2[/"Driver SQL Server
+SqlServerConnection.Provider.connect()"\]
+DV3[/MariaDB Driver\]
+DBC{{"JDBC
+DriverManager.getConnection()"}}
+app1-->DBC
+DBC-->DV1-->DB1
+DV1-->DB3
+DBC-->DV3-->DB3
+DBC-->DV2-->DB2
+end
+```
+
+### Url de conexión
+
+**`jdbc::///?`**
+
+ejm. `jdbc:mysql//localhost:3306/control_de_stock`
+
+## Proyecto
+
+### Configuración de maven - [pom.xml](./java_jdbc/control-de-stock/pom.xml)
+
+```xml
+
+ 4.0.0
+ com.alura
+ control-de-stock
+ 0.0.1-SNAPSHOT
+ Control de Stock
+ Proyecto para trabajar con bases de datos con JDBC
+
+ 17
+
+
+
+ mysql
+ mysql-connector-java
+ 8.0.33
+
+
+
+
+
+ org.apache.plugins
+ maven.compiler.plugin
+ 4.0.0
+
+ ${java.version}
+ ${java.version}
+ true
+
+
+
+
+
+```
+
+### Prueba del conector
+
+```java
+package com.alura.tests;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+public class PruebaConexion {
+
+ private static String dbname = "control_de_stock";
+ private static String dbuser = "alura";
+ private static String dbpass = "alura";
+ private static String dburl = "192.168.0.8:3306";
+ private static String params = "useTimeZone=true&serverTimeZone=UTC";
+
+ public static void main(String[] args) throws SQLException {
+ System.out.println("hola");
+ Connection con = DriverManager.getConnection(
+ "jdbc:mysql://"+dburl+"/"+dbname+"?"+params, dbuser, dbpass);
+ con.close();
+ System.out.println("chao");
+ }
+
+}
+```
+
+### Uso de Clase
+[ConnectionFactory](./java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/factory/ConnectionFactory.java)
+
+```java
+package com.alura.jdbc.factory;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+public class ConnectionFactory {
+
+ private static String dbdriver = "jdbc:mysql://";
+ private static String dburl = dbdriver+"192.168.0.8:3306/";
+ private static String params = "?useTimeZone=true&serverTimeZone=UTC";
+ private static String dbname = "control_de_stock";
+ private static String dbuser = "alura";
+ private static String dbpass = "alura";
+
+ public Connection recuperaConexion() throws SQLException {
+ return DriverManager.getConnection(dburl + dbname + params, dbuser, dbpass);
+ }
+}
+```
+
+### Uso de try with resources para las conexiones
+
+En clase
+[ProductoController](./java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/controller/ProductoController.java)
+para cierre automático de recursos `Statement`, `ResultSet`, `Connection`.
+
+Y tratamiento de *querys* con `PreparedStatement`
+
+```java
+...
+
+ public void guardar(Map producto) throws SQLException {
+ String nombre = producto.get("NOMBRE");
+ String descripcion = producto.get("DESCRIPCION");
+ int cantidad = Integer.valueOf(producto.get("CANTIDAD"));
+ int max_cantidad = 50;
+
+ final Connection con = new ConnectionFactory().recuperaConexion();
+ try (con) {
+ con.setAutoCommit(false);
+ final PreparedStatement statement = con.prepareStatement(
+ "INSERT INTO producto(nombre, descripcion, cantidad) VALUES(?,?,?)",
+ Statement.RETURN_GENERATED_KEYS);
+
+ try (statement) {
+ do {
+ int cantidad_a_guardar = Math.min(cantidad, max_cantidad);
+ ejecutaRegistro(nombre, descripcion, cantidad_a_guardar, statement);
+ cantidad -= max_cantidad;
+ } while (cantidad > 0);
+ con.commit();
+ } catch (Exception e) {
+ con.rollback();
+ }
+ }
+ }
+
+...
+```
+
+## Pool de conexiones
+
+```mermaid
+%%{init: {'theme': 'dark','themeVariables': {'clusterBkg': '#2b2f38'}}}%%
+flowchart
+subgraph "Conexión a base de datos"
+DB1[(MySQL)]
+DB2[(SQL Server)]
+DB3[(MariaDB)]
+app1("Aplicación
+Java")
+app2>"Connection
+Factory"]
+app1-->app2
+subgraph DS[Datasource]
+subgraph C3P0
+pl("Pool de
+Conexiones")
+end
+end
+app2-->DS-->DBC
+DV1[/"Driver MySQL
+MySqlConnector.getConnection()"\]
+DV2[/"Driver SQL Server
+SqlServerConnection.Provider.connect()"\]
+DV3[/MariaDB Driver\]
+DBC{{"JDBC
+DriverManager.getConnection()"}}
+DBC-->DV1-->DB1
+DV1-->DB3
+DBC-->DV3-->DB3
+DBC-->DV2-->DB2
+end
+```
+
+### Implentando Pool Datasource en [ConnectionFactory](./java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/factory/ConnectionFactory.java)
+
+
+- [c3p0](https://www.mchange.com/projects/c3p0/) - DJBC3 Connection and Statement
+Pooling
+
+- [mchange-commons-java](https://www.mchange.com/projects/mchange-commons-java/index.html)
+
+Dependencias [pom.xml](./java_jdbc/control-de-stock/pom.xml)
+
+```xml
+ ...
+
+ com.mchange
+ c3p0
+ 0.9.5.5
+
+
+ com.mchange
+ mchange-commons-java
+ 0.2.20
+ 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> z8kfsxax1wlsb2gfqn07n|10dba097, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> null, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> z8kfsxax1wlsb2gfqn07n|10dba097, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://192.168.0.8:3306/control_de_stock?useTimeZone=true&serverTimeZone=UTC, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 10, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {password=******, user=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
+Abriendo conexión nro: 1
+Abriendo conexión nro: 2
+Abriendo conexión nro: 3
+Abriendo conexión nro: 4
+Abriendo conexión nro: 5
+Abriendo conexión nro: 6
+Abriendo conexión nro: 7
+Abriendo conexión nro: 8
+Abriendo conexión nro: 9
+Abriendo conexión nro: 10
+```
+
+```sql
+SHOW PROCESSLIST;
+
++-----+-------+--------------------+------------------+---------+------+----------+------------------+----------+
+| Id | User | Host | db | Command | Time | State | Info | Progress |
++-----+-------+--------------------+------------------+---------+------+----------+------------------+----------+
+| 193 | alura | 192.168.0.10:36856 | control_de_stock | Sleep | 15 | | NULL | 0.000 |
+| 194 | alura | 192.168.0.10:36854 | control_de_stock | Sleep | 15 | | NULL | 0.000 |
+| 195 | alura | 192.168.0.10:36852 | control_de_stock | Sleep | 15 | | NULL | 0.000 |
+| 196 | alura | 192.168.0.10:36882 | control_de_stock | Sleep | 15 | | NULL | 0.000 |
+| 197 | alura | 192.168.0.10:36896 | control_de_stock | Sleep | 15 | | NULL | 0.000 |
+| 198 | alura | 192.168.0.10:36880 | control_de_stock | Sleep | 15 | | NULL | 0.000 |
+| 199 | alura | 192.168.0.10:36902 | control_de_stock | Sleep | 15 | | NULL | 0.000 |
+| 200 | alura | 192.168.0.10:36918 | control_de_stock | Sleep | 15 | | NULL | 0.000 |
+| 201 | alura | 192.168.0.10:36920 | control_de_stock | Sleep | 15 | | NULL | 0.000 |
+| 202 | alura | 192.168.0.10:36934 | control_de_stock | Sleep | 15 | | NULL | 0.000 |
+| 203 | alura | 192.168.0.8:42234 | NULL | Query | 0 | starting | show processlist | 0.000 |
++-----+-------+--------------------+------------------+---------+------+----------+------------------+----------+
+11 rows in set (0.001 sec)
+```
+
+## Dao
+
+Data Access Object [wiki](https://en.wikipedia.org/wiki/Data_access_object) en
+clase
+[ProductoDAO](./java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/dao/ProductoDAO.java)
+
+
+```mermaid
+%%{init: {'theme': 'dark','themeVariables': {'clusterBkg': '#2b2f38'}}}%%
+flowchart
+subgraph "Conexión a base de datos"
+DB1[(MySQL)]
+DB2[(SQL Server)]
+DB3[(MariaDB)]
+app1("Aplicación
+Java")
+CF("Connection
+Factory")
+subgraph DAO
+direction RL
+MD[Modelo]
+CT[Controlador]
+CT-->CF
+CT-->MD
+end
+app1-->CT
+app1-->MD
+subgraph DS[Datasource]
+subgraph C3P0
+PC("Pool de
+Conexiones")
+end
+end
+CF-->DS-->DBC
+DV1[/"Driver MySQL
+MySqlConnector.getConnection()"\]
+DV2[/"Driver SQL Server
+SqlServerConnection.Provider.connect()"\]
+DV3[/MariaDB Driver\]
+DBC{{"JDBC
+DriverManager.getConnection()"}}
+DBC-->DV1-->DB1
+DV1-->DB3
+DBC-->DV3-->DB3
+DBC-->DV2-->DB2
+end
+```
+
+```java
+package com.alura.jdbc.dao;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import com.alura.jdbc.modelo.Producto;
+
+public class ProductoDAO {
+ private final Connection con;
+
+ public ProductoDAO(Connection conexion) {
+ this.con = conexion;
+ }
+
+ public void guardar(Producto producto) {
+ try {
+ final PreparedStatement statement = con.prepareStatement(
+ "INSERT INTO producto(nombre, descripcion, cantidad) VALUES(?,?,?)",
+ Statement.RETURN_GENERATED_KEYS);
+ try (statement) {
+ ejecutaRegistro(producto, statement);
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void ejecutaRegistro(Producto producto, PreparedStatement statement)
+ throws SQLException {
+ statement.setString(1, producto.getNombre());
+ statement.setString(2, producto.getDescripcion());
+ statement.setInt(3, producto.getCantidad());
+ statement.execute();
+ final ResultSet resultSet = statement.getGeneratedKeys();
+ try (resultSet) {
+ while (resultSet.next()) {
+ producto.setId(resultSet.getInt(1));
+ System.out.println(String.format("Producto insertado %s: ", producto));
+ }
+ }
+ }
+
+ public List listar() {
+ List resultado = new ArrayList<>();
+ try {
+ final PreparedStatement statement = con.prepareStatement(
+ "SELECT ID, NOMBRE, DESCRIPCION, CANTIDAD FROM producto;");
+ try (statement) {
+ statement.execute();
+ ResultSet resultSet = statement.getResultSet();
+ while (resultSet.next()) {
+ Producto fila = new Producto(
+ resultSet.getInt("ID"),
+ resultSet.getString("NOMBRE"),
+ resultSet.getString("DESCRIPCION"),
+ resultSet.getInt("CANTIDAD")
+ );
+ resultado.add(fila);
+ }
+ return resultado;
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public int modificar(Producto producto) {
+ try {
+ final String query = "UPDATE producto SET NOMBRE=?, DESCRIPCION=?, CANTIDAD=? WHERE ID=?;";
+ final PreparedStatement statement = con.prepareStatement(query);
+ try (statement) {
+ statement.setString(1, producto.getNombre());
+ statement.setString(2, producto.getDescripcion());
+ statement.setInt(3, producto.getCantidad());
+ statement.setInt(4, producto.getId());
+ statement.execute();
+ int resultado = statement.getUpdateCount();
+ return resultado;
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public int eliminar(Integer id) {
+ try {
+ final PreparedStatement statement = con.prepareStatement("DELETE FROM producto WHERE ID=?;");
+ try (statement) {
+ statement.setInt(1, id);
+ statement.execute();
+ int resultado = statement.getUpdateCount();
+ return resultado;
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
+```
+
+### MVC
+
+El proyecto sigue el patrón **M**odelo **V**ista **Controlador**
+
+Ejm. Producto
+
+- [Modelo](./java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/modelo/Producto.java)
+- [Vista](./java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/view/ControlDeStockFrame.java)
+- [Controlador](./java_jdbc/control-de-stock/src/main/java/com/alura/jdbc/controller/ProductoController.java)
+
+ ```java
+ package com.alura.jdbc.controller;
+
+ import java.util.List;
+
+ import com.alura.jdbc.dao.ProductoDAO;
+ import com.alura.jdbc.factory.ConnectionFactory;
+ import com.alura.jdbc.modelo.Producto;
+
+ public class ProductoController {
+
+ private ProductoDAO productoDAO;
+
+ public ProductoController() {
+ this.productoDAO = new ProductoDAO(new ConnectionFactory().recuperaConexion());
+ }
+
+ public int modificar(Producto producto) {
+ return productoDAO.modificar(producto);
+ }
+
+ public int eliminar(Integer id) {
+ return productoDAO.eliminar(id);
+ }
+
+ public List listar() {
+ return productoDAO.listar();
+ }
+
+ public void guardar(Producto producto) {
+ productoDAO.guardar(producto);
+ }
+
+
+ }
+ ```
+
+- Para cada tabla del modelo hay una clase de dominio
+ - Para la tabla de `producto` hay una clase `Producto` asociada
+ - Los objetos del tipo `Producto` representan un registro de la tabla
+- Para acceder a la tabla se utiliza el estándar **Data Access Object (DAO)**
+ - Para cada clase de dominio hay un **DAO** asociado. Ejm, la clase
+ `Producto` posee la clase `ProductoDAO`
+ - Todos los métodos JDBC relacionados al producto están encapsulados en `ProductoDAO`
+- Aplicación escrita en capas
+ - Las capas más conocidas son las de **view**, **controller**, **modelo** y
+ **persistencia**, que componen el estándar **MVC**
+- El flujo de una consulta entre las capas es el siguiente
+ - `view` <--> `controller` <--> `persistencia`
+
+> **Note** No es buena práctica dejar los detalles de implementación de una capa en otras
+que no tienen esta responsabilidad (ejm. la capa de controller lanzar una
+SQLException)
+>
+> Esta es una aplicación desktop embebida, pero hay otros tipos de aplicaciones
+con otros tipos de view, como html para aplicaciones web.
+
+## Relación entre tablas
+
+Creación tabla `categoria` + 4 categorías
+
+```sql
+CREATE TABLE categoria(id INT AUTO_INCREMENT,
+ nombre VARCHAR(50) NOT NULL,
+ PRIMARY KEY(id)
+)Engine=InnoDB;
+
+INSERT INTO categoria(nombre)
+ VALUES('Muebles'),('Tecnología'),('Menaje'),('Calzado');
+```
+
+Modifcando tabla para agregar la columna `categoria_id`
+
+```sql
+ALTER TABLE producto ADD COLUMN categoria_id INT;
+```
+
+Creando **llave foranea**
+
+```sql
+ALTER TABLE producto ADD FOREIGN KEY(categoria_id) REFERENCES categoria(id);
+```
+
+Asignación de categoría a productos
+
+```sql
+UPDATE producto SET categoria_id=1 WHERE id=1;
+...
+UPDATE producto SET categoria_id=1 WHERE id>11 AND id<17;
+...
+```
+
+```sql
+SELECT * FROM categoria;
+
++----+-------------+
+| id | nombre |
++----+-------------+
+| 1 | Muebles |
+| 2 | Tecnología |
+| 3 | Menaje |
+| 4 | Calzado |
++----+-------------+
+```
+
+```sql
+SELECT * FROM producto;
+
++----+------------+--------------------------------+----------+--------------+
+| id | nombre | descripcion | cantidad | categoria_id |
++----+------------+--------------------------------+----------+--------------+
+| 1 | Mesa | Mesa de 4 lugares | 10 | 1 |
+| 2 | Celular | Celular Samsung | 50 | 2 |
+| 3 | Vaso | Vaso de cristal | 10 | 3 |
+| 5 | Cuchara | Cuchara de plastico | 100 | 3 |
+| 7 | Mouse | Mouse inálambrico | 100 | 2 |
+| 9 | Linterna | Linterna con pilas recargables | 50 | 2 |
+| 11 | Zapatillas | Zapatillas de futbol | 40 | 4 |
+| 12 | Botellas | Botellas de vidrio | 50 | 1 |
+| 13 | Botellas | Botellas de plástico | 24 | 1 |
+| 14 | Platos | Platos de plastico | 50 | 1 |
+| 15 | Platos | Platos de plastico | 10 | 1 |
+| 16 | Platos | Platos de loza | 50 | 1 |
+| 30 | Teclado | Teclado inalámbrico | 6 | 2 |
++----+------------+--------------------------------+----------+--------------+
+```
+
+## Inner JOIN
+
+Cuando se tiene una relación entre tablas se debe cuidar de no crear el problema
+de quieries `N+1`. Es decir, no buscar los datos de una relación en otras queries.
+
+Esto puede producir problemas de performance tanto en la aplicación como en la DB.
+
+Para ello utilizar ***join*** en la query SQL
+
+```java
+...
+ public List listarConProductos() {
+ List resultado = new ArrayList<>();
+ final String query = "SELECT C.ID, C.NOMBRE, P.ID, P.NOMBRE, P.CANTIDAD"
+ + "FROM categoria C INNER JOIN producto P ON C.ID = P.CATEGORIA_ID ";
+ System.out.println(query);
+ try {
+ final PreparedStatement statement = con.prepareStatement(query);
+ try (statement) {
+ statement.execute();
+ final ResultSet resultSet = statement.getResultSet();
+ try (resultSet){
+ while (resultSet.next()) {
+ Integer categoriaId = resultSet.getInt("C.ID");
+ String categoriaNombre = resultSet.getString("C.NOMBRE");
+ var categoria = resultado
+ .stream()
+ .filter(cat -> cat.getId().equals(categoriaId))
+ .findAny().orElseGet(() -> {
+ Categoria cat = new Categoria(categoriaId,
+ categoriaNombre);
+ resultado.add(cat);
+ return cat;
+ });
+ Producto producto = new Producto(resultSet.getInt("P.ID"),
+ resultSet.getString("P.NOMBRE"),
+ resultSet.getInt("P.CANTIDAD"));
+ categoria.agregar(producto);
+ }
+ };
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ return resultado;
+ }
+...
+```
+
+Aplicación [Control de Stock](./java_jdbc/control-de-stock/)
+