在将新的 class 添加到图形的提交和关闭事务之后 - 新的 Tx 在架构中看不到 class,尽管它是持久的
After a commited & shutdown transaction which added new class to a graph - a new Tx doesn't see the class in schema, though it is persisted
我们在一段代码中保存一个图表,然后用另一个代码尝试检索它。我们用这个 Spring bean 打开我们的交易。任何想要访问数据库的人总是调用这个 bean 的 getGraph() 方法。
public class OrientDatabaseConnectionManager {
private OrientGraphFactory factory;
public OrientDatabaseConnectionManager(String path, String name, String pass) {
factory = new OrientGraphFactory(path, name, pass).setupPool(1,10);
}
public OrientGraphFactory getFactory() {
return factory;
}
public void setFactory(OrientGraphFactory factory) {
this.factory = factory;
}
/**
* Method returns graph instance from the factory's pool.
* @return
*/
public OrientGraph getGraph(){
OrientGraph resultGraph = factory.getTx();
resultGraph.setThreadMode(OrientBaseGraph.THREAD_MODE.ALWAYS_AUTOSET);
return resultGraph;
}
}
(我没能完全理解thread_mode,但我认为这应该与问题无关。)
保持图形提交和关闭的代码,如您在此处所见:
OrientDatabaseConnectionManager connMan; //this is an injected bean from above.
public boolean saveGraphToOrientDB(
SparseMultigraph<SocialVertex, SocialEdge> graph, String label) {
boolean isSavedCorrectly = false;
OrientGraph graphO = connMan.getGraph();
try {
graphDBinput.saveGraph(graph, label, graphO);
// LOG System.out.println("Graph was saved with label "+label);
isSavedCorrectly = true;
} catch (AlreadyUsedGraphLabelException ex) {
Logger.getLogger(GraphDBFacade.class.getName()).log(Level.SEVERE, null, ex);
} finally {
graphO.shutdown(); //calls .commit() automatically normally, but commit already happens inside.
}
return isSavedCorrectly;
}
这次提交效果很好——数据总是持久化的,我每次都在 orientdb 管理界面中检查,第一个持久化图总是可见的。可能需要注意的是,在保存期间使用的标签定义了新的 class(据我所知,因此修改了架构)并将其用于持久图。
图形的检索看起来像这样:
@Override
public SocialGraph getSocialGraph(String label) {
OrientGraph graph = connMan.getGraph();
SocialGraph socialGraph = null;
try {
socialGraph = new SocialGraph(getAllSocialNodes(label, graph), getAllSocialEdges(label, graph));
} catch (Exception e) {
logger.error(e);
} finally {
graph.shutdown();
}
return socialGraph;
}
public List<Node> getAllSocialNodes(String label, OrientGraph graph) {
return constructNodes(graphFilterMan.getAllNodesFromGraph(label, graph));
}
public Set<Vertex> getAllNodesFromGraph(String graphLabel, OrientGraph graph) {
Set<Vertex> labelledGraph = new HashSet<>();
try{
Iterable<Vertex> configGraph = graph.getVerticesOfClass(graphLabel);
for(Vertex v : configGraph){ //THE CODE CRASHES HERE, WITH "CLASS WITH NAME graphLabel DOES NOT EXIST
labelledGraph.add(v);
}
} catch(Exception ex){
logger.error(ex);
graph.rollback();
}
return labelledGraph;
}
所以问题是,当我们用新的 class 持久化一个新图时,说 "graph01" 然后我们想要检索它,它没问题。后来,我们创建了一个 "graph02" 并且我们想要检索它,但是它崩溃了,正如上面评论的那样 - OrientDb 告诉你,名称为 "graph02" 的 class 不存在。
它当时确实存在于管理界面中,但是,当我调试时,class 实际上在调用 factory.getTx()
之后不在架构中
一开始,当我们从工厂获得一个事务图实例时,我们得到一个具有上下文的图,其中 rawGraph 的底层数据库的元数据共享模式代理委托模式 classes 没有新的 class,我显然可以看到它已提交到数据库中。
或者在图片上:
架构中应该还有一个 class。前一段时间持久化(并提交)的那个 - 也可以在 orientDb 管理界面中看到(不存在于变量中)
我认为正在发生的事情是工厂从中获取事务的池具有某种缓存模式或其他内容。当我们添加新的 class.
时,它不会刷新架构
为什么当我们试图取出新图表时,架构不显示新图表 class?架构不会刷新吗?
我发现 here in schema documentation
NOTE: Changes to the schema are not transactional, so execute them outside a transaction.
那么我们是否应该在事务之外创建新的 class 然后我们会在上下文中的模式中获得更新?
//可能是我理解错了-我昨天才接触到OrientDb,我要在一个已经写好的代码中找出问题。
我们使用的db是一个remote:localhost/socialGraph
OrientDB 1.7.4版本
我们在代码中注意到了同样的问题,架构更改在池连接中不可见。
我们还有一种可以连接的工厂。我们所做的是保留模式版本号,每次我们进行一些更改模式的操作时,我们都会修改该版本号,当打开新连接时,我们会检查模式版本是否已更改。
架构更改后,我们会重新加载架构,关闭池并重新创建它。该方法已被证明对我们有效(我们目前使用的是 2.0.15 版)。
相关代码如下:
private static volatile int schemaVersion = -1;
private OPartitionedDatabasePool pool;
protected void createPool() {
pool = new OPartitionedDatabasePool(getUrl(), getUsername(), getPassword());
}
@Override
public synchronized ODatabaseDocumentTx openDatabase() {
ODatabaseDocumentTx db = pool.acquire();
//DatabaseInfo is a simple class put in a static contect that holds the schema version.
DatabaseInfo databaseInfo = CurrentDatabaseInfo.getDatabaseInfo();
ODocument document = db.load((ORID) databaseInfo.getId(), "schemaVersion:0", true);
Integer version = document.field("schemaVersion");
if (schemaVersion == -1) {
schemaVersion = version;
} else if (schemaVersion < version) {
db.getMetadata().getSchema().reload();
schemaVersion = version;
pool.close();
createPool();
db = pool.acquire();
}
return db;
}
最后的问题是,我们有两个 liferay 项目,每个项目在其 WAR 文件中都有自己的 spring 应用程序上下文,当我们将这些项目部署为 Liferay 中的 portlet 时,两个项目创建了两个上下文,每个上下文都有一个 OrientDatabaseConnectionManager。
在一种情况下,模式正在更改。即使我重置了连接并重新加载了模式,它也只发生在连接管理器/工厂的一个上下文中。图表的检索发生在另一个项目的 portlet 中,导致了一个过时的模式(没有重新加载,因为重新加载发生在另一个 spring 上下文中)——因此出现错误。
因此您必须小心 - 要么与所有 portlet 的 bean 共享一个 spring 应用程序上下文(这可以通过具有父应用程序上下文,you can read more about it here)
或
检查同一项目中架构的更改,稍后您也将使用该项目检索数据。
我们在一段代码中保存一个图表,然后用另一个代码尝试检索它。我们用这个 Spring bean 打开我们的交易。任何想要访问数据库的人总是调用这个 bean 的 getGraph() 方法。
public class OrientDatabaseConnectionManager {
private OrientGraphFactory factory;
public OrientDatabaseConnectionManager(String path, String name, String pass) {
factory = new OrientGraphFactory(path, name, pass).setupPool(1,10);
}
public OrientGraphFactory getFactory() {
return factory;
}
public void setFactory(OrientGraphFactory factory) {
this.factory = factory;
}
/**
* Method returns graph instance from the factory's pool.
* @return
*/
public OrientGraph getGraph(){
OrientGraph resultGraph = factory.getTx();
resultGraph.setThreadMode(OrientBaseGraph.THREAD_MODE.ALWAYS_AUTOSET);
return resultGraph;
}
}
(我没能完全理解thread_mode,但我认为这应该与问题无关。)
保持图形提交和关闭的代码,如您在此处所见:
OrientDatabaseConnectionManager connMan; //this is an injected bean from above.
public boolean saveGraphToOrientDB(
SparseMultigraph<SocialVertex, SocialEdge> graph, String label) {
boolean isSavedCorrectly = false;
OrientGraph graphO = connMan.getGraph();
try {
graphDBinput.saveGraph(graph, label, graphO);
// LOG System.out.println("Graph was saved with label "+label);
isSavedCorrectly = true;
} catch (AlreadyUsedGraphLabelException ex) {
Logger.getLogger(GraphDBFacade.class.getName()).log(Level.SEVERE, null, ex);
} finally {
graphO.shutdown(); //calls .commit() automatically normally, but commit already happens inside.
}
return isSavedCorrectly;
}
这次提交效果很好——数据总是持久化的,我每次都在 orientdb 管理界面中检查,第一个持久化图总是可见的。可能需要注意的是,在保存期间使用的标签定义了新的 class(据我所知,因此修改了架构)并将其用于持久图。
图形的检索看起来像这样:
@Override
public SocialGraph getSocialGraph(String label) {
OrientGraph graph = connMan.getGraph();
SocialGraph socialGraph = null;
try {
socialGraph = new SocialGraph(getAllSocialNodes(label, graph), getAllSocialEdges(label, graph));
} catch (Exception e) {
logger.error(e);
} finally {
graph.shutdown();
}
return socialGraph;
}
public List<Node> getAllSocialNodes(String label, OrientGraph graph) {
return constructNodes(graphFilterMan.getAllNodesFromGraph(label, graph));
}
public Set<Vertex> getAllNodesFromGraph(String graphLabel, OrientGraph graph) {
Set<Vertex> labelledGraph = new HashSet<>();
try{
Iterable<Vertex> configGraph = graph.getVerticesOfClass(graphLabel);
for(Vertex v : configGraph){ //THE CODE CRASHES HERE, WITH "CLASS WITH NAME graphLabel DOES NOT EXIST
labelledGraph.add(v);
}
} catch(Exception ex){
logger.error(ex);
graph.rollback();
}
return labelledGraph;
}
所以问题是,当我们用新的 class 持久化一个新图时,说 "graph01" 然后我们想要检索它,它没问题。后来,我们创建了一个 "graph02" 并且我们想要检索它,但是它崩溃了,正如上面评论的那样 - OrientDb 告诉你,名称为 "graph02" 的 class 不存在。
它当时确实存在于管理界面中,但是,当我调试时,class 实际上在调用 factory.getTx()
一开始,当我们从工厂获得一个事务图实例时,我们得到一个具有上下文的图,其中 rawGraph 的底层数据库的元数据共享模式代理委托模式 classes 没有新的 class,我显然可以看到它已提交到数据库中。
或者在图片上:
架构中应该还有一个 class。前一段时间持久化(并提交)的那个 - 也可以在 orientDb 管理界面中看到(不存在于变量中)
我认为正在发生的事情是工厂从中获取事务的池具有某种缓存模式或其他内容。当我们添加新的 class.
时,它不会刷新架构为什么当我们试图取出新图表时,架构不显示新图表 class?架构不会刷新吗?
我发现 here in schema documentation
NOTE: Changes to the schema are not transactional, so execute them outside a transaction.
那么我们是否应该在事务之外创建新的 class 然后我们会在上下文中的模式中获得更新?
//可能是我理解错了-我昨天才接触到OrientDb,我要在一个已经写好的代码中找出问题。
我们使用的db是一个remote:localhost/socialGraph
OrientDB 1.7.4版本
我们在代码中注意到了同样的问题,架构更改在池连接中不可见。
我们还有一种可以连接的工厂。我们所做的是保留模式版本号,每次我们进行一些更改模式的操作时,我们都会修改该版本号,当打开新连接时,我们会检查模式版本是否已更改。
架构更改后,我们会重新加载架构,关闭池并重新创建它。该方法已被证明对我们有效(我们目前使用的是 2.0.15 版)。
相关代码如下:
private static volatile int schemaVersion = -1;
private OPartitionedDatabasePool pool;
protected void createPool() {
pool = new OPartitionedDatabasePool(getUrl(), getUsername(), getPassword());
}
@Override
public synchronized ODatabaseDocumentTx openDatabase() {
ODatabaseDocumentTx db = pool.acquire();
//DatabaseInfo is a simple class put in a static contect that holds the schema version.
DatabaseInfo databaseInfo = CurrentDatabaseInfo.getDatabaseInfo();
ODocument document = db.load((ORID) databaseInfo.getId(), "schemaVersion:0", true);
Integer version = document.field("schemaVersion");
if (schemaVersion == -1) {
schemaVersion = version;
} else if (schemaVersion < version) {
db.getMetadata().getSchema().reload();
schemaVersion = version;
pool.close();
createPool();
db = pool.acquire();
}
return db;
}
最后的问题是,我们有两个 liferay 项目,每个项目在其 WAR 文件中都有自己的 spring 应用程序上下文,当我们将这些项目部署为 Liferay 中的 portlet 时,两个项目创建了两个上下文,每个上下文都有一个 OrientDatabaseConnectionManager。
在一种情况下,模式正在更改。即使我重置了连接并重新加载了模式,它也只发生在连接管理器/工厂的一个上下文中。图表的检索发生在另一个项目的 portlet 中,导致了一个过时的模式(没有重新加载,因为重新加载发生在另一个 spring 上下文中)——因此出现错误。
因此您必须小心 - 要么与所有 portlet 的 bean 共享一个 spring 应用程序上下文(这可以通过具有父应用程序上下文,you can read more about it here)
或
检查同一项目中架构的更改,稍后您也将使用该项目检索数据。