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

主页:http://www.cnblogs.com/shangbingbing

空间:http://shangbingbing.qzone.qq.com

Neo4j图数据库管理系统开发笔记之三:构建安全的RMI Service(Server)的更多相关文章

  1. Neo4j图数据库管理系统开发笔记之一:Neo4j Java 工具包

    1 应用开发概述 基于数据传输效率以及接口自定义等特殊性需求,我们暂时放弃使用Neo4j服务器版本,而是在Neo4j嵌入式版本的基础上进行一些封装性的开发.封装的重点,是解决Neo4j嵌入式版本Emb ...

  2. Neo4j图数据库管理系统开发笔记之二:管理系统Server端界面一览

    最近在neo4j java api和rmi的基础上,设计了一套neo4j管理工具,分为server端和client端,中间用rmi进行通信.基本功能包括图数据库基本信息维护管理(创建.编辑.删除.统计 ...

  3. Neo4j资料 Neo4j教程 Neo4j视频教程 Neo4j 图数据库视频教程

    课程发布地址 地址: 腾讯课堂<Neo4j 图数据库视频教程> https://ke.qq.com/course/327374?tuin=442d3e14 作者 庞国明,<Neo4j ...

  4. Neo4j视频教程 Neo4j 图数据库视频教程

    课程名称 课程发布地址 地址: 腾讯课堂<Neo4j 图数据库视频教程> https://ke.qq.com/course/327374?tuin=442d3e14 作者 庞国明,< ...

  5. Neo4j教程 Neo4j视频教程 Neo4j 图数据库视频教程

    课程发布地址 地址: 腾讯课堂<Neo4j 图数据库视频教程> https://ke.qq.com/course/327374?tuin=442d3e14 作者 庞国明,<Neo4j ...

  6. Neo4j图数据库从入门到精通

    目录 第一章:介绍 Neo4j是什么 Neo4j的特点 Neo4j的优点 第二章:安装 1.环境 2.下载 3.开启远程访问 4.启动 第三章:CQL 1.CQL简介 2.Neo4j CQL命令/条款 ...

  7. Neo4j图数据库从入门到精通(转)

    add by zhj: 转载时,目录没整理好,还会跳转到原文 其实RDB也可以存储多对多的关系,使用的是中间表,GDB使用的是边,RDB中的实体存储在数据表,而GDB存储在节点.两者使用的底层技术不同 ...

  8. neo4j 图数据库安装及介绍

    neo4j 图数据库安装及介绍 一.neo4j图数据库介绍 图数据库,顾名思义就是利用了"图的数据结构来作为数据存储逻辑体现的一种数据库",所以要想学好图数据库当然需要了解一些关于 ...

  9. Ubuntu16.04下Neo4j图数据库官网安装部署步骤(图文详解)(博主推荐)

    不多说,直接上干货! 说在前面的话  首先,查看下你的操作系统的版本. root@zhouls-virtual-machine:~# cat /etc/issue Ubuntu LTS \n \l r ...

随机推荐

  1. [Bootstrap]7天深入Bootstrap(1)入门准备

    由于申请了一个域名,一个云主机,开始弄个人网站. 发现Bootstrap非常方便,和重要,故开始学习与分享关于Bootstrap的技术. 推个广告 个人网站:http://www.neverc.cn ...

  2. sprint3(第二天)

    今天完成的任务有统计用户,全局管理员可以对员工或者用户设置权限. 燃尽图

  3. SqlServer常用语句参考

    1.SQL SERVER中如何使用SQL 语句复制表结构: select * into 新表 from 旧表 where 1=2 把“旧表”的表结构复制到“新表”,1=2不复制数据,如果要复制数据,就 ...

  4. csharp: DataTable Rename ColumnName and remove Column

    enum ChangeNume { /// <summary> /// 简体 /// </summary> gbk=1, /// <summary> /// 英文 ...

  5. FreeBSD暂时用9.X系列为宜

    今天尝试在FreeBSD10 上编译c代码,发现gcc被换成llvm后,环境配置需要重新学习.

  6. java设计模式--工厂模式

       所谓工厂,就是要造产品,比如一个小型砖瓦厂,只有一个窑,既能造砖又能造瓦,这种叫简单工厂模式.规模稍大一点呢,我多个窑,有的窑专门造砖,有的窑专门造瓦,想造别的,比如瓷砖,我再用个专门的窑,这种 ...

  7. 序列中找子序列的dp

    题目网址: http://codeforces.com/problemset/problem/414/B Description Mashmokh's boss, Bimokh, didn't lik ...

  8. Yii2学习笔记之场景

    场景 一个模型可能在多个场景中使用,在不同的场景中,模型可能使用不同的业务逻辑和规则.例如, User 模型可能在用户登录时使用,也可能在用户注册时使用,某些属性可能在用户注册时强制要求有,在用户登录 ...

  9. python pip 升级每个包

    pip本身不自带升级所有包的功能, 但可以通过下面的脚本实现. import pip from subprocess import call for dist in pip.get_installed ...

  10. [小北De编程手记] : Selenium For C# 教程目录

    写<Selnium For C#>系列文章的初衷是因为有很多朋友问我应该从哪里开始学习自动化测试,于是就为大家写下了这个系列的文章,希望对你有些帮助吧.而我想表达的是Selenium(同时 ...