martes, abril 06, 2010

JPA: Modificar unidades de persistencia programáticamente

Si trabajáis con JPA, nosotros concretamente con Netbeans (aunque en Eclipse funciona de forma similar), durante el desarrollo de las aplicaciones, procedemos a crear un modelo de datos y a desplegar el modelo físico sobre nuestro motor de base de datos.

En el caso de Netbeans, mediante wizards, creamos nuestra conexión, la unidad de persistencia ("persistence.xml") y podemos generar nuestras entidades a partir del modelo físico.

¿Y el proceso inverso?. Supongamos que ya tenemos nuestra aplicación y lo que queremos es que al desplegar nuestra aplicación en los servidores de producción, en base a nuestras entidades, se recree la base de datos, sin necesidad de hacer una importación (debe existir el esquema y el usuario, por supuesto, pero no es preciso importar las tablas "manualmente").

Esto es posible modificando programáticamente una unidad de persistencia existente en el archivo "persistence.xml". Con un poco de trabajo, se puede crear un "wizard" en nuestra aplicación que detecte si la instalación es nueva y que sea capaz de recrear las tablas en base a nuestras entidades ya existentes.

Al menos, esta es la forma que hemos encontrado de hacerlo... es posible que haya formas mejores.

(Este ejemplo enlaza con el post anterior sobre el motor InnoDB de MySQL. En nuestro caso, las tablas originales eran InnoDB, pero cuando se recreaban las tablas en otro esquema, aparecían como MyISAM.)

En el ejemplo, uso TopLink Essentials, de Oracle. Las propiedades para Hibernate o EclipseLink, varían.

Este sería nuestro archivo "persistence.xml". Como véis, no tiene propiedades. Sólamente declaramos el nombre de la unidad, el proveedor y las entidades, pero no las propiedades:


<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="PersistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>oracle.toplink.essentials.PersistenceProvider</provider>
<class>test.entities.Entidad2</class>
<class>test.entities.Entidad1</class>
</persistence-unit>
</persistence>


Modificar la unidad de persistencia pasando las propiedades programáticamente, es simple:


Properties props = new Properties();
props.put(TopLinkProperties.JDBC_USER,"user");
props.put(TopLinkProperties.JDBC_PASSWORD,"password");
props.put(TopLinkProperties.JDBC_URL,"jdbc:mysql://hostname:3306/targetddbb?sessionVariables=storage_engine=InnoDB");
props.put("toplink.ddl-generation","create-tables");
props.put(TopLinkProperties.JDBC_DRIVER,"com.mysql.jdbc.Driver");
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PersistenceUnit",props);
EntityManager em = emf.createEntityManager();
em.close();
emf.close();


Si os fijáis, hay una propiedad "toplink.ddl-generation", con el valor "create-tables". Esto hace que al instanciar el objeto EntityManager, se regeneren las tablas en la base de datos, en base a las entidades declaradas en "persistence.xml" y asociadas a la unidad "PersistenceUnit". Es decir, sólo con ejecutar este código, se regeneran las tablas.

Por último insistir en que para realizar las pruebas, hemos usado:

- Netbeans 6.8
- TopLink Essentials
- MySQL

Los esquemas de origen y destino de nuestras tablas eran MySQL. No hemos probado a regenerar las tablas en una base de datos distinta...

MySQL: forzar la creación de tablas InnoDB

Hola a todos,

es posible que en alguna ocasión al crear nuevas tablas en MySQL os encontréis con que por defecto las tablas se crean con el motor MyISAM, en lugar de InnoDB. Esto ocurre porque en la instalación del servidor se ha seleccionado MyISAM como motor por defecto.

La primera solución que se nos puede ocurrir es modificar la configuración de MySQL para que el motor por defecto sea InnoDB. Esto tiene sus inconvenientes, ya que en un entorno de producción modificar la configuración del servidor puede ser un problema.

Sin embargo, hay una solución muy simple y que evitará que nos peleemos con el administrador de turno. En la propia cadena de conexión de MySQL, podemos pasar parámetros adicionales que modifican la forma en que el servidor se comporta para dicha conexión. El detalle de estos parámetros se puede consultar aquí (es para la versión 5.5, pero existen para versiones anteriores):

http://dev.mysql.com/doc/refman/5.5/en/connector-j-reference-configuration-properties.html

El parámetro que nos interesa se llama "sessionVariables". En este parámetro se puede pasar múltiples valores separados por coma que determinan el funcionamiento de la sesión de MySQL.

En concreto para forzar que el motor por defecto durante nuestra sesión sea InnoDB, deberíamos crear una conexión de este tipo:

"jdbc:mysql://hostname:3306/ddbbname?sessionVariables=storage_engine=InnoDB"

De esta manera, cualquier sentencia "CREATE TABLE" creará tablas InnoDB aunque el motor por defecto sea otro.