unidades:04_claves_primarias_y_tipos_datos:02_claves_primarias
Diferencias
Muestra las diferencias entre dos versiones de la página.
— | unidades:04_claves_primarias_y_tipos_datos:02_claves_primarias [2023/04/07 21:26] (actual) – creado - editor externo 127.0.0.1 | ||
---|---|---|---|
Línea 1: | Línea 1: | ||
+ | ====== Claves primarias ====== | ||
+ | Antes de explicar las herramientas que posee Hibernate al respecto de las claves primarias pasemos a repasar un poco los conceptos de bases de datos sobre claves primarias. | ||
+ | ===== Claves primarias y bases de datos ===== | ||
+ | |||
+ | ==== Propiedades de las claves primarias ==== | ||
+ | ¿Qué propiedades debe tener una buena clave primaria? | ||
+ | * Debe ser única | ||
+ | * No puede ser null | ||
+ | * Nunca debe cambiar | ||
+ | * Debe ser una única columna | ||
+ | * Debe ser rápida de generar | ||
+ | === Debe ser única === | ||
+ | Esta propiedad es obvia para todos , así que no me extenderé más. | ||
+ | |||
+ | === No puede ser null === | ||
+ | Esta propiedad también está clara para cualquiera que sepa de bases de datos | ||
+ | |||
+ | === Nunca debe cambiar === | ||
+ | Esta propiedad no es esencial que la cumpla una clave primaria pero si cambia se obliga a modificar todas las filas de otras tablas que tuvieran relación con la clave primaria que se ha modificado. Se podría dar el caso de que fuera necesario modificar miles o millones de filas de una base de datos si cambia una clave primaria, así que es recomendable que una clave primaria nunca cambie. | ||
+ | |||
+ | === Debe ser una única columna === | ||
+ | Esta propiedad tampoco es esencial que la cumpla una clave primaria. Sin embargo en ciertas circunstancias al usar SUBSELECTs en el lenguaje SQL se tiene que recurrir a extensiones propietarias de Oracle por culpa de tener claves primarias compuestas. Además de que podemos encontrarnos con algunas herramientas de desarrollo - como las de generación de informes- que no soporten claves primarias compuestas. | ||
+ | Así que para evitar problemas sería recomendable usar claves primarias de una única columna. | ||
+ | |||
+ | === Debe ser rápida de generar === | ||
+ | Dado que para cada fila que generemos es necesaria una clave primaria es recomendable que el coste de generarla sea lo más bajo posible ya que puede ser un gran coste de tiempo/ | ||
+ | En los entornos en la nube actuales, en los que se cobra por uso de CPU, la elección de una correcta clave primaria puede ser un ahorro de tiempo y dinero. | ||
+ | |||
+ | |||
+ | ==== La adecuada clave primaria ==== | ||
+ | ¿Cómo elegir entonces una clave primaria que satisfaga todas las anteriores características? | ||
+ | La respuesta es sencilla: en la mayoría de los casos la clave primaria debería ser un valor numérico generado automáticamente. | ||
+ | |||
+ | ¿Y qué hacemos con las clave primarias naturales? | ||
+ | Una clave primaria natural es aquella columna/s de base de datos que actua de clave primaria en el modelo de negocio en el que estamos trabajando. | ||
+ | |||
+ | Por ejemplo, en la Agencia tributaria (Hacienda) debe ser un requerimiento legal que una persona disponga de un NIF para poder tributar. Lo natural es que si tuvieran una tabla '' | ||
+ | |||
+ | Pero, ¿qué problemas tiene usar una clave primaria natural como clave primaria? | ||
+ | * **Puede cambiar**: Por normal general una clave primaria //natural// puede llegar a cambiar por requerimientos del propio negocio. Aunque pensemos que el NIF de una persona no cambia sí que lo hace, ya que los niños pueden sacarse un NIF en hacienda sin tener DNI y posteriormente cuando tienen DNI se cambia el NIF inicial por el del DNI. | ||
+ | * **Puede ser null**: Quizás no tenga valor para un caso concreto del negocio. Por ejemplo, si nuestra app necesita un DNI para funcionar, los recién nacidos no tienen un DNI por lo que la pp fallaría en ciertos casos si la usáramos como clave primaria. | ||
+ | * **Puede ser compuesta**: | ||
+ | |||
+ | Por todo ello seguimos recomendando usar una clave primaria que **NO** sea natural y que **NO** tenga significado en el modelo de negocio para evitarnos cualquier tipo de problema indeseado en el futuro. | ||
+ | |||
+ | |||
+ | ===== Clases Java ===== | ||
+ | Antes de entrar en cómo se implementa en Hibernate la generación de la clave primaria , veamos las clases Java y las tablas que se usan. | ||
+ | |||
+ | Para nuestro ejemplo vamos a usar la clase: | ||
+ | * Profesor | ||
+ | |||
+ | <code java 1> | ||
+ | public class Profesor implements Serializable | ||
+ | | ||
+ | private int id; | ||
+ | private String nombre; | ||
+ | private String ape1; | ||
+ | private String ape2; | ||
+ | |||
+ | public Profesor(){ | ||
+ | } | ||
+ | |||
+ | public Profesor(String nombre, String ape1, String ape2) { | ||
+ | this.nombre = nombre; | ||
+ | this.ape1 = ape1; | ||
+ | this.ape2 = ape2; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Podemos apreciar en la línea 11 que al constructor ya no se le pasa el valor de la propiedad '' | ||
+ | |||
+ | <note important> | ||
+ | |||
+ | En el siguiente diagrama UML muestra la clase '' | ||
+ | |||
+ | <uml> | ||
+ | class Profesor | ||
+ | Profesor : int id | ||
+ | Profesor : String nombre | ||
+ | Profesor : String ape1 | ||
+ | Profesor : String ape2 | ||
+ | </ | ||
+ | |||
+ | ===== Tablas ===== | ||
+ | La tablas de base de datos quedarían de la siguiente forma: | ||
+ | |||
+ | <uml> | ||
+ | class Profesor << | ||
+ | Profesor : INTEGER id | ||
+ | Profesor : VARCHAR nombre | ||
+ | Profesor : VARCHAR ape1 | ||
+ | Profesor : VARCHAR ape2 | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== Fichero de mapeo '' | ||
+ | Al persistir la clase será necesario un único fichero de persistencia: | ||
+ | * '' | ||
+ | |||
+ | ==== Profesor.hbm.xml ==== | ||
+ | El fichero '' | ||
+ | |||
+ | <code xml 1| Fichero Profesor.hbm.xml> | ||
+ | <?xml version=" | ||
+ | < | ||
+ | < | ||
+ | <class name=" | ||
+ | <id column=" | ||
+ | < | ||
+ | </id> | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | |||
+ | El fichero básicamente es muy sencillo excepto por el nuevo tag ''< | ||
+ | |||
+ | === Tag generator === | ||
+ | El tag ''< | ||
+ | * '' | ||
+ | |||
+ | Los valores más usados del atributo '' | ||
+ | ^ Valor ^ Descripción ^ | ||
+ | | native | Hibernate usará alguno de los siguientes métodos dependiendo de la base de datos. De esta forma si cambiamos de base de datos se seguirá usando la mejor forma de generar la clave primaria | | ||
+ | | identity | Hibernate usará el valor de la columna de tipo autoincremento. Es decir, que al insertar la fila, la base de datos le asignará el valor. La columna de base de datos debe ser de tipo autonumérico | | ||
+ | | sequence | Se utiliza una secuencia como las que existen en Oracle o PostgreSQL , no es compatible con MySQL. La columna de base de datos debe ser de tipo numérico | | ||
+ | | increment | Se lanza una consulta '' | ||
+ | | uuid.hex | Hibernate genera un identificador único como un String. Se usa para generar claves primarias únicas entre distintas bases de datos.La columna de base de datos debe ser de tipo alfanumérico. | | ||
+ | | guid | Hibernate genera un identificador único como un String pero usando las funciones que provee SQL Server y MySQL. Se usa para generar claves primarias únicas entre distintas bases de datos.La columna de base de datos debe ser de tipo alfanumérico. | | ||
+ | | foreign | Se usará el valor de otro objeto como la clave primaria. Un uso de ello es en relaciones //uno a uno// donde el segundo objeto debe tener la misma clave primaria que el primer objeto a guardar. | | ||
+ | |||
+ | |||
+ | |||
+ | < | ||
+ | Al usar el método '' | ||
+ | <code xml 1> | ||
+ | <id column=" | ||
+ | < | ||
+ | <param name=" | ||
+ | </ | ||
+ | </id> | ||
+ | </ | ||
+ | |||
+ | Vemos que en la línea 3 se ha añadido el tag ''< | ||
+ | </ | ||
+ | |||
+ | < | ||
+ | Al usar el método '' | ||
+ | <code xml 1> | ||
+ | <id column=" | ||
+ | < | ||
+ | <param name=" | ||
+ | </ | ||
+ | </id> | ||
+ | </ | ||
+ | |||
+ | Vemos que en la línea 3 se ha añadido el tag ''< | ||
+ | </ | ||
+ | |||
+ | <note tip> | ||
+ | ¿Cuál es el método mas adecuado para generar la clave primaria? | ||
+ | Como siempre en informática dependerá de nuestro proyecto. Pero unas sugerencias que pueden ayudar son las siguientes: | ||
+ | * Si debemos | ||
+ | * El peor de los casos es usar '' | ||
+ | * El uso de '' | ||
+ | * No recomiendo el uso de '' | ||
+ | </ | ||
+ | ===== Anotaciones ===== | ||
+ | Para usar anotaciones deberemos modificar el código fuente de las clases Java y **no** usar los ficheros '' | ||
+ | |||
+ | El código fuente de la clase '' | ||
+ | |||
+ | <code java 1| Clase Profesor anotada > | ||
+ | @Entity | ||
+ | @Table(name=" | ||
+ | public class Profesor implements Serializable | ||
+ | | ||
+ | @Id | ||
+ | @Column(name=" | ||
+ | @GeneratedValue(strategy=GenerationType.IDENTITY) | ||
+ | private int id; | ||
+ | | ||
+ | @Column(name=" | ||
+ | private String nombre; | ||
+ | | ||
+ | @Column(name=" | ||
+ | private String ape1; | ||
+ | | ||
+ | @Column(name=" | ||
+ | private String ape2; | ||
+ | |||
+ | public Profesor(){ | ||
+ | } | ||
+ | |||
+ | public Profesor(String nombre, String ape1, String ape2) { | ||
+ | this.nombre = nombre; | ||
+ | this.ape1 = ape1; | ||
+ | this.ape2 = ape2; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Podemos ver cómo se ha añadido la anotación '' | ||
+ | |||
+ | * **@GeneratedValue**: | ||
+ | * **strategy**: | ||
+ | |||
+ | ^ Estrategia ^ Descripción ^ | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | |||
+ | < | ||
+ | Al usar el método '' | ||
+ | <code java 5> | ||
+ | @Id | ||
+ | @Column(name=" | ||
+ | @GeneratedValue(strategy=GenerationType.SEQUENCE, | ||
+ | private int id; | ||
+ | </ | ||
+ | Vemos que en la línea 7 se ha añadido el atributo '' | ||
+ | </ | ||
+ | ==== Anotaciones propietarias ==== | ||
+ | Acabamos de ver cómo usar las anotaciones del estándar de JPA, pero JPA no dispone de tantos métodos de generación de claves primarias como Hibernate. | ||
+ | |||
+ | Si queremos hacer uso de todos los métodos de generación de claves primarias de que dispone Hibernate mediante el uso de anotaciones, | ||
+ | |||
+ | En este caso el código fuente de la clase Persona se debe modificar de la siguiente forma: | ||
+ | |||
+ | <code java 5> | ||
+ | @Id | ||
+ | @Column(name=" | ||
+ | @GeneratedValue( generator = " | ||
+ | @org.hibernate.annotations.GenericGenerator( | ||
+ | name = " | ||
+ | strategy = " | ||
+ | ) | ||
+ | private int id; | ||
+ | </ | ||
+ | |||
+ | Como podemos ver la línea 7 se ha modificado y en la línea 8 se ha añadido la anotación '' | ||
+ | |||
+ | * **@org.hibernate.annotations.GenericGenerator**: | ||
+ | * **name**: Es el nombre del nuevo generador de claves primarias.Puede tener cualquier valor. | ||
+ | * **strategy**: | ||
+ | |||
+ | La anotación '' | ||
+ | |||
+ | ==== foreign ==== | ||
+ | Veamos ahora cómo se puede generar la clave primaria de forma que sea el mismo valor que la clave primaria de otro objeto. Como en el caso de usar el fichero '' | ||
+ | |||
+ | La forma de anotar la clase '' | ||
+ | <code java 1> | ||
+ | @Id | ||
+ | @Column(name=" | ||
+ | @GeneratedValue(generator=" | ||
+ | @org.hibernate.annotations.GenericGenerator( | ||
+ | name=" | ||
+ | strategy=" | ||
+ | parameters=@Parameter(name=" | ||
+ | ) | ||
+ | private int id; | ||
+ | </ | ||
+ | |||
+ | Vemos cómo, en esta caso, la estrategia de generación de la clave primaria es "'' | ||
+ | ===== Código Java ===== | ||
+ | Ahora que ya tenemos preparadas la clase Java para que pueda persistirse veamos el código necesario para persistirla. | ||
+ | |||
+ | <code java 1 | Persistiendo la clase Profesor> | ||
+ | Profesor profesor=new Profesor(" | ||
+ | |||
+ | Session session=sessionFactory.openSession(); | ||
+ | session.beginTransaction(); | ||
+ | |||
+ | session.save(profesor); | ||
+ | |||
+ | session.getTransaction().commit(); | ||
+ | session.close(); | ||
+ | </ | ||
+ | |||
+ | Como podemos ver en la línea 1, al permitir que la clave primaria la genere Hibernate nos ahorramos tener que incluirla en el constructor. Es decir que como en otros casos , el código Java sigue estando muy simplificado gracias a Hibernate. | ||