Pregunta Código mínimo para almacenar de forma confiable el objeto java en un archivo


En mi pequeña y pequeña aplicación Java, quiero almacenar información.

Mis requisitos:

  • leer y escribir objetos java (no quiero usar SQL, y tampoco es necesario consultar)
  • fácil de usar
  • fácil de instalar
  • dependencias externas mínimas

Por lo tanto, quiero usar jaxb para almacenar toda la información en un simple Archivo XML en el sistema de archivos. Mi aplicación de ejemplo se ve así (copie todo el código en un archivo llamado Application.java y compilar, sin requisitos adicionales!):

@XmlRootElement
class DataStorage {
    String emailAddress;
    List<String> familyMembers;
    // List<Address> addresses;
}

public class Application {

    private static JAXBContext jc;
    private static File storageLocation = new File("data.xml");

    public static void main(String[] args) throws Exception {
        jc = JAXBContext.newInstance(DataStorage.class);

        DataStorage dataStorage = load();

        // the main application will be executed here

        // data manipulation like this:
        dataStorage.emailAddress = "me@example.com";
        dataStorage.familyMembers.add("Mike");

        save(dataStorage);
    }

    protected static DataStorage load() throws JAXBException {
        if (storageLocation.exists()) {
            StreamSource source = new StreamSource(storageLocation);
            return (DataStorage) jc.createUnmarshaller().unmarshal(source);
        }
        return new DataStorage();
    }

    protected static void save(DataStorage dataStorage) throws JAXBException {
        jc.createMarshaller().marshal(dataStorage, storageLocation);
    }
}

¿Cómo puedo superar estos inconvenientes?

  • Iniciar la aplicación varias veces podría llevar a inconsistencias: Varios usuarios podrían ejecutar la aplicación en una unidad de red y experimentar concurrencia cuestiones
  • Abortar el proceso de escritura podría llevar a corrompido datos o perder todos los datos

8
2018-02-18 10:43


origen


Respuestas:


Para responder a sus tres problemas, usted mencionó:

Iniciar la aplicación varias veces podría generar inconsistencias

¿Por qué conduciría a inconsistencias? Si lo que quiere decir es que la edición simultánea múltiple dará lugar a inconsistencias, solo tiene que bloquear el archivo antes de editarlo. La forma más fácil de crear un archivo de bloqueo junto al archivo. Antes de comenzar la edición, simplemente verifique si existe un archivo de bloqueo.

Si desea que sea más tolerante a fallas, también puede poner un tiempo de espera en el archivo. p.ej. un archivo de bloqueo es válido por 10 minutos. Podrías escribir un uuid generado aleatoriamente en el archivo de bloqueo y, antes de guardarlo, podrías verificar si el estilo de uuid coincide.

Varios usuarios podrían ejecutar la aplicación en una unidad de red y experimentar problemas de concurrencia

Creo que esto es lo mismo que el número 1.

Abortar el proceso de escritura podría conducir a datos dañados o perder todos los datos

Esto puede resolverse haciendo que la escritura atómica o el archivo sea inmutable. Para que sea atómico, en lugar de editar el archivo directamente, simplemente copie el archivo y edítelo en la copia. Después de guardar la copia, simplemente cambie el nombre de los archivos. Pero si quieres estar más seguro, siempre puedes hacer cosas como agregar la marca de tiempo en el archivo y nunca editar o eliminar un archivo. Por lo tanto, cada vez que se realiza una edición, se crea una copia, con una marca de tiempo más nueva añadida al archivo. Y para leer, leerás siempre el más nuevo.


2
2018-02-29 21:58



Ver sus requisitos:

  • Iniciando la aplicación varias veces
  • Varios usuarios podrían ejecutar la aplicación en una unidad de red
  • Protección contra la corrupción de datos

Creo que un sistema de archivos basado en XML no será suficiente. Si considera que una base de datos relacional adecuada es una exageración, aún puede optar por una H2 db. Este es un archivo db superligero que resolvería todos estos problemas anteriormente (incluso si no perfectamente, pero seguramente mucho mejor que un db XML escrito a mano), y sigue siendo muy fácil de configurar y mantener.

Puede configurarlo para que conserve sus cambios en el disco, puede configurarse para ejecutarse como un servidor independiente y aceptar múltiples conexiones, o también puede ejecutarse como parte de su aplicación en modo incrustado.

Con respecto a "¿Cómo se guardan los datos?" parte:

En caso de que no desee utilizar ninguna biblioteca avanzada de ORM (como Hibernate o cualquier otra implementación de JPA), puede seguir utilizando el antiguo JDBC simple. O al menos algo de Spring-JDBC, que es muy liviano y fácil de usar.

"¿Qué es lo que ahorras?"

H2 es una base de datos relacional. Entonces, sea lo que sea que guardes, terminará en columnas. ¡Pero! Si realmente no planea consultar sus datos (ni aplicar scripts de migración sobre ellos), guarde sus objetos ya serializados en XML es una opción. Puede definir fácilmente una tabla con una ID + columna varchar de "datos" y guardar su xml allí. No hay límite en la longitud de datos en H2DB.

Nota: Guardar XML en una base de datos relacional generalmente no es una buena idea. Solo le estoy aconsejando que evalúe esta opción, porque parece seguro de que solo necesita un determinado conjunto de características a partir de lo que puede proporcionar una implementación de SQL.


7
2018-02-18 11:17



Las inconsistencias y la concurrencia se manejan de dos maneras:

  • bloqueando
  • por versionar

La escritura corrupta no se puede manejar muy bien a nivel de aplicación. El sistema de archivos debe admitir el registro en diario, que intenta corregirlo hasta cierto punto. Puedes hacer esto también por

  • hacer su propio archivo de registro en diario (es decir, un archivo separado de corta duración que contenga los cambios que se comprometerán con el archivo de datos real).

Todas estas características están disponibles incluso en la base de datos relacional más simple, p. H2, SQLite e incluso una página web pueden usar tales funciones en HTML5. Es bastante exagerado volver a implementarlos desde cero, y la implementación adecuada de la capa de almacenamiento de datos realmente complicará bastante sus necesidades simples.

Pero, solo por los registros:

Manejo de concurrencia con cerraduras

Manejo de consistencia (atomicidad) con cerraduras

  • otras instancias de aplicaciones aún pueden intentar leer el archivo, mientras una de las aplicaciones lo está escribiendo. Esto puede causar inconsistencia (aka dirty-read). Asegúrese de que durante la escritura, el proceso de escritura tenga un bloqueo exclusivo en el archivo. Si no es posible obtener un bloqueo de acceso exclusivo, el escritor tiene que esperar un poco y volver a intentarlo.

  • una aplicación que lea el archivo lo leerá (si puede obtener acceso, ninguna otra instancia hace un bloqueo exclusivo), luego cierre el archivo. Si no es posible leer (debido a otro bloqueo de la aplicación), espere y vuelva a intentarlo.

  • aún una aplicación externa (por ejemplo, un bloc de notas) puede cambiar el xml. Es posible que prefiera un bloqueo de lectura exclusivo mientras lee el archivo.

Diario básico

Aquí la idea es que si necesita hacer muchas escrituras, (o si más adelante desea deshacer sus escrituras) no quiere tocar el archivo real. En lugar:

  • escribe cuando los cambios van a un archivo de diario separado, creado y bloqueado por la instancia de su aplicación

  • la instancia de su aplicación no bloquea el archivo principal, solo bloquea el archivo de diario

  • Una vez que todas las escrituras están listas, su aplicación abre el archivo real con bloqueo de escritura exclusivo y confirma cada cambio en el archivo de diario, luego cierra el archivo.

Como puede ver, la solución con bloqueos lo convierte en un recurso compartido, que está protegido por bloqueos y solo una aplicación puede acceder al archivo a la vez. Esto resuelve los problemas de concurrencia, pero también hace que el acceso al archivo sea un cuello de botella. Por lo tanto, las bases de datos modernas como Oracle usan control de versiones en lugar de bloquear. El control de versiones significa que tanto la versión anterior como la nueva del archivo están disponibles al mismo tiempo. Los lectores recibirán el archivo antiguo y más completo. Una vez que finalice la escritura de la nueva versión, se fusionará con la versión anterior y los nuevos datos estarán disponibles de inmediato. Esto es más complicado de implementar, pero como permite leer todo el tiempo para todas las aplicaciones en paralelo, se escala mucho mejor.


3
2018-02-23 17:27



tenga en cuenta que su respuesta simple no manejará las escrituras simultáneas en instancias diferentes. si dos instancias realizan cambios y guardan, simplemente elegir el más nuevo terminará perdiendo los cambios de la otra instancia. como se menciona en otras respuestas, probablemente deberías intentar usar el bloqueo de archivos para esto.

una solución relativamente simple:

  • use un archivo de bloqueo separado para escribir "data.xml.lck". bloquear esto al escribir el archivo
  • como mencioné en mi comentario, escriba primero en un archivo temp "data.xml.tmp", luego cambie el nombre al nombre final cuando se complete la escritura "data.xml". esto dará una seguridad razonable de que cualquier persona que lea el archivo obtendrá un archivo completo.
  • incluso con el bloqueo de archivos, todavía tiene que manejar el problema de "fusión" (una instancia lee, otra escribe, luego la primera quiere escribir). para manejar esto, debe tener un número de versión en el contenido del archivo. cuando una instancia quiere escribir, primero adquiere el bloqueo. luego verifica su número de versión local con el número de versión del archivo. si está desactualizado, debe fusionar lo que está en el archivo con los cambios locales. entonces puede escribir una nueva versión.

2
2018-02-27 15:12



Después de pensarlo por un tiempo, me gustaría intentar implementarlo así:

  • Abre el data.<timestamp>.xml-archivo con la última marca de tiempo.
  • Uso único solo lectura modo.
  • Hacer cambios.
  • Guarde el archivo como data.<timestamp>.xml - No sobrescribir y comprobar que no existe ningún archivo con la marca de tiempo más nueva.

0
2018-02-23 13:27