Search code examples
javaspringbackupspring-data-jpahsqldb

Backup and Restore HsqlDb Embeded File (NOT DATABASE)


Every one talk about Backup Hsqldb outside, but what i require is to Backup the embeded files...

I'm using Spring JPA, and the application is always running, so files are in use, also since there is no DBMS, i'm wonder if there are ways to backup and restore? If there are, please guide me...

Otherwise, i though of copying database file, after somehow (don't know how) putting spring JPA in offline mode, and then ZIP those file, which i don't know in java, and then let user to download it, if spring allow's it... somehow (as i use stand alone spring boot, which is a single file, and it doesn't offer lot of those fancy folder, which we can point to them as website url...)

In the end in any scenario i want to send the file to client.

Sorry if i know none of this, i came from C#, and after 5 years, it's my second time using Java, which i never beem pro in it.

UPDATE

Although i'm not sure if i can make a zip file from running database, and store it in that place... I write this code through searching, i find several method that returns current directory, but non of them point to directory i want... one of them, in debug it point to very inner location, like targer/class/x/y/z, after i package it to jar file, it may be different, another one point to C/..../temp, ... i need to get write to where my DB file is located, then pass those file to make zip file function, and tell user to download the files

@RestController
@RequestMapping(value = "/rest/database-manager")
public class DatabaseManager {

    private ServletContext servletContext;
    private final Environment env;

    @Autowired
    public DatabaseManager(Environment env, ServletContext servletContext) {
        this.env = env;
        this.servletContext = servletContext;
    }

    @RequestMapping(value = "/get-backup", method=RequestMethod.GET)
    private FileSystemResource getBackup() throws IOException {
        //String directory = DemoApplication.class.getResource("").getPath();
        String outputLocation = servletContext.getRealPath("./");
        String dataBaseFilePath = servletContext.getRealPath(env.getProperty("application.database-file-location"));
        Calendar cal = new GregorianCalendar();
        String zipFile = outputLocation + "/backup-"
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.YEAR)), 4, "0")
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.MONTH)), 2, "0")
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.DAY_OF_MONTH)), 2, "0")
                + "-"
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.HOUR)), 2, "0")
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.MINUTE)), 2, "0")
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.SECOND)), 2, "0")
                + ".zip";
        ZipUtil.ToZip(new String[]{""}, zipFile);
        return new FileSystemResource(zipFile);
    }
}

zip function:

public class ZipUtil {
    public static void ToZip(String inputFiles[], String outputFile) throws IOException {
        //create byte buffer
        byte[] buffer = new byte[1024];

                /*
                 * To create a zip file, use
                 *
                 * ZipOutputStream(OutputStream out)
                 * constructor of ZipOutputStream class.
                */

        //create object of FileOutputStream
        FileOutputStream fout = new FileOutputStream(outputFile);

        //create object of ZipOutputStream from FileOutputStream
        ZipOutputStream zout = new ZipOutputStream(fout);

        for (int i = 0; i < inputFiles.length; i++) {
            //create object of FileInputStream for source file
            FileInputStream fin = new FileInputStream(inputFiles[i]);

                        /*
                         * To begin writing ZipEntry in the zip file, use
                         *
                         * void putNextEntry(ZipEntry entry)
                         * method of ZipOutputStream class.
                         *
                         * This method begins writing a new Zip entry to
                         * the zip file and positions the stream to the start
                         * of the entry data.
                         */

            zout.putNextEntry(new ZipEntry(inputFiles[i]));

                        /*
                         * After creating entry in the zip file, actually
                         * write the file.
                         */
            int length;

            while ((length = fin.read(buffer)) > 0) {
                zout.write(buffer, 0, length);
            }

                        /*
                         * After writing the file to ZipOutputStream, use
                         *
                         * void closeEntry() method of ZipOutputStream class to
                         * close the current entry and position the stream to
                         * write the next entry.
                         */

            zout.closeEntry();

            //close the InputStream
            fin.close();

        }


        //close the ZipOutputStream
        zout.close();
    }
}

Full Answer:

I'm not sure if produce do anything, as i directly write into response inside, and return void.

My Response Method:

    @RequestMapping(value = "/get-backup", method = RequestMethod.GET)
    private void getBackup(HttpServletResponse response) throws IOException {
        /*//String directory = DemoApplication.class.getResource("").getPath();
        String outputLocation = servletContext.getRealPath("./");
        String dataBaseFilePath = servletContext.getRealPath(env.getProperty("application.database-file-location"));
        Calendar cal = new GregorianCalendar();
        String zipFile = outputLocation + "/backup-"
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.YEAR)), 4, "0")
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.MONTH)), 2, "0")
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.DAY_OF_MONTH)), 2, "0")
                + "-"
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.HOUR)), 2, "0")
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.MINUTE)), 2, "0")
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.SECOND)), 2, "0")
                + ".zip";

        SQLQuery query = session.createSQLQuery("BACKUP DATABASE TO '/tmp/backup.tar.gz' BLOCKING");
        query.executeUpdate();*/

        File zipFile = null;

        Calendar cal = new GregorianCalendar();
        String prefix = "DBK-"
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.YEAR)), 4, "0")
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.MONTH)), 2, "0")
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.DAY_OF_MONTH)), 2, "0")
                + "-"
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.HOUR)), 2, "0")
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.MINUTE)), 2, "0")
                + StrMgr.leftPad(String.valueOf(cal.get(Calendar.SECOND)), 2, "0");
        String suffix = ".tar.gz";
        zipFile = File.createTempFile(prefix, suffix);

        zipFile.delete();

        databaseManagerRepository.Backup(zipFile.getAbsolutePath());

        response.setContentType("application/gzip");
        response.setHeader("Content-disposition", "attachment; filename=" + zipFile.getName());

//            OutputStream out = response.getOutputStream();
//            FileInputStream in = new FileInputStream(zipFile);
            /*
             * copy from in to out
             */
        OutputStream out = null;
        FileInputStream in = null;
        try {
            out = response.getOutputStream();
            in = new FileInputStream(zipFile);

            byte[] buffer = new byte[4096]; // To hold file contents
            int bytes_read; // How many bytes in buffer

            // Read a chunk of bytes into the buffer, then write them out,
            // looping until we reach the end of the file (when read() returns
            // -1). Note the combination of assignment and comparison in this
            // while loop. This is a common I/O programming idiom.
            while ((bytes_read = in.read(buffer)) != -1)
                // Read until EOF
                out.write(buffer, 0, bytes_read); // write
        }
        catch (Exception ex){
            System.out.print(ex.getMessage());
        }
        // Always close the streams, even if exceptions were thrown
        finally {
            if (in != null)
                try {
                    in.close();
                } catch (IOException e) {
                    ;
                }
            if (out != null)
                try {
                    out.close();
                } catch (IOException e) {
                    ;
                }
        }

        zipFile.delete();
    }

My Backup Native SQL Action Executor

@Repository
public class DatabaseManagerRepository {
    @PersistenceContext
    private EntityManager entityManager;

    /*The query force us to use transaction, and since the DB is online and we use spring, it also force us to use spring transaction*/
    @Transactional(rollbackFor = {Exception.class})
    public void Backup(String outputDir) {
            //NOT BLOCKING -> For large database, backup is performed while the database perform other operations
            //AS FILES -> We only define directory not the file itself
            Query q = entityManager.createNativeQuery("BACKUP DATABASE TO '" + outputDir + "' BLOCKING"/*" AS FILES"*/);
            q.executeUpdate();
    }
}

Solution

  • Backup is the same with an HSQLDB Server or embedded database. You execute the SQL statement:

    BACKUP DATABASE TO <directory name> BLOCKING AS FILES
    

    The directory name is a path to the target directory to store the backup files. For example BACKUP DATABASE TO 'C://db_backups/' BLOCKING AS FILES

    http://hsqldb.org/doc/2.0/guide/management-chapt.html#mtc_online_backup

    The AS FILES backup database creates a set of files which you can zip.