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. struts2基础——请求与响应、获取web资源

    一.请求与响应 Action1.含义:(1) struts.xml 中的 action 元素,也指 from 表单的 action 属性,总之代表一个 struts2 请求.(2) 用于处理 Stru ...

  2. java和javascript双引号嵌套的问题

    双引号不能嵌套双引号,否则一对双引号就结束了. java中双引号中嵌套双引号需要转义: /** * java双引号转义符测试 * @author asus * */ public static voi ...

  3. 图片轮播(淡入淡出)--JS原生和jQuery实现

    图片轮播(淡入淡出)--js原生和jquery实现 图片轮播有很多种方式,这里采用其中的 淡入淡出形式 js原生和jQuery都可以实现,jquery因为封装了很多用法,所以用起来就简单许多,转换成j ...

  4. Silverlight开源项目与第三方控件收集

    http://easysl.codeplex.com/ http://compositewpf.codeplex.com/ http://silverlight.codeplex.com/releas ...

  5. 使用事务操作SQLite数据批量插入,提高数据批量写入速度,源码讲解

    SQLite数据库作为一般单机版软件的数据库,是非常优秀的,我目前单机版的软件产品线基本上全部替换Access作为优选的数据库了,在开发过程中,有时候需要批量写入数据的情况,发现传统的插入数据模式非常 ...

  6. JAVA的网络编程基础概念

    网络编程的目的就是指直接或间接地通过网络协议与其他计算机进行通讯.网络编程中有两个主要的问题,一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输.在TCP/IP协 ...

  7. 无废话WCF入门教程一[什么是WCF]

    http://www.cnblogs.com/iamlilinfeng/archive/2012/09/25/2700049.html wcf技术交流,同学习共进步,欢迎加群:  群号:3981831 ...

  8. Python好用的网站收集

    第三方Pthon包查找:http://www.lfd.uci.edu/ http://www.cnblogs.com/lanxuezaipiao/p/3543658.html

  9. 织梦CMS的MVC体系

    13年无意中翻看DedeCMS的代码,发现DedeCMS中是有了一个基本MVC框架的,在现有的版本中,主要是应用到了ask.book等模块上. 织梦这个东西,里面很多设计思想是非常优秀的,但整体代码的 ...

  10. POJ 3233 Matrix Power Series 矩阵快速幂+二分求和

    矩阵快速幂,请参照模板 http://www.cnblogs.com/pach/p/5978475.html 直接sum=A+A2+A3...+Ak这样累加肯定会超时,但是 sum=A+A2+...+ ...