Neo4j图数据库管理系统开发笔记之三:构建安全的RMI Service(Server)
RMI Server(服务端)主要包括以下功能:远程用户权限验证管理、远程服务接口实现类、Neo4j实体映射转换等。项目目录结构如下图所示:
3.2.1 远程用户权限验证管理
3.2.1.1 用户权限验证机制
用户权限验证机制分为三个层级。
第一级,远程主机IP地址验证。检查是否允许远程主机IP地址访问RMI服务。
第二级,远程用户信息验证。检查用户名称和密码是否正确,用户是否启用等。
第三级,远程服务及接口方法验证。检查用户是否有权访问某个RMI服务以及服务下的指定接口方法。
3.2.1.2 远程用户配置信息
远程用户配置信息在文件remote.users.config.xml中,内容格式如下表所示:
<?xml version="1.0"?>
<remote-users>
<remote-user user-id="1" login-name="admin" password="admin" user-name="管理员用户" enabled="true"></remote-user>
<remote-user user-id="2" login-name="test" password="test" user-name="测试用户" enabled="true"></remote-user>
</remote-users>
3.2.1.3 远程主机配置信息
远程主机配置信息在文件remote.hosts.config.xml中,内容格式如下表所示:
<?xml version="1.0"?>
<hosts>
<allow-hosts>
<host>*</host>
</allow-hosts>
<forbid-hosts>
<host></host>
</forbid-hosts>
</hosts>
3.2.1.4 用户权限配置信息
用户权限配置信息在文件remote.users.permission.xml中,内容格式如下表所示:
<?xml version="1.0"?>
<remote-users>
<remote-user login-name="admin,test">
<remote-service name="neo4j-graph-manage-service">
<allow-methods>
<method>*</method>
</allow-methods>
<forbid-methods>
<method></method>
</forbid-methods>
</remote-service>
<remote-service name="neo4j-graph-node-service">
<allow-methods>
<method>*</method>
</allow-methods>
<forbid-methods>
<method></method>
</forbid-methods>
</remote-service>
<remote-service name="neo4j-graph-index-service">
<allow-methods>
<method>*</method>
</allow-methods>
<forbid-methods>
<method></method>
</forbid-methods>
</remote-service>
<remote-service name="neo4j-graph-path-service">
<allow-methods>
<method>*</method>
</allow-methods>
<forbid-methods>
<method></method>
</forbid-methods>
</remote-service>
<remote-service name="neo4j-graph-cypher-service">
<allow-methods>
<method>*</method>
</allow-methods>
<forbid-methods>
<method></method>
</forbid-methods>
</remote-service>
</remote-user>
</remote-users>
3.2.2 远程服务接口实现类
绝大部分的非业务类工作都是在远程服务基础接口实现类BaseRemoteServiceImpl中完成了,譬如,获取图数据库服务对象实例、用户权限验证、日志记录、Neo4j实体映射转换等。如下表所示:
package com.hnepri.neo4j.rmi.service; import java.rmi.RemoteException;
import java.rmi.server.RemoteServer;
import java.rmi.server.ServerNotActiveException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List; import org.apache.commons.lang.StringUtils;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.Transaction; import com.hnepri.common.util.DateTimeUtil;
import com.hnepri.neo4j.client.rmi.bean.GDirection;
import com.hnepri.neo4j.client.rmi.bean.GNode;
import com.hnepri.neo4j.client.rmi.bean.GPage;
import com.hnepri.neo4j.client.rmi.bean.GPath;
import com.hnepri.neo4j.client.rmi.bean.GRelationship;
import com.hnepri.neo4j.client.rmi.service.IBaseRemoteService;
import com.hnepri.neo4j.client.rmi.util.RemoteClientFactory;
import com.hnepri.neo4j.client.rmi.util.RemoteLoginStatusUtil;
import com.hnepri.neo4j.common.model.GraphPageModel;
import com.hnepri.neo4j.common.util.GraphTemplate;
import com.hnepri.neo4j.rmi.bean.RemoteUser;
import com.hnepri.neo4j.rmi.util.RemoteHostUtil;
import com.hnepri.neo4j.rmi.util.RemoteServerFactory;
import com.hnepri.neo4j.rmi.util.RemoteUserPermissionUtil;
import com.hnepri.neo4j.rmi.util.RemoteUserUtil; /**
* Description: 远程服务基类实现类<br>
* Copyright: Copyright (c) 2015<br>
* Company: 河南电力科学研究院智能电网所<br>
* @author shangbingbing 2015-09-01编写
* @version 1.0
*/
@SuppressWarnings("deprecation")
public class BaseRemoteServiceImpl extends UnicastRemoteObject implements IBaseRemoteService {
private static final long serialVersionUID = 7292764643219275924L;
private String loginName = "";
private String password = "";
private String serviceName = "";
private String clientAddress = "";
private boolean loginStatus = false;
private String loginStatusMessage = "";
private String graphName;
private String graphPath; /**
* 获取图数据库操作实例GraphTemplate
* @return
*/
public GraphTemplate getTemplate() {
if(StringUtils.isNotBlank(this.getGraphName())) {
return GraphTemplate.getInstanceByName(this.getGraphName());
} else {
return GraphTemplate.getInstance(this.getGraphPath());
}
} public BaseRemoteServiceImpl() throws RemoteException {
super();
} /**
* 获取登录用户名称
* @return
*/
protected String getLoginName() {
return loginName;
}
/**
* 获取登录用户密码
* @return
*/
protected String getPassword() {
return password;
}
/**
* 获取远程服务名称(主要用于记录日志)
* @return
*/
protected String getServiceName() {
return serviceName;
}
/**
* 设置远程服务名称(主要用于记录日志)
* @param serviceName
*/
protected void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
protected String getClientAddress() {
try {
this.clientAddress = RemoteServer.getClientHost();
} catch (ServerNotActiveException e) {
e.printStackTrace();
}
return clientAddress;
}
/**
* 获取用户登录状态。
* @return
*/
protected boolean getLoginStatus() {
return loginStatus;
}
/**
* 获取用户登录状态信息。
* @return
*/
protected String getLoginStatusMessage() {
return loginStatusMessage;
}
/**
* 获取当前数据库名称。
* @return
*/
public String getGraphName() {
return graphName;
}
/**
* 获取当前数据库路径。
* @return
*/
public String getGraphPath() {
return graphPath;
} /**
* 检查远程调用方法的权限
* @param methodName
*/
protected void checkRemoteMethodPermission(String methodName) throws RemoteException {
boolean hasPermission = RemoteUserPermissionUtil.isCanAccessServiceMethod(this.getLoginName(), this.getServiceName(), methodName);
if(hasPermission) {
String log = String.format("%s\t来自【%s】的用户【%s】调用%s中的接口方法【%s】一次!", DateTimeUtil.getFormatDateTime(new Date()), this.getClientAddress(), this.getLoginName(), this.getServiceName(), methodName);
RemoteServerFactory.addLog(log);
} else {
String log = String.format("警告:来自【%s】的用户【%s】无权调用%s中的接口方法【%s】!", this.getClientAddress(), this.getLoginName(), this.getServiceName(), methodName);
RemoteServerFactory.addLog(log);
throw new RemoteException(log);
}
} @Override
public String remoteTest() throws RemoteException {
return RemoteClientFactory.REMOTE_CALL_STATUS_SUCCESS;
} @Override
public int login(String loginName, String password) throws RemoteException {
this.loginName = loginName;
this.password = password;
RemoteServerFactory.addLog(String.format("%s\t来自【%s】的用户【%s】正在验证访问【%s】服务!", DateTimeUtil.getFormatDateTime(new Date()), this.getClientAddress(), this.getLoginName(), this.getServiceName()));
int loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_SUCCESS;
if(RemoteHostUtil.isAllowHostAccess(this.getClientAddress())) {
if(StringUtils.isBlank(loginName) || StringUtils.isBlank(password)) {
loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_USER_PASSWORD_EMPTY;
} else {
if(RemoteUserUtil.getRemoteUserList().containsKey(loginName)) {
if(RemoteUserUtil.getRemoteUserList().get(loginName).isEnabled()) {
RemoteUser user = RemoteUserUtil.getRemoteUserList().get(loginName);
if(user.getPassword().equals(password)) {
if(RemoteUserPermissionUtil.isCanAccessService(loginName, this.getServiceName())) {
loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_SUCCESS; if(RemoteUserUtil.getOnlineRemoteHostList().containsKey(this.getClientAddress())) {
RemoteUserUtil.getOnlineRemoteHostList().remove(this.getClientAddress());
}
RemoteUserUtil.getOnlineRemoteHostList().put(this.getClientAddress(), DateTimeUtil.getFormatDateTime(new Date())); if(RemoteUserUtil.getOnlineRemoteUserList().containsKey(this.getLoginName())) {
RemoteUserUtil.getOnlineRemoteUserList().remove(this.getLoginName());
}
RemoteUserUtil.getOnlineRemoteUserList().put(this.getLoginName(), DateTimeUtil.getFormatDateTime(new Date()));
} else {
loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_USER_PERMISSION_ERROR;
}
} else {
loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_PASSWORD_ERROR;
}
} else {
loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_USER_ERROR;
}
} else {
loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_USER_ERROR;
}
}
} else {
loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_IP_PERMISSION_ERROR;
} this.loginStatusMessage = RemoteClientFactory.parseLoginStatusValue(loginStatusValue);
if(loginStatusValue == 0) {
this.loginStatus = true;
RemoteServerFactory.addLog(String.format("%s\t来自【%s】的用户【%s】通过【%s】服务的访问验证!", DateTimeUtil.getFormatDateTime(new Date()), this.getClientAddress(), this.getLoginName(), this.getServiceName()));
} else {
this.loginStatus = false;
RemoteServerFactory.addLog(String.format("%s\t来自【%s】的用户【%s】未通过【%s】服务的访问验证。\r\n%s", DateTimeUtil.getFormatDateTime(new Date()), this.getClientAddress(), this.getLoginName(), this.getServiceName(), this.getLoginStatusMessage()));
}
return loginStatusValue;
} @Override
public void initGraphName(String graphName) throws RemoteException {
this.graphName = graphName;
} @Override
public void initGraphPath(String graphPath) throws RemoteException {
this.graphPath = graphPath;
}
}
3.2.3 Neo4j实体映射转换
Neo4j实体映射转换,即将Neo4j原生的接口类对象映射转换为我们在RMI Client中自定义的可序列化的远程服务实体类。主要转换方法如下所示。
/**
* 将Node转换为GNode对象。<br>
* <b>【备注:未进行事务处理】</b>
* @param node
* @return
*/
protected GNode convertNodeToGNode(Node node) {
if(node == null) {
return null;
} GNode gnode = new GNode();
gnode.setId(node.getId());
gnode.setDegree(node.getDegree()); Iterator<Label> itLabel = node.getLabels().iterator();
while(itLabel.hasNext()) {
Label label = itLabel.next();
gnode.getLabelNameList().add(label.name());
} for(String name : node.getAllProperties().keySet()) {
Object value = node.getProperty(name);
gnode.getPropertyList().put(name, value);
} Iterator<Relationship> itRelationship = node.getRelationships().iterator();
while(itRelationship.hasNext()) {
Relationship rel = itRelationship.next();
String relType = rel.getType().name();
gnode.getRelationshipList().add(rel.getId());
if(gnode.getRelationshipTypeList().containsKey(relType)) {
gnode.getRelationshipTypeList().get(relType).add(rel.getId());
} else {
ArrayList<Long> list = new ArrayList<Long>();
list.add(rel.getId());
gnode.getRelationshipTypeList().put(relType, list);
}
} Iterator<Relationship> itRelationshipIncoming = node.getRelationships(Direction.INCOMING).iterator();
ArrayList<Long> incomingList = new ArrayList<Long>();
while(itRelationshipIncoming.hasNext()) {
Relationship rel = itRelationshipIncoming.next();
incomingList.add(rel.getId());
}
gnode.getRelationshipDirectionList().put(Direction.INCOMING.name(), incomingList); Iterator<Relationship> itRelationshipOutgoing = node.getRelationships(Direction.OUTGOING).iterator();
ArrayList<Long> outgoingList = new ArrayList<Long>();
while(itRelationshipOutgoing.hasNext()) {
Relationship rel = itRelationshipOutgoing.next();
outgoingList.add(rel.getId());
}
gnode.getRelationshipDirectionList().put(Direction.OUTGOING.name(), outgoingList);
return gnode;
} /**
* 将Node对象转换为GNode对象。<br>
* <b>【备注:已进行事务处理】</b>
* @param nodes
* @return
*/
public List<GNode> parseNodes(List<Node> nodes) {
List<GNode> gNodeList = new ArrayList<GNode>();
if(nodes == null || nodes.size() == 0) return gNodeList;
if(this.getTemplate() == null) return gNodeList; Transaction tx = this.getTemplate().createTransaction();
try {
for(Node node : nodes) {
GNode gnode = this.convertNodeToGNode(node);
if(gnode == null) continue;
gNodeList.add(gnode);
}
} catch (Exception ex) {
ex.printStackTrace();
tx.failure();
} finally {
tx.finish();
} return gNodeList;
} /**
* 将Node对象转换为GNode对象。<br>
* <b>【备注:已进行事务处理】</b>
* @param node
* @return
*/
public GNode parseNode(Node node) {
List<Node> nodes = Arrays.asList(node);
List<GNode> gNodeList = this.parseNodes(nodes);
if(gNodeList == null || gNodeList.size() == 0) {
return null;
} else {
return gNodeList.get(0);
}
} /**
* 根据编码解析节点对象,将其转为GNode对象。<br>
* <b>【备注:已进行事务处理】</b>
* @param nodeIDs
* @return
*/
public List<GNode> parseNodesByID(List<Long> nodeIDs) {
List<GNode> gNodeList = new ArrayList<GNode>();
if(nodeIDs == null || nodeIDs.size() == 0) return gNodeList;
if(this.getTemplate() == null) return gNodeList; Transaction tx = this.getTemplate().createTransaction();
try {
for(long nodeID : nodeIDs) {
Node node = this.getTemplate().getGraphDBService().getNodeById(nodeID);
GNode gnode = this.convertNodeToGNode(node);
if(gnode == null) continue;
gNodeList.add(gnode);
}
} catch (Exception ex) {
ex.printStackTrace();
tx.failure();
} finally {
tx.finish();
} return gNodeList;
} /**
* 根据编码解析节点对象,将其转为GNode对象。<br>
* <b>【备注:已进行事务处理】</b>
* @param nodeID
* @return
*/
public GNode parseNodeByID(long nodeID) {
List<Long> nodeIDs = Arrays.asList(nodeID);
List<GNode> gNodeList = this.parseNodesByID(nodeIDs);
if(gNodeList == null || gNodeList.size() == 0) {
return null;
} else {
return gNodeList.get(0);
}
} /**
* 将Relationship转换为GRelationship对象。<br>
* <b>【备注:未进行事务处理】</b>
* @param relationship
* @return
*/
protected GRelationship convertRelToGRel(Relationship relationship) {
if(relationship == null) {
return null;
} GRelationship grelationship = new GRelationship();
grelationship.setId(relationship.getId());
grelationship.setStartNodeID(relationship.getStartNode().getId());
grelationship.setEndNodeID(relationship.getEndNode().getId());
grelationship.setRelationshipType(relationship.getType().name());
for(String name : relationship.getAllProperties().keySet()) {
Object value = relationship.getProperty(name);
grelationship.getPropertyList().put(name, value);
}
return grelationship;
} /**
* 将Relationship对象转换为GRelationship对象。<br>
* <b>【备注:已进行事务处理】</b>
* @param relationships
* @return
*/
public List<GRelationship> parseRelationships(List<Relationship> relationships) {
List<GRelationship> gRelationshipList = new ArrayList<GRelationship>();
if(relationships == null || relationships.size() == 0) return gRelationshipList;
if(this.getTemplate() == null) return gRelationshipList; Transaction tx = this.getTemplate().createTransaction();
try {
for(Relationship relationship : relationships) {
GRelationship grelationship = this.convertRelToGRel(relationship);
if(grelationship == null) continue;
gRelationshipList.add(grelationship);
}
} catch (Exception ex) {
ex.printStackTrace();
tx.failure();
} finally {
tx.finish();
} return gRelationshipList;
} /**
* 将Relationship对象转换为GRelationship对象。<br>
* <b>【备注:已进行事务处理】</b>
* @param relationship
* @return
*/
public GRelationship parseRelationship(Relationship relationship) {
List<Relationship> relationships = Arrays.asList(relationship);
List<GRelationship> gRelationshipList = this.parseRelationships(relationships);
if(gRelationshipList == null || gRelationshipList.size() == 0) {
return null;
} else {
return gRelationshipList.get(0);
}
} /**
* 根据编码解析关系对象,将其转为GRelationship对象。<br>
* <b>【备注:已进行事务处理】</b>
* @param relationshipIDs
* @return
*/
public List<GRelationship> parseRelationshipsByID(List<Long> relationshipIDs) {
List<GRelationship> gRelationshipList = new ArrayList<GRelationship>();
if(relationshipIDs == null || relationshipIDs.size() == 0) return gRelationshipList;
if(this.getTemplate() == null) return gRelationshipList; Transaction tx = this.getTemplate().createTransaction();
try {
for(long relationshipID : relationshipIDs) {
Relationship relationship = this.getTemplate().getGraphDBService().getRelationshipById(relationshipID);
GRelationship grelationship = this.convertRelToGRel(relationship);
if(grelationship == null) continue;
gRelationshipList.add(grelationship);
}
} catch (Exception ex) {
ex.printStackTrace();
tx.failure();
} finally {
tx.finish();
} return gRelationshipList;
} /**
* 根据编码解析关系对象,将其转为GRelationship对象。<br>
* <b>【备注:已进行事务处理】</b>
* @param relationshipID
* @return
*/
public GRelationship parseRelationshipByID(long relationshipID) {
List<Long> relationshipIDs = Arrays.asList(relationshipID);
List<GRelationship> gRelationshipList = this.parseRelationshipsByID(relationshipIDs);
if(gRelationshipList == null || gRelationshipList.size() == 0) {
return null;
} else {
return gRelationshipList.get(0);
}
} /**
* 将Path对象转换为GPath对象。<br>
* <b>【备注:未进行事务处理】</b>
* @param paths
* @return
*/
protected List<GPath> convertPathToGPath(List<Path> paths) {
List<GPath> gPathList = new ArrayList<GPath>();
if(paths == null || paths.size() == 0) return gPathList; for(Path path : paths) {
GPath gpath = new GPath(); Iterator<Node> itNode = path.nodes().iterator();
while(itNode.hasNext()) {
Node node = itNode.next();
GNode gnode = this.convertNodeToGNode(node);
if(gnode == null) continue;
gpath.getNodes().add(gnode);
} Iterator<Relationship> itRelationship = path.relationships().iterator();
while(itRelationship.hasNext()) {
Relationship relationship = itRelationship.next();
GRelationship grelationship = this.convertRelToGRel(relationship);
if(grelationship == null) continue;
gpath.getRelationships().add(grelationship);
} gPathList.add(gpath);
} return gPathList;
} /**
* 将Path对象转换为GPath对象。<br>
* <b>【备注:未进行事务处理】</b>
* @param path
* @return
*/
protected GPath convertPathToGPath(Path path) {
List<Path> paths = Arrays.asList(path);
List<GPath> gPathList = this.convertPathToGPath(paths);
if(gPathList == null || gPathList.size() == 0) {
return null;
} else {
return gPathList.get(0);
}
} /**
* 将GPage对象转换为GraphPageModel对象。
* @param gpage
* @return
*/
public GraphPageModel parseGPageToGraphPageModel(GPage gpage) {
GraphPageModel pageModel = null;
if(gpage == null) {
pageModel = new GraphPageModel(20);
} else {
pageModel = new GraphPageModel(gpage.getPageSize());
pageModel.setPageIndex(gpage.getPageIndex());
pageModel.setTotalCount(gpage.getTotalCount());
}
return pageModel;
} /**
* 将GraphPageModel对象转换为GPage对象。
* @param pageModel
* @return
*/
public GPage parseGraphPageModelToGPage(GraphPageModel pageModel) {
GPage gpage = null;
if(pageModel == null) {
gpage = new GPage(20);
} else {
gpage = new GPage(pageModel.getPageSize());
gpage.setPageIndex(pageModel.getPageIndex());
gpage.setTotalCount(pageModel.getTotalCount());
List<GNode> nodeList = this.parseNodes(pageModel.getNodeList());
for(GNode node : nodeList) {
gpage.getNodeList().add(node);
}
List<GRelationship> relationshipList = this.parseRelationships(pageModel.getRelationshipList());
for(GRelationship relationship : relationshipList) {
gpage.getRelationshipList().add(relationship);
}
}
return gpage;
} /**
* 将GDirection对象转换为Direction对象。
* @param gdirection
* @return
*/
public Direction parseGDirection(GDirection gDirection) {
if(gDirection == null) {
return null;
}
if(StringUtils.isBlank(gDirection.getName())) {
return null;
}
return Direction.valueOf(gDirection.getName());
} /**
* 将关系类型名称解析为关系类型枚举对象。
* @param relationshipType
* @return
*/
public RelationshipType parseRelationshipType(String relationshipType) {
if(StringUtils.isBlank(relationshipType)) {
return null;
}
return this.getTemplate().getRelTypeUtil().get(relationshipType);
}
3.3. RMI Server Form
RMI Server Form,即RMI服务窗口管理器,是通过窗口方式来管理RMI服务,包括启动RMI服务,停止RMI服务,监控RMI日志信息,监控远程登录用户信息,初始化相关配置等操作。此功能集成在“图数据库管理系统Server端”,功能界面如下图所示:
【完】
作者:商兵兵
单位:河南省电力科学研究院智能电网所
QQ:52190634
Neo4j图数据库管理系统开发笔记之三:构建安全的RMI Service(Server)的更多相关文章
- Neo4j图数据库管理系统开发笔记之一:Neo4j Java 工具包
1 应用开发概述 基于数据传输效率以及接口自定义等特殊性需求,我们暂时放弃使用Neo4j服务器版本,而是在Neo4j嵌入式版本的基础上进行一些封装性的开发.封装的重点,是解决Neo4j嵌入式版本Emb ...
- Neo4j图数据库管理系统开发笔记之二:管理系统Server端界面一览
最近在neo4j java api和rmi的基础上,设计了一套neo4j管理工具,分为server端和client端,中间用rmi进行通信.基本功能包括图数据库基本信息维护管理(创建.编辑.删除.统计 ...
- Neo4j资料 Neo4j教程 Neo4j视频教程 Neo4j 图数据库视频教程
课程发布地址 地址: 腾讯课堂<Neo4j 图数据库视频教程> https://ke.qq.com/course/327374?tuin=442d3e14 作者 庞国明,<Neo4j ...
- Neo4j视频教程 Neo4j 图数据库视频教程
课程名称 课程发布地址 地址: 腾讯课堂<Neo4j 图数据库视频教程> https://ke.qq.com/course/327374?tuin=442d3e14 作者 庞国明,< ...
- Neo4j教程 Neo4j视频教程 Neo4j 图数据库视频教程
课程发布地址 地址: 腾讯课堂<Neo4j 图数据库视频教程> https://ke.qq.com/course/327374?tuin=442d3e14 作者 庞国明,<Neo4j ...
- Neo4j图数据库从入门到精通
目录 第一章:介绍 Neo4j是什么 Neo4j的特点 Neo4j的优点 第二章:安装 1.环境 2.下载 3.开启远程访问 4.启动 第三章:CQL 1.CQL简介 2.Neo4j CQL命令/条款 ...
- Neo4j图数据库从入门到精通(转)
add by zhj: 转载时,目录没整理好,还会跳转到原文 其实RDB也可以存储多对多的关系,使用的是中间表,GDB使用的是边,RDB中的实体存储在数据表,而GDB存储在节点.两者使用的底层技术不同 ...
- neo4j 图数据库安装及介绍
neo4j 图数据库安装及介绍 一.neo4j图数据库介绍 图数据库,顾名思义就是利用了"图的数据结构来作为数据存储逻辑体现的一种数据库",所以要想学好图数据库当然需要了解一些关于 ...
- Ubuntu16.04下Neo4j图数据库官网安装部署步骤(图文详解)(博主推荐)
不多说,直接上干货! 说在前面的话 首先,查看下你的操作系统的版本. root@zhouls-virtual-machine:~# cat /etc/issue Ubuntu LTS \n \l r ...
随机推荐
- js页面跳转整理(转载未整理)
js方式的页面跳转1.window.location.href方式 <script language="JavaScript" type="text/java ...
- [CLR via C#]13. 接口
一.类和接口继承 在Microsoft.Net Framwork中,有一个名为System.Object的类,它定义了4个公共实例方法:ToString, Equals, GetHashCode和Ge ...
- ASP.NET MVC4 传递Model到View
原文发表在:http://www.star110.com/Note/ReadArticle/60641215331146140043.html 开发环境:.NET MVC4 + EF6.0 模型: 1 ...
- ASP.NET中Session简单原理图
刚学习Session,对session的理解相当肤浅,按照我的想法画了原理图,麻烦各位大神指正,谢了!
- PHP使用SnowFlake算法生成唯一ID
前言:最近需要做一套CMS系统,由于功能比较单一,而且要求灵活,所以放弃了WP这样的成熟系统,自己做一套相对简单一点的.文章的详情页URL想要做成url伪静态的格式即xxx.html 其中xxx考虑过 ...
- VS2012/2013/2015关闭单击文件进行预览的功能
Visual Studio在2010版本后推出了点击项目管理器预览文件的功能,但是对于配置不咋地的旧电脑总是觉得有点卡,下面是解决方案. 英文版方法:Tools->Options->Env ...
- SQL2012 提示评估已过期 解决方案- sql server问题
SQL2012 提示评估已过期 解决方案提示评估已过期的解决方法和 sql2008一样 第1步:进入SQL2012配置工具中的安装中心第2步:再进入维护界面,选择版本升级第3步:进入产品密钥,输入密钥 ...
- Winform使用外部浏览器解决webbrowser问题
对于还是一个菜鸟的我,在最近自己接手了个项目,搞的自己也是醉了,身边也有没大神的现场指导,只能靠度娘和谷歌的大力帮助,要不然这么个小项目可定现在还交不了,不过在这过程种也确确实实学到了不少东西,我先说 ...
- 在Java中调用C
在Java代码中通过JNI调用C函数的步骤如下: 第一步:编写Java代码 第二步:编译Java代码(javac Java文件) 第三步:生成C代码头文件(javah java类名,自动生成) 第四步 ...
- 【转】深入浅出Android Support Annotation
[转自]http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0427/2797.html http://www.flysnow.org/201 ...