diff --git a/o2server/configSample/dumpRestoreData.json b/o2server/configSample/dumpRestoreData.json index 0805916115033fdd5440d26c3901a8909320b96f..9cd3b94a3b704328524eafb898a4e02346dac77d 100644 --- a/o2server/configSample/dumpRestoreData.json +++ b/o2server/configSample/dumpRestoreData.json @@ -2,11 +2,15 @@ "enable": false, "includes": [], "excludes": [], - "batchSize": 1000.0, "mode": "lite", + "parallel": true, + "redistribute": true, + "exceptionInvalidStorage": true, "###enable": "是否启用.###", "###includes": "导出导入包含对象,可以使用通配符*.###", "###excludes": "导出导入排除对象,可以使用通配符*.###", - "###batchSize": "批量对象数量.###", - "###mode": "导出数据模式,lite|full,默认使用lite###" + "###mode": "导出数据模式,lite|full,默认使用lite###", + "###parallel": "使用并行导出,默认true###", + "###redistribute": "是否进行重新分布.###", + "###exceptionInvalidStorage": "无法获取storage是否升起错误.###" } \ No newline at end of file diff --git a/o2server/configSample/node_127.0.0.1.json b/o2server/configSample/node_127.0.0.1.json index 064bffa7d320f660ceacd0ae15c8b10cfd99a6f2..2c85fd6f484f94225249b3c40fe89a6c261d7fae 100644 --- a/o2server/configSample/node_127.0.0.1.json +++ b/o2server/configSample/node_127.0.0.1.json @@ -138,16 +138,6 @@ "###size": "最大保留份数,超过将自动删除最久的数据.###", "###path": "备份路径###" }, - "dumpStorage": { - "enable": true, - "cron": "", - "size": 7.0, - "path": "", - "###enable": "是否启用,默认每天凌晨4点进行备份.###", - "###cron": "定时任务cron表达式###", - "###size": "最大保留份数,超过将自动删除最久的数据.###", - "###path": "备份路径###" - }, "restoreData": { "enable": false, "cron": "", @@ -156,14 +146,6 @@ "###cron": "定时任务cron表达式###", "###path": "恢复路径###" }, - "restoreStorage": { - "enable": false, - "cron": "", - "path": "", - "###enable": "是否启用.###", - "###cron": "定时任务cron表达式###", - "###path": "恢复路径###" - }, "nodeAgentEnable": true, "nodeAgentPort": 20010.0, "nodeAgentEncrypt": true, @@ -178,9 +160,7 @@ "###storage": "Storage服务器配置###", "###logLevel": "日志级别,默认当前节点的slf4j日志级别,通过系统变量\"org.slf4j.simpleLogger.defaultLogLevel\"设置到当前jvm中.###", "###dumpData": "定时数据导出配置###", - "###dumpStorage": "定时存储文件导出配置###", "###restoreData": "定时数据导入配置###", - "###restoreStorage": "定时存储文件导入配置###", "###logSize": "日志文件保留天数.###", "###auditLogSize": "审计日志文件保留天数.###", "###nodeAgentEnable": "是否启用节点代理###", diff --git a/o2server/pom.xml b/o2server/pom.xml index 58ee7c45bf163c36b486e4989cb0b63d866f5b21..61b7f5e23c5cec1ea4da9e49daab81240dc8a7db 100644 --- a/o2server/pom.xml +++ b/o2server/pom.xml @@ -494,7 +494,7 @@ commons-io commons-io - 2.6 + 2.7 org.apache.commons diff --git a/o2server/x_base_core_project/src/main/java/com/x/base/core/project/build/CheckCore.java b/o2server/x_base_core_project/src/main/java/com/x/base/core/project/build/CheckCore.java index a8d29903fc1335da2d48e198f067cd473f4e66da..a4e77c1c8c044864da7a865e03a6185117316e45 100644 --- a/o2server/x_base_core_project/src/main/java/com/x/base/core/project/build/CheckCore.java +++ b/o2server/x_base_core_project/src/main/java/com/x/base/core/project/build/CheckCore.java @@ -19,6 +19,8 @@ import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.openjpa.persistence.jdbc.ContainerTable; import com.x.base.core.entity.JpaObject; +import com.x.base.core.entity.JsonProperties; +import com.x.base.core.entity.StorageObject; import com.x.base.core.entity.annotation.ContainerEntity; import com.x.base.core.project.annotation.FieldDescribe; import com.x.base.core.project.annotation.Module; @@ -44,6 +46,7 @@ public class CheckCore { checkTableNameUniqueConstraintName(classes); checkIdCreateTimeUpdateTimeSequenceIndex(classes); checkEnum(classes); + checkDumpSize(classes); } } @@ -215,17 +218,33 @@ public class CheckCore { for (Class cls : classes) { List fields = FieldUtils.getFieldsListWithAnnotation(cls, Column.class); for (Field field : fields) { - if ((!String.class.isAssignableFrom(field.getType())) && (!field.getType().isEnum())) { - Column column = field.getAnnotation(Column.class); - if (column.length() != 255) { - System.err.println(String.format("checkColumnLength error: class: %s, field: %s.", - cls.getName(), field.getName())); + if ((!String.class.isAssignableFrom(field.getType())) && (!field.getType().isEnum()) + && (!JsonProperties.class.isAssignableFrom(field.getType()))) { + Lob lob = cls.getAnnotation(Lob.class); + if (null != lob) { + Column column = field.getAnnotation(Column.class); + if (column.length() > 255) { + System.err.println(String.format("checkColumnLength error: class: %s, field: %s.", + cls.getName(), field.getName())); + } } } } } } + /* 检查StorageObject的dumpSize是否设置正确 */ + public static void checkDumpSize(List> classes) throws Exception { + for (Class cls : classes) { + if (StorageObject.class.isAssignableFrom(cls)) { + ContainerEntity containerEntity = cls.getAnnotation(ContainerEntity.class); + if (containerEntity.dumpSize() > 10) { + System.err.println(String.format("checkDumpSize error: class: %s.", cls.getName())); + } + } + } + } + public static void checkIdUnique(List> classes) throws Exception { for (Class cls : classes) { Field idField = FieldUtils.getField(cls, JpaObject.id_FIELDNAME, true); diff --git a/o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/DumpRestoreData.java b/o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/DumpRestoreData.java index f7b1a3ae15636a97d4bcc89bd38407e4073d66a3..08b9b724b4514ffd6ec2fa5284c54e03890f4da8 100644 --- a/o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/DumpRestoreData.java +++ b/o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/DumpRestoreData.java @@ -11,22 +11,27 @@ import com.x.base.core.project.tools.ListTools; public class DumpRestoreData extends ConfigObject { - public static String TYPE_FULL = "full"; - public static String TYPE_LITE = "lite"; + private static final long serialVersionUID = 8910820385137391619L; public static DumpRestoreData defaultInstance() { return new DumpRestoreData(); } - public static final int default_batchSize = 1000; - public static final String default_type = TYPE_LITE; + public static final String TYPE_FULL = "full"; + public static final String TYPE_LITE = "lite"; + public static final String DEFAULT_TYPE = TYPE_LITE; + public static final Boolean DEFAULT_PARALLEL = true; + public static final Boolean DEFAULT_REDISTRIBUTE = true; + public static final Boolean DEFAULT_EXCEPTIONINVALIDSTORAGE = true; public DumpRestoreData() { this.enable = false; - this.includes = new ArrayList(); - this.excludes = new ArrayList(); - this.batchSize = default_batchSize; - this.mode = default_type; + this.includes = new ArrayList<>(); + this.excludes = new ArrayList<>(); + this.mode = DEFAULT_TYPE; + this.parallel = DEFAULT_PARALLEL; + this.redistribute = DEFAULT_REDISTRIBUTE; + this.exceptionInvalidStorage = DEFAULT_EXCEPTIONINVALIDSTORAGE; } @FieldDescribe("是否启用.") @@ -38,12 +43,30 @@ public class DumpRestoreData extends ConfigObject { @FieldDescribe("导出导入排除对象,可以使用通配符*.") private List excludes; - @FieldDescribe("批量对象数量.") - private Integer batchSize; - @FieldDescribe("导出数据模式,lite|full,默认使用lite") private String mode; + @FieldDescribe("使用并行导出,默认true") + private Boolean parallel; + + @FieldDescribe("是否进行重新分布.") + private Boolean redistribute; + + @FieldDescribe("无法获取storage是否升起错误.") + private Boolean exceptionInvalidStorage; + + public Boolean getRedistribute() { + return BooleanUtils.isNotFalse(redistribute); + } + + public Boolean getExceptionInvalidStorage() { + return BooleanUtils.isNotFalse(exceptionInvalidStorage); + } + + public Boolean getParallel() { + return BooleanUtils.isNotFalse(parallel); + } + public String getMode() { return StringUtils.equals(TYPE_FULL, mode) ? TYPE_FULL : TYPE_LITE; } @@ -68,13 +91,6 @@ public class DumpRestoreData extends ConfigObject { return list; } - public Integer getBatchSize() { - if ((null == this.batchSize) || (this.batchSize < 1)) { - return default_batchSize; - } - return this.batchSize; - } - public void setIncludes(List includes) { this.includes = includes; } @@ -83,8 +99,16 @@ public class DumpRestoreData extends ConfigObject { this.excludes = excludes; } - public void setBatchSize(Integer batchSize) { - this.batchSize = batchSize; + public void setParallel(Boolean parallel) { + this.parallel = parallel; + } + + public void setRedistribute(Boolean redistribute) { + this.redistribute = redistribute; + } + + public void setExceptionInvalidStorage(Boolean exceptionInvalidStorage) { + this.exceptionInvalidStorage = exceptionInvalidStorage; } public void setEnable(Boolean enable) { diff --git a/o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Node.java b/o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Node.java index 942115db037933775c3dee7d471e50afce5250b5..906af18518857fe0647bf588bdc4fa6b4b9b9743 100644 --- a/o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Node.java +++ b/o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Node.java @@ -1,16 +1,16 @@ package com.x.base.core.project.config; -import com.x.base.core.project.annotation.FieldDescribe; -import com.x.base.core.project.tools.DateTools; - import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import com.x.base.core.project.annotation.FieldDescribe; +import com.x.base.core.project.tools.DateTools; + public class Node extends ConfigObject { - public static final Integer default_nodeAgentPort = 20010; - public static final String default_banner = "O2OA"; - public static final Integer default_logSize = 14; + public static final Integer DEFAULT_NODEAGENTPORT = 20010; + public static final String DEFAULT_BANNER = "O2OA"; + public static final Integer DEFAULT_LOGSIZE = 14; public static Node defaultInstance() { Node o = new Node(); @@ -23,12 +23,10 @@ public class Node extends ConfigObject { o.storage = StorageServer.defaultInstance(); o.logLevel = "warn"; o.dumpData = new ScheduleDumpData(); - o.dumpStorage = new ScheduleDumpStorage(); o.restoreData = new ScheduleRestoreData(); - o.restoreStorage = new ScheduleRestoreStorage(); o.nodeAgentEnable = true; o.nodeAgentEncrypt = true; - o.nodeAgentPort = default_nodeAgentPort; + o.nodeAgentPort = DEFAULT_NODEAGENTPORT; o.quickStartWebApp = false; o.autoStart = true; return o; @@ -52,12 +50,8 @@ public class Node extends ConfigObject { private String logLevel; @FieldDescribe("定时数据导出配置") private ScheduleDumpData dumpData; - @FieldDescribe("定时存储文件导出配置") - private ScheduleDumpStorage dumpStorage; @FieldDescribe("定时数据导入配置") private ScheduleRestoreData restoreData; - @FieldDescribe("定时存储文件导入配置") - private ScheduleRestoreStorage restoreStorage; @FieldDescribe("日志文件保留天数.") private Integer logSize; @FieldDescribe("审计日志文件保留天数.") @@ -83,7 +77,6 @@ public class Node extends ConfigObject { } /* 20191009兼容centerServer end */ - public Boolean getEraseContentEnable() { return BooleanUtils.isNotFalse(eraseContentEnable); } @@ -136,14 +129,14 @@ public class Node extends ConfigObject { } public String getBanner() { - return StringUtils.isBlank(this.banner) ? default_banner : this.banner; + return StringUtils.isBlank(this.banner) ? DEFAULT_BANNER : this.banner; } public Integer logSize() { if ((this.logSize != null) && (this.logSize > 0)) { return this.logSize; } - return default_logSize; + return DEFAULT_LOGSIZE; } public Boolean getQuickStartWebApp() { @@ -152,7 +145,7 @@ public class Node extends ConfigObject { public Integer nodeAgentPort() { if (null == this.nodeAgentPort || this.nodeAgentPort < 0) { - return default_nodeAgentPort; + return DEFAULT_NODEAGENTPORT; } return this.nodeAgentPort; } @@ -173,18 +166,10 @@ public class Node extends ConfigObject { return (dumpData == null) ? new ScheduleDumpData() : this.dumpData; } - public ScheduleDumpStorage dumpStorage() { - return (dumpStorage == null) ? new ScheduleDumpStorage() : this.dumpStorage; - } - public ScheduleRestoreData restoreData() { return (restoreData == null) ? new ScheduleRestoreData() : this.restoreData; } - public ScheduleRestoreStorage restoreStorage() { - return (restoreStorage == null) ? new ScheduleRestoreStorage() : this.restoreStorage; - } - public static class ScheduleDumpData extends ConfigObject { public static ScheduleDumpData defaultInstance() { @@ -225,46 +210,6 @@ public class Node extends ConfigObject { } - public static class ScheduleDumpStorage extends ConfigObject { - - public static ScheduleDumpStorage defaultInstance() { - return new ScheduleDumpStorage(); - } - - public boolean available() { - return DateTools.cronAvailable(this.cron()); - } - - @FieldDescribe("是否启用,默认每天凌晨4点进行备份.") - private Boolean enable = true; - - @FieldDescribe("定时任务cron表达式") - private String cron = ""; - - @FieldDescribe("最大保留份数,超过将自动删除最久的数据.") - private Integer size = 7; - - @FieldDescribe("备份路径") - private String path = ""; - - public Boolean enable() { - return (BooleanUtils.isTrue(this.enable)) ? true : false; - } - - public String cron() { - return (null == cron) ? "5 0 4 * * ?" : this.cron; - } - - public Integer size() { - return (null == size) ? 14 : this.size; - } - - public String path() { - return StringUtils.trim(path); - } - - } - public static class ScheduleRestoreData extends ConfigObject { public static ScheduleRestoreData defaultInstance() { @@ -298,37 +243,4 @@ public class Node extends ConfigObject { } - public static class ScheduleRestoreStorage extends ConfigObject { - - public static ScheduleRestoreStorage defaultInstance() { - return new ScheduleRestoreStorage(); - } - - public boolean available() { - return DateTools.cronAvailable(this.cron) && StringUtils.isNotEmpty(this.path); - } - - @FieldDescribe("是否启用.") - private Boolean enable = false; - - @FieldDescribe("定时任务cron表达式") - private String cron = ""; - - @FieldDescribe("恢复路径") - private String path = ""; - - public Boolean enable() { - return (BooleanUtils.isTrue(this.enable)) ? true : false; - } - - public String cron() { - return (null == cron) ? "" : this.cron; - } - - public String path() { - return StringUtils.trim(path); - } - - } - } diff --git a/o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/StringTools.java b/o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/StringTools.java index c0d31ab0804a6cd00bd92a1ebb0e8aba50692adb..fb5d1afa1d0e7cf3130fadc27ff9e951527830a4 100644 --- a/o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/StringTools.java +++ b/o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/StringTools.java @@ -35,7 +35,26 @@ public class StringTools { /** 中文,英文,数字,-,.· 【】() */ public static final Pattern SIMPLY_REGEX = Pattern .compile("^[\u4e00-\u9fa5a-zA-Z0-9\\_\\(\\)\\-\\ \\.\\ \\·\\【\\】\\(\\)]*$"); - public static final Pattern FILENAME_REGEX = Pattern.compile("[^/\\\\<>*?|\"]+(\\.?)[^/\\\\<>*?|\"]+"); + /** + * MSDN + * https://docs.microsoft.com/zh-cn/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN#file_and_directory_names + */ + public static final Pattern FILENAME_REGEX = Pattern.compile( + "# Match a valid Windows filename (unspecified file system). \n" + + "^ # Anchor to start of string. \n" + + "(?! # Assert filename is not: CON, PRN, \n" + + " (?: # AUX, NUL, COM1, COM2, COM3, COM4, \n" + + " CON|PRN|AUX|NUL| # COM5, COM6, COM7, COM8, COM9, \n" + + " COM[1-9]|LPT[1-9] # LPT1, LPT2, LPT3, LPT4, LPT5, \n" + + " ) # LPT6, LPT7, LPT8, and LPT9... \n" + + " (?:\\.[^.]*)? # followed by optional extension \n" + + " $ # and end of string \n" + + ") # End negative lookahead assertion. \n" + + "[^<>:\"/\\\\|?*\\x00-\\x1F]* # Zero or more valid filename chars.\n" + + "[^<>:\"/\\\\|?*\\x00-\\x1F\\ .] # Last char is not a space or dot. \n" + + "$ # Anchor to end of string. ", + Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | Pattern.COMMENTS); + /** * RFC822 compliant regex adapted for Java * http://stackoverflow.com/questions/8204680/java-regex-email @@ -480,37 +499,37 @@ public class StringTools { while (tok.hasMoreTokens()) { String nextTok = tok.nextToken(); switch (state) { - case inQuote: - if ("\'".equals(nextTok)) { - lastTokenHasBeenQuoted = true; - state = normal; - } else { - current.append(nextTok); - } - break; - case inDoubleQuote: - if ("\"".equals(nextTok)) { - lastTokenHasBeenQuoted = true; - state = normal; - } else { - current.append(nextTok); - } - break; - default: - if ("\'".equals(nextTok)) { - state = inQuote; - } else if ("\"".equals(nextTok)) { - state = inDoubleQuote; - } else if (" ".equals(nextTok)) { - if (lastTokenHasBeenQuoted || current.length() > 0) { - result.add(current.toString()); - current.setLength(0); - } - } else { - current.append(nextTok); + case inQuote: + if ("\'".equals(nextTok)) { + lastTokenHasBeenQuoted = true; + state = normal; + } else { + current.append(nextTok); + } + break; + case inDoubleQuote: + if ("\"".equals(nextTok)) { + lastTokenHasBeenQuoted = true; + state = normal; + } else { + current.append(nextTok); + } + break; + default: + if ("\'".equals(nextTok)) { + state = inQuote; + } else if ("\"".equals(nextTok)) { + state = inDoubleQuote; + } else if (" ".equals(nextTok)) { + if (lastTokenHasBeenQuoted || current.length() > 0) { + result.add(current.toString()); + current.setLength(0); } - lastTokenHasBeenQuoted = false; - break; + } else { + current.append(nextTok); + } + lastTokenHasBeenQuoted = false; + break; } } if (lastTokenHasBeenQuoted || current.length() > 0) { diff --git a/o2server/x_bbs_core_entity/src/main/java/com/x/bbs/entity/BBSSubjectAttachment.java b/o2server/x_bbs_core_entity/src/main/java/com/x/bbs/entity/BBSSubjectAttachment.java index d8cc92abfff41f8ff08d98fa01fd1f082456a48c..daeeae00155e1fb12810c968f8e13e5b5b3e8328 100644 --- a/o2server/x_bbs_core_entity/src/main/java/com/x/bbs/entity/BBSSubjectAttachment.java +++ b/o2server/x_bbs_core_entity/src/main/java/com/x/bbs/entity/BBSSubjectAttachment.java @@ -29,7 +29,7 @@ import com.x.base.core.project.tools.DateTools; /** * 附件信息管理表 */ -@ContainerEntity(dumpSize = 1000, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) +@ContainerEntity(dumpSize = 10, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) @Entity @Table(name = PersistenceProperties.BBSSubjectAttachment.table, uniqueConstraints = { @UniqueConstraint(name = PersistenceProperties.BBSSubjectAttachment.table + JpaObject.IndexNameMiddle diff --git a/o2server/x_cms_core_entity/src/main/java/com/x/cms/core/entity/FileInfo.java b/o2server/x_cms_core_entity/src/main/java/com/x/cms/core/entity/FileInfo.java index b2fda22e917cbe67b7699db721116f784ea9e37a..0440805f353e0c7c0c186656795d186eef077c53 100644 --- a/o2server/x_cms_core_entity/src/main/java/com/x/cms/core/entity/FileInfo.java +++ b/o2server/x_cms_core_entity/src/main/java/com/x/cms/core/entity/FileInfo.java @@ -39,7 +39,7 @@ import com.x.base.core.project.tools.DateTools; * 内容管理应用目录分类信息 * */ -@ContainerEntity(dumpSize = 1000, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) +@ContainerEntity(dumpSize = 5, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) @Entity @Table(name = PersistenceProperties.FileInfo.table, uniqueConstraints = { @UniqueConstraint(name = PersistenceProperties.FileInfo.table + JpaObject.IndexNameMiddle diff --git a/o2server/x_console/src/main/java/com/x/server/console/DumpStorageTask.java b/o2server/x_console/src/main/java/com/x/server/console/DumpStorageTask.java index 6b345653ea27375ee6880813f1a910d0fda7d269..7fd87410256ff7f9d3888078d056822df0eff974 100644 --- a/o2server/x_console/src/main/java/com/x/server/console/DumpStorageTask.java +++ b/o2server/x_console/src/main/java/com/x/server/console/DumpStorageTask.java @@ -1,56 +1,56 @@ -package com.x.server.console; - -import java.io.File; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.filefilter.FalseFileFilter; -import org.apache.commons.io.filefilter.RegexFileFilter; -import org.quartz.Job; -import org.quartz.JobExecutionContext; -import org.quartz.JobExecutionException; - -import com.x.base.core.project.config.Config; -import com.x.base.core.project.logger.Logger; -import com.x.base.core.project.logger.LoggerFactory; -import com.x.server.console.action.DumpStorage; - -public class DumpStorageTask implements Job { - - private static Logger logger = LoggerFactory.getLogger(DumpStorageTask.class); - - @Override - public void execute(JobExecutionContext arg0) throws JobExecutionException { - try { - logger.print("schedule dump storage task start."); - DumpStorage action = new DumpStorage(); - action.execute(Config.currentNode().dumpStorage().path()); - if (Config.currentNode().dumpStorage().size() > 0) { - File dir = new File(Config.base(), "local/dump"); - List list = new ArrayList<>(); - if (dir.exists() && dir.isDirectory()) { - for (File f : FileUtils.listFilesAndDirs(dir, FalseFileFilter.FALSE, new RegexFileFilter( - "^dumpStorage_[1,2][0,9][0-9][0-9][0,1][0-9][0-3][0-9][0-5][0-9][0-5][0-9][0-5][0-9]$"))) { - if (dir != f) { - list.add(f); - } - } - list = list.stream().sorted(Comparator.comparing(File::getName).reversed()) - .collect(Collectors.toList()); - if (list.size() > Config.currentNode().dumpStorage().size()) { - for (int i = Config.currentNode().dumpStorage().size(); i < list.size(); i++) { - File file = list.get(i); - logger.print("dumpStorageTask delete{}.", file.getAbsolutePath()); - FileUtils.forceDelete(file); - } - } - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } -} \ No newline at end of file +//package com.x.server.console; +// +//import java.io.File; +//import java.util.ArrayList; +//import java.util.Comparator; +//import java.util.List; +//import java.util.stream.Collectors; +// +//import org.apache.commons.io.FileUtils; +//import org.apache.commons.io.filefilter.FalseFileFilter; +//import org.apache.commons.io.filefilter.RegexFileFilter; +//import org.quartz.Job; +//import org.quartz.JobExecutionContext; +//import org.quartz.JobExecutionException; +// +//import com.x.base.core.project.config.Config; +//import com.x.base.core.project.logger.Logger; +//import com.x.base.core.project.logger.LoggerFactory; +//import com.x.server.console.action.DumpStorage; +// +//public class DumpStorageTask implements Job { +// +// private static Logger logger = LoggerFactory.getLogger(DumpStorageTask.class); +// +// @Override +// public void execute(JobExecutionContext arg0) throws JobExecutionException { +// try { +// logger.print("schedule dump storage task start."); +// DumpStorage action = new DumpStorage(); +// action.execute(Config.currentNode().dumpStorage().path()); +// if (Config.currentNode().dumpStorage().size() > 0) { +// File dir = new File(Config.base(), "local/dump"); +// List list = new ArrayList<>(); +// if (dir.exists() && dir.isDirectory()) { +// for (File f : FileUtils.listFilesAndDirs(dir, FalseFileFilter.FALSE, new RegexFileFilter( +// "^dumpStorage_[1,2][0,9][0-9][0-9][0,1][0-9][0-3][0-9][0-5][0-9][0-5][0-9][0-5][0-9]$"))) { +// if (dir != f) { +// list.add(f); +// } +// } +// list = list.stream().sorted(Comparator.comparing(File::getName).reversed()) +// .collect(Collectors.toList()); +// if (list.size() > Config.currentNode().dumpStorage().size()) { +// for (int i = Config.currentNode().dumpStorage().size(); i < list.size(); i++) { +// File file = list.get(i); +// logger.print("dumpStorageTask delete{}.", file.getAbsolutePath()); +// FileUtils.forceDelete(file); +// } +// } +// } +// } +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +//} \ No newline at end of file diff --git a/o2server/x_console/src/main/java/com/x/server/console/Main.java b/o2server/x_console/src/main/java/com/x/server/console/Main.java index bc2973fc3072b91c3093324b5e960872057770de..622784a11ca38cf09099365da0c7297efe5e42a8 100644 --- a/o2server/x_console/src/main/java/com/x/server/console/Main.java +++ b/o2server/x_console/src/main/java/com/x/server/console/Main.java @@ -3,8 +3,11 @@ package com.x.server.console; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; +import java.io.IOException; import java.io.InputStreamReader; import java.io.RandomAccessFile; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; @@ -12,12 +15,22 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.channels.FileLock; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import java.util.regex.Matcher; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.quartz.Scheduler; + import com.x.base.core.project.config.ApplicationServer; import com.x.base.core.project.config.CenterServer; import com.x.base.core.project.config.Config; @@ -34,14 +47,6 @@ import com.x.server.console.action.ActionVersion; import com.x.server.console.log.LogTools; import com.x.server.console.server.Servers; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; -import org.eclipse.jetty.deploy.App; -import org.eclipse.jetty.deploy.DeploymentManager; -import org.quartz.Scheduler; - public class Main { private static final String MANIFEST_FILENAME = "manifest.cfg"; @@ -50,6 +55,7 @@ public class Main { public static void main(String[] args) throws Exception { String base = getBasePath(); + pid(base); scanWar(base); loadJars(base); /* getVersion需要FileUtils在后面运行 */ @@ -620,4 +626,12 @@ public class Main { return false; } + private static void pid(String base) throws IOException { + RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean(); + String jvmName = runtimeBean.getName(); + long pid = Long.parseLong(jvmName.split("@")[0]); + Path path = Paths.get(base, "pid.log"); + Files.write(path, Long.toString(pid).getBytes(), StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); + } } \ No newline at end of file diff --git a/o2server/x_console/src/main/java/com/x/server/console/RestoreStorageTask.java b/o2server/x_console/src/main/java/com/x/server/console/RestoreStorageTask.java index c6314daa189d5ab8a2b26b552650ee5e71f9219a..377c16c761a66b2a0dac56630631e2f88823c7ce 100644 --- a/o2server/x_console/src/main/java/com/x/server/console/RestoreStorageTask.java +++ b/o2server/x_console/src/main/java/com/x/server/console/RestoreStorageTask.java @@ -1,29 +1,29 @@ -package com.x.server.console; - -import com.x.base.core.project.config.Config; -import com.x.base.core.project.logger.Logger; -import com.x.base.core.project.logger.LoggerFactory; -import com.x.server.console.action.RestoreStorage; - -import org.quartz.Job; -import org.quartz.JobExecutionContext; -import org.quartz.JobExecutionException; - -public class RestoreStorageTask implements Job { - - private static Logger logger = LoggerFactory.getLogger(RestoreStorageTask.class); - - @Override - public void execute(JobExecutionContext arg0) throws JobExecutionException { - try { - logger.print("schedule restore storage task start, restore from:{}.", - Config.currentNode().restoreData().path()); - RestoreStorage action = new RestoreStorage(); - action.execute(Config.currentNode().restoreStorage().path()); - } catch (Exception e) { - throw new JobExecutionException(e); - } - - } - -} \ No newline at end of file +//package com.x.server.console; +// +//import com.x.base.core.project.config.Config; +//import com.x.base.core.project.logger.Logger; +//import com.x.base.core.project.logger.LoggerFactory; +//import com.x.server.console.action.RestoreStorage; +// +//import org.quartz.Job; +//import org.quartz.JobExecutionContext; +//import org.quartz.JobExecutionException; +// +//public class RestoreStorageTask implements Job { +// +// private static Logger logger = LoggerFactory.getLogger(RestoreStorageTask.class); +// +// @Override +// public void execute(JobExecutionContext arg0) throws JobExecutionException { +// try { +// logger.print("schedule restore storage task start, restore from:{}.", +// Config.currentNode().restoreData().path()); +// RestoreStorage action = new RestoreStorage(); +// action.execute(Config.currentNode().restoreStorage().path()); +// } catch (Exception e) { +// throw new JobExecutionException(e); +// } +// +// } +// +//} \ No newline at end of file diff --git a/o2server/x_console/src/main/java/com/x/server/console/SchedulerBuilder.java b/o2server/x_console/src/main/java/com/x/server/console/SchedulerBuilder.java index 8a82d6a18a9ea685b42dc7e128d2be84411fa3db..73e2e39e7ce720256d65eb366c9e7e528fbbeb04 100644 --- a/o2server/x_console/src/main/java/com/x/server/console/SchedulerBuilder.java +++ b/o2server/x_console/src/main/java/com/x/server/console/SchedulerBuilder.java @@ -28,14 +28,14 @@ public class SchedulerBuilder { .withSchedule(CronScheduleBuilder.cronSchedule(Config.currentNode().dumpData().cron())).build(); scheduler.scheduleJob(jobDetail, trigger); } - if (Config.currentNode().dumpStorage().enable() && Config.currentNode().dumpStorage().available()) { - JobDetail jobDetail = JobBuilder.newJob(DumpStorageTask.class) - .withIdentity(DumpStorageTask.class.getName(), scheduleGroup).withDescription(Config.node()) - .build(); - Trigger trigger = TriggerBuilder.newTrigger().withIdentity(DumpStorageTask.class.getName(), scheduleGroup) - .withSchedule(CronScheduleBuilder.cronSchedule(Config.currentNode().dumpStorage().cron())).build(); - scheduler.scheduleJob(jobDetail, trigger); - } +// if (Config.currentNode().dumpStorage().enable() && Config.currentNode().dumpStorage().available()) { +// JobDetail jobDetail = JobBuilder.newJob(DumpStorageTask.class) +// .withIdentity(DumpStorageTask.class.getName(), scheduleGroup).withDescription(Config.node()) +// .build(); +// Trigger trigger = TriggerBuilder.newTrigger().withIdentity(DumpStorageTask.class.getName(), scheduleGroup) +// .withSchedule(CronScheduleBuilder.cronSchedule(Config.currentNode().dumpStorage().cron())).build(); +// scheduler.scheduleJob(jobDetail, trigger); +// } if (Config.currentNode().restoreData().enable() && Config.currentNode().restoreData().available()) { JobDetail jobDetail = JobBuilder.newJob(RestoreDataTask.class) .withIdentity(RestoreDataTask.class.getName(), scheduleGroup).withDescription(Config.node()) @@ -44,16 +44,16 @@ public class SchedulerBuilder { .withSchedule(CronScheduleBuilder.cronSchedule(Config.currentNode().restoreData().cron())).build(); scheduler.scheduleJob(jobDetail, trigger); } - if (Config.currentNode().restoreStorage().enable() && Config.currentNode().restoreStorage().available()) { - JobDetail jobDetail = JobBuilder.newJob(RestoreStorageTask.class) - .withIdentity(RestoreStorageTask.class.getName(), scheduleGroup).withDescription(Config.node()) - .build(); - Trigger trigger = TriggerBuilder.newTrigger() - .withIdentity(RestoreStorageTask.class.getName(), scheduleGroup) - .withSchedule(CronScheduleBuilder.cronSchedule(Config.currentNode().restoreStorage().cron())) - .build(); - scheduler.scheduleJob(jobDetail, trigger); - } +// if (Config.currentNode().restoreStorage().enable() && Config.currentNode().restoreStorage().available()) { +// JobDetail jobDetail = JobBuilder.newJob(RestoreStorageTask.class) +// .withIdentity(RestoreStorageTask.class.getName(), scheduleGroup).withDescription(Config.node()) +// .build(); +// Trigger trigger = TriggerBuilder.newTrigger() +// .withIdentity(RestoreStorageTask.class.getName(), scheduleGroup) +// .withSchedule(CronScheduleBuilder.cronSchedule(Config.currentNode().restoreStorage().cron())) +// .build(); +// scheduler.scheduleJob(jobDetail, trigger); +// } this.registApplicationsAndVoteCenterTask(scheduler, scheduleGroup); return scheduler; diff --git a/o2server/x_console/src/main/java/com/x/server/console/action/ActionControl.java b/o2server/x_console/src/main/java/com/x/server/console/action/ActionControl.java index ba8fd23cb37e3fa1c3ad81bab0f4b3bbce53a767..e9c0f510ea03dfdd34c0f5c428c77bfc2b914fe2 100644 --- a/o2server/x_console/src/main/java/com/x/server/console/action/ActionControl.java +++ b/o2server/x_console/src/main/java/com/x/server/console/action/ActionControl.java @@ -33,9 +33,9 @@ public class ActionControl extends ActionBase { private static final String CMD_TD = "td"; private static final String CMD_EC = "ec"; private static final String CMD_DD = "dd"; - private static final String CMD_DS = "ds"; + // private static final String CMD_DS = "ds"; private static final String CMD_RD = "rd"; - private static final String CMD_RS = "rs"; + // private static final String CMD_RS = "rs"; private static final String CMD_CLH2 = "clh2"; private static final String CMD_UF = "uf"; private static final String CMD_DDL = "ddl"; @@ -63,12 +63,12 @@ public class ActionControl extends ActionBase { ec(cmd); } else if (cmd.hasOption(CMD_DD)) { dd(cmd); - } else if (cmd.hasOption(CMD_DS)) { - ds(cmd); +// } else if (cmd.hasOption(CMD_DS)) { +// ds(cmd); } else if (cmd.hasOption(CMD_RD)) { rd(cmd); - } else if (cmd.hasOption(CMD_RS)) { - rs(cmd); +// } else if (cmd.hasOption(CMD_RS)) { +// rs(cmd); } else if (cmd.hasOption(CMD_CLH2)) { clh2(cmd); } else if (cmd.hasOption(CMD_UF)) { @@ -96,9 +96,9 @@ public class ActionControl extends ActionBase { options.addOption(tdOption()); options.addOption(ecOption()); options.addOption(ddOption()); - options.addOption(dsOption()); + // options.addOption(dsOption()); options.addOption(rdOption()); - options.addOption(rsOption()); + // options.addOption(rsOption()); options.addOption(clh2Option()); options.addOption(ufOption()); options.addOption(ddlOption()); @@ -145,20 +145,20 @@ public class ActionControl extends ActionBase { .desc("导出数据库服务器的数据转换成json格式保存到本地文件.").build(); } - private static Option dsOption() { - return Option.builder("ds").longOpt("dumpStorage").argName("path").hasArg().optionalArg(true) - .desc("导出存储服务器的文件数据转换成json格式保存到本地文件.").build(); - } +// private static Option dsOption() { +// return Option.builder("ds").longOpt("dumpStorage").argName("path").hasArg().optionalArg(true) +// .desc("导出存储服务器的文件数据转换成json格式保存到本地文件.").build(); +// } private static Option rdOption() { return Option.builder("rd").longOpt("restoreData").argName("path or date").hasArg() .desc("将导出的json格式数据恢复到数据库服务器.").build(); } - private static Option rsOption() { - return Option.builder("rs").longOpt("restoreStorage").argName("path or date").hasArg() - .desc("将导出的json格式文件数据恢复到存储服务器.").build(); - } +// private static Option rsOption() { +// return Option.builder("rs").longOpt("restoreStorage").argName("path or date").hasArg() +// .desc("将导出的json格式文件数据恢复到存储服务器.").build(); +// } private static Option ufOption() { return Option.builder("uf").longOpt("updateFile").argName("path").hasArg().desc("升级服务器,升级前请注意备份.").build(); @@ -229,11 +229,11 @@ public class ActionControl extends ActionBase { dumpData.execute(path); } - private void ds(CommandLine cmd) throws Exception { - String path = Objects.toString(cmd.getOptionValue(CMD_DS), ""); - DumpStorage dumpStorage = new DumpStorage(); - dumpStorage.execute(path); - } +// private void ds(CommandLine cmd) throws Exception { +// String path = Objects.toString(cmd.getOptionValue(CMD_DS), ""); +// DumpStorage dumpStorage = new DumpStorage(); +// dumpStorage.execute(path); +// } private void rd(CommandLine cmd) throws Exception { String path = Objects.toString(cmd.getOptionValue(CMD_RD), ""); @@ -241,11 +241,11 @@ public class ActionControl extends ActionBase { restoreData.execute(path); } - private void rs(CommandLine cmd) throws Exception { - String path = Objects.toString(cmd.getOptionValue(CMD_RS), ""); - RestoreStorage restoreStorage = new RestoreStorage(); - restoreStorage.execute(path); - } +// private void rs(CommandLine cmd) throws Exception { +// String path = Objects.toString(cmd.getOptionValue(CMD_RS), ""); +// RestoreStorage restoreStorage = new RestoreStorage(); +// restoreStorage.execute(path); +// } private void hs(CommandLine cmd) { final Integer repeat = this.getArgInteger(cmd, CMD_HS, 1); diff --git a/o2server/x_console/src/main/java/com/x/server/console/action/DumpData.java b/o2server/x_console/src/main/java/com/x/server/console/action/DumpData.java index cc2989b3a986f542318653e60a1332f7f91f60ff..58a70dbc6e1b2314c9742af09bed9c4b9bfd19a6 100644 --- a/o2server/x_console/src/main/java/com/x/server/console/action/DumpData.java +++ b/o2server/x_console/src/main/java/com/x/server/console/action/DumpData.java @@ -1,12 +1,16 @@ package com.x.server.console.action; -import java.io.File; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; @@ -16,152 +20,198 @@ import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.apache.commons.beanutils.BeanUtils; -import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.file.PathUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.openjpa.persistence.OpenJPAPersistence; import com.google.gson.Gson; import com.x.base.core.container.factory.PersistenceXmlHelper; import com.x.base.core.entity.JpaObject; +import com.x.base.core.entity.StorageObject; import com.x.base.core.entity.annotation.ContainerEntity; import com.x.base.core.entity.annotation.ContainerEntity.Reference; import com.x.base.core.project.config.Config; import com.x.base.core.project.config.DumpRestoreData; +import com.x.base.core.project.config.StorageMapping; +import com.x.base.core.project.config.StorageMappings; import com.x.base.core.project.gson.XGsonBuilder; import com.x.base.core.project.logger.Logger; import com.x.base.core.project.logger.LoggerFactory; import com.x.base.core.project.tools.DateTools; -import com.x.base.core.project.tools.DefaultCharset; import com.x.base.core.project.tools.ListTools; public class DumpData { private static Logger logger = LoggerFactory.getLogger(DumpData.class); - private Date start = new Date(); - - private File dir; - - private DumpRestoreDataCatalog catalog; - - private Gson pureGsonDateFormated = XGsonBuilder.instance(); - - @SuppressWarnings("unchecked") public boolean execute(String path) throws Exception { + Path dir = null; + Date start = new Date(); if (StringUtils.isEmpty(path)) { - this.dir = new File(Config.base(), "local/dump/dumpData_" + DateTools.compact(this.start)); + dir = Paths.get(Config.base(), "local", "dump", "dumpData_" + DateTools.compact(start)); } else { - this.dir = new File(path); - if (dir.getAbsolutePath().startsWith(Config.base())) { - logger.print("path can not in base directory."); + dir = Paths.get(path); + if (dir.startsWith(Paths.get(Config.base()))) { + logger.warn("path can not in base directory."); return false; } } - FileUtils.forceMkdir(this.dir); - this.catalog = new DumpRestoreDataCatalog(); - - /* 初始化完成 */ - - List containerEntityNames = this.entities(); - - /** 过滤include exclude 条件 */ - List classNames = ListTools.includesExcludesWildcard(containerEntityNames, - Config.dumpRestoreData().getIncludes(), Config.dumpRestoreData().getExcludes()); - logger.print("dump data find {} data to dump, start at {}.", classNames.size(), DateTools.format(start)); - File persistence = new File(Config.dir_local_temp_classes(), DateTools.compact(this.start) + "_dump.xml"); - PersistenceXmlHelper.write(persistence.getAbsolutePath(), classNames); - for (int i = 0; i < classNames.size(); i++) { - Class cls = (Class) Class.forName(classNames.get(i)); - EntityManagerFactory emf = OpenJPAPersistence.createEntityManagerFactory(cls.getName(), - persistence.getName(), PersistenceXmlHelper.properties(cls.getName(), Config.slice().getEnable())); - EntityManager em = emf.createEntityManager(); - try { - long estimateCount = this.estimateCount(em, cls); - logger.print("dump data({}/{}): {}, count: {}.", (i + 1), classNames.size(), cls.getName(), - estimateCount); - this.dump(cls, em, estimateCount); - } finally { - em.close(); - emf.close(); - } - } - FileUtils.write(new File(dir, "catalog.json"), pureGsonDateFormated.toJson(this.catalog), - DefaultCharset.charset); - logger.print("dump data completed, directory: {}, count: {}, elapsed: {} minutes.", dir.getAbsolutePath(), - this.count(), (System.currentTimeMillis() - start.getTime()) / 1000 / 60); + Files.createDirectories(dir); + Thread thread = new Thread(new RunnableImpl(dir, start)); + thread.start(); return true; } - private long estimateCount(EntityManager em, Class cls) { - CriteriaBuilder cb = em.getCriteriaBuilder(); - CriteriaQuery cq = cb.createQuery(Long.class); - Root root = cq.from(cls); - cq.select(cb.count(root)); - return em.createQuery(cq).getSingleResult(); - } + public class RunnableImpl implements Runnable { - private Integer count() { - return this.catalog.values().stream().mapToInt(Integer::intValue).sum(); - } + private Path dir; + private Date start; + private DumpRestoreDataCatalog catalog; + private Gson pureGsonDateFormated; + + public RunnableImpl(Path dir, Date start) { + this.dir = dir; + this.start = start; + this.catalog = new DumpRestoreDataCatalog(); + this.pureGsonDateFormated = XGsonBuilder.instance(); + } - private void dump(Class cls, EntityManager em, long total) - throws IOException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { - /** 创建最终存储文件的目录 */ - File directory = new File(dir, cls.getName()); - FileUtils.forceMkdir(directory); - FileUtils.cleanDirectory(directory); - ContainerEntity containerEntity = cls.getAnnotation(ContainerEntity.class); - String id = ""; - List list = null; - int count = 0; - int loop = 1; - int btach = (int) ((total + 0.0) / containerEntity.dumpSize()); - do { - list = this.list(em, cls, id, containerEntity.dumpSize()); - if (ListTools.isNotEmpty(list)) { - count = count + list.size(); - id = BeanUtils.getProperty(list.get(list.size() - 1), JpaObject.id_FIELDNAME); - File file = new File(directory, count + ".json"); - FileUtils.write(file, pureGsonDateFormated.toJson(list), DefaultCharset.charset); - logger.print("dumping {}/{} part of data:{}.", loop++, btach, cls.getName()); + public void run() { + try { + List classNames = entities(); + logger.print("find {} data to dump, start at {}.", classNames.size(), DateTools.format(start)); + Path xml = Paths.get(Config.dir_local_temp_classes().getAbsolutePath(), + DateTools.compact(start) + "_dump.xml"); + PersistenceXmlHelper.write(xml.toString(), classNames); + StorageMappings storageMappings = Config.storageMappings(); + Stream stream = BooleanUtils.isTrue(Config.dumpRestoreData().getParallel()) + ? classNames.parallelStream() + : classNames.stream(); + AtomicInteger idx = new AtomicInteger(1); + stream.forEach(className -> { + String nameOfThread = Thread.currentThread().getName(); + EntityManagerFactory emf = null; + EntityManager em = null; + try { + Thread.currentThread().setName(DumpData.class.getName() + ":" + className); + @SuppressWarnings("unchecked") + Class cls = (Class) Class.forName(className); + emf = OpenJPAPersistence.createEntityManagerFactory(cls.getName(), xml.getFileName().toString(), + PersistenceXmlHelper.properties(cls.getName(), Config.slice().getEnable())); + em = emf.createEntityManager(); + long estimateCount = estimateCount(em, cls); + logger.print("dump data({}/{}): {}, count: {}.", idx.getAndAdd(1), classNames.size(), + cls.getName(), estimateCount); + dump(cls, em, storageMappings, estimateCount); + } catch (Exception e) { + logger.error(new Exception(String.format("dump:%s error.", className), e)); + } finally { + Thread.currentThread().setName(nameOfThread); + em.close(); + emf.close(); + } + }); + Files.write(dir.resolve("catalog.json"), + pureGsonDateFormated.toJson(catalog).getBytes(StandardCharsets.UTF_8)); + logger.print("dump data completed, directory: {}, count: {}, elapsed: {} minutes.", dir.toString(), + count(), (System.currentTimeMillis() - start.getTime()) / 1000 / 60); + } catch (Exception e) { + e.printStackTrace(); } - em.clear(); - } while (ListTools.isNotEmpty(list)); - this.catalog.put(cls.getName(), count); - } + } + + @SuppressWarnings("unchecked") + private List entities() throws Exception { + List list = new ArrayList<>(); + if (StringUtils.equals(Config.dumpRestoreData().getMode(), DumpRestoreData.TYPE_FULL)) { + list.addAll((List) Config.resource(Config.RESOURCE_CONTAINERENTITYNAMES)); + return list; + } + for (String str : (List) Config.resource(Config.RESOURCE_CONTAINERENTITYNAMES)) { + Class cls = Class.forName(str); + ContainerEntity containerEntity = cls.getAnnotation(ContainerEntity.class); + if (Objects.equals(containerEntity.reference(), Reference.strong)) { + list.add(str); + } + } + return ListTools.includesExcludesWildcard(list, Config.dumpRestoreData().getIncludes(), + Config.dumpRestoreData().getExcludes()); + } - private List list(EntityManager em, Class cls, String id, Integer size) { - CriteriaBuilder cb = em.getCriteriaBuilder(); - CriteriaQuery cq = cb.createQuery(cls); - Root root = cq.from(cls); - Predicate p = cb.conjunction(); - if (StringUtils.isNotEmpty(id)) { - p = cb.greaterThan(root.get(JpaObject.id_FIELDNAME), id); + private long estimateCount(EntityManager em, Class cls) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Long.class); + Root root = cq.from(cls); + cq.select(cb.count(root)); + return em.createQuery(cq).getSingleResult(); } - cq.select(root).where(p).orderBy(cb.asc(root.get(JpaObject.id_FIELDNAME))); - return em.createQuery(cq).setMaxResults(size).getResultList(); - } - /** - * 根据设置的模式不同输出需要dump的entity className - * - * @return - * @throws Exception - */ - @SuppressWarnings("unchecked") - private List entities() throws Exception { - List list = new ArrayList<>(); - if (StringUtils.equals(Config.dumpRestoreData().getMode(), DumpRestoreData.TYPE_FULL)) { - list.addAll((List) Config.resource(Config.RESOURCE_CONTAINERENTITYNAMES)); - return list; + private Integer count() { + return catalog.values().stream().mapToInt(Integer::intValue).sum(); } - for (String str : (List) Config.resource(Config.RESOURCE_CONTAINERENTITYNAMES)) { - Class cls = Class.forName(str); + + private List list(EntityManager em, Class cls, String id, Integer size) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(cls); + Root root = cq.from(cls); + Predicate p = cb.conjunction(); + if (StringUtils.isNotEmpty(id)) { + p = cb.greaterThan(root.get(JpaObject.id_FIELDNAME), id); + } + cq.select(root).where(p).orderBy(cb.asc(root.get(JpaObject.id_FIELDNAME))); + return em.createQuery(cq).setMaxResults(size).getResultList(); + } + + private void dump(Class cls, EntityManager em, StorageMappings storageMappings, long total) + throws Exception { + // 创建最终存储文件的目录 + Path directory = dir.resolve(cls.getName()); + Files.createDirectories(directory); + PathUtils.cleanDirectory(directory); ContainerEntity containerEntity = cls.getAnnotation(ContainerEntity.class); - if (Objects.equals(containerEntity.reference(), Reference.strong)) { - list.add(str); + String id = ""; + List list = null; + int count = 0; + int loop = 1; + int btach = (int) Math.ceil((total + 0.0) / containerEntity.dumpSize()); + do { + list = list(em, cls, id, containerEntity.dumpSize()); + if (ListTools.isNotEmpty(list)) { + count = count + list.size(); + id = BeanUtils.getProperty(list.get(list.size() - 1), JpaObject.id_FIELDNAME); + if (StorageObject.class.isAssignableFrom(cls)) { + Path sub = directory.resolve(count + ""); + Files.createDirectories(sub); + binary(list, sub, storageMappings); + } + Files.write(directory.resolve(count + ".json"), + pureGsonDateFormated.toJson(list).getBytes(StandardCharsets.UTF_8)); + logger.print("dump data {}/{} part of data:{}.", loop++, btach, cls.getName()); + } + em.clear(); + } while (ListTools.isNotEmpty(list)); + catalog.put(cls.getName(), count); + } + + private void binary(List list, Path sub, StorageMappings storageMappings) throws Exception { + for (T t : list) { + StorageObject s = (StorageObject) t; + String name = s.getStorage(); + StorageMapping mapping = storageMappings.get(s.getClass(), name); + if (null == mapping && Config.dumpRestoreStorage().getExceptionInvalidStorage()) { + throw new ExceptionInvalidStorage(s); + } + if (null != mapping) { + Path p = sub.resolve(FilenameUtils.getName(s.path())); + if (s.existContent(mapping)) { + try (OutputStream out = Files.newOutputStream(p)) { + out.write(s.readContent(mapping)); + } + } + } } } - return list; } } \ No newline at end of file diff --git a/o2server/x_console/src/main/java/com/x/server/console/action/DumpStorage.java b/o2server/x_console/src/main/java/com/x/server/console/action/DumpStorage.java deleted file mode 100644 index 04a2235e2af61e9b89029a4cb4b8c695cc27e7bb..0000000000000000000000000000000000000000 --- a/o2server/x_console/src/main/java/com/x/server/console/action/DumpStorage.java +++ /dev/null @@ -1,261 +0,0 @@ -package com.x.server.console.action; - -import java.io.File; -import java.io.FileOutputStream; -import java.util.ArrayList; -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Objects; - -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; - -import org.apache.commons.beanutils.BeanUtils; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.openjpa.persistence.OpenJPAPersistence; - -import com.google.gson.Gson; -import com.x.base.core.container.factory.PersistenceXmlHelper; -import com.x.base.core.entity.JpaObject; -import com.x.base.core.entity.StorageObject; -import com.x.base.core.entity.annotation.ContainerEntity; -import com.x.base.core.entity.annotation.ContainerEntity.Reference; -import com.x.base.core.project.config.Config; -import com.x.base.core.project.config.DumpRestoreData; -import com.x.base.core.project.config.StorageMapping; -import com.x.base.core.project.config.StorageMappings; -import com.x.base.core.project.gson.XGsonBuilder; -import com.x.base.core.project.logger.Logger; -import com.x.base.core.project.logger.LoggerFactory; -import com.x.base.core.project.tools.DateTools; -import com.x.base.core.project.tools.DefaultCharset; -import com.x.base.core.project.tools.ListTools; - -public class DumpStorage { - - private static Logger logger = LoggerFactory.getLogger(DumpStorage.class); - - private Date start = new Date(); - - private File dir; - - private DumpRestoreStorageCatalog catalog; - - private Gson pureGsonDateFormated = XGsonBuilder.instance(); - - public boolean execute(String path) throws Exception { - if (StringUtils.isEmpty(path)) { - this.dir = new File(Config.base(), "local/dump/dumpStorage_" + DateTools.compact(this.start)); - } else { - this.dir = new File(path); - if (dir.getAbsolutePath().startsWith(Config.base())) { - logger.print("path can not in base directory."); - return false; - } - } - FileUtils.forceMkdir(this.dir); - this.catalog = new DumpRestoreStorageCatalog(); - - List storageContainerEntityNames = this.entities(); - List classNames = ListTools.includesExcludesWildcard(storageContainerEntityNames, - Config.dumpRestoreStorage().getIncludes(), Config.dumpRestoreStorage().getExcludes()); - logger.print("dump storage find {} data to dump, start at {}.", classNames.size(), DateTools.format(start)); - StorageMappings storageMappings = Config.storageMappings(); - File persistence = new File(Config.dir_local_temp_classes(), DateTools.compact(this.start) + "_dump.xml"); - PersistenceXmlHelper.write(persistence.getAbsolutePath(), classNames); - for (int i = 0; i < classNames.size(); i++) { - Class cls = (Class) Class.forName(classNames.get(i)); - EntityManagerFactory emf = OpenJPAPersistence.createEntityManagerFactory(cls.getName(), - persistence.getName(), PersistenceXmlHelper.properties(cls.getName(), Config.slice().getEnable())); - EntityManager em = emf.createEntityManager(); - try { - logger.print("dump storage({}/{}): {}, estimate count: {}, estimate size: {}M.", (i + 1), - classNames.size(), cls.getName(), this.estimateCount(em, cls), - (this.estimateSize(em, cls) / 1024 / 1024)); - this.dump(cls, em, storageMappings); - } finally { - em.close(); - emf.close(); - } - } - FileUtils.write(new File(dir, "catalog.json"), XGsonBuilder.instance().toJson(this.catalog), - DefaultCharset.charset); - logger.print( - "dump storage completed, directory: {}, count: {}, normal: {}, empty: {}, invalidStorage: {}, size: {}M, elapsed: {} minutes.", - dir.getAbsolutePath(), this.count(), this.normal(), this.empty(), this.invalidStorage(), - (this.size() / 1024 / 1024), (System.currentTimeMillis() - start.getTime()) / 1000 / 60); - return true; - } - - private Integer count() { - return this.catalog.values().stream().mapToInt(DumpRestoreStorageCatalogItem::getCount).sum(); - } - - private Long size() { - return this.catalog.values().stream().mapToLong(DumpRestoreStorageCatalogItem::getSize).sum(); - } - - private Long normal() { - return this.catalog.values().stream().mapToLong(DumpRestoreStorageCatalogItem::getNormal).sum(); - } - - private Long empty() { - return this.catalog.values().stream().mapToLong(DumpRestoreStorageCatalogItem::getEmpty).sum(); - } - - private Long invalidStorage() { - return this.catalog.values().stream().mapToLong(DumpRestoreStorageCatalogItem::getInvalidStorage).sum(); - } - - private long estimateCount(EntityManager em, Class cls) { - CriteriaBuilder cb = em.getCriteriaBuilder(); - CriteriaQuery cq = cb.createQuery(Long.class); - Root root = cq.from(cls); - cq.select(cb.count(root)); - return em.createQuery(cq).getSingleResult(); - } - - private long estimateSize(EntityManager em, Class cls) { - CriteriaBuilder cb = em.getCriteriaBuilder(); - CriteriaQuery cq = cb.createQuery(Long.class); - Root root = cq.from(cls); - cq.select(root.get("length")); - List list = em.createQuery(cq).getResultList(); - if (!list.isEmpty()) { - /** 上面的语句有可能返回的是null值,所以要先过滤 */ - return list.stream().filter(o -> null != o).mapToLong(Long::longValue).sum(); - } else { - return 0L; - } - } - - private void dump(Class cls, EntityManager em, StorageMappings storageMappings) - throws Exception { - /** 创建最终存储文件的目录 */ - File classDirectory = new File(dir, cls.getName()); - FileUtils.forceMkdir(classDirectory); - FileUtils.cleanDirectory(classDirectory); - int count = 0; - long size = 0L; - int normal = 0; - int invalidStorage = 0; - int empty = 0; - String id = ""; - String name = ""; - List list = null; - File directory = null; - StorageMapping mapping = null; - List normalList = null; - List emptyList = null; - List invalidStorageList = null; - ContainerEntity containerEntity = cls.getAnnotation(ContainerEntity.class); - do { - list = this.list(em, cls, id, containerEntity.dumpSize()); - if (ListTools.isNotEmpty(list)) { - count += list.size(); - directory = new File(classDirectory, Integer.toString(count)); - FileUtils.forceMkdir(directory); - FileUtils.cleanDirectory(directory); - normalList = new ArrayList(); - emptyList = new ArrayList(); - invalidStorageList = new ArrayList(); - for (T t : list) { - name = t.getStorage(); - mapping = storageMappings.get(cls, name); - if (StringUtils.isNotEmpty(name)) { - if (null == mapping && Config.dumpRestoreStorage().getExceptionInvalidStorage()) { - throw new Exception("can not find storageMapping class: " + cls.getName() + ", storage: " - + name + ", id: " + t.getId() + ", name: " + t.getName() - + ", set exceptionInvalidStorage to false will ignore item."); - } - } - if (null != mapping) { - File file = new File(directory, FilenameUtils.getName(t.path())); - if (t.existContent(mapping)) { - try (FileOutputStream fos = new FileOutputStream(file)) { - size += t.readContent(mapping, fos); - normalList.add(t); - normal++; - } - } else { - emptyList.add(t); - empty++; - } - } else { - invalidStorageList.add(t); - invalidStorage++; - } - } - id = BeanUtils.getProperty(list.get(list.size() - 1), JpaObject.id_FIELDNAME); - File file = new File(classDirectory, count + ".json"); - this.dumpWrite(file, normalList, emptyList, invalidStorageList); - } - em.clear(); - } while (ListTools.isNotEmpty(list)); - DumpRestoreStorageCatalogItem item = new DumpRestoreStorageCatalogItem(); - item.setCount(count); - item.setNormal(normal); - item.setEmpty(empty); - item.setSize(size); - item.setInvalidStorage(invalidStorage); - this.catalog.put(cls.getName(), item); - logger.print( - "dumped storage: " + cls.getName() + ", count: " + count + ", normal: " + normal + ", invalidStorage: " - + invalidStorage + ", empty: " + empty + ", size: " + (size / 1024 / 1024) + "M."); - } - - private void dumpWrite(File file, List normalList, List emptyList, - List invalidStorageList) throws Exception { - LinkedHashMap> o = new LinkedHashMap<>(); - o.put("normals", normalList); - o.put("emptys", emptyList); - o.put("invalidStorages", invalidStorageList); - FileUtils.write(file, pureGsonDateFormated.toJson(o), DefaultCharset.charset); - } - - private List list(EntityManager em, Class cls, String id, Integer size) { - CriteriaBuilder cb = em.getCriteriaBuilder(); - CriteriaQuery cq = cb.createQuery(cls); - Root root = cq.from(cls); - Predicate p = cb.conjunction(); - if (StringUtils.isNotEmpty(id)) { - p = cb.greaterThan(root.get("id"), id); - } - cq.select(root).where(p).orderBy(cb.asc(root.get("id"))); - return em.createQuery(cq).setMaxResults(size).getResultList(); - } - - /** - * 根据设置的模式不同输出需要dump的entity className - * - * @return - * @throws Exception - */ - @SuppressWarnings("unchecked") - private List entities() throws Exception { - List list = new ArrayList<>(); - if (StringUtils.equals(Config.dumpRestoreData().getMode(), DumpRestoreData.TYPE_FULL)) { - list.addAll((List) Config.resource(Config.RESOURCE_STORAGECONTAINERENTITYNAMES)); - return list; - } - for (String str : (List) Config.resource(Config.RESOURCE_STORAGECONTAINERENTITYNAMES)) { - Class cls = Class.forName(str); - ContainerEntity containerEntity = cls.getAnnotation(ContainerEntity.class); - if (Objects.equals(containerEntity.reference(), Reference.strong)) { - list.add(str); - } - } - return list; - } - - public static class Item { - - } -} \ No newline at end of file diff --git a/o2server/x_console/src/main/java/com/x/server/console/action/ExceptionDirectoryNotExist.java b/o2server/x_console/src/main/java/com/x/server/console/action/ExceptionDirectoryNotExist.java new file mode 100644 index 0000000000000000000000000000000000000000..f49f129740ffb75585a6e1fb5591a873b241fcb5 --- /dev/null +++ b/o2server/x_console/src/main/java/com/x/server/console/action/ExceptionDirectoryNotExist.java @@ -0,0 +1,15 @@ +package com.x.server.console.action; + +import java.nio.file.Path; + +import com.x.base.core.project.exception.PromptException; + +class ExceptionDirectoryNotExist extends PromptException { + + private static final long serialVersionUID = -5515077418025884395L; + + ExceptionDirectoryNotExist(Path path) { + super("directory not exist: {}.", path.toString()); + } + +} diff --git a/o2server/x_console/src/main/java/com/x/server/console/action/ExceptionFileNotExist.java b/o2server/x_console/src/main/java/com/x/server/console/action/ExceptionFileNotExist.java new file mode 100644 index 0000000000000000000000000000000000000000..cfdd2169b81d62cb887bddc4dd798bdd802c7bff --- /dev/null +++ b/o2server/x_console/src/main/java/com/x/server/console/action/ExceptionFileNotExist.java @@ -0,0 +1,16 @@ +package com.x.server.console.action; + + +import java.nio.file.Path; + +import com.x.base.core.project.exception.PromptException; + +class ExceptionFileNotExist extends PromptException { + + private static final long serialVersionUID = -5515077418025884395L; + + ExceptionFileNotExist(Path path) { + super("file not exist: {}.", path.toString()); + } + +} diff --git a/o2server/x_console/src/main/java/com/x/server/console/action/ExceptionInvalidStorage.java b/o2server/x_console/src/main/java/com/x/server/console/action/ExceptionInvalidStorage.java new file mode 100644 index 0000000000000000000000000000000000000000..21275660e91a24c1c05b24f92437fba96351172b --- /dev/null +++ b/o2server/x_console/src/main/java/com/x/server/console/action/ExceptionInvalidStorage.java @@ -0,0 +1,16 @@ +package com.x.server.console.action; + +import com.x.base.core.entity.StorageObject; +import com.x.base.core.project.exception.PromptException; + +class ExceptionInvalidStorage extends PromptException { + + private static final long serialVersionUID = -5515077418025884395L; + + ExceptionInvalidStorage(StorageObject storageObject) { + super("can not find storageMapping class: " + storageObject.getClass().getName() + ", storage: " + + storageObject.getStorage() + ", id: " + storageObject.getId() + ", name: " + storageObject.getName() + + ", set exceptionInvalidStorage to false will ignore item."); + } + +} diff --git a/o2server/x_console/src/main/java/com/x/server/console/action/ExceptionMappingNotExist.java b/o2server/x_console/src/main/java/com/x/server/console/action/ExceptionMappingNotExist.java new file mode 100644 index 0000000000000000000000000000000000000000..13038869914c5e03acb066b0974ec2c9099ea6ab --- /dev/null +++ b/o2server/x_console/src/main/java/com/x/server/console/action/ExceptionMappingNotExist.java @@ -0,0 +1,13 @@ +package com.x.server.console.action; + +import com.x.base.core.project.exception.PromptException; + +class ExceptionMappingNotExist extends PromptException { + + private static final long serialVersionUID = -5515077418025884395L; + + ExceptionMappingNotExist() { + super("can not find storageMapping"); + } + +} diff --git a/o2server/x_console/src/main/java/com/x/server/console/action/RestoreData.java b/o2server/x_console/src/main/java/com/x/server/console/action/RestoreData.java index 03f5bd5a7f2605efc742922699ed26fcf912bd60..4f6b7035b2f7ef984caa794c235dad9dfaa72ad6 100644 --- a/o2server/x_console/src/main/java/com/x/server/console/action/RestoreData.java +++ b/o2server/x_console/src/main/java/com/x/server/console/action/RestoreData.java @@ -1,176 +1,249 @@ package com.x.server.console.action; -import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.Date; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Stream; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; -import javax.persistence.FlushModeType; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Root; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.openjpa.persistence.OpenJPAPersistence; + import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.x.base.core.container.factory.PersistenceXmlHelper; import com.x.base.core.entity.JpaObject; +import com.x.base.core.entity.StorageObject; +import com.x.base.core.entity.annotation.ContainerEntity; import com.x.base.core.project.config.Config; +import com.x.base.core.project.config.StorageMapping; +import com.x.base.core.project.config.StorageMappings; import com.x.base.core.project.gson.XGsonBuilder; import com.x.base.core.project.logger.Logger; import com.x.base.core.project.logger.LoggerFactory; -import com.x.base.core.project.tools.BaseTools; import com.x.base.core.project.tools.DateTools; -import com.x.base.core.project.tools.DefaultCharset; import com.x.base.core.project.tools.ListTools; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.openjpa.persistence.OpenJPAPersistence; - -/** - * @author zhourui - */ public class RestoreData { private static Logger logger = LoggerFactory.getLogger(RestoreData.class); - private Date start = new Date(); - - private File dir; - - private DumpRestoreDataCatalog catalog; - - private Gson pureGsonDateFormated = XGsonBuilder.instance(); - public boolean execute(String path) throws Exception { + Date start = new Date(); + Path dir; if (StringUtils.isEmpty(path)) { - logger.print("path is empty."); + logger.warn("path is empty."); } if (BooleanUtils.isTrue(DateTools.isCompactDateTime(path))) { - this.dir = new File(Config.base(), "local/dump/dumpData_" + path); - this.catalog = BaseTools.readConfigObject("local/dump/dumpData_" + path + "/catalog.json", - DumpRestoreDataCatalog.class); + dir = Paths.get(Config.base(), "local", "dump", "dumpData_" + path); } else { - this.dir = new File(path); - if (!(this.dir.exists() && this.dir.isDirectory())) { - logger.print("dir not exist: {}.", path); + dir = Paths.get(path); + if ((!Files.exists(dir)) || (!Files.isDirectory(dir))) { + logger.warn("directory not exist: {}.", path); return false; - } else if (StringUtils.startsWith(dir.getAbsolutePath(), Config.base())) { - logger.print("path can not in base directory."); + } else if (dir.startsWith(Paths.get(Config.base()))) { + logger.warn("path can not in base directory."); return false; } } - this.catalog = XGsonBuilder.instance().fromJson( - FileUtils.readFileToString(new File(dir, "catalog.json"), DefaultCharset.charset_utf_8), - DumpRestoreDataCatalog.class); - return this.execute(); + Thread thread = new Thread(new RunnableImpl(dir, start)); + thread.start(); + return true; } - @SuppressWarnings("unchecked") - public boolean execute() throws Exception { - List containerEntityNames = new ArrayList<>(); - containerEntityNames.addAll((List) Config.resource(Config.RESOURCE_CONTAINERENTITYNAMES)); - List classNames = new ArrayList<>(); - classNames.addAll(this.catalog.keySet()); - classNames = ListTools.includesExcludesWildcard(classNames, Config.dumpRestoreData().getIncludes(), - Config.dumpRestoreData().getExcludes()); - classNames = ListTools.includesExcludesWildcard(containerEntityNames, classNames, null); - - logger.print("find: {} data to restore, path: {}.", classNames.size(), this.dir.getAbsolutePath()); - File persistence = new File(Config.dir_local_temp_classes(), DateTools.compact(this.start) + "_dump.xml"); - PersistenceXmlHelper.write(persistence.getAbsolutePath(), classNames); - long count = 0; - for (int i = 0; i < classNames.size(); i++) { - Class cls = (Class) Class.forName(classNames.get(i)); + public class RunnableImpl implements Runnable { + + private Path dir; + private Date start; + private DumpRestoreDataCatalog catalog; + private Gson gson; + + public RunnableImpl(Path dir, Date start) throws IOException { + this.dir = dir; + this.start = start; + this.catalog = new DumpRestoreDataCatalog(); + this.gson = XGsonBuilder.instance(); + Path path = dir.resolve("catalog.json"); + this.catalog = XGsonBuilder.instance().fromJson( + new String(Files.readAllBytes(path), StandardCharsets.UTF_8), DumpRestoreDataCatalog.class); + } + + @Override + public void run() { + try { + List classNames = this.entities(); + logger.print("find: {} data to restore, path: {}.", classNames.size(), this.dir.toString()); + Path xml = Paths.get(Config.dir_local_temp_classes().getAbsolutePath(), + DateTools.compact(start) + "_restore.xml"); + PersistenceXmlHelper.write(xml.toString(), classNames); + Stream stream = BooleanUtils.isTrue(Config.dumpRestoreData().getParallel()) + ? classNames.parallelStream() + : classNames.stream(); + AtomicInteger idx = new AtomicInteger(1); + AtomicLong total = new AtomicLong(0); + stream.forEach(className -> { + String nameOfThread = Thread.currentThread().getName(); + try { + Thread.currentThread().setName(RestoreData.class.getName() + ":" + className); + @SuppressWarnings("unchecked") + Class cls = (Class) Class.forName(className); + logger.print("restore data({}/{}): {}.", idx.getAndAdd(1), classNames.size(), cls.getName()); + long size = restore(cls, xml); + total.getAndAdd(size); + } catch (Exception e) { + logger.error(new Exception(String.format("restore:%s error.", className), e)); + } finally { + Thread.currentThread().setName(nameOfThread); + } + }); + logger.print("restore data completed, directory: {}, count: {}, total: {}, elapsed: {} minutes.", + dir.toString(), idx.get(), total.longValue(), + (System.currentTimeMillis() - start.getTime()) / 1000 / 60); + } catch (Exception e) { + logger.error(e); + } + } + + @SuppressWarnings("unchecked") + private List entities() throws Exception { + List containerEntityNames = new ArrayList<>(); + containerEntityNames.addAll((List) Config.resource(Config.RESOURCE_CONTAINERENTITYNAMES)); + List classNames = new ArrayList<>(); + classNames.addAll(this.catalog.keySet()); + classNames = ListTools.includesExcludesWildcard(classNames, Config.dumpRestoreData().getIncludes(), + Config.dumpRestoreData().getExcludes()); + return ListTools.includesExcludesWildcard(containerEntityNames, classNames, null); + } + + private long restore(Class cls, Path xml) throws Exception { EntityManagerFactory emf = OpenJPAPersistence.createEntityManagerFactory(cls.getName(), - persistence.getName(), PersistenceXmlHelper.properties(cls.getName(), Config.slice().getEnable())); - if (emf != null) { - EntityManager em = emf.createEntityManager(); - em.setFlushMode(FlushModeType.COMMIT); - try { - logger.print("restore data({}/{}): {}, count: {}.", (i + 1), classNames.size(), cls.getName(), - catalog.get(cls.getName())); - count = count + this.store(cls, em); - } finally { - em.close(); - emf.close(); + xml.getFileName().toString(), + PersistenceXmlHelper.properties(cls.getName(), Config.slice().getEnable())); + AtomicLong count = new AtomicLong(0); + AtomicInteger batch = new AtomicInteger(1); + try { + Path directory = dir.resolve(cls.getName()); + if ((!Files.exists(directory)) || (!Files.isDirectory(directory))) { + throw new ExceptionDirectoryNotExist(directory); } - } else { - logger.warn("can not create 'EntityManagerFactory' for Entity:[" + cls.getName() + "]"); + StorageMappings storageMappings = Config.storageMappings(); + this.clean(cls, emf, storageMappings, cls.getAnnotation(ContainerEntity.class)); + List paths = this.list(directory); + paths.stream().forEachOrdered(o -> { + logger.print("restore {}/{} part of data:{}.", batch.getAndAdd(1), paths.size(), cls.getName()); + EntityManager em = null; + try { + em = emf.createEntityManager(); + em.getTransaction().begin(); + JsonArray raws = this.convert(o); + for (JsonElement json : raws) { + Object t = gson.fromJson(json, cls); + if (StorageObject.class.isAssignableFrom(cls)) { + Path sub = o.resolveSibling(FilenameUtils.getBaseName(o.getFileName().toString())); + this.binary(t, cls, sub, storageMappings); + } + em.persist(t); + count.getAndAdd(1); + } + em.getTransaction().commit(); + em.clear(); + } catch (Exception e) { + logger.error(new Exception(String.format("restore error with file:%s.", o.toString()), e)); + } finally { + em.close(); + } + }); + logger.print("restore data: {} completed, count: {}.", cls.getName(), count.intValue()); + } catch (Exception e) { + logger.error(e); + } finally { + emf.close(); } + return count.longValue(); } - logger.print("restore data completed, total count: {}, elapsed: {} minutes.", count, - (System.currentTimeMillis() - start.getTime()) / 1000 / 60); - return true; - } - private long store(Class cls, EntityManager em) throws Exception { - File directory = new File(this.dir, cls.getName()); - if ((!directory.exists()) || (!directory.isDirectory())) { - throw new Exception("can not find directory: " + directory.getAbsolutePath() + "."); + private List list(Path directory) throws IOException { + List list = new ArrayList<>(); + try (Stream stream = Files.list(directory)) { + stream.filter(p -> StringUtils.endsWithIgnoreCase(p.getFileName().toString(), ".json")) + .sorted((Path p1, Path p2) -> { + Integer i1 = Integer.parseInt(FilenameUtils.getBaseName(p1.getFileName().toString())); + Integer i2 = Integer.parseInt(FilenameUtils.getBaseName(p2.getFileName().toString())); + return i1.compareTo(i2); + }).forEach(list::add); + } + return list; } - long count = 0; - List files = new ArrayList<>(FileUtils.listFiles(directory, new String[] { "json" }, false)); - /** 对文件进行排序,和dump的时候的顺序保持一直 */ - Collections.sort(files, new Comparator() { - public int compare(File o1, File o2) { - String n1 = FilenameUtils.getBaseName(o1.getName()); - String n2 = FilenameUtils.getBaseName(o2.getName()); - Integer i1 = Integer.parseInt(n1); - Integer i2 = Integer.parseInt(n2); - return i1.compareTo(i2); + + @SuppressWarnings("unchecked") + private void binary(Object o, Class cls, Path sub, StorageMappings storageMappings) throws Exception { + StorageObject so = (StorageObject) o; + StorageMapping mapping = null; + if (BooleanUtils.isTrue(Config.dumpRestoreData().getRedistribute())) { + mapping = storageMappings.random((Class) cls); + } else { + mapping = storageMappings.get((Class) cls, so.getStorage()); + } + if (null == mapping) { + throw new ExceptionMappingNotExist(); + } + Path path = sub.resolve(Paths.get(so.path()).getFileName()); + if (!Files.exists(path)) { + throw new ExceptionFileNotExist(path); } - }); - /** 尽量在最后进行清空操作 */ - this.clean(cls, em); - File file = null; - for (int i = 0; i < files.size(); i++) { - file = files.get(i); - logger.print("restoring {}/{} part of data:{}.", (i + 1), files.size(), cls.getName()); - JsonArray raws = this.convert(file); - em.getTransaction().begin(); - for (JsonElement o : raws) { - T t = pureGsonDateFormated.fromJson(o, cls); - em.persist(t); - count++; + try (InputStream input = Files.newInputStream(path)) { + so.saveContent(mapping, input, so.getName()); } - em.getTransaction().commit(); - em.clear(); } - System.out.println("restore data: " + cls.getName() + " completed, count: " + count + "."); - return count; - - } - private JsonArray convert(File file) throws Exception { - /* 必须先转换成 jsonElement 不能直接转成泛型T,如果直接转会有类型不匹配比如Integer变成了Double */ - String json = FileUtils.readFileToString(file, DefaultCharset.charset); - JsonElement jsonElement = pureGsonDateFormated.fromJson(json, JsonElement.class); - return jsonElement.getAsJsonArray(); - } + private JsonArray convert(Path path) throws IOException { + // 必须先转换成 jsonElement 不能直接转成泛型T,如果直接转会有类型不匹配比如Integer变成了Double + String json = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); + JsonElement jsonElement = gson.fromJson(json, JsonElement.class); + return jsonElement.getAsJsonArray(); + } - private void clean(Class cls, EntityManager em) throws Exception { - List list = null; - do { - if (ListTools.isNotEmpty(list)) { - em.getTransaction().begin(); - for (T t : list) { - em.remove(t); + private void clean(Class cls, EntityManagerFactory emf, StorageMappings storageMappings, + ContainerEntity containerEntity) throws Exception { + EntityManager em = emf.createEntityManager(); + List list = null; + do { + if (ListTools.isNotEmpty(list)) { + em.getTransaction().begin(); + for (T t : list) { + em.remove(t); + if (StorageObject.class.isAssignableFrom(cls)) { + StorageObject so = (StorageObject) t; + @SuppressWarnings("unchecked") + StorageMapping mapping = storageMappings.get((Class) cls, so.getStorage()); + if (null != mapping) { + so.deleteContent(mapping); + } + } + } + em.getTransaction().commit(); } - em.getTransaction().commit(); - } - CriteriaBuilder cb = em.getCriteriaBuilder(); - CriteriaQuery cq = cb.createQuery(cls); - Root root = cq.from(cls); - cq.select(root); - list = em.createQuery(cq).setMaxResults(Config.dumpRestoreData().getBatchSize()).getResultList(); - } while (ListTools.isNotEmpty(list)); + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(cls); + Root root = cq.from(cls); + list = em.createQuery(cq.select(root)).setMaxResults(containerEntity.dumpSize()).getResultList(); + } while (ListTools.isNotEmpty(list)); + } } } \ No newline at end of file diff --git a/o2server/x_console/src/main/java/com/x/server/console/action/RestoreStorage.java b/o2server/x_console/src/main/java/com/x/server/console/action/RestoreStorage.java deleted file mode 100644 index 9162e70857a1ef5f0fb631aa3ac8e57fc5143929..0000000000000000000000000000000000000000 --- a/o2server/x_console/src/main/java/com/x/server/console/action/RestoreStorage.java +++ /dev/null @@ -1,206 +0,0 @@ -package com.x.server.console.action; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.List; - -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.FlushModeType; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Root; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.openjpa.persistence.OpenJPAPersistence; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.x.base.core.container.factory.PersistenceXmlHelper; -import com.x.base.core.entity.StorageObject; -import com.x.base.core.project.config.Config; -import com.x.base.core.project.config.StorageMapping; -import com.x.base.core.project.config.StorageMappings; -import com.x.base.core.project.gson.XGsonBuilder; -import com.x.base.core.project.logger.Logger; -import com.x.base.core.project.logger.LoggerFactory; -import com.x.base.core.project.tools.BaseTools; -import com.x.base.core.project.tools.DateTools; -import com.x.base.core.project.tools.DefaultCharset; -import com.x.base.core.project.tools.ListTools; - -/** - * @author zhourui - */ - -public class RestoreStorage { - - private static Logger logger = LoggerFactory.getLogger(RestoreStorage.class); - - private Date start = new Date(); - - private File dir; - - private DumpRestoreStorageCatalog catalog; - - private Gson pureGsonDateFormated = XGsonBuilder.instance(); - - public boolean execute(final String path) throws Exception { - if (StringUtils.isEmpty(path)) { - logger.print("path is empty."); - } - if (BooleanUtils.isTrue(DateTools.isCompactDateTime(path))) { - this.dir = new File(Config.base(), "local/dump/dumpStorage_" + path); - this.catalog = BaseTools.readConfigObject("local/dump/dumpStorage_" + path + "/catalog.json", - DumpRestoreStorageCatalog.class); - } else { - this.dir = new File(path); - if (!(this.dir.exists() && this.dir.isDirectory())) { - logger.print("dir not exist: {}.", path); - return false; - } else if (StringUtils.startsWith(dir.getAbsolutePath(), Config.base())) { - logger.print("path can not in base directory."); - return false; - } - this.catalog = XGsonBuilder.instance().fromJson(FileUtils - .readFileToString(new File(dir.getAbsolutePath(), "catalog.json"), DefaultCharset.charset_utf_8), - DumpRestoreStorageCatalog.class); - } - return this.execute(); - } - - private boolean execute() throws Exception { - final List storageContainerEntityNames = new ArrayList<>(); - storageContainerEntityNames.addAll((List) Config.resource(Config.RESOURCE_STORAGECONTAINERENTITYNAMES)); - List classNames = new ArrayList<>(); - classNames.addAll(this.catalog.keySet()); - classNames = ListTools.includesExcludesWildcard(classNames, Config.dumpRestoreStorage().getIncludes(), - Config.dumpRestoreStorage().getExcludes()); - logger.print("restore storage find {} to restore.", classNames.size()); - final File persistence = new File(Config.dir_local_temp_classes(), DateTools.compact(this.start) + "_dump.xml"); - PersistenceXmlHelper.write(persistence.getAbsolutePath(), classNames); - final StorageMappings storageMappings = Config.storageMappings(); - int count = 0; - for (int i = 0; i < classNames.size(); i++) { - final Class cls = (Class) Class.forName(classNames.get(i)); - final EntityManagerFactory emf = OpenJPAPersistence.createEntityManagerFactory(cls.getName(), - persistence.getName(), PersistenceXmlHelper.properties(cls.getName(), Config.slice().getEnable())); - final EntityManager em = emf.createEntityManager(); - em.setFlushMode(FlushModeType.COMMIT); - try { - final DumpRestoreStorageCatalogItem item = this.catalog.get(cls.getName()); - logger.print( - "restore storage({}/{}): {}, count: {}, normal: {} will be restore, invalidStorage: {} and empty: {} will be ignore, size: {}M.", - (i + 1), classNames.size(), cls.getName(), item.getCount(), item.getNormal(), - item.getInvalidStorage(), item.getEmpty(), (item.getSize() / 1024 / 1024)); - count += this.store(cls, em, storageMappings); - } finally { - em.close(); - emf.close(); - } - logger.print("restore storage completed, total count: {}, elapsed: {} minutes.", count, - (System.currentTimeMillis() - start.getTime()) / 1000 / 60); - } - return false; - } - - private long store(final Class cls, final EntityManager em, - final StorageMappings storageMappings) throws Exception { - final File classDirectory = new File(this.dir, cls.getName()); - if ((!classDirectory.exists()) || (!classDirectory.isDirectory())) { - throw new Exception("can not find directory: " + classDirectory.getAbsolutePath() + "."); - } - long count = 0; - final List files = new ArrayList( - FileUtils.listFiles(classDirectory, new String[] { "json" }, false)); - /** 对文件进行排序,和dump的时候的顺序保持一直 */ - Collections.sort(files, new Comparator() { - public int compare(final File o1, final File o2) { - final String n1 = FilenameUtils.getBaseName(o1.getName()); - final String n2 = FilenameUtils.getBaseName(o2.getName()); - final Integer i1 = Integer.parseInt(n1); - final Integer i2 = Integer.parseInt(n2); - return i1.compareTo(i2); - } - }); - /** 尽量将删除的工作放在后面 */ - this.clean(cls, em, storageMappings); - StorageMapping mapping = null; - File file = null; - for (int i = 0; i < files.size(); i++) { - file = files.get(i); - /** 必须先转换成 jsonElement 不能直接转成泛型T,如果直接转会有类型不匹配比如Integer变成了Double */ - logger.print("restoring " + (i + 1) + "/" + files.size() + " part of storage: " + cls.getName() + "."); - final JsonArray raws = this.convert(file); - if (null != raws) { - em.getTransaction().begin(); - for (final JsonElement o : raws) { - final T t = pureGsonDateFormated.fromJson(o, cls); - if (Config.dumpRestoreStorage().getRedistribute()) { - mapping = storageMappings.random(cls); - } else { - mapping = storageMappings.get(cls, t.getStorage()); - } - if (null == mapping) { - throw new Exception( - "can not find storageMapping class: " + cls.getName() + ", name:" + t.getName()); - } - final File source = new File(classDirectory, FilenameUtils.getBaseName(file.getName()) - + StorageObject.PATHSEPARATOR + FilenameUtils.getName(t.path())); - try (FileInputStream input = new FileInputStream(source)) { - t.saveContent(mapping, input, t.getName()); - } - em.persist(t); - count++; - } - em.getTransaction().commit(); - em.clear(); - Runtime.getRuntime().gc(); - } - } - return count; - } - - private JsonArray convert(final File file) throws IOException { - /** 这里不进行判断,因为格式是严格约定的,出现意外应该先报错停止 */ - final String json = FileUtils.readFileToString(file, DefaultCharset.charset); - final JsonElement jsonElement = pureGsonDateFormated.fromJson(json, JsonElement.class); - final JsonObject jsonObject = jsonElement.getAsJsonObject(); - return jsonObject.get("normals").getAsJsonArray(); - } - - private void clean(final Class cls, final EntityManager em, - final StorageMappings storageMappings) throws Exception { - List list = null; - StorageMapping mapping = null; - do { - if (ListTools.isNotEmpty(list)) { - em.getTransaction().begin(); - for (final T t : list) { - mapping = storageMappings.get(cls, t.getStorage()); - if (null != mapping) { - t.deleteContent(mapping); - } - em.remove(t); - } - em.getTransaction().commit(); - } - final CriteriaBuilder cb = em.getCriteriaBuilder(); - final CriteriaQuery cq = cb.createQuery(cls); - final Root root = cq.from(cls); - cq.select(root); - list = em.createQuery(cq).setMaxResults(Config.dumpRestoreData().getBatchSize()).getResultList(); - } while (ListTools.isNotEmpty(list)); - } - -} \ No newline at end of file diff --git a/o2server/x_file_core_entity/src/main/java/com/x/file/core/entity/personal/Attachment.java b/o2server/x_file_core_entity/src/main/java/com/x/file/core/entity/personal/Attachment.java index abbf1975ac00ea1d98422beca5333c439663c563..33b584e5efce1cc7729b1dac686a34284b0ccccd 100644 --- a/o2server/x_file_core_entity/src/main/java/com/x/file/core/entity/personal/Attachment.java +++ b/o2server/x_file_core_entity/src/main/java/com/x/file/core/entity/personal/Attachment.java @@ -36,7 +36,7 @@ import com.x.base.core.project.annotation.FieldDescribe; import com.x.base.core.project.tools.DateTools; import com.x.file.core.entity.PersistenceProperties; -@ContainerEntity(dumpSize = 1000, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) +@ContainerEntity(dumpSize = 5, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) @Entity @Table(name = PersistenceProperties.Personal.Attachment.table, uniqueConstraints = { @UniqueConstraint(name = PersistenceProperties.Personal.Attachment.table + JpaObject.IndexNameMiddle diff --git a/o2server/x_meeting_core_entity/src/main/java/com/x/meeting/core/entity/Attachment.java b/o2server/x_meeting_core_entity/src/main/java/com/x/meeting/core/entity/Attachment.java index 1a4a76f26f84c82f6a7bb6c074499a28201f3f6b..586db8a2df91fe746eda5c1cd8a012c5a7fde29d 100644 --- a/o2server/x_meeting_core_entity/src/main/java/com/x/meeting/core/entity/Attachment.java +++ b/o2server/x_meeting_core_entity/src/main/java/com/x/meeting/core/entity/Attachment.java @@ -23,7 +23,7 @@ import com.x.base.core.entity.annotation.CheckPersist; import com.x.base.core.entity.annotation.ContainerEntity; import com.x.base.core.project.annotation.FieldDescribe; -@ContainerEntity(dumpSize = 1000, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) +@ContainerEntity(dumpSize = 5, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) @Entity @Table(name = PersistenceProperties.Attachment.table, uniqueConstraints = { @UniqueConstraint(name = PersistenceProperties.Attachment.table + JpaObject.IndexNameMiddle diff --git a/o2server/x_message_core_entity/src/main/java/com/x/message/core/entity/IMMsgFile.java b/o2server/x_message_core_entity/src/main/java/com/x/message/core/entity/IMMsgFile.java index 90c6f86b0d5f57577d2c87bb6f4fdfdab6167670..745b16524bcd1dcff918f2c725bb751bda8fc090 100644 --- a/o2server/x_message_core_entity/src/main/java/com/x/message/core/entity/IMMsgFile.java +++ b/o2server/x_message_core_entity/src/main/java/com/x/message/core/entity/IMMsgFile.java @@ -20,7 +20,7 @@ import java.util.Date; */ -@ContainerEntity(dumpSize = 1000, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) +@ContainerEntity(dumpSize = 5, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) @Entity @Table(name = PersistenceProperties.IMMsgFile.table, uniqueConstraints = { @UniqueConstraint(name = PersistenceProperties.IMMsgFile.table + JpaObject.IndexNameMiddle diff --git a/o2server/x_okr_core_entity/src/main/java/com/x/okr/entity/OkrAttachmentFileInfo.java b/o2server/x_okr_core_entity/src/main/java/com/x/okr/entity/OkrAttachmentFileInfo.java index 3323bd638c3352a2a2e3c0e8224acfbcaa3dbd30..61d33a2c223844566372c8b077cd61f1cf2bb77f 100644 --- a/o2server/x_okr_core_entity/src/main/java/com/x/okr/entity/OkrAttachmentFileInfo.java +++ b/o2server/x_okr_core_entity/src/main/java/com/x/okr/entity/OkrAttachmentFileInfo.java @@ -30,7 +30,7 @@ import com.x.base.core.project.tools.DateTools; * 附件文件信息管理表 * */ -@ContainerEntity(dumpSize = 1000, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) +@ContainerEntity(dumpSize = 5, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) @Entity @Table(name = PersistenceProperties.OkrAttachmentFileInfo.table, uniqueConstraints = { @UniqueConstraint(name = PersistenceProperties.OkrAttachmentFileInfo.table + JpaObject.IndexNameMiddle diff --git a/o2server/x_processplatform_core_entity/src/main/java/com/x/processplatform/core/entity/content/Attachment.java b/o2server/x_processplatform_core_entity/src/main/java/com/x/processplatform/core/entity/content/Attachment.java index 30a8fc5a7d5c8dd67427540ba6393cdd158267cb..1ec45c5cc8d75efa3347a21eb9fc940271d3f8a7 100644 --- a/o2server/x_processplatform_core_entity/src/main/java/com/x/processplatform/core/entity/content/Attachment.java +++ b/o2server/x_processplatform_core_entity/src/main/java/com/x/processplatform/core/entity/content/Attachment.java @@ -38,7 +38,7 @@ import com.x.base.core.project.tools.DateTools; import com.x.processplatform.core.entity.PersistenceProperties; import com.x.processplatform.core.entity.element.ActivityType; -@ContainerEntity(dumpSize = 1000, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) +@ContainerEntity(dumpSize = 5, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) @Entity @Table(name = PersistenceProperties.Content.Attachment.table, uniqueConstraints = { @UniqueConstraint(name = PersistenceProperties.Content.Attachment.table + JpaObject.IndexNameMiddle diff --git a/o2server/x_program_center_core_entity/src/main/java/com/x/program/center/core/entity/Structure.java b/o2server/x_program_center_core_entity/src/main/java/com/x/program/center/core/entity/Structure.java index 7d14cf4610108bb1f364613567037a7c53feb227..1a27fdb178b9109aaf86de481087a0b444340675 100644 --- a/o2server/x_program_center_core_entity/src/main/java/com/x/program/center/core/entity/Structure.java +++ b/o2server/x_program_center_core_entity/src/main/java/com/x/program/center/core/entity/Structure.java @@ -25,7 +25,7 @@ import com.x.base.core.project.annotation.FieldDescribe; import com.x.base.core.project.tools.StringTools; @Entity -@ContainerEntity(dumpSize = 1000, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) +@ContainerEntity(dumpSize = 10, type = ContainerEntity.Type.content, reference = ContainerEntity.Reference.strong) @Table(name = PersistenceProperties.Structure.table, uniqueConstraints = { @UniqueConstraint(name = PersistenceProperties.Structure.table + JpaObject.IndexNameMiddle + JpaObject.DefaultUniqueConstraintSuffix, columnNames = { JpaObject.IDCOLUMN,