openxava / documentación / Aplicaciones multiesquema

Tabla de contenidos

Aplicaciones multiesquema
Aplicaciones multiesquema
Aplicacion contra múltiples bases de datos (new in v4.2.2)
Un ejemplo
Cómo funciona
Cambiando de esquema con parámetros en la URL (nuevo en v4m4)
Este documento explica como crear aplicaciones multiesquema con OpenXava.
Las aplicaciones multiesquema funcionan con Hibernate y EJB3 JPA como mecanismos de persistencia. EJB2 CMP no soporta multiesquema.
Si quieres una aplicación multiempresa sin programar visita la sección sobre multitenencia.

Aplicaciones multiesquema

Una aplicación multiesquema permite replicar toda la estructura de tabla de nuestra base de datos en varios esquemas de base de datos. Entonces cada usuario (o en cada sesión) puede trabajar con un esquema diferente, o sea contra diferentes datos.
Por ejemplo, podemos tener los datos de 3 empresas diferentes en el mismo servidor de base de datos. Nuestra aplicación OpenXava, que está desplegadas solo una vez en un servidor de aplicaciones, puede ser usada por los usuarios de estas tres empresas, pero cada empleado solo puede acceder a los datos de su compañia. Una aplicación multiesquema permite implementar esto.
Usar varios esquemas en la base de datos no es solo por seguridad, sino también para evitar tener tablas de base de datos inmensas; porque podemos separar la información por empresas, años, departamentos, municipios, etc.

Aplicacion contra múltiples bases de datos (new in v4.2.2)

En lugar de varios esquemas en la misma aplicación puedes optar por usar varias bases de datos completamente diferentes. Para hacer esto sigue las instrucciones de abajo cambiando el controlador DefaultSchema por PersistenceUnit y SetDefaultSchemaAction por SetPersistenceUnitAction. Además, has de definir varias unidades de persistencia en persistence.xml, una por cada base de datos.

Un ejemplo

Veamos un ejemplo de un módulo OpenXava que usa multiesquema.
Primero, necesitamos una clase entidad, como esta:
@Entity
public class Incidencia {
 
    @Id @Column(length=5) @Required
    private String id;
 
    @Column(length=40) @Required
    private String descripcion;
 
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
 
    public String getDescripcion() {
        return descripcion;
    }
    public void setDescripcion(String descripcion) {
        this.descripcion = descripcion;
    }
 
}
O, si estamos usando los components XML clásicos de OpenXava:
<componente nombre="Incidencia">
    <entidad>
        <propiedad nombre="id" tipo="String" clave="true"
            longitud="5" requerido="true"/>
        <propiedad nombre="descripcion" tipo="String"
            longitud="40" requerido="true"/>
    </entidad>
    <mapeo-entidad tabla="**INCIDENCIA**">
        <mapeo-propiedad
            propiedad-modelo="id" columna-tabla="ID"/>
        <mapeo-propiedad
            propiedad-modelo="descripcion" columna-tabla="DESCRIPCION"/>
    </mapeo-propiedad>
 </componente>
Se puede ver como mapeamos el componente contra la tabla INCIDENCIA, pero no indicamos el esquema.

Ahora, podemos definir el módulo en aplicacion.xml, como sigue:
 <modulo nombre="Incidencias">
    <modelo nombre="Incidencia"/>
    <controlador nombre="Typical"/>
    <controlador nombre="Incidencias"/>
</modulo>
Entonces, definimos nuestro propio controlador Incidencias, en controladores.xml, de esta forma:
<controlador nombre="Incidencias">
    <hereda-de controlador="DefaultSchema"/>
    <accion nombre="cambiarAEmpresa1" al-iniciar="true"
        clase="org.openxava.actions.SetDefaultSchemaAction">
        <poner propiedad="newDefaultSchema" value="EMPRESA1"/>
        <usa-objeto nombre="xava_defaultSchema"/>  <!-- No necesario desde v4m2 -->
    </accion>
    <accion nombre="cambiarAEmpresa2"
        clase="org.openxava.actions.SetDefaultSchemaAction">
        <poner propiedad="newDefaultSchema" value="EMPRESA2"/>
        <usa-objeto nombre="xava_defaultSchema"/>  <!-- No necesario desde v4m2 -->
    </accion>
</controlador>
Y ahora tenemos un modulo que puede trabajar contra el esquema 'EMPRESA1' o contra el esquema 'EMPRESA2'. El usuario solo ha de pulsa en el botón correspondiente para cambiar en caliente el origen de los datos.

Eso es todo.

Cómo funciona

Podemos usar estos controladores y acciones que están listos para usar, pero si además sabemos como funcionan, podemos hacerlo nosotros mismos y así podemos adaptarnos más a nuestras necesidades específicas. La clave está en la clase XPersistence, usando esta clase es posible cambiar el esquema por defecto en tiempo de ejecución:
XPersistence.setDefaultSchema("EMPRESA1");
Esto cambia el esquema por defecto a 'EMPRESA1', pero solo para el hilo de ejecución actual.
Ahora, si usamos un objeto de sesión (ver la sección 7.2 de la guía de referencia) y usamos una acción con en-cada-peticion="true" para establecer el esquema asociado al usuario actual como el esquema por defecto para el hilo de la petición, tendremos el problema resuelto.

Intentemos hacerlo.
Define un objeto de sesión para almacenar el esquema actual por usuario:
<object name="xava_defaultSchema" class="java.lang.String" scope="global"/>
Esto está en OpenXava/xava/default-controllers.xml, por lo tanto está disponible para tí; aunque puedes crearte tu propio objeto de sesión en tu propio controladores.xml si así lo prefieres.

Define una acción (en tu propio controlador) que se ejecute antes de cada petición, en controladores.xml:
<controlador ... >
    <accion nombre="ponerEsquemaPorDefecto" antes-de-cada-peticion="true" oculta="true"
        clase="org.openxava.actions.SetDefaultSchemaAction">
        <usa-objeto nombre="xava_defaultSchema"/>  <!-- No necesario desde v4m2 -->
    </accion>
    ...
</controlador>
(El controlador DefaultSchema de OpenXava tiene esta acción incluida)
En esta acción solo necesitas tener el objeto de sesión (en este caso xava_defaultSchema) y ponerlo como esquema por defecto usando XPersistence:
public class SetDefaultSchemaAction extends BaseAction {
 
    @Inject  // A partir de v4m2
    private String defaultSchema;
    private String newDefaultSchema;
 
    public void execute() throws Exception {
        if (newDefaultSchema != null)    defaultSchema = newDefaultSchema;
        XPersistence.setDefaultSchema(defaultSchema);
    }
 
    /**
     * The current default schema used by OpenXava and JPA.
     */
    public String getDefaultSchema() {
        return defaultSchema;
    }
 
    /**
     * The current default schema used by OpenXava and JPA.
     */
    public void setDefaultSchema(String company) {
        this.defaultSchema = company;
    }
 
    /**
     * The new default schema for OpenXava and JPA. <P>
     *
     * This value update the property 'defaultSchema'.
     */
    public String getNewDefaultSchema() {
        return newDefaultSchema;
    }
 
    /**
     * The new default schema for OpenXava and JPA. <P>
     *
     * This value update the property 'defaultSchema'.
     */
    public void setNewDefaultSchema(String newCompany) {
        this.newDefaultSchema = newCompany;
    }
}
Ya que defaultSchema es inyectado usando <usa-objeto /> (en todas las versiones) o @Inject (desde v4m2) cuando cambiamos la propiedad defaultSchema también estamos cambiando el objeto de sesión xava_defaultSchema.
Esta acción forma parte del nucleo de OpenXava (en org.openxava.actions), puedes usarla tal cual, o crearte la tuya propia usando una técnica similar.

Este técnica se puede usar también con XHibernate (XHibernate se quitó en v7.0).

Ahora puedes llamar a esta acción (u otra parecida) cuando quieras cambiar el esquema actual para el usuario.

Cambiando de esquema con parámetros en la URL (nuevo en v4m4)

Puede cambiar de esquema a través de parámetros en la URL, observe los detalles en la sección de How to.