Modificación y escritura de datos
.. include-document:: org.gvsig.fmap.dal/reference-links
:rest:
En este apartado veremos conceptos como:
* inserción de nuevos fenómenos en un almacén
* modificación de fenómenos ya existentes en un almacén
* eliminación de fenómenos de un almacén.
* reglas de validación.
El *almacén* dispone de servicios que nos permiten
la modificación de los datos. Para poder modificar los
datos, lo primero que deberemos hacer es notificar al
*almacén* que vamos a modificar datos suyos. Y, al
igual que le notificamos que queremos modificarlos,
tendremos que informarle cuando hayamos terminado
de hacerlo. Una vez informamos al almacén que vamos a
modificar los datos, este entra en modo *modificación
de datos*, y permanecerá en ese modo hasta que le
digamos que hemos terminado.
Antes de seguir adelante vamos ha hacer un inciso sobre
el modo de operación en el que se puede encontrar un FeatureStore_ .
Estos puede ser:
* Modo consulta, *MODE_QUERY*. Es el modo por defecto
para un *almacén*. Cuando es creado uno se queda en
este modo. En este modo solo se pueden realizar operaciones
de consulta, no están permitidas la ejecución de operaciones
de edición. En caso de intentar ejecutar alguna se producirá
una excepción NeedEditingModeException_ .
* Modo edición, *MODE_FULLEDIT*. En este modo se permiten
todos los comandos de edición. Se dispondrá de servicios
de hacer/rehacer operaciones y las operaciones de edición
no se consolidaran hasta que se termine la edición.
* Modo añadir, *MODE_APPEND*. Es un modo de edición limitado.
No están habilitados los servicios de hacer/rehacer, y las
únicas acciones relacionadas con edición que están permitidas
son las de *insert* de un nuevo fenómeno y *finishEditing*.
La consolidación de los cambios queda a criterio del proveedor
de datos final si se realiza cada vez que se ejecute la
acción *insert* o al ejecutarse la acción *finishediting*.
No todos los proveedores de datos soportan este modo. En
caso de que el proveedor no lo soporte y sea solicitado,
el *almacén* entrara automáticamente en modo *edición*.
Hay que destacar que el *Modo edición* está disponible para *todos* los
almacenes. Ahora bien, es posible de que el *almacén* no tenga
permitida la persistencia de los datos (ver método *allowWrite*) por
lo que, al finalizar la edición podemos obtener una excepción dependiendo
si podemos escribir en el *almacén* o si hemos realizado o no cambios *mayores*.
Ahora veamos cuales son las acciones, relacionadas con
edición que nos permite realizar el *almacén* de fenómenos:
* *allowWrite*. Esta acción nos informa si el *almacén*
en cuestión es susceptible de escribir las modificaciones
sobre el soporte de almacenamiento que tenga asociado.
* *edit*. Esta acción es con la que le indicamos
al almacén que queremos entrar en modo *modificación
de datos*. Esta acción sin ningún otro parámetro mas
entrara en modo *edición*, de otra forma habrá que indicar como
parámetro en que modo de edición se quiere entrar.
* *cancelEditing*. Esta acción nos permite indicarle al
*almacén* que queremos cancelar todas las modificaciones
y pasar a modo normal o de consulta.
* *finishEditing*. Con esta acción le indicamos al almacén
que de por terminadas las acciones de modificación, consolide
los cambios y salga del modo de *modificación de datos*.
* *isEditing*. Nos informa si el almacén se
encuentra en modo *modificación de datos* o
no.
* *isAppending*. Nos informa si el almacén se
encuentra en modo *adición de datos* o
no.
* *undo*. Una vez estamos en modo *modificación de datos*
y ya hemos realizado algunas modificaciones sobre el
*almacén*, esta acción nos permite ir *deshaciendo* una
a una las modificaciones realizadas.
* *redo*. Cuando hemos invocado al método *undo*, podemos
volver a indicarle al *almacén* que de por valida la ultima
acción cancelada mediante la acción *undo* invocando a
este método. Podremos ir rehaciendo todas las acciones
canceladas con el método *undo* invocando sucesivamente al
método *redo*
* *insert*. Inserta un fenómeno en el almacén.
* *update*. Actualizar un fenómeno del almacén.
* *delete*. Elimina un fenómeno del almacén.
* *createNewFeature*. Al invocar a este método con un FeatureType_ ,
el *almacén* creara un nuevo fenómeno con la estructura
indicada en el FeatureType_ pasado. Además podemos indicar
si queremos o no que se el nuevo fenómeno se rellene con los
valores por defecto para ese tipo.
* *isAppendModeSupported*, nos informa si el *almacén*
soporta el modo de edición *añadir nuevos fenómenos*.
Este modo de edición presenta solo un subconjunto de las
funciones de edición del *almacén* y esta orientado a la
creación en modo no interactivo de nuevos juegos de datos.
* *beginEditingGroup*, *endEditingGroup*, que permiten
agrupar un conjunto de instrucciones de edición como si
de una sola se tratase a efectos de las acciones hacer/rehacer.
Además de las acciones del *almacén* que nos permiten realizar
modificaciones en los datos, conviene repasar cuales son las
acciones de los fenómenos que nos van a permitir modificar sus
datos. En principio, un Feature_ es de solo *lectura*, es decir,
solo podemos realizar operaciones de consulta sobre el. Si queremos
modificar un Feature_ tendremos que obtener un EditableFeature_ a
partir de el, el cual ya tendrá acciones asociadas a modificar sus
datos. Para obtener un EditableFeature_ usaremos el método *getEditable*
del Feature_ . El método *createNewFeature* ya devuelve un EditableFeature_ .
El EditableFeature_ se comporta como un Feature_ añadiéndole
una serie de acciones mas. Las mas importantes serian:
* *set* para permitirnos modificar los valores de los
atributos.
* *setString, setInt, setGeometry* para permitirnos modificar los valores de los
atributos usando tipos específicos.
* *getNotEditableCopy*, que nos devolverá una copia no modificable
del EditableFeature_ .
Veamos ahora un ejemplo simple de como usar estas acciones para
insertar un nuevo fenómeno dentro el almacén. Vamos a suponer que
disponemos de un archivo *dbf* que contiene dos campos, un campo
*NOMBRE* y otro *TIPO* de tipo cadena. La apertura del almacén
se realiza tal como hemos hecho para acceder en modo lectura.
Una vez tenemos una instancia de *almacén*, el código para
añadir un nuevo fenómeno seria:
.. code-block:: java
store = manager.createStore(parameters);
store.edit();
EditableFeature feature = store.createNewFeature();
feature.set("NOMBRE", "Burjasot");
feature.set("TIPO", "MUNICIPIO");
store.insert(feature);
store.finishEditing();
store.dispose();
Los pasos serian los siguientes:
* comunicar al *almacén* que vamos a entrar en
modo *modificación de datos*.
* Pedirle al almacén que nos suministre un fenómeno
vació de contenido y con la estructura de los
fenómenos del almacén.
* Asignamos los nuevos valores a los atributos.
* Insertamos en el almacén la nueva feature.
* Y por ultimo le comunicamos al almacén que
hemos terminado de hacer modificaciones sobre el.
Tenemos que tener en cuenta que asta que no se ejecute la
acción *finishEditing* sobre el almacén, los cambios
que se hayan realizados no serán visibles para otros
usuarios o aplicaciones que estén accediendo al mismo
*almacén*.
Una cuestión importante a tener en cuenta relacionada con la
actualización o borrado de datos de un almacén es que pasa con
los conjuntos de datos que se han obtenido de ese almacén y sobre
los que se puede estar trabajando desde otros puntos de la aplicación.
En principio el almacén invalida todos los FeatureSet_ que
se han obtenido de el tras una operación de modificación o
borrado en el almacén. De forma que tras esta operación cualquier
intento de acceder a un FeatureSet_ obtenido con anterioridad producirá
una excepción de tiempo de ejecución ConcurrentModificationException_ .
Para evitar que mientras estemos recorriendo un FeatureSet_ y realizando
actualización o borrados en él este quede invalidado, usaremos los
métodos *delete*, *insert* y *update* de FeatureSet_ o el método *remove*
del *iterador* asociado al FeatureSet_.
Veamos como podríamos hacer para modificar los fenómenos
de nuestro almacén en *dbf*. Supongamos que queremos
cambiar el valor del atributo *TIPO* para el fenómeno
que tiene en el atributo *NOMBRE* el valor "Burjasot".
.. code-block:: java
store.edit();
EditableFeature feature;
FeatureQuery query = store.createFeatureQuery();
query.setFilter( manager.createExpresion("NOMBRE = 'Burjasot'") );
features = store.getFeatureSet(query);
Iterator it = features.iterator();
while( it.hasNext() ) {
feature = ((Feature)it.next()).getEditable();
feature.set("TIPO", "Municipio");
features.update(feature);
}
features.dispose();
store.finishEditing();
store.dispose();
* Filtramos sobre el *almacén* para obtener solo los fenómenos
que queremos.
* Informamos al almacén que queremos realizar modificaciones sobre
el.
* para cada fenómeno recuperado, le pedimos un EditableFeature_ ,
y lo modificamos.
* después pedimos al FeatureSet_ que actualice el fenómeno
en el *almacén*
* Y por ultimo le decimos al *almacén* que hemos terminado
de realizar modificaciones.
Si quisiésemos eliminar los fenómenos que tienen como *NOMBRE*
el valor "Burjasot" seria similar a la actualización pero
invocando al método *delete* en lugar de *update*:
.. code-block:: java
store.edit();
FeatureQuery query = store.createFeatureQuery();
query.setFilter( manager.createExpresion("NOMBRE = 'Burjasot'") );
features = store.getFeatureSet(query);
Iterator it = features.iterator();
while( it.hasNext() ) {
it.next();
it.remove();
}
features.dispose();
store.finishEditing();
store.dispose();
Relacionado con la modificación e inserción de fenómenos en un
*almacén de datos*, esta la posibilidad de asignar reglas de validación
a los fenómenos de un *almacén*. Las reglas de validación se asignan
a cada tipo de fenómeno del almacén, es decir es el FeatureType_
el que controla que reglas hay que aplicar a los fenómenos de su tipo.
Podemos invocar manualmente a las reglas de validación sobre un fenómeno
mediante la acción *validate* de Feature_ o sobre todos fenómenos del
*almacén* mediante la acción *validateFeatures* del FeatureStore_ .
A la hora de crear una regla de validación tendremos que tener en cuenta que
una regla de validación puede ejecutarse...
* Cuando se inserte o actualice un fenómeno en el *almacén de datos*
* Al finalizar la edición se ejecutan las reglas de validación de
todas los fenómenos del *almacén*.
* En ambos casos.
Según nos interese tendremos que indicar cuando queremos que se ejecute
la regla de validación.
Vamos a crear una regla de validación que se encarga de que
el sentido de los segmentos de los polígonos sea el adecuado.
Para ello deberemos crear una clase que implemente el interface FeatureRule_ .
Este interface nos obliga a suministrar los siguientes métodos:
* *getName*, con el nombre que le queramos dar a la regla de
validación.
* *getDescription*, opcionalmente podemos incluir una descripción de
unas lineas asociada a la regla de validación.
* *checkAtUpdate*, devolverá el valor de cierto si la regla debe ser
ejecutada en el momento de realizarse una modificación o inserción de un
nuevo fenómeno.
* *checkAtFinishEdition*, devolverá el valor cierto si la regla debe
ejecutarse sobre todos los fenómenos del almacén al finalizar la
edición.
* *validate*, que ejecutara la validación sobre el fenómeno que reciba
como parámetro. En caso de que no pase la regla de validación lanzara
una excepción.
Además de este interface, existe una clase abstract AbstractFeatureRule_
que nos resuelve la gestión de cuando ha de ejecutarse, el nombre y la descripción
de la regla, dejando únicamente pendiente le implementación del método
*validate*.
FIXME:
El ejemplo siguiente esta asumiendo que el validate solo
va a ser llamado estando en modo edición. Habría que comprobar
esto antes de invocar al *update* del store.
Veamos como quedaría nuestro ejemplo:
.. code-block:: java
public class FeatureRulePolygon extends AbstractFeatureRule {
public FeatureRulePolygon() {
super("RulePolygon", "Ensure orientation of geometry");
}
public void validate(Feature feature, FeatureStore store)
throws DataException {
try {
Geometry geom = feature.getDefaultGeometry();
GeneralPathX gp = new GeneralPathX();
gp.append(geom.getPathIterator(null, Converter.FLATNESS), true);
if (gp.isClosed()) {
if (gp.isCCW()) {
gp.flip();
GeometryFactory geomFactory = GeometryManager.getInstance()
.getGeometryFactory();
geom = geomFactory.createPolygon2D(gp);
EditableFeature editable = feature.getEditable();
editable.setDefaultGeometry(geom);
store.update(editable);
}
}
} catch (Exception e) {
throw new FeatureRulePolygonException(e, store.getName());
}
}
public class FeatureRulePolygonException extends DataException {
private static final long serialVersionUID = -3014970171661713021L;
private final static String MESSAGE_FORMAT = "Can't apply rule in store %(store)s.";
private final static String MESSAGE_KEY = "_FeatureRulePolygonException";
public FeatureRulePolygonException(Throwable cause, String store) {
super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
this.setValue("store", store);
}
}
}
Podemos observar que el método *validate* recibe la feature a validar
y el *almacén* donde esta vive, y se limita a coger la geometría
de la feature, procesarla y si es preciso actualizar el feature con
los nuevos valores.
Durante la validación de una regla podremos actualizar el fenómeno que
estamos tratando, pero no deberemos cambiar la estructura del almacén, es
decir, no deberemos realizar ninguna operación que modifique los FeatureType_
de este.
Si durante el proceso de validación de fenómenos que se realiza al
finalizar la edición alguien modifica algún fenómeno, se abortara el
proceso, quedando a decisión del que invoco a *finishEdition* comprobar
si fue por una modificación de los fenómenos mientras se procesaban para
volver a lanzar la operación.
Nota:
Aquí habría que poner un ejemplo de como hacer esto.