├── .gitignore ├── diagrams ├── dao.png ├── Diagrams.png ├── arquitectura.png ├── Diagrams.drawio └── dto.svg ├── src └── main │ ├── resources │ ├── application.properties │ ├── .env │ └── .env.example │ └── java │ └── es │ └── joseluisgs │ └── dam │ └── blog │ ├── model │ ├── Category.java │ ├── Login.java │ ├── User.java │ ├── Comment.java │ └── Post.java │ ├── dto │ ├── LoginDTO.java │ ├── CategoryDTO.java │ ├── CommentDTO.java │ ├── PostDTO.java │ └── UserDTO.java │ ├── mapper │ ├── BaseMapper.java │ ├── CategoryMapper.java │ ├── LoginMapper.java │ ├── UserMapper.java │ ├── CommentMapper.java │ └── PostMapper.java │ ├── repository │ ├── CrudRespository.java │ ├── LoginRepository.java │ ├── CategoryRepository.java │ ├── PostRepository.java │ ├── UserRepository.java │ └── CommentRepository.java │ ├── App.java │ ├── utils │ ├── ApplicationProperties.java │ └── Cifrador.java │ ├── service │ ├── BaseService.java │ ├── CategoryService.java │ ├── UserService.java │ ├── LoginService.java │ ├── CommentService.java │ └── PostService.java │ ├── controller │ ├── LoginController.java │ ├── CategoryController.java │ ├── PostController.java │ ├── UserController.java │ └── CommentController.java │ ├── database │ └── DataBaseController.java │ └── Blog.java ├── .idea ├── vcs.xml ├── .gitignore ├── encodings.xml ├── runConfigurations.xml ├── misc.xml ├── compiler.xml ├── jarRepositories.xml └── uiDesigner.xml ├── docker ├── mariadb │ ├── Dockerfile │ └── sql │ │ └── init-db.sql └── docker-compose.yml ├── LICENSE ├── pom.xml ├── sql └── blog.sql └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Project exclude paths 2 | /target/ 3 | src/main/resources/.env 4 | -------------------------------------------------------------------------------- /diagrams/dao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joseluisgs/Blog-Relacional-AccesoDatos-2021-2022/HEAD/diagrams/dao.png -------------------------------------------------------------------------------- /diagrams/Diagrams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joseluisgs/Blog-Relacional-AccesoDatos-2021-2022/HEAD/diagrams/Diagrams.png -------------------------------------------------------------------------------- /diagrams/arquitectura.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joseluisgs/Blog-Relacional-AccesoDatos-2021-2022/HEAD/diagrams/arquitectura.png -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joseluisgs/Blog-Relacional-AccesoDatos-2021-2022/HEAD/src/main/resources/application.properties -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /docker/mariadb/Dockerfile: -------------------------------------------------------------------------------- 1 | # MariaDB 2 | FROM yobasystems/alpine-mariadb:10 3 | # FROM mariadb:10.5 4 | 5 | # Configuramos BBDD 6 | ENV MYSQL_ROOT_PASSWORD 123 7 | ENV MYSQL_USER blog 8 | ENV MYSQL_PASSWORD blog1234 9 | ENV MYSQL_DATABASE blog 10 | 11 | # Copiamos los ficheros sql para que se ejecuten 12 | COPY ./sql /docker-entrypoint-initdb.d/ -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/model/Category.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.model; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Builder 8 | public class Category { 9 | private Long id; 10 | private String texto; 11 | 12 | 13 | public Category(Long id, String texto) { 14 | this.id = id; 15 | this.texto = texto; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/dto/LoginDTO.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.dto; 2 | 3 | import es.joseluisgs.dam.blog.model.User; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | import java.time.LocalDateTime; 8 | 9 | @Data 10 | @Builder 11 | public class LoginDTO { 12 | private Long user_id; 13 | private User user; 14 | private LocalDateTime ultimoAcceso; 15 | private String token; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/resources/.env: -------------------------------------------------------------------------------- 1 | # Para los ficheros secretos o claves si no las quiero codificar 2 | # no subir nunca github. En mi caso no hay problema 3 | # Podemos definir secretos en variables de entorno clave valor 4 | 5 | # Datos de la Base de Datos 6 | DATABASE_SERVER_URL=localhost 7 | DATABASE_SERVER_PORT=3306 8 | DATABASE_NAME=blog 9 | DATABASE_USER=blog 10 | DATABASE_PASSWORD=blog1234 11 | DATABASE_JDBC_DRIVER=com.mysql.cj.jdbc.Driver 12 | -------------------------------------------------------------------------------- /src/main/resources/.env.example: -------------------------------------------------------------------------------- 1 | # Para los ficheros secretos o claves si no las quiero codificar 2 | # no subir nunca github. En mi caso no hay problema 3 | # Podemos definir secretos en variables de entorno clave valor 4 | 5 | # Datos de la Base de Datos 6 | DATABASE_SERVER_URL=localhost 7 | DATABASE_SERVER_PORT=3306 8 | DATABASE_NAME=blog 9 | DATABASE_USER=blog 10 | DATABASE_PASSWORD=blog1234 11 | DATABASE_JDBC_DRIVER=com.mysql.cj.jdbc.Driver 12 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/model/Login.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.model; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | @Data 9 | @Builder 10 | public class Login { 11 | private Long user_id; 12 | private LocalDateTime ultimoAcceso; 13 | private String token; 14 | 15 | public Login(Long userId, LocalDateTime ultimoAcceso, String token) { 16 | this.user_id = userId; 17 | this.ultimoAcceso = ultimoAcceso; 18 | this.token = token; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/mapper/BaseMapper.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.mapper; 2 | 3 | import java.util.List; 4 | import java.util.stream.Collectors; 5 | 6 | public abstract class BaseMapper { 7 | public List fromDTO(List items) { 8 | return items.stream().map(this::fromDTO).collect(Collectors.toList()); 9 | } 10 | 11 | public abstract T fromDTO(DTO item); 12 | 13 | public List toDTO(List items) { 14 | return items.stream().map(this::toDTO).collect(Collectors.toList()); 15 | } 16 | 17 | public abstract DTO toDTO(T item); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/repository/CrudRespository.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.repository; 2 | 3 | import java.sql.SQLException; 4 | import java.util.List; 5 | 6 | public interface CrudRespository { 7 | 8 | // Operaciones CRUD 9 | 10 | // Obtiene todos 11 | List findAll() throws SQLException; 12 | 13 | // Obtiene por ID 14 | T getById(ID id) throws SQLException; 15 | 16 | // Salva 17 | T save(T t) throws SQLException; 18 | 19 | // Actualiza 20 | T update(T t) throws SQLException; 21 | 22 | // Elimina 23 | T delete(T t) throws SQLException; 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/model/User.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.model; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | import java.time.LocalDate; 7 | 8 | @Data 9 | @Builder 10 | public class User { 11 | private Long id; 12 | private String nombre; 13 | private String email; 14 | private String password; 15 | private LocalDate fechaRegistro; 16 | 17 | 18 | public User(Long id, String nombre, String email, String password, LocalDate fechaRegistro) { 19 | this.id = id; 20 | this.nombre = nombre; 21 | this.email = email; 22 | this.fechaRegistro = fechaRegistro; 23 | this.password = password; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/mapper/CategoryMapper.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.mapper; 2 | 3 | import es.joseluisgs.dam.blog.dto.CategoryDTO; 4 | import es.joseluisgs.dam.blog.model.Category; 5 | 6 | public class CategoryMapper extends BaseMapper { 7 | @Override 8 | public Category fromDTO(CategoryDTO item) { 9 | return Category.builder() 10 | .id(item.getId()) 11 | .texto(item.getTexto()) 12 | .build(); 13 | } 14 | 15 | @Override 16 | public CategoryDTO toDTO(Category item) { 17 | return CategoryDTO.builder() 18 | .id(item.getId()) 19 | .texto(item.getTexto()) 20 | .build(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/model/Comment.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.model; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | @Data 9 | @Builder 10 | public class Comment { 11 | private Long id; 12 | private String texto; 13 | private LocalDateTime fechaPublicacion; 14 | // Autor que la realiza 15 | private Long user_id; 16 | // Post al que pertenece 17 | private Long post_id; 18 | 19 | 20 | public Comment(Long id, String texto, LocalDateTime fechaPublicacion, Long user_id, Long post_id) { 21 | this.id = id; 22 | this.texto = texto; 23 | this.fechaPublicacion = fechaPublicacion; 24 | this.user_id = user_id; 25 | this.post_id = post_id; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/mapper/LoginMapper.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.mapper; 2 | 3 | import es.joseluisgs.dam.blog.dto.LoginDTO; 4 | import es.joseluisgs.dam.blog.model.Login; 5 | 6 | public class LoginMapper extends BaseMapper { 7 | @Override 8 | public Login fromDTO(LoginDTO item) { 9 | return Login.builder() 10 | .user_id(item.getUser_id()) 11 | .ultimoAcceso(item.getUltimoAcceso()) 12 | .token(item.getToken()) 13 | .build(); 14 | } 15 | 16 | @Override 17 | public LoginDTO toDTO(Login item) { 18 | return LoginDTO.builder() 19 | .user_id(item.getUser_id()) 20 | .ultimoAcceso(item.getUltimoAcceso()) 21 | .token(item.getToken()) 22 | .build(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/dto/CategoryDTO.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.dto; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | 8 | @Data 9 | @Builder 10 | public class CategoryDTO { 11 | private Long id; 12 | private String texto; 13 | 14 | // TODO Bidireccionalidad 15 | // Lista de post que tiene asociado. Por ahora suprimo la bidireccionalidad 16 | // private final Set posts = new HashSet<>(); 17 | 18 | 19 | // From/To JSON 20 | public static CategoryDTO fromJSON(String json) { 21 | final Gson gson = new Gson(); 22 | return gson.fromJson(json, CategoryDTO.class); 23 | } 24 | 25 | public String toJSON() { 26 | final Gson prettyGson = new GsonBuilder().setPrettyPrinting().create(); 27 | return prettyGson.toJson(this); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # Indicamos la versión 2 | # Para iniciar docker-compose up -d 3 | version: '3.7' 4 | 5 | # Mis servicios 6 | # Iniciamos los servicios 7 | services: 8 | # MARIA DB 9 | mariadb: 10 | build: ./mariadb 11 | image: mariadb 12 | container_name: mariadb 13 | ports: 14 | - 3306:3306 15 | expose: 16 | - 3306 17 | volumes: 18 | - mariadb-volume:/var/lib/mysql 19 | networks: 20 | - mariadb-network 21 | # restart: always 22 | 23 | # ADMIN MARIADB 24 | adminer: 25 | image: adminer 26 | container_name: adminer 27 | # restart: always 28 | ports: 29 | - 8080:8080 30 | networks: 31 | - mariadb-network 32 | depends_on: 33 | - mariadb 34 | 35 | # Mi volumenes de datos compartidos 36 | volumes: 37 | mariadb-volume: 38 | 39 | # Si queremos que tengan una red propia a otros contenedores 40 | networks: 41 | mariadb-network: 42 | driver: bridge 43 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/model/Post.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.model; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | @Data 9 | @Builder 10 | public class Post { 11 | private Long id; 12 | private String titulo; 13 | private String url; 14 | private String contenido; 15 | private LocalDateTime fechaPublicacion; 16 | 17 | // Autor 18 | private Long user_id; 19 | // Categoría a la que pertenece 20 | private Long category_id; 21 | 22 | 23 | public Post(Long id, String titulo, String url, String contenido, LocalDateTime fechaPublicacion, Long user_id, Long category_id) { 24 | this.id = id; 25 | this.titulo = titulo; 26 | this.url = url; 27 | this.contenido = contenido; 28 | this.fechaPublicacion = fechaPublicacion; 29 | this.user_id = user_id; 30 | this.category_id = category_id; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.mapper; 2 | 3 | import es.joseluisgs.dam.blog.dto.UserDTO; 4 | import es.joseluisgs.dam.blog.model.User; 5 | 6 | public class UserMapper extends BaseMapper { 7 | @Override 8 | public User fromDTO(UserDTO item) { 9 | return User.builder() 10 | .id(item.getId()) 11 | .nombre(item.getNombre()) 12 | .email(item.getEmail()) 13 | .password(item.getPassword()) 14 | .fechaRegistro(item.getFechaRegistro()) 15 | .build(); 16 | } 17 | 18 | @Override 19 | public UserDTO toDTO(User item) { 20 | return UserDTO.builder() 21 | .id(item.getId()) 22 | .nombre(item.getNombre()) 23 | .email(item.getEmail()) 24 | .password(item.getPassword()) 25 | .fechaRegistro(item.getFechaRegistro()) 26 | .build(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/mapper/CommentMapper.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.mapper; 2 | 3 | import es.joseluisgs.dam.blog.dto.CommentDTO; 4 | import es.joseluisgs.dam.blog.model.Comment; 5 | 6 | public class CommentMapper extends BaseMapper { 7 | @Override 8 | public Comment fromDTO(CommentDTO item) { 9 | return Comment.builder() 10 | .id(item.getId()) 11 | .texto(item.getTexto()) 12 | .fechaPublicacion(item.getFechaPublicacion()) 13 | // .user_id(item.getUser().getId()) 14 | .user_id(item.getUser_id()) 15 | // .post_id(item.getPost().getId()) 16 | .post_id(item.getPost_id()) 17 | .build(); 18 | } 19 | 20 | @Override 21 | public CommentDTO toDTO(Comment item) { 22 | return CommentDTO.builder() 23 | .id(item.getId()) 24 | .texto(item.getTexto()) 25 | .fechaPublicacion(item.getFechaPublicacion()) 26 | .build(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 José Luis González Sánchez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/App.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog; 2 | 3 | import es.joseluisgs.dam.blog.utils.ApplicationProperties; 4 | 5 | public class App { 6 | public static void main(String[] args) { 7 | ApplicationProperties properties = new ApplicationProperties(); 8 | System.out.println("Bienvenid@s a " + 9 | properties.readProperty("app.title") + " " 10 | + properties.readProperty("app.version") + " de " + 11 | properties.readProperty("app.curso")); 12 | 13 | Blog blog = Blog.getInstance(); 14 | // Chequeamos el sistema 15 | blog.checkService(); 16 | 17 | // Iniciamos la base de datos al estado original en cada prueba 18 | if (properties.readProperty("database.init").equals("true")) 19 | blog.initDataBase(); 20 | 21 | // Categorías 22 | blog.Categories(); 23 | 24 | // Usuarios 25 | blog.Users(); 26 | 27 | // Post 28 | blog.Posts(); 29 | 30 | // Comentarios 31 | blog.Comments(); 32 | 33 | // Login 34 | blog.Login(); 35 | 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/utils/ApplicationProperties.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.utils; 2 | 3 | import java.io.IOException; 4 | import java.util.Properties; 5 | import java.util.logging.Level; 6 | import java.util.logging.Logger; 7 | 8 | public class ApplicationProperties { 9 | 10 | private final Properties properties; 11 | 12 | public ApplicationProperties() { 13 | properties = new Properties(); 14 | try { 15 | properties.load(getClass().getClassLoader().getResourceAsStream("application.properties")); 16 | 17 | } catch (IOException ex) { 18 | System.err.println("IOException Ocurrido al leer el fichero de propiedades: " + ex.getMessage()); 19 | Logger.getLogger(getClass().getName()).log(Level.ALL, "IOException Ocurrido al leer el fichero de propiedades: " + ex.getMessage()); 20 | } 21 | } 22 | 23 | public String readProperty(String keyName) { 24 | // Logger.getLogger(getClass().getName()).log(Level.INFO, "Leyendo propiedad " + keyName); 25 | return properties.getProperty(keyName, "No existe esa clave en el fichero de propiedades"); 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/service/BaseService.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.service; 2 | 3 | import es.joseluisgs.dam.blog.repository.CrudRespository; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | import java.sql.SQLException; 7 | import java.util.List; 8 | 9 | @RequiredArgsConstructor // Requerimos un constructor con al menos las propiedades finales 10 | public abstract class BaseService> { 11 | protected final R repository; 12 | 13 | // Operaciones CRUD 14 | 15 | // Obtiene todos 16 | public List findAll() throws SQLException { 17 | return repository.findAll(); 18 | } 19 | 20 | // Obtiene por ID 21 | public T getById(ID id) throws SQLException { 22 | return repository.getById(id); 23 | } 24 | 25 | // Salva 26 | public T save(T t) throws SQLException { 27 | return repository.save(t); 28 | } 29 | 30 | // Actualiza 31 | public T update(T t) throws SQLException { 32 | return repository.update(t); 33 | } 34 | 35 | // Elimina 36 | public T delete(T t) throws SQLException { 37 | return repository.delete(t); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/utils/Cifrador.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.utils; 2 | 3 | import java.nio.charset.StandardCharsets; 4 | import java.security.MessageDigest; 5 | 6 | public class Cifrador { 7 | private static Cifrador cif = null; 8 | 9 | private Cifrador() { 10 | } 11 | 12 | public static Cifrador getInstance() { 13 | if (cif == null) { 14 | cif = new Cifrador(); 15 | } 16 | return cif; 17 | } 18 | 19 | public String SHA256(String cadena) { 20 | MessageDigest md = null; 21 | byte[] hash = null; 22 | // Llamamos a la función de hash de java 23 | try { 24 | md = MessageDigest.getInstance("SHA-1"); 25 | hash = md.digest(cadena.getBytes(StandardCharsets.UTF_8)); 26 | } catch (Exception ex) { 27 | ex.printStackTrace(); 28 | } 29 | return convertToHex(hash); 30 | } 31 | 32 | private String convertToHex(byte[] raw) { 33 | StringBuffer sb = new StringBuffer(); 34 | for (int i = 0; i < raw.length; i++) { 35 | sb.append(Integer.toString((raw[i] & 0xff) + 0x100, 16).substring(1)); 36 | } 37 | return sb.toString(); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/mapper/PostMapper.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.mapper; 2 | 3 | import es.joseluisgs.dam.blog.dto.PostDTO; 4 | import es.joseluisgs.dam.blog.model.Post; 5 | 6 | public class PostMapper extends BaseMapper { 7 | @Override 8 | public Post fromDTO(PostDTO item) { 9 | return Post.builder() 10 | .id(item.getId()) 11 | .titulo(item.getTitulo()) 12 | .url(item.getUrl()) 13 | .contenido(item.getContenido()) 14 | .fechaPublicacion(item.getFechaPublicacion()) 15 | // .user_id(item.getUser().getId()) 16 | .user_id(item.getUser_id()) 17 | // .category_id(item.getCategory().getId()) 18 | .category_id(item.getCategory_id()) 19 | .build(); 20 | } 21 | 22 | @Override 23 | public PostDTO toDTO(Post item) { 24 | return PostDTO.builder() 25 | .id(item.getId()) 26 | .titulo(item.getTitulo()) 27 | .url(item.getUrl()) 28 | .contenido(item.getContenido()) 29 | .fechaPublicacion(item.getFechaPublicacion()) 30 | .build(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/dto/CommentDTO.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.dto; 2 | 3 | import com.google.gson.ExclusionStrategy; 4 | import com.google.gson.FieldAttributes; 5 | import com.google.gson.Gson; 6 | import com.google.gson.GsonBuilder; 7 | import es.joseluisgs.dam.blog.model.Post; 8 | import es.joseluisgs.dam.blog.model.User; 9 | import lombok.Builder; 10 | import lombok.Data; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | @Data 15 | @Builder 16 | public class CommentDTO { 17 | ExclusionStrategy strategy = new ExclusionStrategy() { 18 | @Override 19 | public boolean shouldSkipClass(Class clazz) { 20 | return false; 21 | } 22 | 23 | @Override 24 | public boolean shouldSkipField(FieldAttributes field) { 25 | return field.getName().startsWith("password") 26 | || field.getName().startsWith("user_id") 27 | || field.getName().startsWith("category_id"); 28 | } 29 | }; 30 | private Long id; 31 | private String texto; 32 | private LocalDateTime fechaPublicacion; 33 | // Autor que la realiza 34 | private User user; 35 | // Post al que pertenece 36 | private Post post; 37 | // Para mejorar las relaciones y como es un dTO vamos a poner los ids 38 | private Long user_id, post_id; 39 | 40 | // From/To JSON 41 | public static CategoryDTO fromJSON(String json) { 42 | final Gson gson = new Gson(); 43 | return gson.fromJson(json, CategoryDTO.class); 44 | } 45 | 46 | public String toJSON() { 47 | final Gson prettyGson = new GsonBuilder() 48 | .addSerializationExclusionStrategy(strategy) 49 | .setPrettyPrinting() 50 | .create(); 51 | return prettyGson.toJson(this); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/service/CategoryService.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.service; 2 | 3 | import es.joseluisgs.dam.blog.dto.CategoryDTO; 4 | import es.joseluisgs.dam.blog.mapper.CategoryMapper; 5 | import es.joseluisgs.dam.blog.model.Category; 6 | import es.joseluisgs.dam.blog.repository.CategoryRepository; 7 | 8 | import java.sql.SQLException; 9 | import java.util.List; 10 | 11 | public class CategoryService extends BaseService { 12 | CategoryMapper mapper = new CategoryMapper(); 13 | 14 | // Inyección de dependencias en el constructor. El servicio necesita este repositorio 15 | public CategoryService(CategoryRepository repository) { 16 | super(repository); 17 | } 18 | 19 | // Otras operaciones o especificaciones para CRUD 20 | // O podíamos mapear el nombre 21 | // O simplemente ocultar las que no queramos usar en niveles superiores 22 | // Utilizamos los DTO para par datos del servico al controlador que los presenta 23 | public List getAllCategories() throws SQLException { 24 | return mapper.toDTO(this.findAll()); 25 | } 26 | 27 | public CategoryDTO getCategoryById(Long id) throws SQLException { 28 | return mapper.toDTO(this.getById(id)); 29 | } 30 | 31 | public CategoryDTO postCategory(CategoryDTO categoryDTO) throws SQLException { 32 | Category res = this.save(mapper.fromDTO(categoryDTO)); 33 | return mapper.toDTO(res); 34 | } 35 | 36 | public CategoryDTO updateCategory(CategoryDTO categoryDTO) throws SQLException { 37 | Category res = this.update(mapper.fromDTO(categoryDTO)); 38 | return mapper.toDTO(res); 39 | } 40 | 41 | public CategoryDTO deleteCategory(CategoryDTO categoryDTO) throws SQLException { 42 | Category res = this.delete(mapper.fromDTO(categoryDTO)); 43 | return mapper.toDTO(res); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/service/UserService.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.service; 2 | 3 | import es.joseluisgs.dam.blog.dto.UserDTO; 4 | import es.joseluisgs.dam.blog.mapper.UserMapper; 5 | import es.joseluisgs.dam.blog.model.User; 6 | import es.joseluisgs.dam.blog.repository.UserRepository; 7 | import es.joseluisgs.dam.blog.utils.Cifrador; 8 | 9 | import java.sql.SQLException; 10 | import java.util.List; 11 | 12 | public class UserService extends BaseService { 13 | UserMapper mapper = new UserMapper(); 14 | 15 | // Inyección de dependencias en el constructor. El servicio necesita este repositorio 16 | public UserService(UserRepository repository) { 17 | super(repository); 18 | } 19 | 20 | // Otras operaciones o especificaciones para CRUD 21 | // O podíamos mapear el nombre 22 | // O simplemente ocultar las que no queramos usar en niveles superiores 23 | public List getAllUsers() throws SQLException { 24 | return mapper.toDTO(this.findAll()); 25 | } 26 | 27 | public UserDTO getUserById(Long id) throws SQLException { 28 | return mapper.toDTO(this.getById(id)); 29 | } 30 | 31 | public UserDTO postUser(UserDTO userDTO) throws SQLException { 32 | // Ciframos antes el password 33 | userDTO.setPassword(Cifrador.getInstance().SHA256(userDTO.getPassword())); 34 | User res = this.save(mapper.fromDTO(userDTO)); 35 | return mapper.toDTO(res); 36 | } 37 | 38 | public UserDTO updateUser(UserDTO userDTO) throws SQLException { 39 | User res = this.update(mapper.fromDTO(userDTO)); 40 | return mapper.toDTO(res); 41 | } 42 | 43 | public UserDTO deleteUser(UserDTO userDTO) throws SQLException { 44 | User res = this.delete(mapper.fromDTO(userDTO)); 45 | return mapper.toDTO(res); 46 | } 47 | 48 | public User getUserByMail(String userMail) throws SQLException { 49 | return repository.getByMail(userMail); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/dto/PostDTO.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.dto; 2 | 3 | import com.google.gson.ExclusionStrategy; 4 | import com.google.gson.FieldAttributes; 5 | import com.google.gson.Gson; 6 | import com.google.gson.GsonBuilder; 7 | import es.joseluisgs.dam.blog.model.Category; 8 | import es.joseluisgs.dam.blog.model.Comment; 9 | import es.joseluisgs.dam.blog.model.User; 10 | import lombok.Builder; 11 | import lombok.Data; 12 | 13 | import java.time.LocalDateTime; 14 | import java.util.List; 15 | 16 | @Data 17 | @Builder 18 | public class PostDTO { 19 | ExclusionStrategy strategy = new ExclusionStrategy() { 20 | @Override 21 | public boolean shouldSkipClass(Class clazz) { 22 | return false; 23 | } 24 | 25 | @Override 26 | public boolean shouldSkipField(FieldAttributes field) { 27 | return field.getName().startsWith("password") 28 | || field.getName().startsWith("user_id") 29 | || field.getName().startsWith("category_id"); 30 | } 31 | }; 32 | private Long id; 33 | private String titulo; 34 | private String url; 35 | private String contenido; 36 | private LocalDateTime fechaPublicacion; 37 | // Autor 38 | private User user; 39 | // Categoría a la que pertenece 40 | private Category category; 41 | // Para mejorar las relaciones y como es un dTO vamos a poner los ids 42 | private Long user_id, category_id; 43 | // Lista de comentarios asociados 44 | private List comments; 45 | 46 | // From/To JSON 47 | public static CategoryDTO fromJSON(String json) { 48 | final Gson gson = new Gson(); 49 | return gson.fromJson(json, CategoryDTO.class); 50 | } 51 | 52 | public String toJSON() { 53 | final Gson prettyGson = new GsonBuilder() 54 | .addSerializationExclusionStrategy(strategy) 55 | .setPrettyPrinting() 56 | .create(); 57 | return prettyGson.toJson(this); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/repository/LoginRepository.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.repository; 2 | 3 | import es.joseluisgs.dam.blog.database.DataBaseController; 4 | import es.joseluisgs.dam.blog.model.Login; 5 | 6 | import java.sql.SQLException; 7 | import java.util.List; 8 | import java.util.UUID; 9 | 10 | public class LoginRepository implements CrudRespository { 11 | @Override 12 | public List findAll() throws SQLException { 13 | throw new SQLException("Error: Método no implementado"); 14 | } 15 | 16 | @Override 17 | public Login getById(Long id) throws SQLException { 18 | throw new SQLException("Error: Método no implementado"); 19 | } 20 | 21 | @Override 22 | public Login save(Login login) throws SQLException { 23 | UUID uuid = UUID.randomUUID(); 24 | String query = "INSERT INTO login VALUES (?, ?, ?)"; 25 | DataBaseController db = DataBaseController.getInstance(); 26 | db.open(); 27 | db.insert(query, login.getUser_id(), login.getUltimoAcceso(), 28 | uuid.toString()).orElseThrow(() -> new SQLException("Error LoginRepository al insertar Login")); 29 | // una vez insertado comprobamos que esta correcto para devolverlo 30 | login.setToken(uuid.toString()); 31 | db.close(); 32 | return login; 33 | } 34 | 35 | @Override 36 | public Login update(Login login) { 37 | return null; 38 | } 39 | 40 | @Override 41 | public Login delete(Login login) { 42 | return null; 43 | } 44 | 45 | public Long deleteById(Long id) throws SQLException { 46 | String query = "DELETE FROM login WHERE user_id = ?"; 47 | DataBaseController db = DataBaseController.getInstance(); 48 | db.open(); 49 | int res = db.delete(query, id); 50 | db.close(); 51 | if (res > 0) 52 | return id; 53 | else 54 | throw new SQLException("Error LoginRepository al eliminar login con User ID: " + id); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/dto/UserDTO.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.dto; 2 | 3 | import com.google.gson.ExclusionStrategy; 4 | import com.google.gson.FieldAttributes; 5 | import com.google.gson.Gson; 6 | import com.google.gson.GsonBuilder; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | 10 | import java.time.LocalDate; 11 | 12 | @Data 13 | @Builder 14 | public class UserDTO { 15 | ExclusionStrategy strategy = new ExclusionStrategy() { 16 | @Override 17 | public boolean shouldSkipClass(Class clazz) { 18 | return false; 19 | } 20 | 21 | @Override 22 | public boolean shouldSkipField(FieldAttributes field) { 23 | return field.getName().startsWith("password"); 24 | } 25 | }; 26 | private Long id; 27 | private String nombre; 28 | private String email; 29 | private LocalDate fechaRegistro; 30 | 31 | // TODO Bidireccionalidad 32 | // Lista de Comentarios 33 | //private Set comentarios = new HashSet<>(); 34 | // Lista de Posts 35 | //private Set posts = new HashSet<>(); 36 | // Su login activo si lo tiene 37 | //private Login login; 38 | 39 | // Eliminar campos de las serialización 40 | // https://www.baeldung.com/gson-exclude-fields-serialization 41 | private String password; 42 | 43 | // From/To JSON 44 | public static UserDTO fromJSON(String json) { 45 | final Gson gson = new Gson(); 46 | return gson.fromJson(json, UserDTO.class); 47 | } 48 | 49 | public String toJSON() { 50 | final Gson prettyGson = new GsonBuilder() 51 | // .excludeFieldsWithoutExposeAnnotation() // Quitamos los campos que no están expuestos y evitamos lo anterior 52 | .addSerializationExclusionStrategy(strategy) 53 | .setPrettyPrinting() 54 | .create(); 55 | // Otra manera de quitar un campo determinado para imprimir 56 | // prettyGson.toJsonTree(this).getAsJsonObject().remove("password"); 57 | return prettyGson.toJson(this); 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/service/LoginService.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.service; 2 | 3 | import es.joseluisgs.dam.blog.dto.LoginDTO; 4 | import es.joseluisgs.dam.blog.mapper.LoginMapper; 5 | import es.joseluisgs.dam.blog.model.Login; 6 | import es.joseluisgs.dam.blog.model.User; 7 | import es.joseluisgs.dam.blog.repository.LoginRepository; 8 | import es.joseluisgs.dam.blog.repository.UserRepository; 9 | import es.joseluisgs.dam.blog.utils.Cifrador; 10 | 11 | import java.sql.SQLException; 12 | import java.time.LocalDateTime; 13 | import java.util.List; 14 | import java.util.Optional; 15 | 16 | public class LoginService extends BaseService { 17 | LoginMapper mapper = new LoginMapper(); 18 | 19 | // Inyección de dependencias en el constructor. El servicio necesita este repositorio 20 | public LoginService(LoginRepository repository) { 21 | super(repository); 22 | } 23 | 24 | // Otras operaciones o especificaciones para CRUD 25 | // O podíamos mapear el nombre 26 | // O simplemente ocultar las que no queramos usar en niveles superiores 27 | public Optional> getAllLogins() throws SQLException { 28 | return null; 29 | } 30 | 31 | public LoginDTO login(String userMail, String userPassword) throws SQLException { 32 | User user = getUserByMail(userMail); 33 | Cifrador cif = Cifrador.getInstance(); 34 | if ((user != null) && user.getPassword().equals(cif.SHA256(userPassword))) { 35 | // System.out.println("SI"); 36 | LoginDTO login = mapper.toDTO(repository.save(new Login(user.getId(), LocalDateTime.now(), null))); 37 | login.setUser(user); 38 | return login; 39 | } 40 | return null; 41 | } 42 | 43 | private User getUserByMail(String userMail) throws SQLException { 44 | UserService service = new UserService(new UserRepository()); 45 | return service.getUserByMail(userMail); 46 | } 47 | 48 | public boolean logout(Long id) throws SQLException { 49 | return repository.deleteById(id) > 0; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /diagrams/Diagrams.drawio: -------------------------------------------------------------------------------- 1 | 7VxrU+I6GP41fDkzMvQKfhTwcvawZ1w96nq+xTa00bRh2yDir9+Epi1tWm5LKWpnmLF5yf15Et4neWtLG3hvlwGYuN+JDXFL7dhvLW3YUlVFVdUW/3TsubAo+mlkcQJkC1tquEXvUBg7wjpFNgwzGSkhmKJJ1mgR34cWzdhAEJBZNtuY4GyrE+BAyXBrASxbH5BN3cjaU7up/Qoix41bVkwxPg/EmcVIQhfYZLZk0s5b2iAghEZP3tsAYj578bw8/D1/wKMX8/Lbj/AXuOv/89+/9ydRZRfbFEmGEECf7lx19//LM32MTiZIMXzr5XvnCjgnhhgancfzBW02fSJJAuoSh/gAn6fWfkCmvg15rR2WSvOMCJkwo8KMz5DSueACmFLCTC71sPgWviH6kxdvGyL1KCrjz8O35cQ8Tvg0mC8V4snHuD6eSIstUnG5aHx8UDk6rJlLkS8k08ASRd9fxhdX9/rzz8n1xY9Z5/Ls6duJmPMOBYED6YqJTgnDlhokHmSdZOUCiAFFr9nOAUF5J8mXwsoeBLLFKK/q5CvAU9HSXQgDCfpwhjwMfI7xmPg0ZgHrdx9g5Pjs2WKzxkpq/VcYUMRW2Zn4gnLs+5aLsD0CczLlcxFSYL3Eqb5LAvTOqgUxDdjXARUkUc1MjlteUiAYwJDluY4BUxLTCIRU5LEIxmASoqdFh3kWjwGC/D6hlHhxRRneJqt5kaABeUn2ByXhzaY04bMB35ZAlJEW3/YEFmI7VTWRnqV7k2IKm7u8L8XGInKI1m7Y/gl8h81B0lyyecXbt7phewyQTHMAM9x9QGGfz2IoUTIZ6UYsfXh1rKvBzaR7rp3f3z0+D1X114kmsbSl9kWhM/Z3RHxH4iybdpoAOCCYMHIOfRKRGGGcM8U8xnBMS1kcToCFfGe0yDPUU8uNmB9uIqzsGC8Y5CLbhv6CYRRQEJGQ02pCkE8XE2j02YdN84BvXwbr+ICllTTNPjx7QAfEZ2MBaME6yBg+g5zlBXxcudLX83GexXlbPuyyNxWirpeg7hPvKYAR8rc0QA32e8feUGvGXi3BHnoA4Qb6CqHv9mqG3iyBfgLCcEYCu0G/QvSVjl4z/N0S+MfQcsENdBDHNuLAkDkdDQP2zYBN/c7KGNCTGHBN2JAbTXJ4TaJ19R1FibGCHZ9DlJw2omSb3WnlUv84okSRT0wi2CmiU0wa56RC8GtXJYpSAv40aERJlcjXLkqUMkFqMQ8A+shuVn6V+NcvS5SyM8iFLrmePmGGkIWI30iT6lhQuzRRZHWqlMG8dK21LeKlusVj6C1u2mYuovCWIc3bnAVgIimGqiSBqufuKU7lvVkvwmTFLcWfYSLrRaXd/uuLwaJpxwaLrI8GbFN0iKi7kfKHlfJmft321Lax2XbaM8o58jm0vFqm6hoxv/nvebLiP46aV8sEXXPHWD36tct5Tb5h7nw9z8HQcme8dXsOmvHlnWyzc2TenF5rOGD8vAgGTEID14UDtpaCAZdiA1eGA+4S/bcqgGE5+m/lkWIuDvEA8YCF3TE6HwXnPeMV47AWsHiJHQlgZpEHk0fQt894vDRLPWHC9U0kgfJGlu8C4YxKWkofF17mhigUORcbIyOqu+bOVPkJiGbkdt2o86JUiq9UUf52VdO72YoiMkoV7Uv1mPKh9hc8OZHg1LsFwvigv7amfNj8BYHJH2kdAzCyYhgQz4N+E55Sx5mWmmOIbm54xKQoKxjyOY60TFlFNUda2x5qJOv94xxpmWXxsxza5pa6UvBrP9Ey5UuX5pb60CyoPVilW/TmTLkmFEDaIHQXP+glYm+bdzK3+8FfL/bEgLZRe0bBjBv7EYB6zg3QtV0FoLJGSe5PAJa/eprhyYgwR6/xZdf6stJusCnbS33Z3OufRrWe7I6tVevHFnK07IaucWM3JeM2b53X4MUWdk8+mYpQBxbbt4UX2ycEQyDvVg34fwb+Ib3Ywu6VhVpOMUUeObMsGDZvgFWH/yH918LuyQq2024f9xXw/v2BZMrnufQBzj4LUZFDXxtU0n9mtH9UWDL9D0KRN5X+Iybt/Dc= -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.controller; 2 | 3 | import com.google.gson.ExclusionStrategy; 4 | import com.google.gson.FieldAttributes; 5 | import com.google.gson.Gson; 6 | import com.google.gson.GsonBuilder; 7 | import es.joseluisgs.dam.blog.dto.LoginDTO; 8 | import es.joseluisgs.dam.blog.repository.LoginRepository; 9 | import es.joseluisgs.dam.blog.service.LoginService; 10 | 11 | import java.sql.SQLException; 12 | 13 | public class LoginController { 14 | private static LoginController controller = null; 15 | 16 | // Mi Servicio unido al repositorio 17 | private final LoginService loginService; 18 | 19 | // Para evitar sacar el password del usuario y el user_id 20 | ExclusionStrategy strategy = new ExclusionStrategy() { 21 | @Override 22 | public boolean shouldSkipClass(Class clazz) { 23 | return false; 24 | } 25 | 26 | @Override 27 | public boolean shouldSkipField(FieldAttributes field) { 28 | return field.getName().startsWith("password") || field.getName().startsWith("user_id"); 29 | } 30 | }; 31 | 32 | // Implementamos nuestro Singleton para el controlador 33 | private LoginController(LoginService userService) { 34 | this.loginService = userService; 35 | } 36 | 37 | public static LoginController getInstance() { 38 | if (controller == null) { 39 | controller = new LoginController(new LoginService(new LoginRepository())); 40 | } 41 | return controller; 42 | } 43 | 44 | // Ejemplo de operaciones 45 | public String login(String userMail, String userPassword) { 46 | try { 47 | LoginDTO login = loginService.login(userMail, userPassword); 48 | if (login != null) { 49 | final Gson prettyGson = new GsonBuilder() 50 | .addSerializationExclusionStrategy(strategy) 51 | .setPrettyPrinting() 52 | .create(); 53 | return prettyGson.toJson(login); 54 | } else 55 | return "Error Login: Usuario/a no existe o los datos son incorrectos"; 56 | } catch (SQLException e) { 57 | return "Error Login: Usuario/a no existe o los datos son incorrectos"; 58 | } 59 | 60 | } 61 | 62 | public String logout(Long ID) { 63 | try { 64 | if (loginService.logout(ID)) { 65 | return "Logout OK"; 66 | } else { 67 | return "Error Logout: Usuario/a no existe o los datos son incorrectos"; 68 | } 69 | } catch (SQLException e) { 70 | return "Error Logout: Usuario/a no existe o los datos son incorrectos"; 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/service/CommentService.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.service; 2 | 3 | import es.joseluisgs.dam.blog.dto.CommentDTO; 4 | import es.joseluisgs.dam.blog.mapper.CommentMapper; 5 | import es.joseluisgs.dam.blog.model.Comment; 6 | import es.joseluisgs.dam.blog.model.Post; 7 | import es.joseluisgs.dam.blog.model.User; 8 | import es.joseluisgs.dam.blog.repository.CommentRepository; 9 | import es.joseluisgs.dam.blog.repository.PostRepository; 10 | import es.joseluisgs.dam.blog.repository.UserRepository; 11 | 12 | import java.sql.SQLException; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | 17 | public class CommentService extends BaseService { 18 | CommentMapper mapper = new CommentMapper(); 19 | 20 | // Inyección de dependencias en el constructor. El servicio necesita este repositorio 21 | public CommentService(CommentRepository repository) { 22 | super(repository); 23 | } 24 | 25 | // Otras operaciones o especificaciones para CRUD 26 | // O podíamos mapear el nombre 27 | // O simplemente ocultar las que no queramos usar en niveles superiores 28 | public List getAllComments() throws SQLException { 29 | // Obtenemos la lista 30 | List posts = this.findAll(); 31 | List result = new ArrayList<>(); 32 | 33 | // Ahora debemos añadir al DTO el Usuario como objeto y el Post, 34 | // no como ID que es lo que nos viene de la BD 35 | for (Comment comment : posts) { 36 | CommentDTO commentDTO = mapper.toDTO(comment); 37 | commentDTO.setUser(this.getUserById(comment.getUser_id())); 38 | commentDTO.setPost(this.getPostById(comment.getPost_id())); 39 | result.add(commentDTO); 40 | } 41 | return result; 42 | } 43 | 44 | public CommentDTO getCommentById(Long id) throws SQLException { 45 | Comment comment = this.getById(id); 46 | CommentDTO commentDTO = mapper.toDTO(comment); 47 | commentDTO.setUser(this.getUserById(comment.getUser_id())); 48 | commentDTO.setPost(this.getPostById(comment.getPost_id())); 49 | return commentDTO; 50 | } 51 | 52 | public CommentDTO postComment(CommentDTO commentDTO) throws SQLException { 53 | Comment comment = this.save(mapper.fromDTO(commentDTO)); 54 | CommentDTO res = mapper.toDTO(comment); 55 | res.setUser(this.getUserById(comment.getUser_id())); 56 | res.setPost(this.getPostById(comment.getPost_id())); 57 | return res; 58 | } 59 | 60 | public CommentDTO updateComment(CommentDTO commentDTO) throws SQLException { 61 | Comment comment = this.update(mapper.fromDTO(commentDTO)); 62 | CommentDTO res = mapper.toDTO(comment); 63 | res.setUser(this.getUserById(comment.getUser_id())); 64 | res.setPost(this.getPostById(comment.getPost_id())); 65 | return res; 66 | } 67 | 68 | public CommentDTO deleteComment(CommentDTO commentDTO) throws SQLException { 69 | Comment comment = this.delete(mapper.fromDTO(commentDTO)); 70 | CommentDTO res = mapper.toDTO(comment); 71 | res.setUser(this.getUserById(comment.getUser_id())); 72 | res.setPost(this.getPostById(comment.getPost_id())); 73 | return res; 74 | } 75 | 76 | private User getUserById(Long id) throws SQLException { 77 | UserService service = new UserService(new UserRepository()); 78 | return service.getById(id); 79 | } 80 | 81 | private Post getPostById(Long id) throws SQLException { 82 | PostService service = new PostService(new PostRepository()); 83 | return service.getById(id); 84 | } 85 | 86 | public List getCommentsByPost(Long id) throws SQLException { 87 | return repository.getByPost(id); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/controller/CategoryController.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.controller; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import es.joseluisgs.dam.blog.dto.CategoryDTO; 6 | import es.joseluisgs.dam.blog.repository.CategoryRepository; 7 | import es.joseluisgs.dam.blog.service.CategoryService; 8 | 9 | import java.sql.SQLException; 10 | 11 | public class CategoryController { 12 | private static CategoryController controller = null; 13 | 14 | // Mi Servicio unido al repositorio 15 | private final CategoryService categoryService; 16 | 17 | // Implementamos nuestro Singleton para el controlador 18 | private CategoryController(CategoryService categoryService) { 19 | this.categoryService = categoryService; 20 | } 21 | 22 | public static CategoryController getInstance() { 23 | if (controller == null) { 24 | controller = new CategoryController(new CategoryService(new CategoryRepository())); 25 | } 26 | return controller; 27 | } 28 | 29 | // Ejemplo de operaciones 30 | // Usamos DTO para implementar este patrón en represantación y trasporte de la información 31 | // public List getAllCategories() { 32 | // return categoryService.getAllCategories(); 33 | // } 34 | 35 | public String getAllCategoriesJSON() { 36 | try { 37 | // Vamos a devolver el JSON de las categorías 38 | final Gson prettyGson = new GsonBuilder().setPrettyPrinting().create(); 39 | return prettyGson.toJson(categoryService.getAllCategories()); 40 | } catch (SQLException e) { 41 | System.err.println("Error CategoryController en getAllCategories: " + e.getMessage()); 42 | return "Error CategoryController en getAllCategories: " + e.getMessage(); 43 | } 44 | } 45 | 46 | public String getCategoryByIdJSON(Long id) { 47 | try { 48 | // Vamos a devolver el JSON de las categorías 49 | final Gson prettyGson = new GsonBuilder().setPrettyPrinting().create(); 50 | return prettyGson.toJson(categoryService.getCategoryById(id)); 51 | } catch (SQLException e) { 52 | System.err.println("Error CategoryController en getCategoryById: " + e.getMessage()); 53 | return "Error CategoryController en getCategoryById: " + e.getMessage(); 54 | } 55 | } 56 | 57 | public String postCategoryJSON(CategoryDTO categoryDTO) { 58 | try { 59 | final Gson prettyGson = new GsonBuilder().setPrettyPrinting().create(); 60 | return prettyGson.toJson(categoryService.postCategory(categoryDTO)); 61 | } catch (SQLException e) { 62 | System.err.println("Error CategoryController en postCategory: " + e.getMessage()); 63 | return "Error CategoryController en postCategory: " + e.getMessage(); 64 | } 65 | } 66 | 67 | public String updateCategoryJSON(CategoryDTO categoryDTO) { 68 | try { 69 | final Gson prettyGson = new GsonBuilder().setPrettyPrinting().create(); 70 | return prettyGson.toJson(categoryService.updateCategory(categoryDTO)); 71 | } catch (SQLException e) { 72 | System.err.println("Error CategoryController en updateCategory: " + e.getMessage()); 73 | return "Error CategoryController en updateCategory: " + e.getMessage(); 74 | } 75 | } 76 | 77 | public String deleteCategoryJSON(CategoryDTO categoryDTO) { 78 | try { 79 | final Gson prettyGson = new GsonBuilder().setPrettyPrinting().create(); 80 | return prettyGson.toJson(categoryService.deleteCategory(categoryDTO)); 81 | } catch (SQLException e) { 82 | System.err.println("Error CategoryController en deleteCategory: " + e.getMessage()); 83 | return "Error CategoryController en deleteCategory: " + e.getMessage(); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/repository/CategoryRepository.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.repository; 2 | 3 | import es.joseluisgs.dam.blog.database.DataBaseController; 4 | import es.joseluisgs.dam.blog.model.Category; 5 | 6 | import java.sql.ResultSet; 7 | import java.sql.SQLException; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class CategoryRepository implements CrudRespository { 12 | @Override 13 | public List findAll() throws SQLException { 14 | String query = "SELECT * FROM category"; 15 | DataBaseController db = DataBaseController.getInstance(); 16 | db.open(); 17 | ResultSet result = db.select(query).orElseThrow(() -> new SQLException("Error CategoryRepository al consultar registros de Categorías")); 18 | ArrayList list = new ArrayList(); 19 | while (result.next()) { 20 | list.add( 21 | new Category( 22 | result.getLong("id"), 23 | result.getString("texto") 24 | ) 25 | ); 26 | } 27 | db.close(); 28 | return list; 29 | } 30 | 31 | @Override 32 | public Category getById(Long ID) throws SQLException { 33 | String query = "SELECT * FROM category WHERE id = ?"; 34 | DataBaseController db = DataBaseController.getInstance(); 35 | db.open(); 36 | ResultSet result = db.select(query, ID).orElseThrow(() -> new SQLException("Error CategoryRepository al consultar categoría con ID " + ID)); 37 | if (result.first()) { 38 | Category category = new Category( 39 | result.getLong("id"), 40 | result.getString("texto") 41 | ); 42 | db.close(); 43 | return category; 44 | } else 45 | throw new SQLException("Error CategoryRepository no existe categoría con ID: " + ID); 46 | } 47 | 48 | @Override 49 | public Category save(Category category) throws SQLException { 50 | // si ponemos como primer parametro null como primero, y pasamos en la llamada 51 | // al tener configurado el servidor con Prepared generated keys, obtenemos el ID generado autoincremntal de MariaDB 52 | String query = "INSERT INTO category VALUES (null, ?)"; 53 | DataBaseController db = DataBaseController.getInstance(); 54 | db.open(); 55 | ResultSet res = db.insert(query, category.getTexto()).orElseThrow(() -> new SQLException("Error CategoryRepository al insertar categoría")); 56 | // Para obtener su ID 57 | if (res.first()) { 58 | category.setId(res.getLong(1)); 59 | db.close(); 60 | return category; 61 | } else 62 | throw new SQLException("Error CategoryRepository al insertar cantegoria en BD"); 63 | } 64 | 65 | @Override 66 | public Category update(Category category) throws SQLException { 67 | String query = "UPDATE category SET texto = ? WHERE id = ?"; 68 | DataBaseController db = DataBaseController.getInstance(); 69 | db.open(); 70 | int res = db.update(query, category.getTexto(), category.getId()); 71 | db.close(); 72 | if (res > 0) 73 | return category; 74 | else 75 | throw new SQLException("Error CategoryRepository al actualizar categoria con id: " + category.getId()); 76 | } 77 | 78 | @Override 79 | public Category delete(Category category) throws SQLException { 80 | String query = "DELETE FROM category WHERE id = ?"; 81 | DataBaseController db = DataBaseController.getInstance(); 82 | db.open(); 83 | int res = db.delete(query, category.getId()); 84 | db.close(); 85 | if (res > 0) 86 | return category; 87 | else 88 | throw new SQLException("Error CategoryRepository al eliminar categoria con id: " + category.getId()); 89 | 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | es.joseluisgs.dam.blog 8 | Blog-Relacional-AccesoDatos-2021-2022 9 | 1.0-SNAPSHOT 10 | 11 | Blog-Relacional-AccesoDatos-2021-2022 12 | 13 | http://www.example.com 14 | 15 | 16 | UTF-8 17 | 11 18 | 11 19 | 20 | 21 | 22 | 23 | 24 | io.github.cdimascio 25 | dotenv-java 26 | 2.2.0 27 | 28 | 29 | 30 | org.projectlombok 31 | lombok 32 | RELEASE 33 | compile 34 | 35 | 36 | 37 | org.mariadb.jdbc 38 | mariadb-java-client 39 | 2.7.4 40 | 41 | 42 | 43 | com.google.code.gson 44 | gson 45 | 2.8.8 46 | 47 | 48 | 49 | org.mybatis 50 | mybatis 51 | 3.5.7 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | maven-clean-plugin 61 | 3.1.0 62 | 63 | 64 | 65 | maven-resources-plugin 66 | 3.0.2 67 | 68 | 69 | maven-compiler-plugin 70 | 3.8.0 71 | 72 | 73 | maven-surefire-plugin 74 | 2.22.1 75 | 76 | 77 | maven-jar-plugin 78 | 3.0.2 79 | 80 | 81 | maven-install-plugin 82 | 2.5.2 83 | 84 | 85 | maven-deploy-plugin 86 | 2.8.2 87 | 88 | 89 | 90 | maven-site-plugin 91 | 3.7.1 92 | 93 | 94 | maven-project-info-reports-plugin 95 | 3.0.0 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/repository/PostRepository.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.repository; 2 | 3 | import es.joseluisgs.dam.blog.database.DataBaseController; 4 | import es.joseluisgs.dam.blog.model.Post; 5 | 6 | import java.sql.ResultSet; 7 | import java.sql.SQLException; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class PostRepository implements CrudRespository { 12 | @Override 13 | public List findAll() throws SQLException { 14 | String query = "SELECT * FROM post"; 15 | DataBaseController db = DataBaseController.getInstance(); 16 | db.open(); 17 | ResultSet result = db.select(query).orElseThrow(() -> new SQLException("Error PostRepository al consultar registros de post")); 18 | ArrayList list = new ArrayList(); 19 | while (result.next()) { 20 | list.add( 21 | new Post( 22 | result.getLong("id"), 23 | result.getString("titulo"), 24 | result.getString("url"), 25 | result.getString("contenido"), 26 | result.getTimestamp("fecha_publicacion").toLocalDateTime(), 27 | result.getLong("user_id"), 28 | result.getLong("category_id") 29 | ) 30 | ); 31 | } 32 | db.close(); 33 | return list; 34 | } 35 | 36 | @Override 37 | public Post getById(Long ID) throws SQLException { 38 | String query = "SELECT * FROM post WHERE id = ?"; 39 | DataBaseController db = DataBaseController.getInstance(); 40 | db.open(); 41 | ResultSet result = db.select(query, ID).orElseThrow(() -> new SQLException("Error PostRepository al consultar post con ID " + ID)); 42 | if (result.first()) { 43 | Post post = new Post( 44 | result.getLong("id"), 45 | result.getString("titulo"), 46 | result.getString("url"), 47 | result.getString("contenido"), 48 | result.getTimestamp("fecha_publicacion").toLocalDateTime(), 49 | result.getLong("user_id"), 50 | result.getLong("category_id") 51 | ); 52 | db.close(); 53 | return post; 54 | } else 55 | throw new SQLException("Error PostRepository no existe Post con ID: " + ID); 56 | } 57 | 58 | @Override 59 | public Post save(Post post) throws SQLException { 60 | String query = "INSERT INTO post VALUES (null, ?, ?, ?, ?, ?, ?)"; 61 | DataBaseController db = DataBaseController.getInstance(); 62 | db.open(); 63 | ResultSet res = db.insert(query, post.getTitulo(), post.getUrl(), post.getContenido(), 64 | post.getFechaPublicacion(), post.getUser_id(), 65 | post.getCategory_id()).orElseThrow(() -> new SQLException("Error PostRepository al insertar Post")); 66 | // Para obtener su ID 67 | if (res.first()) { 68 | post.setId(res.getLong(1)); 69 | // una vez insertado comprobamos que está correcto para devolverlo 70 | db.close(); 71 | return post; 72 | } else 73 | throw new SQLException("Error PostRepository al insertar post en BD"); 74 | } 75 | 76 | @Override 77 | public Post update(Post post) throws SQLException { 78 | String query = "UPDATE post SET titulo = ?, url = ?, contenido = ?, fecha_publicacion = ?, " + 79 | "user_id = ?, category_id = ? WHERE id = ?"; 80 | 81 | DataBaseController db = DataBaseController.getInstance(); 82 | db.open(); 83 | int res = db.update(query, post.getTitulo(), post.getUrl(), post.getContenido(), 84 | post.getFechaPublicacion(), post.getUser_id(), post.getCategory_id(), post.getId()); 85 | db.close(); 86 | if (res > 0) 87 | return post; 88 | else 89 | throw new SQLException("Error PostRepository al actualizar post con id: " + post.getId()); 90 | } 91 | 92 | @Override 93 | public Post delete(Post post) throws SQLException { 94 | String query = "DELETE FROM post WHERE id = ?"; 95 | 96 | DataBaseController db = DataBaseController.getInstance(); 97 | db.open(); 98 | int res = db.delete(query, post.getId()); 99 | db.close(); 100 | if (res > 0) 101 | return post; 102 | else 103 | throw new SQLException("Error PostRepository al eliminar post con id: " + post.getId()); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/controller/PostController.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.controller; 2 | 3 | import com.google.gson.ExclusionStrategy; 4 | import com.google.gson.FieldAttributes; 5 | import com.google.gson.Gson; 6 | import com.google.gson.GsonBuilder; 7 | import es.joseluisgs.dam.blog.dto.PostDTO; 8 | import es.joseluisgs.dam.blog.repository.PostRepository; 9 | import es.joseluisgs.dam.blog.service.PostService; 10 | 11 | import java.sql.SQLException; 12 | 13 | public class PostController { 14 | private static PostController controller = null; 15 | 16 | // Mi Servicio unido al repositorio 17 | private final PostService postService; 18 | // Eliminamos los campso que qno queremos que salgan en el JSON 19 | ExclusionStrategy strategy = new ExclusionStrategy() { 20 | @Override 21 | public boolean shouldSkipClass(Class clazz) { 22 | return false; 23 | } 24 | 25 | @Override 26 | public boolean shouldSkipField(FieldAttributes field) { 27 | return field.getName().startsWith("password") 28 | || field.getName().startsWith("user_id") 29 | || field.getName().startsWith("category_id"); 30 | } 31 | }; 32 | 33 | // Implementamos nuestro Singleton para el controlador 34 | private PostController(PostService postService) { 35 | this.postService = postService; 36 | } 37 | 38 | public static PostController getInstance() { 39 | if (controller == null) { 40 | controller = new PostController(new PostService(new PostRepository())); 41 | } 42 | return controller; 43 | } 44 | 45 | // Ejemplo de operaciones 46 | public String getAllPostJSON() { 47 | // Vamos a devolver el JSON de las categorías 48 | try { 49 | final Gson prettyGson = new GsonBuilder() 50 | .addSerializationExclusionStrategy(strategy) 51 | .setPrettyPrinting() 52 | .create(); 53 | return prettyGson.toJson(postService.getAllPosts()); 54 | } catch (SQLException e) { 55 | System.err.println("Error PostController en getAllPots: " + e.getMessage()); 56 | return "Error PostController en getAllPost: " + e.getMessage(); 57 | } 58 | } 59 | 60 | public String getPostByIdJSON(Long id) { 61 | // Vamos a devolver el JSON de las categorías 62 | try { 63 | final Gson prettyGson = new GsonBuilder() 64 | .addSerializationExclusionStrategy(strategy) 65 | .setPrettyPrinting() 66 | .create(); 67 | return prettyGson.toJson(postService.getPostById(id)); 68 | } catch (SQLException e) { 69 | System.err.println("Error PostController en getPostById " + e.getMessage()); 70 | return "Error PostController en getPostById: " + e.getMessage(); 71 | } 72 | } 73 | 74 | public String postPostJSON(PostDTO postDTO) { 75 | try { 76 | final Gson prettyGson = new GsonBuilder() 77 | .addSerializationExclusionStrategy(strategy) 78 | .setPrettyPrinting() 79 | .create(); 80 | return prettyGson.toJson(postService.postPost(postDTO)); 81 | } catch (SQLException e) { 82 | System.err.println("Error PostController en postPost: " + e.getMessage()); 83 | return "Error PostController en postPost: " + e.getMessage(); 84 | } 85 | } 86 | 87 | public String updatePostJSON(PostDTO postDTO) { 88 | try { 89 | final Gson prettyGson = new GsonBuilder() 90 | .addSerializationExclusionStrategy(strategy) 91 | .setPrettyPrinting() 92 | .create(); 93 | return prettyGson.toJson(postService.updatePost(postDTO)); 94 | } catch (SQLException e) { 95 | System.err.println("Error PostController en updatePost: " + e.getMessage()); 96 | return "Error PostController en updatePost: " + e.getMessage(); 97 | } 98 | } 99 | 100 | public String deletePostJSON(PostDTO postDTO) { 101 | try { 102 | final Gson prettyGson = new GsonBuilder() 103 | .addSerializationExclusionStrategy(strategy) 104 | .setPrettyPrinting() 105 | .create(); 106 | return prettyGson.toJson(postService.deletePost(postDTO)); 107 | } catch (SQLException e) { 108 | System.err.println("Error PostController en deletePost: " + e.getMessage()); 109 | return "Error PostController en deletePost: " + e.getMessage(); 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/service/PostService.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.service; 2 | 3 | import es.joseluisgs.dam.blog.dto.PostDTO; 4 | import es.joseluisgs.dam.blog.mapper.PostMapper; 5 | import es.joseluisgs.dam.blog.model.Category; 6 | import es.joseluisgs.dam.blog.model.Comment; 7 | import es.joseluisgs.dam.blog.model.Post; 8 | import es.joseluisgs.dam.blog.model.User; 9 | import es.joseluisgs.dam.blog.repository.CategoryRepository; 10 | import es.joseluisgs.dam.blog.repository.CommentRepository; 11 | import es.joseluisgs.dam.blog.repository.PostRepository; 12 | import es.joseluisgs.dam.blog.repository.UserRepository; 13 | 14 | import java.sql.SQLException; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | public class PostService extends BaseService { 19 | PostMapper mapper = new PostMapper(); 20 | 21 | // Inyección de dependencias en el constructor. El servicio necesita este repositorio 22 | public PostService(PostRepository repository) { 23 | super(repository); 24 | } 25 | 26 | // Otras operaciones o especificaciones para CRUD 27 | // O podíamos mapear el nombre 28 | // O simplemente ocultar las que no queramos usar en niveles superiores 29 | public List getAllPosts() throws SQLException { 30 | // Obtenemos la lista 31 | List posts = this.findAll(); 32 | List result = new ArrayList<>(); 33 | 34 | // Ahora debemos añadir al DTO el Usuario como objeto y la Categoria, 35 | // no como ID que es lo que nos viene de la BD 36 | for (Post post : posts) { 37 | PostDTO postDTO = mapper.toDTO(post); 38 | postDTO.setUser(this.getUserById(post.getUser_id())); 39 | postDTO.setCategory(this.getCategoryById(post.getCategory_id())); 40 | // Tenemos que cargar los comentarios que tenga 41 | postDTO.setComments(getPostComments(postDTO.getId())); 42 | result.add(postDTO); 43 | } 44 | return result; 45 | } 46 | 47 | public PostDTO getPostById(Long id) throws SQLException { 48 | Post post = this.getById(id); 49 | PostDTO postDTO = mapper.toDTO(post); 50 | postDTO.setUser(this.getUserById(post.getUser_id())); 51 | postDTO.setCategory(this.getCategoryById(post.getCategory_id())); 52 | // Tenemos que cargar los comentarios que tenga 53 | postDTO.setComments(getPostComments(postDTO.getId())); 54 | return postDTO; 55 | } 56 | 57 | public PostDTO postPost(PostDTO postDTO) throws SQLException { 58 | Post post = this.save(mapper.fromDTO(postDTO)); 59 | PostDTO res = mapper.toDTO(post); 60 | res.setUser(this.getUserById(post.getUser_id())); 61 | res.setCategory(this.getCategoryById(post.getCategory_id())); 62 | return res; 63 | } 64 | 65 | public PostDTO updatePost(PostDTO postDTO) throws SQLException { 66 | Post post = this.update(mapper.fromDTO(postDTO)); 67 | PostDTO res = mapper.toDTO(post); 68 | res.setUser(this.getUserById(post.getUser_id())); 69 | res.setCategory(this.getCategoryById(post.getCategory_id())); 70 | // Tenemos que cargar los comentarios que tenga 71 | postDTO.setComments(getPostComments(res.getId())); 72 | return res; 73 | } 74 | 75 | public PostDTO deletePost(PostDTO postDTO) throws SQLException { 76 | // Debemos borrar los comentarios antes 77 | for (Comment comment : getPostComments(postDTO.getId())) { 78 | deleteComment(comment); 79 | } 80 | // Ahora borramos el post 81 | Post post = this.delete(mapper.fromDTO(postDTO)); 82 | PostDTO res = mapper.toDTO(post); 83 | res.setUser(this.getUserById(post.getUser_id())); 84 | res.setCategory(this.getCategoryById(post.getCategory_id())); 85 | return res; 86 | } 87 | 88 | private User getUserById(Long id) throws SQLException { 89 | UserService service = new UserService(new UserRepository()); 90 | return service.getById(id); 91 | } 92 | 93 | private Category getCategoryById(Long id) throws SQLException { 94 | CategoryService service = new CategoryService(new CategoryRepository()); 95 | return service.getById(id); 96 | } 97 | 98 | private List getPostComments(Long id) throws SQLException { 99 | CommentService service = new CommentService(new CommentRepository()); 100 | return service.getCommentsByPost(id); 101 | } 102 | 103 | private Comment deleteComment(Comment comment) throws SQLException { 104 | CommentService service = new CommentService(new CommentRepository()); 105 | return service.repository.delete(comment); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.repository; 2 | 3 | import es.joseluisgs.dam.blog.database.DataBaseController; 4 | import es.joseluisgs.dam.blog.model.User; 5 | 6 | import java.sql.ResultSet; 7 | import java.sql.SQLException; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class UserRepository implements CrudRespository { 12 | @Override 13 | public List findAll() throws SQLException { 14 | String query = "SELECT * FROM user"; 15 | DataBaseController db = DataBaseController.getInstance(); 16 | db.open(); 17 | ResultSet result = db.select(query).orElseThrow(() -> new SQLException("Error al consultar registros de Usuarios")); 18 | ArrayList list = new ArrayList(); 19 | while (result.next()) { 20 | list.add( 21 | new User( 22 | result.getLong("id"), 23 | result.getString("nombre"), 24 | result.getString("email"), 25 | result.getString("password"), 26 | result.getDate("fecha_registro").toLocalDate() 27 | ) 28 | ); 29 | } 30 | db.close(); 31 | return list; 32 | } 33 | 34 | @Override 35 | public User getById(Long ID) throws SQLException { 36 | String query = "SELECT * FROM user WHERE id = ?"; 37 | DataBaseController db = DataBaseController.getInstance(); 38 | db.open(); 39 | ResultSet result = db.select(query, ID).orElseThrow(() -> new SQLException("Error al consultar usuario con ID " + ID)); 40 | if (result.first()) { 41 | User user = new User( 42 | result.getLong("id"), 43 | result.getString("nombre"), 44 | result.getString("email"), 45 | result.getString("password"), 46 | result.getDate("fecha_registro").toLocalDate() 47 | ); 48 | db.close(); 49 | return user; 50 | } else 51 | throw new SQLException("Error no existe Usuario con ID: " + ID); 52 | } 53 | 54 | @Override 55 | public User save(User user) throws SQLException { 56 | String query = "INSERT INTO user VALUES (null, ?, ?, ?, ?)"; 57 | DataBaseController db = DataBaseController.getInstance(); 58 | db.open(); 59 | ResultSet res = db.insert(query, user.getNombre(), user.getEmail(), 60 | user.getPassword(), user.getFechaRegistro()).orElseThrow(() -> new SQLException("Error UserRepository al insertar Usuario")); 61 | // Para obtener su ID 62 | if (res.first()) { 63 | user.setId(res.getLong(1)); 64 | // una vez insertado comprobamos que esta correcto para devolverlo 65 | db.close(); 66 | return user; 67 | } else 68 | throw new SQLException("Error UserRepository al insertar usuario en BD"); 69 | } 70 | 71 | @Override 72 | public User update(User user) throws SQLException { 73 | String query = "UPDATE user SET nombre = ?, email = ? WHERE id = ?"; 74 | DataBaseController db = DataBaseController.getInstance(); 75 | db.open(); 76 | int res = db.update(query, user.getNombre(), user.getEmail(), user.getId()); 77 | db.close(); 78 | if (res > 0) 79 | return user; 80 | else 81 | throw new SQLException("Error UserRepository al actualizar usuario con id: " + user.getId()); 82 | } 83 | 84 | @Override 85 | public User delete(User user) throws SQLException { 86 | String query = "DELETE FROM user WHERE id = ?"; 87 | DataBaseController db = DataBaseController.getInstance(); 88 | db.open(); 89 | int res = db.delete(query, user.getId()); 90 | db.close(); 91 | if (res > 0) 92 | return user; 93 | throw new SQLException("Error UserRepository al actualizar usuario con id: " + user.getId()); 94 | } 95 | 96 | public User getByMail(String userMail) throws SQLException { 97 | String query = "SELECT * FROM user WHERE email = ?"; 98 | DataBaseController db = DataBaseController.getInstance(); 99 | db.open(); 100 | ResultSet result = db.select(query, userMail).orElseThrow(() -> new SQLException("Error UserRepository no existe usuario con email " + userMail)); 101 | result.first(); 102 | User user = new User( 103 | result.getLong("id"), 104 | result.getString("nombre"), 105 | result.getString("email"), 106 | result.getString("password"), 107 | result.getDate("fecha_registro").toLocalDate() 108 | ); 109 | db.close(); 110 | return user; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.controller; 2 | 3 | import com.google.gson.ExclusionStrategy; 4 | import com.google.gson.FieldAttributes; 5 | import com.google.gson.Gson; 6 | import com.google.gson.GsonBuilder; 7 | import es.joseluisgs.dam.blog.dto.UserDTO; 8 | import es.joseluisgs.dam.blog.repository.UserRepository; 9 | import es.joseluisgs.dam.blog.service.UserService; 10 | 11 | import java.sql.SQLException; 12 | 13 | public class UserController { 14 | private static UserController controller = null; 15 | 16 | // Mi Servicio unido al repositorio 17 | private final UserService userService; 18 | // Con esto evitamos que se imprima el campo password si no queremos 19 | // https://www.baeldung.com/gson-exclude-fields-serialization 20 | ExclusionStrategy strategy = new ExclusionStrategy() { 21 | @Override 22 | public boolean shouldSkipClass(Class clazz) { 23 | return false; 24 | } 25 | 26 | @Override 27 | public boolean shouldSkipField(FieldAttributes field) { 28 | return field.getName().startsWith("password"); 29 | } 30 | }; 31 | 32 | // Implementamos nuestro Singleton para el controlador 33 | private UserController(UserService userService) { 34 | this.userService = userService; 35 | } 36 | 37 | public static UserController getInstance() { 38 | if (controller == null) { 39 | controller = new UserController(new UserService(new UserRepository())); 40 | } 41 | return controller; 42 | } 43 | 44 | // Ejemplo de operaciones 45 | // Usamos DTO para implementar este patrón en represantación y trasporte de la información 46 | // public List getAllUsers() { 47 | // return userService.getAllUsers(); 48 | // } 49 | 50 | public String getAllUsersJSON() { 51 | // Vamos a devolver el JSON de las categorías 52 | try { 53 | final Gson prettyGson = new GsonBuilder() 54 | .addSerializationExclusionStrategy(strategy) 55 | .setPrettyPrinting() 56 | .create(); 57 | return prettyGson.toJson(userService.getAllUsers()); 58 | } catch (SQLException e) { 59 | System.err.println("Error UserController en getAllUser: " + e.getMessage()); 60 | return "Error UserController en getAllUser: " + e.getMessage(); 61 | } 62 | } 63 | 64 | public String getUserByIdJSON(Long id) { 65 | // Vamos a devolver el JSON de las categorías 66 | try { 67 | final Gson prettyGson = new GsonBuilder() 68 | .addSerializationExclusionStrategy(strategy) 69 | .setPrettyPrinting() 70 | .create(); 71 | return prettyGson.toJson(userService.getUserById(id)); 72 | } catch (SQLException e) { 73 | System.err.println("Error UserController en getUserById " + e.getMessage()); 74 | return "Error UserController en getUserById: " + e.getMessage(); 75 | } 76 | } 77 | 78 | public String postUserJSON(UserDTO userDTO) { 79 | try { 80 | final Gson prettyGson = new GsonBuilder() 81 | .addSerializationExclusionStrategy(strategy) 82 | .setPrettyPrinting() 83 | .create(); 84 | return prettyGson.toJson(userService.postUser(userDTO)); 85 | } catch (SQLException e) { 86 | System.err.println("Error UserController en postUser " + e.getMessage()); 87 | return "Error UserController en postUser: " + e.getMessage(); 88 | } 89 | } 90 | 91 | public String updateUserJSON(UserDTO userDTO) { 92 | try { 93 | final Gson prettyGson = new GsonBuilder() 94 | .addSerializationExclusionStrategy(strategy) 95 | .setPrettyPrinting() 96 | .create(); 97 | return prettyGson.toJson(userService.updateUser(userDTO)); 98 | } catch (SQLException e) { 99 | System.err.println("Error UserController en updateUser " + e.getMessage()); 100 | return "Error UserController en updateUser: " + e.getMessage(); 101 | } 102 | } 103 | 104 | public String deleteUserJSON(UserDTO userDTO) { 105 | try { 106 | final Gson prettyGson = new GsonBuilder() 107 | .addSerializationExclusionStrategy(strategy) 108 | .setPrettyPrinting() 109 | .create(); 110 | return prettyGson.toJson(userService.deleteUser(userDTO)); 111 | } catch (SQLException e) { 112 | System.err.println("Error UserController en deleteUser " + e.getMessage()); 113 | return "Error UserController en deleteUser: " + e.getMessage(); 114 | } 115 | } 116 | 117 | } 118 | 119 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/controller/CommentController.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.controller; 2 | 3 | import com.google.gson.ExclusionStrategy; 4 | import com.google.gson.FieldAttributes; 5 | import com.google.gson.Gson; 6 | import com.google.gson.GsonBuilder; 7 | import es.joseluisgs.dam.blog.dto.CommentDTO; 8 | import es.joseluisgs.dam.blog.repository.CommentRepository; 9 | import es.joseluisgs.dam.blog.service.CommentService; 10 | 11 | import java.sql.SQLException; 12 | 13 | public class CommentController { 14 | private static CommentController controller = null; 15 | 16 | // Mi Servicio unido al repositorio 17 | private final CommentService commentService; 18 | // Eliminamos los campso que qno queremos que salgan en el JSON 19 | ExclusionStrategy strategy = new ExclusionStrategy() { 20 | @Override 21 | public boolean shouldSkipClass(Class clazz) { 22 | return false; 23 | } 24 | 25 | @Override 26 | public boolean shouldSkipField(FieldAttributes field) { 27 | return field.getName().startsWith("password") 28 | || field.getName().startsWith("user_id") 29 | || field.getName().startsWith("category_id"); 30 | } 31 | }; 32 | 33 | // Implementamos nuestro Singleton para el controlador 34 | private CommentController(CommentService commentService) { 35 | this.commentService = commentService; 36 | } 37 | 38 | public static CommentController getInstance() { 39 | if (controller == null) { 40 | controller = new CommentController(new CommentService(new CommentRepository())); 41 | } 42 | return controller; 43 | } 44 | 45 | // Ejemplo de operaciones 46 | public String getAllCommentsJSON() { 47 | // Vamos a devolver el JSON de las categorías 48 | try { 49 | final Gson prettyGson = new GsonBuilder() 50 | .addSerializationExclusionStrategy(strategy) 51 | .setPrettyPrinting() 52 | .create(); 53 | return prettyGson.toJson(commentService.getAllComments()); 54 | } catch (SQLException e) { 55 | System.err.println("Error CommentController en getAllComments: " + e.getMessage()); 56 | return "Error CommentController en getAllComments: " + e.getMessage(); 57 | } 58 | } 59 | 60 | public String getCommentByIdJSON(Long id) { 61 | try { 62 | // Vamos a devolver el JSON de las categorías 63 | final Gson prettyGson = new GsonBuilder() 64 | .addSerializationExclusionStrategy(strategy) 65 | .setPrettyPrinting() 66 | .create(); 67 | return prettyGson.toJson(commentService.getCommentById(id)); 68 | } catch (SQLException e) { 69 | System.err.println("Error CommentController en getCommentById: " + e.getMessage()); 70 | return "Error CommentController en getCommentById: " + e.getMessage(); 71 | } 72 | } 73 | 74 | public String postCommentJSON(CommentDTO commentDTO) { 75 | try { 76 | final Gson prettyGson = new GsonBuilder() 77 | .addSerializationExclusionStrategy(strategy) 78 | .setPrettyPrinting() 79 | .create(); 80 | return prettyGson.toJson(commentService.postComment(commentDTO)); 81 | } catch (SQLException e) { 82 | System.err.println("Error CommentController en postComment: " + e.getMessage()); 83 | return "Error CommentController en postComment: " + e.getMessage(); 84 | } 85 | } 86 | 87 | public String updateCommentJSON(CommentDTO commentDTO) { 88 | try { 89 | final Gson prettyGson = new GsonBuilder() 90 | .addSerializationExclusionStrategy(strategy) 91 | .setPrettyPrinting() 92 | .create(); 93 | return prettyGson.toJson(commentService.updateComment(commentDTO)); 94 | } catch (SQLException e) { 95 | System.err.println("Error CommentController en updateCommment: " + e.getMessage()); 96 | return "Error CommentController en updateComment: " + e.getMessage(); 97 | } 98 | } 99 | 100 | public String deleteCommentJSON(CommentDTO commentDTO) { 101 | try { 102 | final Gson prettyGson = new GsonBuilder() 103 | .addSerializationExclusionStrategy(strategy) 104 | .setPrettyPrinting() 105 | .create(); 106 | return prettyGson.toJson(commentService.deleteComment(commentDTO)); 107 | } catch (SQLException e) { 108 | System.err.println("Error CommentController en deleteComment: " + e.getMessage()); 109 | return "Error CommentController en deleteComment: " + e.getMessage(); 110 | } 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /sql/blog.sql: -------------------------------------------------------------------------------- 1 | -- Adminer 4.8.1 MySQL 5.5.5-10.5.11-MariaDB dump 2 | 3 | SET NAMES utf8; 4 | SET time_zone = '+00:00'; 5 | SET foreign_key_checks = 0; 6 | SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO'; 7 | 8 | DROP DATABASE IF EXISTS `blog`; 9 | CREATE DATABASE `blog` /*!40100 DEFAULT CHARACTER SET utf8 */; 10 | USE `blog`; 11 | 12 | DROP TABLE IF EXISTS `category`; 13 | CREATE TABLE `category` ( 14 | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 15 | `texto` varchar(255) NOT NULL, 16 | PRIMARY KEY (`id`), 17 | UNIQUE KEY `category_UN` (`texto`) 18 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Base de Datos de Categoría'; 19 | 20 | INSERT INTO `category` (`id`, `texto`) VALUES 21 | (2, 'Dudas'), 22 | (3, 'Evaluación'), 23 | (1, 'General'), 24 | (4, 'Pruebas'); 25 | 26 | DROP TABLE IF EXISTS `comment`; 27 | CREATE TABLE `comment` ( 28 | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 29 | `texto` text NOT NULL, 30 | `fecha_publicacion` datetime NOT NULL, 31 | `user_id` bigint(20) unsigned NOT NULL, 32 | `post_id` bigint(20) unsigned NOT NULL, 33 | `uuid` varchar(100) NOT NULL, 34 | PRIMARY KEY (`id`), 35 | KEY `comment_user_FK` (`user_id`), 36 | KEY `comment_post_FK` (`post_id`), 37 | CONSTRAINT `comment_post_FK` FOREIGN KEY (`post_id`) REFERENCES `post` (`id`), 38 | CONSTRAINT `comment_user_FK` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) 39 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Tabla de comentarios'; 40 | 41 | INSERT INTO `comment` (`id`, `texto`, `fecha_publicacion`, `user_id`, `post_id`, `uuid`) VALUES 42 | (1, 'Comentario 01', '2021-10-01 19:26:08', 1, 1, '7cf47238-234f-11ec-acf4-0242ac120002'), 43 | (2, 'Comentario 02', '2021-10-01 19:26:22', 2, 2, '8141765d-234f-11ec-acf4-0242ac120002'), 44 | (3, 'Comentario 03', '2021-10-01 19:26:36', 3, 2, '8541b571-234f-11ec-acf4-0242ac120002'), 45 | (4, 'Comentario 04', '2021-10-02 08:57:27', 1, 3, '59042b10-234f-11ec-acf4-0242ac120002'), 46 | (5, 'Comentario 05', '2021-10-01 19:27:24', 4, 4, '89c8ad7f-234f-11ec-acf4-0242ac120002'), 47 | (6, 'Comentario 06', '2021-10-02 08:57:27', 1, 3, '8cb836bc-234f-11ec-acf4-0242ac120002'), 48 | (7, 'Comentario 07', '2021-10-02 07:10:26', 4, 4, 'd18810bd-234f-11ec-acf4-0242ac120002'); 49 | 50 | DROP TABLE IF EXISTS `login`; 51 | CREATE TABLE `login` ( 52 | `user_id` bigint(20) unsigned NOT NULL, 53 | `ultimo_acceso` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE current_timestamp(), 54 | `token` varchar(100) NOT NULL, 55 | PRIMARY KEY (`user_id`), 56 | CONSTRAINT `login_user_FK` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE 57 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Tabla de login'; 58 | 59 | 60 | 61 | 62 | DROP TABLE IF EXISTS `post`; 63 | CREATE TABLE `post` ( 64 | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 65 | `titulo` varchar(250) NOT NULL, 66 | `url` varchar(250) DEFAULT NULL, 67 | `contenido` text NOT NULL, 68 | `fecha_publicacion` datetime NOT NULL, 69 | `user_id` bigint(20) unsigned NOT NULL, 70 | `category_id` bigint(20) unsigned NOT NULL, 71 | PRIMARY KEY (`id`), 72 | UNIQUE KEY `post_UN` (`url`), 73 | KEY `post_user_FK` (`user_id`), 74 | KEY `post_category_FK` (`category_id`), 75 | CONSTRAINT `post_category_FK` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON UPDATE CASCADE, 76 | CONSTRAINT `post_user_FK` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON UPDATE CASCADE 77 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Tabla de posts'; 78 | 79 | INSERT INTO `post` (`id`, `titulo`, `url`, `contenido`, `fecha_publicacion`, `user_id`, `category_id`) VALUES 80 | (1, 'Post num 1', 'http://post1.com', 'Este es el post num 1', '2021-10-01 16:12:03', 1, 1), 81 | (2, 'Posy num 2', 'http://post2.com', 'Este es el post num 2', '2021-10-01 16:12:28', 2, 2), 82 | (3, 'Post num 3', 'http://post3.com', 'Este es el post num 3', '2021-10-01 16:13:00', 3, 3), 83 | (4, 'Post num 4', 'http://post4.com', 'Esto es el post num 4', '2021-10-01 16:13:40', 1, 1), 84 | (5, 'Post num 5', 'http://post5.com', 'Esto es el post num 5', '2021-10-01 16:14:14', 2, 3); 85 | 86 | DROP TABLE IF EXISTS `test`; 87 | CREATE TABLE `test` ( 88 | `nombre` varchar(30) DEFAULT NULL, 89 | `email` varchar(50) DEFAULT NULL 90 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 91 | 92 | INSERT INTO `test` (`nombre`, `email`) VALUES 93 | ('Jose Luis', 'joseluis@docker.com'), 94 | ('Soraya', 'soraya@docker.com'), 95 | ('Victor', 'victor@docker.com'); 96 | 97 | DROP TABLE IF EXISTS `user`; 98 | CREATE TABLE `user` ( 99 | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 100 | `nombre` varchar(100) NOT NULL, 101 | `email` varchar(100) NOT NULL, 102 | `password` varchar(100) NOT NULL, 103 | `fecha_registro` date NOT NULL, 104 | PRIMARY KEY (`id`), 105 | UNIQUE KEY `user_UN` (`email`) 106 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Tabla de usuarios'; 107 | 108 | INSERT INTO `user` (`id`, `nombre`, `email`, `password`, `fecha_registro`) VALUES 109 | (1, 'Pepe Perez', 'pepe@pepe.es', '7110eda4d09e062aa5e4a390b0a572ac0d2c0220', '2021-09-30'), 110 | (2, 'Ana Anaya', 'ana@anaya.es', '7110eda4d09e062aa5e4a390b0a572ac0d2c0220', '2021-09-30'), 111 | (3, 'Paco Perez', 'paco@perez.es', '7110eda4d09e062aa5e4a390b0a572ac0d2c0220', '2021-09-30'), 112 | (4, 'Son Goku', 'goku@dragonball.es', '7110eda4d09e062aa5e4a390b0a572ac0d2c0220', '2021-09-30'), 113 | (5, 'Chuck Norris', 'chuck@norris.es', '7110eda4d09e062aa5e4a390b0a572ac0d2c0220', '2021-10-01'); 114 | 115 | -- 2021-10-02 07:10:43 -------------------------------------------------------------------------------- /docker/mariadb/sql/init-db.sql: -------------------------------------------------------------------------------- 1 | -- Adminer 4.8.1 MySQL 5.5.5-10.5.11-MariaDB dump 2 | 3 | SET NAMES utf8; 4 | SET time_zone = '+00:00'; 5 | SET foreign_key_checks = 0; 6 | SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO'; 7 | 8 | DROP DATABASE IF EXISTS `blog`; 9 | CREATE DATABASE `blog` /*!40100 DEFAULT CHARACTER SET utf8 */; 10 | USE `blog`; 11 | 12 | DROP TABLE IF EXISTS `category`; 13 | CREATE TABLE `category` ( 14 | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 15 | `texto` varchar(255) NOT NULL, 16 | PRIMARY KEY (`id`), 17 | UNIQUE KEY `category_UN` (`texto`) 18 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Base de Datos de Categoría'; 19 | 20 | INSERT INTO `category` (`id`, `texto`) VALUES 21 | (2, 'Dudas'), 22 | (3, 'Evaluación'), 23 | (1, 'General'), 24 | (4, 'Pruebas'); 25 | 26 | DROP TABLE IF EXISTS `comment`; 27 | CREATE TABLE `comment` ( 28 | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 29 | `texto` text NOT NULL, 30 | `fecha_publicacion` datetime NOT NULL, 31 | `user_id` bigint(20) unsigned NOT NULL, 32 | `post_id` bigint(20) unsigned NOT NULL, 33 | `uuid` varchar(100) NOT NULL, 34 | PRIMARY KEY (`id`), 35 | KEY `comment_user_FK` (`user_id`), 36 | KEY `comment_post_FK` (`post_id`), 37 | CONSTRAINT `comment_post_FK` FOREIGN KEY (`post_id`) REFERENCES `post` (`id`), 38 | CONSTRAINT `comment_user_FK` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) 39 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Tabla de comentarios'; 40 | 41 | INSERT INTO `comment` (`id`, `texto`, `fecha_publicacion`, `user_id`, `post_id`, `uuid`) VALUES 42 | (1, 'Comentario 01', '2021-10-01 19:26:08', 1, 1, '7cf47238-234f-11ec-acf4-0242ac120002'), 43 | (2, 'Comentario 02', '2021-10-01 19:26:22', 2, 2, '8141765d-234f-11ec-acf4-0242ac120002'), 44 | (3, 'Comentario 03', '2021-10-01 19:26:36', 3, 2, '8541b571-234f-11ec-acf4-0242ac120002'), 45 | (4, 'Comentario 04', '2021-10-02 08:57:27', 1, 3, '59042b10-234f-11ec-acf4-0242ac120002'), 46 | (5, 'Comentario 05', '2021-10-01 19:27:24', 4, 4, '89c8ad7f-234f-11ec-acf4-0242ac120002'), 47 | (6, 'Comentario 06', '2021-10-02 08:57:27', 1, 3, '8cb836bc-234f-11ec-acf4-0242ac120002'), 48 | (7, 'Comentario 07', '2021-10-02 07:10:26', 4, 4, 'd18810bd-234f-11ec-acf4-0242ac120002'); 49 | 50 | DROP TABLE IF EXISTS `login`; 51 | CREATE TABLE `login` ( 52 | `user_id` bigint(20) unsigned NOT NULL, 53 | `ultimo_acceso` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE current_timestamp(), 54 | `token` varchar(100) NOT NULL, 55 | PRIMARY KEY (`user_id`), 56 | CONSTRAINT `login_user_FK` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE 57 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Tabla de login'; 58 | 59 | 60 | 61 | DROP TABLE IF EXISTS `post`; 62 | CREATE TABLE `post` ( 63 | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 64 | `titulo` varchar(250) NOT NULL, 65 | `url` varchar(250) DEFAULT NULL, 66 | `contenido` text NOT NULL, 67 | `fecha_publicacion` datetime NOT NULL, 68 | `user_id` bigint(20) unsigned NOT NULL, 69 | `category_id` bigint(20) unsigned NOT NULL, 70 | PRIMARY KEY (`id`), 71 | UNIQUE KEY `post_UN` (`url`), 72 | KEY `post_user_FK` (`user_id`), 73 | KEY `post_category_FK` (`category_id`), 74 | CONSTRAINT `post_category_FK` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON UPDATE CASCADE, 75 | CONSTRAINT `post_user_FK` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON UPDATE CASCADE 76 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Tabla de posts'; 77 | 78 | INSERT INTO `post` (`id`, `titulo`, `url`, `contenido`, `fecha_publicacion`, `user_id`, `category_id`) VALUES 79 | (1, 'Post num 1', 'http://post1.com', 'Este es el post num 1', '2021-10-01 16:12:03', 1, 1), 80 | (2, 'Posy num 2', 'http://post2.com', 'Este es el post num 2', '2021-10-01 16:12:28', 2, 2), 81 | (3, 'Post num 3', 'http://post3.com', 'Este es el post num 3', '2021-10-01 16:13:00', 3, 3), 82 | (4, 'Post num 4', 'http://post4.com', 'Esto es el post num 4', '2021-10-01 16:13:40', 1, 1), 83 | (5, 'Post num 5', 'http://post5.com', 'Esto es el post num 5', '2021-10-01 16:14:14', 2, 3); 84 | 85 | DROP TABLE IF EXISTS `test`; 86 | CREATE TABLE `test` ( 87 | `nombre` varchar(30) DEFAULT NULL, 88 | `email` varchar(50) DEFAULT NULL 89 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 90 | 91 | INSERT INTO `test` (`nombre`, `email`) VALUES 92 | ('Jose Luis', 'joseluis@docker.com'), 93 | ('Soraya', 'soraya@docker.com'), 94 | ('Victor', 'victor@docker.com'); 95 | 96 | DROP TABLE IF EXISTS `user`; 97 | CREATE TABLE `user` ( 98 | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 99 | `nombre` varchar(100) NOT NULL, 100 | `email` varchar(100) NOT NULL, 101 | `password` varchar(100) NOT NULL, 102 | `fecha_registro` date NOT NULL, 103 | PRIMARY KEY (`id`), 104 | UNIQUE KEY `user_UN` (`email`) 105 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Tabla de usuarios'; 106 | 107 | INSERT INTO `user` (`id`, `nombre`, `email`, `password`, `fecha_registro`) VALUES 108 | (1, 'Pepe Perez', 'pepe@pepe.es', '7110eda4d09e062aa5e4a390b0a572ac0d2c0220', '2021-09-30'), 109 | (2, 'Ana Anaya', 'ana@anaya.es', '7110eda4d09e062aa5e4a390b0a572ac0d2c0220', '2021-09-30'), 110 | (3, 'Paco Perez', 'paco@perez.es', '7110eda4d09e062aa5e4a390b0a572ac0d2c0220', '2021-09-30'), 111 | (4, 'Son Goku', 'goku@dragonball.es', '7110eda4d09e062aa5e4a390b0a572ac0d2c0220', '2021-09-30'), 112 | (5, 'Chuck Norris', 'chuck@norris.es', '7110eda4d09e062aa5e4a390b0a572ac0d2c0220', '2021-10-01'); 113 | 114 | -- 2021-10-02 07:10:43 -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/repository/CommentRepository.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.repository; 2 | 3 | import es.joseluisgs.dam.blog.database.DataBaseController; 4 | import es.joseluisgs.dam.blog.model.Comment; 5 | 6 | import java.sql.ResultSet; 7 | import java.sql.SQLException; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.UUID; 11 | 12 | public class CommentRepository implements CrudRespository { 13 | @Override 14 | public List findAll() throws SQLException { 15 | String query = "SELECT * FROM comment"; 16 | DataBaseController db = DataBaseController.getInstance(); 17 | db.open(); 18 | ResultSet result = db.select(query).orElseThrow(() -> new SQLException("Error CommentRepository al consultar registros de comentarios")); 19 | ArrayList list = new ArrayList(); 20 | while (result.next()) { 21 | list.add( 22 | new Comment( 23 | result.getLong("id"), 24 | result.getString("texto"), 25 | result.getTimestamp("fecha_publicacion").toLocalDateTime(), 26 | result.getLong("user_id"), 27 | result.getLong("post_id") 28 | ) 29 | ); 30 | } 31 | db.close(); 32 | return list; 33 | 34 | } 35 | 36 | @Override 37 | public Comment getById(Long ID) throws SQLException { 38 | String query = "SELECT * FROM comment WHERE id = ?"; 39 | DataBaseController db = DataBaseController.getInstance(); 40 | db.open(); 41 | ResultSet result = db.select(query, ID).orElseThrow(() -> new SQLException("Error CommentRepository al consultar comentario con ID " + ID)); 42 | if (result.first()) { 43 | Comment comment = new Comment( 44 | result.getLong("id"), 45 | result.getString("texto"), 46 | result.getTimestamp("fecha_publicacion").toLocalDateTime(), 47 | result.getLong("user_id"), 48 | result.getLong("post_id") 49 | ); 50 | db.close(); 51 | return comment; 52 | } else 53 | throw new SQLException("Error CommentRepository no existe comentario con ID: " + ID); 54 | } 55 | 56 | @Override 57 | public Comment save(Comment comment) throws SQLException { 58 | // Le dotamos de un UUID único, por si hay dos mensajes iguales en texto y momento. 59 | // también podríamos usar un hash, pero podrían llegar dos mensajes iguales en el mismo instante 60 | // es imporbable, pero así explico el uuid 61 | UUID uuid = UUID.randomUUID(); 62 | String query = "INSERT INTO comment VALUES (null, ?, ?, ?, ?, ?)"; 63 | 64 | DataBaseController db = DataBaseController.getInstance(); 65 | db.open(); 66 | ResultSet res = db.insert(query, comment.getTexto(), comment.getFechaPublicacion(), 67 | comment.getUser_id(), comment.getPost_id(), 68 | uuid.toString()).orElseThrow(() -> new SQLException("Error CommentRepository al insertar Commentario")); 69 | // Para obtener su ID 70 | if (res.first()) { 71 | comment.setId(res.getLong(1)); 72 | // una vez insertado comprobamos que esta correcto para devolverlo 73 | db.close(); 74 | return comment; 75 | } else 76 | throw new SQLException("Error CommentRepository al insertar comentario en BD"); 77 | } 78 | 79 | @Override 80 | public Comment update(Comment comment) throws SQLException { 81 | String query = "UPDATE comment SET texto = ?, fecha_publicacion = ?, user_id = ?, post_id = ? WHERE id = ?"; 82 | DataBaseController db = DataBaseController.getInstance(); 83 | db.open(); 84 | int res = db.update(query, comment.getTexto(), comment.getFechaPublicacion(), comment.getUser_id(), 85 | comment.getPost_id(), comment.getId()); 86 | db.close(); 87 | if (res > 0) 88 | return comment; 89 | else 90 | throw new SQLException("Error CommentRepository al actualizar comentario con id: " + comment.getId()); 91 | } 92 | 93 | @Override 94 | public Comment delete(Comment comment) throws SQLException { 95 | String query = "DELETE FROM comment WHERE id = ?"; 96 | 97 | DataBaseController db = DataBaseController.getInstance(); 98 | db.open(); 99 | int res = db.delete(query, comment.getId()); 100 | db.close(); 101 | if (res > 0) 102 | return comment; 103 | else 104 | throw new SQLException("Error CommentRepository al eliminar comentario con id: " + comment.getId()); 105 | } 106 | 107 | public List getByPost(Long idPost) throws SQLException { 108 | String query = "SELECT * FROM comment where post_id = ?"; 109 | DataBaseController db = DataBaseController.getInstance(); 110 | db.open(); 111 | ResultSet result = db.select(query, idPost).orElseThrow(() -> new SQLException("Error CommentRepository no existe comentario con idPost " + idPost)); 112 | ArrayList list = new ArrayList(); 113 | while (result.next()) { 114 | list.add( 115 | new Comment( 116 | result.getLong("id"), 117 | result.getString("texto"), 118 | result.getTimestamp("fecha_publicacion").toLocalDateTime(), 119 | result.getLong("user_id"), 120 | result.getLong("post_id") 121 | ) 122 | ); 123 | } 124 | db.close(); 125 | return list; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/database/DataBaseController.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog.database; 2 | 3 | import es.joseluisgs.dam.blog.utils.ApplicationProperties; 4 | import io.github.cdimascio.dotenv.Dotenv; 5 | import lombok.NonNull; 6 | import org.apache.ibatis.jdbc.ScriptRunner; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.FileNotFoundException; 10 | import java.io.FileReader; 11 | import java.io.Reader; 12 | import java.sql.*; 13 | import java.util.Optional; 14 | 15 | /** 16 | * Controlador de Bases de Datos Relacionales 17 | */ 18 | public class DataBaseController { 19 | private static DataBaseController controller; 20 | @NonNull 21 | private String serverUrl; 22 | @NonNull 23 | private String serverPort; 24 | @NonNull 25 | private String dataBaseName; 26 | @NonNull 27 | private String user; 28 | @NonNull 29 | private String password; 30 | /* 31 | Tipos de Driver 32 | SQLite: "org.sqlite.JDBC"; 33 | MySQL: "com.mysql.jdbc.Driver" 34 | MariaDB: com.mysql.cj.jdbc.Driver 35 | */ 36 | @NonNull 37 | private String jdbcDriver; 38 | // Para manejar las conexiones y respuestas de las mismas 39 | @NonNull 40 | private Connection connection; 41 | @NonNull 42 | private PreparedStatement preparedStatement; 43 | 44 | /** 45 | * Constructor privado para Singleton 46 | */ 47 | private DataBaseController() { 48 | // System.out.println("Mi nombre es: " + this.nombre); 49 | initConfig(); 50 | } 51 | 52 | /** 53 | * Devuelve una instancia del controlador 54 | * 55 | * @return instancia del controladorBD 56 | */ 57 | public static DataBaseController getInstance() { 58 | if (controller == null) { 59 | controller = new DataBaseController(); 60 | } 61 | return controller; 62 | } 63 | 64 | /** 65 | * Carga la configuración de acceso al servidor de Base de Datos 66 | * Puede ser directa "hardcodeada" o asignada dinámicamente a traves de ficheros .env o properties 67 | */ 68 | private void initConfig() { 69 | // Leemos los datos de la base de datos que pueden estar en 70 | // porperties o en .env 71 | // imaginemos que el usuario y pasword estaán en .env y el resto en application.properties 72 | // si no los rellenamos aquí. 73 | ApplicationProperties properties = new ApplicationProperties(); 74 | serverUrl = properties.readProperty("database.server.url"); 75 | serverPort = properties.readProperty("database.server.port"); 76 | dataBaseName = properties.readProperty("database.name"); 77 | jdbcDriver = properties.readProperty("database.jdbc.driver"); 78 | Dotenv dotenv = Dotenv.load(); 79 | user = dotenv.get("DATABASE_USER"); 80 | password = dotenv.get("DATABASE_PASSWORD"); 81 | } 82 | 83 | /** 84 | * Abre la conexión con el servidor de base de datos 85 | * 86 | * @throws SQLException Servidor no accesible por problemas de conexión o datos de acceso incorrectos 87 | */ 88 | public void open() throws SQLException { 89 | //String url = "jdbc:sqlite:"+this.ruta+this.bbdd; //MySQL jdbc:mysql://localhost/prueba", "root", "1daw" 90 | String url = "jdbc:mariadb://" + this.serverUrl + ":" + this.serverPort + "/" + this.dataBaseName; 91 | // System.out.println(url); 92 | // Obtenemos la conexión 93 | connection = DriverManager.getConnection(url, user, password); 94 | } 95 | 96 | /** 97 | * Cierra la conexión con el servidor de base de datos 98 | * 99 | * @throws SQLException Servidor no responde o no puede realizar la operación de cierre 100 | */ 101 | public void close() throws SQLException { 102 | preparedStatement.close(); 103 | connection.close(); 104 | } 105 | 106 | /** 107 | * Realiza una consulta a la base de datos de manera "preparada" obteniendo los parametros opcionales si son necesarios 108 | * 109 | * @param querySQL consulta SQL de tipo select 110 | * @param params parámetros de la consulta parametrizada 111 | * @return ResultSet de la consulta 112 | * @throws SQLException No se ha podido realizar la consulta o la tabla no existe 113 | */ 114 | private ResultSet executeQuery(@NonNull String querySQL, Object... params) throws SQLException { 115 | preparedStatement = connection.prepareStatement(querySQL); 116 | // Vamos a pasarle los parametros usando preparedStatement 117 | for (int i = 0; i < params.length; i++) { 118 | preparedStatement.setObject(i + 1, params[i]); 119 | } 120 | return preparedStatement.executeQuery(); 121 | } 122 | 123 | /** 124 | * Realiza una consulta select a la base de datos de manera "preparada" obteniendo los parametros opcionales si son necesarios 125 | * 126 | * @param querySQL consulta SQL de tipo select 127 | * @param params parámetros de la consulta parametrizada 128 | * @return ResultSet de la consulta 129 | * @throws SQLException No se ha podido realizar la consulta o la tabla no existe 130 | */ 131 | public Optional select(@NonNull String querySQL, Object... params) throws SQLException { 132 | return Optional.of(executeQuery(querySQL, params)); 133 | } 134 | 135 | /** 136 | * Realiza una consulta select a la base de datos de manera "preparada" obteniendo los parametros opcionales si son necesarios 137 | * 138 | * @param querySQL consulta SQL de tipo select 139 | * @param limit número de registros de la página 140 | * @param offset desplazamiento de registros o número de registros ignorados para comenzar la devolución 141 | * @param params parámetros de la consulta parametrizada 142 | * @return ResultSet de la consulta 143 | * @throws SQLException No se ha podido realizar la consulta o la tabla no existe o el desplazamiento es mayor que el número de registros 144 | */ 145 | public Optional select(@NonNull String querySQL, int limit, int offset, Object... params) throws SQLException { 146 | String query = querySQL + " LIMIT " + limit + " OFFSET " + offset; 147 | return Optional.of(executeQuery(query, params)); 148 | } 149 | 150 | /** 151 | * Realiza una consulta de tipo insert de manera "preparada" con los parametros opcionales si son encesarios 152 | * @param insertSQL consulta SQL de tipo insert 153 | * @param params parámetros de la consulta parametrizada 154 | * @return Clave del registro insertado 155 | * @throws SQLException tabla no existe o no se ha podido realizar la operación 156 | */ 157 | public Optional insert(@NonNull String insertSQL, Object... params) throws SQLException { 158 | // Con return generated keys obtenemos las claves generadas is las claves es autonumerica por ejemplo 159 | preparedStatement = connection.prepareStatement(insertSQL, preparedStatement.RETURN_GENERATED_KEYS); 160 | // Vamos a pasarle los parametros usando preparedStatement 161 | for (int i = 0; i < params.length; i++) { 162 | preparedStatement.setObject(i + 1, params[i]); 163 | } 164 | preparedStatement.executeUpdate(); 165 | return Optional.of(preparedStatement.getGeneratedKeys()); 166 | } 167 | 168 | /** 169 | * Realiza una consulta de tipo update de manera "preparada" con los parametros opcionales si son encesarios 170 | * @param updateSQL consulta SQL de tipo update 171 | * @param params parámetros de la consulta parametrizada 172 | * @return número de registros actualizados 173 | * @throws SQLException tabla no existe o no se ha podido realizar la operación 174 | */ 175 | public int update(@NonNull String updateSQL, Object... params) throws SQLException { 176 | return updateQuery(updateSQL, params); 177 | } 178 | 179 | /** 180 | * Realiza una consulta de tipo delete de manera "preparada" con los parametros opcionales si son encesarios 181 | * @param deleteSQL consulta SQL de tipo delete 182 | * @param params parámetros de la consulta parametrizada 183 | * @return número de registros eliminados 184 | * @throws SQLException tabla no existe o no se ha podido realizar la operación 185 | */ 186 | public int delete(@NonNull String deleteSQL, Object... params) throws SQLException { 187 | return updateQuery(deleteSQL, params); 188 | } 189 | 190 | /** 191 | * Realiza una consulta de tipo update, es decir que modifca los datos, de manera "preparada" con los parametros opcionales si son encesarios 192 | * @param genericSQL consulta SQL de tipo update, delete, creted, etc.. que modifica los datos 193 | * @param params parámetros de la consulta parametrizada 194 | * @return número de registros eliminados 195 | * @throws SQLException tabla no existe o no se ha podido realizar la operación 196 | */ 197 | private int updateQuery(@NonNull String genericSQL, Object... params) throws SQLException { 198 | // Con return generated keys obtenemos las claves generadas 199 | preparedStatement = connection.prepareStatement(genericSQL); 200 | // Vamos a pasarle los parametros usando preparedStatement 201 | for (int i = 0; i < params.length; i++) { 202 | preparedStatement.setObject(i + 1, params[i]); 203 | } 204 | return preparedStatement.executeUpdate(); 205 | } 206 | 207 | public void initData(@NonNull String sqlFile) throws FileNotFoundException { 208 | ScriptRunner sr = new ScriptRunner(connection); 209 | Reader reader = new BufferedReader(new FileReader(sqlFile)); 210 | sr.runScript(reader); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blog-Relacional-AccesoDatos-2021-2022 2 | Ejemplo de desarrollo de un blog (backend básico) para Acceso a Datos, usando una base de datos realacional e implementando distintas técnicas y patrones de Acceso a Datos vistos en clase. 3 | 4 | [![Kotlin](https://img.shields.io/badge/Code-Java-blue)](https://www.java.com/es/) 5 | [![LISENCE](https://img.shields.io/badge/Lisence-MIT-green)]() 6 | ![GitHub](https://img.shields.io/github/last-commit/joseluisgs/Blog-Relacional-AccesoDatos-2021-2022) 7 | 8 | - [Blog-Relacional-AccesoDatos-2021-2022](#blog-relacional-accesodatos-2021-2022) 9 | - [Descripción](#descripción) 10 | - [Tecnologías](#tecnologías) 11 | - [Enunciado](#enunciado) 12 | - [Ejemplo de diagrama](#ejemplo-de-diagrama) 13 | - [Desarrollo](#desarrollo) 14 | - [GitFlow](#gitflow) 15 | - [Maven](#maven) 16 | - [Secretos](#secretos) 17 | - [Lombok](#lombok) 18 | - [Arquitectura](#arquitectura) 19 | - [Patrón DAO](#patrón-dao) 20 | - [Controladores - Servicios - Repositorios](#controladores---servicios---repositorios) 21 | - [Controlador](#controlador) 22 | - [Servicio](#servicio) 23 | - [Repositorio](#repositorio) 24 | - [Repositorio vs DAO](#repositorio-vs-dao) 25 | - [Patrón DTO y Mapper](#patrón-dto-y-mapper) 26 | - [Base de datos](#base-de-datos) 27 | - [Ejecución](#ejecución) 28 | - [Docker](#docker) 29 | - [Adminer o cliente de Bases de Datos](#adminer-o-cliente-de-bases-de-datos) 30 | - [Autor](#autor) 31 | - [Contacto](#contacto) 32 | - [Licencia](#licencia) 33 | 34 | ## Descripción 35 | Se ha implementado el desarrollo del un blog a nivel de backend para el acceso a los datos que se necesiten con fines didácticos para el módulo de Acceso a Datos de 2DAM. 36 | Debes entender que es un ejemplo didáctico para clase, por lo que parte de la solución simplemente es para mostrar distintas técnicas y patrones y por lo tanto 37 | puede que no sea la más óptima o adecuada a niveles de producción o empresarial. Tenlo en cuenta. 38 | 39 | ## Tecnologías 40 | Se han usado las siguientes tecnologías: 41 | - Java 11, como lenguaje de programación. 42 | - MariaDB como motor de base de datos relacional. 43 | - Docker para lanzar la base de datos, así como otras utilidades para manejarla. 44 | 45 | ## Enunciado 46 | Se desea implementar la base de un blog teniendo en cuenta que: 47 | - Un usuario una vez registrado mediante email y password puede hacer login y logout en el sistema. 48 | - El usuario puede escribir varios posts los cuales pertenecen solo a una categoría existente, como general, dudas o evaluación. Se pueden crear nuevas categorías. 49 | - Los usuarios pueden hacer distintos comentarios sobre posts existentes. 50 | 51 | ### Ejemplo de diagrama 52 | ![diagrama](./diagrams/Diagrams.png) 53 | 54 | ## Desarrollo 55 | ### GitFlow 56 | Se ha usado GitFlow como modelo de flujo de desarrollo y trabajo con el repositorio. 57 | 58 | ### Maven 59 | Apache Maven es un software de gestión de proyectos. Maven aumenta la reutilización y también se encarga de la mayoría 60 | de las tareas relacionadas con la construcción. Funciona en muchos pasos, como agregar archivos jar a la biblioteca del proyecto, 61 | crear informes y ejecutar casos de prueba o crear archivos Jar para el proyecto e incluso muchas más cosas. 62 | La configuración de Maven se guarda en el fichero [pom.xml](./pom.xml). 63 | 64 | ### Secretos 65 | Para trabajar con secretos y o variables globales se han usado dos enfoques en el directorio recursos: 66 | - Ficheros de Properties (Propiedades): con ellos podemos leer propiedades de la manea clave valor. 67 | - Ficheros .env: Mediante ellos leemos las variables de entorno ya sea del sistema o definidas en un fichero .env 68 | 69 | ### Lombok 70 | Se ha usado [Lombok](https://projectlombok.org/features/all) como sistema de anotaciones para aumentar la productividad 71 | y reducir el código repetitivo. 72 | 73 | ## Arquitectura 74 | ### Patrón DAO 75 | Debemos tener en cuenta que la implementación y formato de la información puede variar según la fuente de los datos el patrón DAO propone separar por completo la lógica de negocio de la lógica para acceder a los datos, de esta forma, el DAO proporcionará los métodos necesarios para insertar, actualizar, borrar y consultar la información; por otra parte, la capa de negocio solo se preocupa por lógica de negocio y utiliza el DAO para interactuar con la fuente de datos. 76 | 77 | ![diagrama](./diagrams/dao.png) 78 | 79 | ### Controladores - Servicios - Repositorios 80 | La arquitectura que seguiremos es tipo CSS (Controladores -> Servicios -> Repositorios) de esta manera cualquier cambio no afectaría a la capa superior, manteniendo nuestra compatibilidad si por ejemplo pasamos de almacenamiento en ficheros XML a bases de datos relacionales o no relacionales. 81 | 82 | ![diagrama](./diagrams/arquitectura.png) 83 | 84 | #### Controlador 85 | Tiene la lógica de la aplicación y controla y redirige las distintas peticiones que se nos hacen. 86 | 87 | #### Servicio 88 | Tienen la lógica de negocio y procesan las peticiones que se nos hacen accediendo a los recursos necesarios para ello usando los repositorios. Son la capa intermedia. 89 | 90 | #### Repositorio 91 | Implementan la lógica de acceso y manipulación de los datos encapsulando dichas operaciones. 92 | 93 | #### Repositorio vs DAO 94 | - DAO implementa las operaciones a más bajo nivel para persistencia y manipulación de la información. DAO es una abstracción de la persistencia de datos. DAO es un concepto de nivel inferior, más cercano a los sistemas de almacenamiento de datos. DAO funciona como una capa de mapeo/acceso de datos. 95 | - Repositorio encapsula el propio sistema de almacenamiento, pero no suele estar tan ligado a dicho sistema de almacenamiento. Un repositorio es una abstracción de una colección de objetos. El el repositorio es un concepto de nivel superior, más cercano a los objetos de dominio. un repositorio es una capa entre dominios y capas de acceso a datos, que oculta la complejidad de recopilar datos y preparar un objeto de dominio. 96 | - Se puede dar el caso que un mismo repositorio trabaje con distintos DAOS, por ejemplo las operaciones de manejo de datos de usuarios estén en una base de datos relacional (login, password) y en una NoSQL otra información (nombre, apellidos, email, etc). Es por ello que el repositorio para manejo de usuario llamará por debajo a dos DAOS separados. Pero si la correspondencia es 1 a 1, las ideas son muy similares y podemos optar por uno de ellos. 97 | 98 | ### Patrón DTO y Mapper 99 | El patrón DTO tiene como finalidad de crear un objeto plano (POJO) con una serie de atributos que puedan ser enviados o recuperados por nuestro servicio y enviados a capas superiores cone l objetivo de condensar o adaptar la información para disminuir las trasferencia y con ello respetar nuestro modelo de datos, pues es en el objeto DTO donde realizamos operaciones de trasferencia de datos. 100 | Por ejemplo en nuestro modelo tenemos usuarios que escriben post. Podemos de una tacada traernos todos los usuarios y sus post en el DTO de usuarios. 101 | 102 | Por otro lado, los Mapper nos ayuda a ensamblar los DTO o desensamblaralos según el modelo de datos que tenemos. Es decir crear un objeto POJO a partir de un objeto DTO o POJO desde objetos DTO. 103 | 104 | ![dto](./diagrams/dto.svg) 105 | 106 | ### Base de datos 107 | Se ha usado MariaDB como motor de base de datos relacional y se ha creado un controlador para su manejo usando PreparedStatements, con ello conseguimos: 108 | - Ahorrar en la construcción de planes de ejecución. Pues la base de estas consultas se repite y tendremos un hash para identificarlas y con ellas podremos aprovechar el plan existente. 109 | - Evitar que nos inyecten SQL ya que al parametrizar la consulta el API de JDBC nos protege contra las este tipo de ataques. Normalmente el uso de consultas parametrizadas mejora el rendimiento entre un 20 y un 30 % a nivel de base de datos. 110 | 111 | ![imagen](https://www.arquitecturajava.com/wp-content/uploads/jdbcpreparedstatementparametros.png) 112 | 113 | ## Ejecución 114 | ### Docker 115 | Entrar en el directorio docker y ejecutar 116 | ```sh 117 | $ docker-compose up -d 118 | ``` 119 | Para iniciar la BD con algunos datos modifica el fichero [docker/mariadb/sql/init.sql](docker/mariadb/sql/init-db.sql) 120 | 121 | 122 | ### Adminer o cliente de Bases de Datos 123 | Debes conectarte a http://localhost:8080/ 124 | - server: mariadb 125 | - user: blog 126 | - password: blog1234 127 | - base de datos blog 128 | 129 | ## Autor 130 | 131 | Codificado con :sparkling_heart: por [José Luis González Sánchez](https://twitter.com/joseluisgonsan) 132 | 133 | [![Twitter](https://img.shields.io/twitter/follow/joseluisgonsan?style=social)](https://twitter.com/joseluisgonsan) 134 | [![GitHub](https://img.shields.io/github/followers/joseluisgs?style=social)](https://github.com/joseluisgs) 135 | 136 | ### Contacto 137 |

138 | Cualquier cosa que necesites házmelo saber por si puedo ayudarte 💬. 139 |

140 |

141 | 142 | 144 |    145 | 146 | 148 |    149 | 150 | 152 |    153 | 154 | 156 | 157 |

158 | 159 | 160 | ## Licencia 161 | 162 | Este proyecto está licenciado bajo licencia **MIT**, si desea saber más, visite el fichero [LICENSE](./LICENSE) para su uso docente y educativo. 163 | -------------------------------------------------------------------------------- /src/main/java/es/joseluisgs/dam/blog/Blog.java: -------------------------------------------------------------------------------- 1 | package es.joseluisgs.dam.blog; 2 | 3 | import es.joseluisgs.dam.blog.controller.*; 4 | import es.joseluisgs.dam.blog.database.DataBaseController; 5 | import es.joseluisgs.dam.blog.dto.CategoryDTO; 6 | import es.joseluisgs.dam.blog.dto.CommentDTO; 7 | import es.joseluisgs.dam.blog.dto.PostDTO; 8 | import es.joseluisgs.dam.blog.dto.UserDTO; 9 | 10 | import java.io.File; 11 | import java.io.FileNotFoundException; 12 | import java.sql.ResultSet; 13 | import java.sql.SQLException; 14 | import java.time.Instant; 15 | import java.time.LocalDate; 16 | import java.time.LocalDateTime; 17 | import java.util.Optional; 18 | 19 | public class Blog { 20 | private static Blog instance; 21 | 22 | private Blog() { 23 | } 24 | 25 | public static Blog getInstance() { 26 | if (instance == null) { 27 | instance = new Blog(); 28 | } 29 | return instance; 30 | } 31 | 32 | public void checkService() { 33 | DataBaseController controller = DataBaseController.getInstance(); 34 | try { 35 | controller.open(); 36 | Optional rs = controller.select("SELECT 'Hello World'"); 37 | if (rs.isPresent()) { 38 | rs.get().first(); 39 | controller.close(); 40 | } 41 | } catch (SQLException e) { 42 | System.err.println("Error al conectar al servidor de Base de Datos: " + e.getMessage()); 43 | System.exit(1); 44 | } 45 | } 46 | 47 | public void initDataBase() { 48 | String sqlFile = System.getProperty("user.dir") + File.separator + "sql" + File.separator + "blog.sql"; 49 | System.out.println(sqlFile); 50 | DataBaseController controller = DataBaseController.getInstance(); 51 | try { 52 | controller.open(); 53 | controller.initData(sqlFile); 54 | controller.close(); 55 | } catch (SQLException e) { 56 | System.err.println("Error al conectar al servidor de Base de Datos: " + e.getMessage()); 57 | System.exit(1); 58 | } catch (FileNotFoundException e) { 59 | System.err.println("Error al leer el fichero de datos iniciales: " + e.getMessage()); 60 | System.exit(1); 61 | } 62 | } 63 | 64 | public void Categories() { 65 | CategoryController categoryController = CategoryController.getInstance(); 66 | // Obtenemos todas las categorías 67 | // List categories = categoryController.getAllCategories(); 68 | // categories.forEach(c-> System.out.println(c.toJSON())); 69 | 70 | System.out.println("GET Todas las categorías"); 71 | System.out.println(categoryController.getAllCategoriesJSON()); 72 | 73 | System.out.println("GET Categoría con ID = 2"); 74 | System.out.println(categoryController.getCategoryByIdJSON(2L)); 75 | 76 | System.out.println("POST Insertando Categoría"); 77 | CategoryDTO categoryDTO = CategoryDTO.builder() 78 | .texto("Prueba " + Instant.now().toString()) 79 | .build(); 80 | System.out.println(categoryController.postCategoryJSON(categoryDTO)); 81 | 82 | System.out.println("UPDATE Categoría con ID = 4"); 83 | categoryDTO = CategoryDTO.builder() 84 | .id(4L) 85 | .texto("Prueba Update") 86 | .build(); 87 | System.out.println(categoryController.updateCategoryJSON(categoryDTO)); 88 | 89 | System.out.println("DELETE Categoría con ID = 4"); 90 | categoryDTO = CategoryDTO.builder() 91 | .id(4L) 92 | .build(); 93 | System.out.println(categoryController.deleteCategoryJSON(categoryDTO)); 94 | } 95 | 96 | public void Users() { 97 | UserController userController = UserController.getInstance(); 98 | 99 | System.out.println("GET Todos los usuarios"); 100 | System.out.println(userController.getAllUsersJSON()); 101 | 102 | System.out.println("GET Usuario con ID = 2"); 103 | System.out.println(userController.getUserByIdJSON(2L)); 104 | 105 | System.out.println("POST Insertando Usuario"); 106 | UserDTO userDTO = UserDTO.builder() 107 | .nombre("Nombre " + Instant.now().toString()) 108 | .email("user" + Math.random() + "@mail.com") 109 | .password("1234") 110 | .fechaRegistro(LocalDate.now()) 111 | .build(); 112 | System.out.println(userController.postUserJSON(userDTO)); 113 | 114 | System.out.println("UPDATE Usuario con ID = 5"); 115 | userDTO = UserDTO.builder() 116 | .id(5L) 117 | .nombre("Prueba Update") 118 | .email("prueba@update.com") 119 | .build(); 120 | System.out.println(userController.updateUserJSON(userDTO)); 121 | 122 | System.out.println("DELETE User con ID = 5"); 123 | userDTO = UserDTO.builder() 124 | .id(5L) 125 | .build(); 126 | System.out.println(userController.deleteUserJSON(userDTO)); 127 | } 128 | 129 | public void Posts() { 130 | PostController postController = PostController.getInstance(); 131 | 132 | System.out.println("GET Todos los Post"); 133 | System.out.println(postController.getAllPostJSON()); 134 | 135 | System.out.println("GET Post con ID = 2"); 136 | System.out.println(postController.getPostByIdJSON(2L)); 137 | 138 | System.out.println("POST Insertando Post"); 139 | PostDTO postDTO = PostDTO.builder() 140 | .titulo("Post " + Instant.now().toString()) 141 | .url("http://" + Math.random() + ".dominio.com") 142 | .contenido(Instant.now().toString()) 143 | .fechaPublicacion(LocalDateTime.now()) 144 | .build(); 145 | // Asignamos el usuario y categorías que existan 146 | postDTO.setUser_id(1L); 147 | postDTO.setCategory_id(1L); 148 | 149 | System.out.println(postController.postPostJSON(postDTO)); 150 | 151 | System.out.println("UPDATE Post con ID = 4"); 152 | // Solo dejamos cambiar el tútulo o el contenido 153 | postDTO = PostDTO.builder() 154 | .id(4L) 155 | .titulo("Update " + Instant.now().toString()) 156 | .contenido("Update " + Instant.now().toString()) 157 | .url("http://" + Math.random() + ".dominio.com") 158 | .fechaPublicacion(LocalDateTime.now()) 159 | .build(); 160 | // Asignamos el usuario y categorías que existan 161 | postDTO.setUser_id(1L); 162 | postDTO.setCategory_id(1L); 163 | System.out.println(postController.updatePostJSON(postDTO)); 164 | 165 | System.out.println("DELETE Post con ID = 5"); 166 | postDTO = PostDTO.builder() 167 | .id(5L) 168 | .build(); 169 | // Asignamos el usuario y categorías que existan 170 | postDTO.setUser_id(2L); 171 | postDTO.setCategory_id(3L); 172 | System.out.println(postController.deletePostJSON(postDTO)); 173 | 174 | System.out.println("DELETE Post con ID = 4, tiene categorias anidadas"); 175 | postDTO = PostDTO.builder() 176 | .id(4L) 177 | .build(); 178 | // Asignamos el usuario y categorías que existan 179 | postDTO.setUser_id(2L); 180 | postDTO.setCategory_id(3L); 181 | System.out.println(postController.deletePostJSON(postDTO)); 182 | 183 | } 184 | 185 | public void Comments() { 186 | CommentController commentController = CommentController.getInstance(); 187 | 188 | System.out.println("GET Todos los Comentarios"); 189 | System.out.println(commentController.getAllCommentsJSON()); 190 | 191 | System.out.println("GET Comentario con ID = 2"); 192 | System.out.println(commentController.getCommentByIdJSON(2L)); 193 | 194 | System.out.println("POST Insertando Comentario"); 195 | CommentDTO commentDTO = CommentDTO.builder() 196 | .texto("Comentario " + Instant.now().toString()) 197 | .fechaPublicacion(LocalDateTime.now()) 198 | .build(); 199 | // Asignamos el usuario y post que existan 200 | commentDTO.setUser_id(1L); 201 | commentDTO.setPost_id(3L); 202 | System.out.println(commentController.postCommentJSON(commentDTO)); 203 | 204 | System.out.println("UPDATE Comentario con ID = 4"); 205 | // Solo dejamos cambiar el tútulo o el contenido 206 | commentDTO = CommentDTO.builder() 207 | .id(4L) 208 | .texto("Update " + Instant.now().toString()) 209 | .fechaPublicacion(LocalDateTime.now()) 210 | .build(); 211 | // Asignamos el usuario y post que existan 212 | commentDTO.setUser_id(1L); 213 | commentDTO.setPost_id(3L); 214 | System.out.println(commentController.updateCommentJSON(commentDTO)); 215 | 216 | System.out.println("DELETE Comentario con ID = 6"); 217 | commentDTO = CommentDTO.builder() 218 | .id(6L) 219 | .build(); 220 | commentDTO.setUser_id(1L); 221 | commentDTO.setPost_id(3L); 222 | System.out.println(commentController.deleteCommentJSON(commentDTO)); 223 | } 224 | 225 | public void Login() { 226 | LoginController loginController = LoginController.getInstance(); 227 | System.out.println("Login con un usario que SI existe"); 228 | System.out.println(loginController.login("pepe@pepe.es", "1234")); 229 | System.out.println("Login con un usario que SI existe Y mal Password datos correctos"); 230 | System.out.println(loginController.login("pepe@pepe.es", "1255")); 231 | System.out.println("Login con un usario que NO existe o mal Password datos correctos"); 232 | System.out.println(loginController.login("pepe@pepe.com", "1255")); 233 | System.out.println("Logout de usuario que está logueado"); 234 | System.out.println(loginController.logout(1L)); 235 | System.out.println("Logout de usuario que no está logueado"); 236 | System.out.println(loginController.logout(33L)); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /diagrams/dto.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | Presentation LayersHTMLJsonUser- name- PasswordRole- nameUserDTO- name- rolesMapper --------------------------------------------------------------------------------