jav*_*csw 5 jpa database-backups
我有一个工作代码,但并不总是有效。这是我的方法:
\n\n创建备份
\n\n从备份加载
\n\n在某些时候,我会使用 JPA 2 元数据来获取要复制的表并选择它们需要复制的顺序(由于约束)。
\n\n由于某种原因,这种方法并不总是有效,因为我看到“丢失”的条目未恢复。
\n\n这是代码:
\n\npackage com.bluecubs.xinco.core.server;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\nimport java.text.SimpleDateFormat;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.ArrayList;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\nimport java.util.zip.ZipOutputStream;\nimport javax.persistence.EntityManager;\nimport javax.persistence.EntityManagerFactory;\nimport javax.persistence.Persistence;\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.filefilter.IOFileFilter;\nimport org.apache.commons.io.filefilter.TrueFileFilter;\n\n/**\n * This is a complex task and is heavily dependant on the architecture\n * of the database.\n * \n * Data needs to be stored in a particular order into the database to comply \n * with database constraints. This order can be observed in a dump file or\n * create script like the ones generated from MySQL Workbench. Using that \n * should be enough. In case that tool is not available basically the logic is\n * populating tables from the outside inwards. From the tables with no relationships\n * or only one working to the more complex ones. As summary before a table is populated all\n * the related tables should be populated already (if we have identifying relationships.\n *\n * @author Javier A. Ortiz Bultr\xc3\xb3n <javier.ortiz.78@gmail.com>\n */\npublic class XincoBackupManager {\n\n private static XincoBackupManager instance;\n private static EntityManagerFactory liveEMF;\n private static EntityManagerFactory backupEMF;\n private static EntityManager live, backup;\n private static final ArrayList<String> tables = new ArrayList<String>();\n private static XincoBackupFile last;\n private static String backupPath;\n public static HashMap<String, Integer> stats = new HashMap<String, Integer>();\n\n static {\n //Non-order-critical tables\n tables.add("XincoCoreAceT");\n tables.add("XincoCoreDataT");\n tables.add("XincoCoreDataTypeAttributeT");\n tables.add("XincoCoreGroupT");\n tables.add("XincoCoreLanguageT");\n tables.add("XincoCoreNodeT");\n tables.add("XincoCoreUserHasXincoCoreGroupT");\n tables.add("XincoCoreUserT");\n tables.add("XincoSettingT");\n tables.add("XincoDependencyTypeT");\n tables.add("XincoCoreDataHasDependencyT");\n tables.add("XincoSetting");\n tables.add("XincoId");\n //Order critical tables\n tables.add("XincoCoreLanguage");\n tables.add("XincoCoreNode");\n tables.add("XincoCoreDataType");\n tables.add("XincoCoreData");\n tables.add("XincoDependencyType");\n tables.add("XincoCoreDataHasDependency");\n tables.add("XincoCoreUser");\n tables.add("XincoCoreUserModifiedRecord");\n tables.add("XincoCoreGroup");\n tables.add("XincoCoreAce");\n tables.add("XincoCoreUserHasXincoCoreGroup");\n tables.add("XincoAddAttribute");\n tables.add("XincoCoreDataTypeAttribute");\n tables.add("XincoCoreLog");\n }\n\n public static XincoBackupManager get() {\n if (instance == null) {\n instance = new XincoBackupManager();\n }\n return instance;\n }\n\n private static void setDBSystemDir(String systemDir) {\n // Set the db system directory.\n System.setProperty("derby.system.home", systemDir);\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINEST,\n "Derby home set at: {0}", systemDir);\n try {\n //Start the embeded DB\n Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();\n } catch (ClassNotFoundException ex) {\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.SEVERE, null, ex);\n } catch (InstantiationException ex) {\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.SEVERE, null, ex);\n } catch (IllegalAccessException ex) {\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.SEVERE, null, ex);\n }\n }\n\n private static void initConnections() {\n try {\n liveEMF = XincoDBManager.getEntityManagerFactory();\n } catch (XincoException ex) {\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.SEVERE, null, ex);\n }\n try {\n backupEMF = Persistence.createEntityManagerFactory("XincoBackup");\n } catch (Exception ex) {\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.SEVERE, null, ex);\n }\n }\n\n protected static boolean backup() throws XincoException {\n try {\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINEST,\n "Initializing connections...");\n initConnections();\n stats.clear();\n backupPath = XincoSettingServer.getSetting("setting.backup.path").getString_value();\n //We need to make sure that there\'s no one in the database\n XincoDBManager.setLocked(true);\n live = liveEMF.createEntityManager();\n //Prepare the backup repository. Create dirs if needed.\n File backupDir = new File(backupPath);\n backupDir.mkdirs();\n //Create folder for this backup\n SimpleDateFormat format = new SimpleDateFormat("MM-dd-yyyy");\n File backupNewDir = new File(backupPath + System.getProperty("file.separator")\n + format.format(new Date()));\n backupNewDir.mkdirs();\n /*\n * Make sure there\'s no derby database stuff in the folder.\n * Any previous interrupted backup might left corrupted database files.\n */\n File tempDir = new File(backupNewDir.getAbsolutePath()\n + System.getProperty("file.separator") + "xinco");\n if (tempDir.exists()) {\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.WARNING,\n "Deleting potentially corrupted database files at: {0}", tempDir);\n FileUtils.deleteDirectory(tempDir);\n //Delete Derby log file\n FileUtils.forceDelete(new File(backupNewDir.getAbsolutePath()\n + System.getProperty("file.separator") + "derby.log"));\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.INFO,\n "Done!");\n }\n /**\n * Prepare system to use derby\n */\n setDBSystemDir(backupNewDir.getAbsolutePath());\n backup = backupEMF.createEntityManager();\n for (String s : tables) {\n copyEntities(s, live, backup);\n }\n /**\n * At this point we should have a <Backup Database name> folder in\n * <Backup Path>/<Date>.\n * Lets zip them for storage.\n */\n format = new SimpleDateFormat("MM dd yyyy hh-mm-ss");\n zipBackupFiles(backupNewDir, backupNewDir.getAbsolutePath()\n + System.getProperty("file.separator") + "Xinco Backup " + format.format(new Date()));\n //Stop Derby database in order to delete\n try {\n DriverManager.getConnection("jdbc:derby:;shutdown=true");\n } catch (SQLException e) {\n //When the database shuts down it\'ll throw an exception\n }\n //Delete backed up files\n String dbName = (String) backup.getProperties().get("javax.persistence.jdbc.url");\n dbName = dbName.substring(dbName.lastIndexOf(":") + 1, dbName.indexOf(";"));\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINEST,\n "Deleting temp folder: {0}", dbName);\n FileUtils.deleteDirectory(new File(backupNewDir.getAbsolutePath()\n + System.getProperty("file.separator") + dbName));\n //Delete Derby log file\n FileUtils.forceDelete(new File(backupNewDir.getAbsolutePath()\n + System.getProperty("file.separator") + "derby.log"));\n } catch (XincoException ex) {\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.SEVERE, null, ex);\n XincoDBManager.setLocked(false);\n return false;\n } catch (Exception ex) {\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.SEVERE, null, ex);\n XincoDBManager.setLocked(false);\n return false;\n } finally {\n if (live != null && live.isOpen()) {\n live.close();\n }\n if (backup != null && backup.isOpen()) {\n backup.close();\n }\n if (backupEMF != null && backupEMF.isOpen()) {\n backupEMF.close();\n }\n }\n XincoDBManager.setLocked(false);\n return true;\n }\n\n private static void zipBackupFiles(File path, String zipName) throws XincoException {\n if (!zipName.endsWith(".zip")) {\n zipName += ".zip";\n }\n // These are the files to include in the ZIP file\n IOFileFilter filter = new IOFileFilter() {\n\n @Override\n public boolean accept(File file) {\n\n if (file.isDirectory()) {\n return true;\n }\n //Ignore other backup files\n if (file.isFile() && !file.getName().endsWith(".zip")) {\n return true;\n }\n return false;\n }\n\n @Override\n public boolean accept(File file, String string) {\n throw new UnsupportedOperationException("Not supported yet.");\n }\n };\n @SuppressWarnings("unchecked")\n Collection<File> fileList = FileUtils.listFiles(path, filter, TrueFileFilter.INSTANCE);\n Object[] files = fileList.toArray();\n\n // Create a buffer for reading the files\n byte[] buf = new byte[1024];\n\n try {\n // Create the ZIP file\n ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipName));\n\n // Compress the files\n for (int i = 0; i < files.length; i++) {\n FileInputStream in = new FileInputStream((File) files[i]);\n String fileName = ((File) files[i]).getPath();\n //Remove not needed folders\n fileName = fileName.substring(fileName.indexOf(path.getAbsolutePath()) + path.getAbsolutePath().length() + 1);\n // Add ZIP entry to output stream.\n out.putNextEntry(new ZipEntry(fileName));\n\n // Transfer bytes from the file to the ZIP file\n int len;\n while ((len = in.read(buf)) > 0) {\n out.write(buf, 0, len);\n }\n\n // Complete the entry\n out.closeEntry();\n in.close();\n last = new XincoBackupFile(new File(zipName));\n }\n // Complete the ZIP file\n out.close();\n } catch (IOException e) {\n throw new XincoException("Error zipping backup: " + e.getLocalizedMessage());\n }\n }\n\n private static void copyEntities(String table, EntityManager source, EntityManager dest) {\n List<Object> result, result2;\n result = source.createNamedQuery(table + ".findAll").getResultList();\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.INFO,\n "Copying from table: {0}", table);\n int i = 0;\n source.clear();\n for (Object o : result) {\n i++;\n Class<?> persistenceClass = null;\n try {\n persistenceClass = Class.forName("com.bluecubs.xinco.core.server.persistence." + table);\n dest.getTransaction().begin();\n if (dest.contains(persistenceClass.cast(o))) {\n //If no exception do a merge because it exists already\n dest.merge(persistenceClass.cast(o));\n } else {\n dest.persist(persistenceClass.cast(o));\n }\n dest.getTransaction().commit();\n } catch (ClassNotFoundException ex) {\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.SEVERE, null, ex);\n throw new XincoException("No persistence enitiy defined for table: " + table);\n }catch (Exception ex) {\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.SEVERE, null, ex);\n throw new XincoException("Exception copying: " + o);\n }\n }\n stats.put(table, i);\n result2 = dest.createNamedQuery(table + ".findAll").getResultList();\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.INFO,\n "Copying for table: {0} completed! Amount of records: {1}",\n new Object[]{table, i});\n //Make sure the copy is accurate.\n //TODO: For some reason XincoId always return twice the amount of records during this routine.\n if (result2.size() != result.size() && !table.equals("XincoId")) {\n throw new XincoException("Error copying records for table " + table + ". Got " + result2.size() + " instead of " + result.size());\n }\n result2.clear();\n }\n\n @SuppressWarnings({"unchecked"})\n public static ArrayList<XincoBackupFile> getBackupFiles() throws XincoException {\n // These are the files to include in the ZIP file\n IOFileFilter filter = new IOFileFilter() {\n\n @Override\n public boolean accept(File file) {\n //Only zip files\n if (file.isFile() && file.getName().endsWith(".zip")\n && file.getName().startsWith("Xinco Backup")) {\n return true;\n }\n return false;\n }\n\n @Override\n public boolean accept(File file, String string) {\n throw new UnsupportedOperationException("Not supported yet.");\n }\n };\n Collection<File> files = FileUtils.listFiles(\n new File(backupPath), filter, TrueFileFilter.INSTANCE);\n ArrayList<XincoBackupFile> backupFiles = new ArrayList<XincoBackupFile>();\n for (File f : files) {\n backupFiles.add(new XincoBackupFile(f));\n }\n //Sort\n Collections.sort(backupFiles, new XincoBackupComparator());\n //Sorted from oldest to newer so we need to invert the list.\n Collections.reverse(backupFiles);\n return backupFiles;\n }\n\n protected static boolean restoreFromBackup(XincoBackupFile backupFile) throws XincoException {\n try {\n stats.clear();\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINEST,\n "Restoring database from: {0}", backupFile.getName());\n //First make a backup of current database just in case\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINEST,\n "Creating a restore point for your current database...");\n backup();\n //We need to make sure that there\'s no one in the database\n XincoDBManager.setLocked(true);\n //Load database from the provided backup\n loadDatabaseFromBackup(backupFile);\n XincoDBManager.setLocked(false);\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINEST,\n "Restore complete!");\n try {\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINEST,\n "Deleting restore point...");\n FileUtils.forceDelete(last);\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINEST,\n "Done!");\n } catch (IOException ex) {\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.SEVERE, null, ex);\n }\n return true;\n } catch (XincoException ex) {\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.SEVERE, null, ex);\n //Recover from last backup\n loadDatabaseFromBackup(getLast());\n XincoDBManager.setLocked(false);\n throw new XincoException("Unable to load backup! Database reverted to original state. \\n" + ex.getMessage());\n }\n }\n\n protected static void loadDatabaseFromBackup(XincoBackupFile backupFile) throws XincoException {\n EntityManager backupEM = null;\n try {\n initConnections();\n live = liveEMF.createEntityManager();\n //Unzip backup\n unzipBackup(backupFile);\n //Delete current database (inverse order than writing)\n Collections.reverse(tables);\n for (String s : tables) {\n clearTable(s, live);\n }\n //Get back to original order\n Collections.reverse(tables);\n //Make derby start where the backup is\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINEST,\n "Connecting to backup data...");\n setDBSystemDir(backupPath + "Temp"\n + System.getProperty("file.separator"));\n //Connect to backup database\n backupEM = Persistence.createEntityManagerFactory("XincoBackup").createEntityManager();\n //Start copying\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINEST,\n "Starting loading entities...");\n for (String s : tables) {\n //Copy values from backup\n copyEntities(s, backupEM, live);\n }\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINEST,\n "Load complete!");\n //Stop Derby database in order to delete\n DriverManager.getConnection("jdbc:derby:;shutdown=true");\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINEST,\n "Delete temp folder!");\n try {\n FileUtils.deleteDirectory(new File(System.getProperty("derby.system.home")));\n } catch (IOException ex) {\n Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(
设计上有什么缺陷吗?有更好的方法吗?任何评论都非常受欢迎!
大多数数据库引擎提供命令或工具,允许转储给定数据库的内容(其中一些甚至支持增量备份)。当您准备好使用解决方案时,JPA 只会效率较低、更加复杂,因此我不认为使用 JPA 来完成此任务有什么意义。
对于 Derby,实际上无需执行任何操作:只需 zip/tar(或使用 rsync)数据库文件即可完成。
如果要将一个数据库引擎的内容复制到另一个引擎,请使用 ETL。
| 归档时间: |
|
| 查看次数: |
7427 次 |
| 最近记录: |