JPA 2 从元模型删除/插入订单
我正在尝试使用 JPA2 元数据来确定从数据库插入/删除行的顺序,因此约束不是问题(稍后在 Java 代码中使用)。这是使用 JPA 的备份/恢复方法的一部分。
这是我的方法:
- 按关系/约束的数量对表进行分组(仅考虑一对多和一对一)
- 具有零个实例的表(按照#1)可以添加/删除记录而不会出现问题
- 具有一个实例的表只要相关表已经“准备就绪”,就可以毫无问题地添加/删除记录
准备就绪 所谓就绪,我的意思是所有相关表记录都已填充,因此外键对于插入有效,或者存在没有其他表引用此表中的记录。
我确信这将是某种递归方法,但我陷入了困境。任何帮助都是非常受欢迎的。
这是到目前为止的代码:
/**
* Get the execution order from the EntityManager meta data model.
*
* This will fail if the EntityManager is not JP2 compliant
* @param em EntityManager to get the metadata from
* @return ArrayList containing the order to process tables
*/
protected static ArrayList<String> getProcessingOrder(EntityManager em) {
ArrayList<String> tables = new ArrayList<String>();
//This holds the amount of relationships and the tables with that same amount
HashMap<Integer, ArrayList<String>> tableStats = new HashMap<Integer, ArrayList<String>>();
//This holds the table and the tables referenced by it
HashMap<String, ArrayList<String>> references = new HashMap<String, ArrayList<String>>();
for (EntityType et : em.getMetamodel().getEntities()) {
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINER, et.getName());
int amount = 0;
Iterator<SingularAttribute> sIterator = et.getSingularAttributes().iterator();
while (sIterator.hasNext()) {
SingularAttribute next = sIterator.next();
switch (next.getPersistentAttributeType()) {
case BASIC:
case ELEMENT_COLLECTION:
case EMBEDDED:
case ONE_TO_MANY:
case ONE_TO_ONE:
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINER,
"Ignoring: {0}", next.getName());
break;
case MANY_TO_MANY:
case MANY_TO_ONE:
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.INFO,
"{3} has a {2} relationship: {0} with: {1}",
new Object[]{next.getName(), next.getBindableJavaType(),
next.getPersistentAttributeType().name(), et.getName()});
if (!references.containsKey(et.getName())) {
references.put(et.getName(), new ArrayList<String>());
}
references.get(et.getName()).add(next.getBindableJavaType().getSimpleName());
amount++;
break;
default:
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.SEVERE,
"Unexpected value: {0}", next.getName());
break;
}
}
Iterator<PluralAttribute> pIterator = et.getPluralAttributes().iterator();
while (pIterator.hasNext()) {
PluralAttribute next = pIterator.next();
switch (next.getPersistentAttributeType()) {
case BASIC:
case ELEMENT_COLLECTION:
case EMBEDDED:
case ONE_TO_MANY:
case MANY_TO_MANY:
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINER,
"Ignoring: {0}", next.getName());
break;
case MANY_TO_ONE:
case ONE_TO_ONE:
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.INFO,
"{3} has a {2} relationship: {0} with: {1}",
new Object[]{next.getName(), next.getBindableJavaType(),
next.getPersistentAttributeType().name(), et.getName()});
if (!references.containsKey(et.getName())) {
references.put(et.getName(), new ArrayList<String>());
}
references.get(et.getName()).add(next.getBindableJavaType().getSimpleName());
amount++;
break;
default:
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.SEVERE,
"Unexpected value: {0}", next.getName());
break;
}
}
if (!tableStats.containsKey(amount)) {
tableStats.put(amount, new ArrayList<String>());
}
tableStats.get(amount).add(et.getName());
}
Iterator<String> iterator = references.keySet().iterator();
while (iterator.hasNext()) {
String next = iterator.next();
Iterator<String> iterator1 = references.get(next).iterator();
StringBuilder refs = new StringBuilder();
while (iterator1.hasNext()) {
refs.append(iterator1.next()).append("\n");
}
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINER, "References for {0}:\n{1}", new Object[]{next, refs.toString()});
}
//Need to sort entities with relationships even further
ArrayList<String> temp = new ArrayList<String>();
for (Entry<Integer, ArrayList<String>> e : tableStats.entrySet()) {
if (e.getKey() > 0) {
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.INFO, "Tables with {0} references", e.getKey());
for (String t : e.getValue()) {
//Check the relationships of the tables
//Here's where I need help
boolean ready = true;
for (String ref : references.get(t)) {
if (!temp.contains(ref)) {
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.INFO,
"{0} is not ready. Referenced table {1} is not ready yet", new Object[]{t, ref});
ready = false;
}
}
if (ready) {
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.INFO, "{0} is ready.", t);
temp.add(t);
}
}
//-------------------------------------------------------
} else {
temp.addAll(e.getValue());
}
}
for (Entry<Integer, ArrayList<String>> e : tableStats.entrySet()) {
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINER,
"Amount of relationships: {0}", e.getKey());
StringBuilder list = new StringBuilder();
for (String t : e.getValue()) {
list.append(t).append("\n");
}
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINER, list.toString());
}
tables.addAll(temp);
return tables;
}
I'm trying to use the JPA2 metadata to figure out the order to insert/delete rows from a database so constraints are not an issue (to be used later in Java code). This is part of a backup/restore approach using JPA.
Here's my approach:
- Group tables by amount of relationships/constraints (Only one-to-many and one-to-one are considered)
- Tables with zero instances (as per #1) can have records added/deleted without issue
- Tables with one instance can have records added/deleted without issue as long as the related table is already "ready"
Ready by ready I mean that all its related tables records are populated so foreign keys are valid for insert or that there are no other tables referencing to records in this table.
I'm sure it'll be some kind of recursive approach but I got stuck. Any help is more than welcomed.
Here's the code so far:
/**
* Get the execution order from the EntityManager meta data model.
*
* This will fail if the EntityManager is not JP2 compliant
* @param em EntityManager to get the metadata from
* @return ArrayList containing the order to process tables
*/
protected static ArrayList<String> getProcessingOrder(EntityManager em) {
ArrayList<String> tables = new ArrayList<String>();
//This holds the amount of relationships and the tables with that same amount
HashMap<Integer, ArrayList<String>> tableStats = new HashMap<Integer, ArrayList<String>>();
//This holds the table and the tables referenced by it
HashMap<String, ArrayList<String>> references = new HashMap<String, ArrayList<String>>();
for (EntityType et : em.getMetamodel().getEntities()) {
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINER, et.getName());
int amount = 0;
Iterator<SingularAttribute> sIterator = et.getSingularAttributes().iterator();
while (sIterator.hasNext()) {
SingularAttribute next = sIterator.next();
switch (next.getPersistentAttributeType()) {
case BASIC:
case ELEMENT_COLLECTION:
case EMBEDDED:
case ONE_TO_MANY:
case ONE_TO_ONE:
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINER,
"Ignoring: {0}", next.getName());
break;
case MANY_TO_MANY:
case MANY_TO_ONE:
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.INFO,
"{3} has a {2} relationship: {0} with: {1}",
new Object[]{next.getName(), next.getBindableJavaType(),
next.getPersistentAttributeType().name(), et.getName()});
if (!references.containsKey(et.getName())) {
references.put(et.getName(), new ArrayList<String>());
}
references.get(et.getName()).add(next.getBindableJavaType().getSimpleName());
amount++;
break;
default:
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.SEVERE,
"Unexpected value: {0}", next.getName());
break;
}
}
Iterator<PluralAttribute> pIterator = et.getPluralAttributes().iterator();
while (pIterator.hasNext()) {
PluralAttribute next = pIterator.next();
switch (next.getPersistentAttributeType()) {
case BASIC:
case ELEMENT_COLLECTION:
case EMBEDDED:
case ONE_TO_MANY:
case MANY_TO_MANY:
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINER,
"Ignoring: {0}", next.getName());
break;
case MANY_TO_ONE:
case ONE_TO_ONE:
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.INFO,
"{3} has a {2} relationship: {0} with: {1}",
new Object[]{next.getName(), next.getBindableJavaType(),
next.getPersistentAttributeType().name(), et.getName()});
if (!references.containsKey(et.getName())) {
references.put(et.getName(), new ArrayList<String>());
}
references.get(et.getName()).add(next.getBindableJavaType().getSimpleName());
amount++;
break;
default:
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.SEVERE,
"Unexpected value: {0}", next.getName());
break;
}
}
if (!tableStats.containsKey(amount)) {
tableStats.put(amount, new ArrayList<String>());
}
tableStats.get(amount).add(et.getName());
}
Iterator<String> iterator = references.keySet().iterator();
while (iterator.hasNext()) {
String next = iterator.next();
Iterator<String> iterator1 = references.get(next).iterator();
StringBuilder refs = new StringBuilder();
while (iterator1.hasNext()) {
refs.append(iterator1.next()).append("\n");
}
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINER, "References for {0}:\n{1}", new Object[]{next, refs.toString()});
}
//Need to sort entities with relationships even further
ArrayList<String> temp = new ArrayList<String>();
for (Entry<Integer, ArrayList<String>> e : tableStats.entrySet()) {
if (e.getKey() > 0) {
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.INFO, "Tables with {0} references", e.getKey());
for (String t : e.getValue()) {
//Check the relationships of the tables
//Here's where I need help
boolean ready = true;
for (String ref : references.get(t)) {
if (!temp.contains(ref)) {
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.INFO,
"{0} is not ready. Referenced table {1} is not ready yet", new Object[]{t, ref});
ready = false;
}
}
if (ready) {
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.INFO, "{0} is ready.", t);
temp.add(t);
}
}
//-------------------------------------------------------
} else {
temp.addAll(e.getValue());
}
}
for (Entry<Integer, ArrayList<String>> e : tableStats.entrySet()) {
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINER,
"Amount of relationships: {0}", e.getKey());
StringBuilder list = new StringBuilder();
for (String t : e.getValue()) {
list.append(t).append("\n");
}
Logger.getLogger(XincoBackupManager.class.getSimpleName()).log(Level.FINER, list.toString());
}
tables.addAll(temp);
return tables;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我会使用 JDBC 的数据库元数据来解决这个问题。
以下方法来自 java.sql.DatabaseMetadata 将在这里使用:
我已经在一些应用程序中使用了这种方法,并且效果很好。
尽管此方法不遵循 JPA 元模型用法,但我相信考虑到您的问题,在 JDBC 元数据级别上操作更合适。
由于可能存在难以通过此类外键依赖关系图处理的循环依赖关系,您可以选择
删除
添加
I'd approach this problem with database metadata from JDBC.
The following methods from java.sql.DatabaseMetadata are to be used here :
I have used this approach in a few applications and it works quite fine.
Although this approach doesn't follow the JPA metamodel usage, I believe operating on the JDBC metadata level is more appropriate given your problem.
As there can be cyclic dependencies which are difficult to handle via such a foreign key dependency graph you could alternatively
for delete
for add