文章目录
简介
图数据库的基本含义是以“图”这种数据结构存储和查询数据,而不是存储图片的数据库。它的数据模型主要是以节点和关系(边)来体现,也可处理键值对。它的优点是快速解决复杂的关系问题。
数据结构:
在一个图中主要包含两种数据类型:Nodes(节点)和Relationships(关系)。他们各自内部又包含key-value形式的属性,然后节点之间通过关系相连,形成了关系型的网状结构
优点:
- 更快的数据库操作。当然,有一个前提条件,那就是数据量较大,在MySql中存储的话需要许多表,并且表之间联系较多(即有不少的操作需要join表)。
- 数据更直观,相应的SQL语句也更好写(Neo4j使用Cypher语言,与传统SQL有很大不同)。
- 更灵活。不管有什么新的数据需要存储,都是一律的节点和边,只需要考虑节点属性和边属性。而MySql中即意味着新的表,还要考虑和其他表的关系。
- 数据库操作的速度并不会随着数据库的增大有明显的降低。这得益于Neo4j特殊的数据存储结构和专门优化的图算法。
基础语法
neo4j的系统操作
:help server 查看系统操作
:server disconnect :退出当前账户
:servcer connect 当前登录信息
简单的 增删改查
-
增加数据:
增加节点
-
create (a:PersonP{name:“xxx”,age:22})
增加的时候,会自动生成ID,如果在新加的时候想得到ID:
-
create (a:PersonP{name:“xxx”,age:22}) retrun ID(a)
-
-
增加节点的关系:
match (p1:Person),(p2:Person) where p1.name=“李四” and p2.name=“张三” create (p1)-[:DD{relation:“弟弟”}]->(p2)
-
查询数据
1. 查询节点
-
查询全部节点
MATCH (n:Person) RETURN n LIMIT 25 LIMIT:限制只查25个,去掉限制就是查询全部
-
按条件查询
-
单条件:
MATCH (n:Person) where n.name=“李四” RETURN n
-
多条件:
MATCH (n:Person) where n.name=“李四” or n.name=“张三” RETURN n
或者
MATCH (n:Person{name:“李四”}),(n2:Person{name:“张三”}) RETURN n,n2
-
查询节点的关系
-
查询所有
MATCH p=()-[r:DD]->() RETURN p LIMIT 25 LIMIT:限制只查25个,去掉限制就是查询全部
-
按条件查询
MATCH (n:Person{name:“王五”})-[r:DD]->(n1:Person{name:“李四”}) RETURN r,n,n1
-
多级查询:
-
查所有
MATCH p=()-[r:DD]->()-[r1:DD]->() RETURN p LIMIT 25
-
按条件
MATCH (n1:Person{name:“王五”})-[r1:DD]->(n2:Person{name:“李四”})-[r2:DD]->(n3:Person{name:“张三”}) RETURN r1,r2,n1,n2,n3
-
-
-
-
-
修改数据
-
节点数据修改
match (n:Person) where n.name=“王五” set n.name=“赵六”
-
-
修改节点关系
MAtch (a:Person{name:“赵六”})-[r:DD]->(b:Person{name:“李四”}) create (a)-[r2:AA{relation:“哥哥”}]->(b) set r2=r WITH r delete r
-
删除
-
根据ID删除 并且删除其相关的关系
match ® where id®= 51439 detach delete r
-
删除节点的属性
match (n:Person{name:“张三”}) REMOVE n.age
-
删除所有
match (n:Person) delete n
-
删除没有关系的节点
match (n:Person) where n.name=“张三” delete n
-
删除所有关系
match (a)-[r]-(b) delete r
-
按条件删除节点间关系
MAtch (a:Person{name:“李四”})-[r:DD]-(b:Person{name:“张三”}) delete r
-
JAVA实现
在配置文件中,定义Neo4j的域名、密码等属性
#图库连接地址
neo4j.uri = bolt://192.168.80.108:7687,bolt://192.168.80.109:7687,bolt://192.168.80.110:7687
#图库连接用户名
neo4j.username = neo4j
#图库连接密码
neo4j.password = neo4j
#连接池总连接的最大数
neo4j.maxTotal = 5
#获取连接的最大等待毫秒数
neo4j.maxWaitMillis = 5000
编写config文件,读取配置文件的内容
@Data
@Configuration
@ConfigurationProperties(prefix = "neo4j")
public class Neo4jConfig {
/**
* 获取连接最长等待时长
*/
public static final Long DEFAULT_MAX_WAIT_MILLIS = 5000L;
public static final Boolean DEFAULT_TEST_ON_BORROW = true;
/**
* 多个用逗号分隔
*/
private String uri;
private String username;
private String password;
private String maxTotal;
private Boolean testOnBorrow = DEFAULT_TEST_ON_BORROW;
private Long maxWaitMillis = DEFAULT_MAX_WAIT_MILLIS;
}
因为是集群,所有编写Factory工厂类
@Component
public class Neo4jSessionFactory extends BasePooledObjectFactory<Session> {
@Autowired
Neo4jConfig neo4jConfig;
static Random random = new Random();
@Override
public Session create() throws Exception {
String driverUrl = chooseDriver(neo4jConfig.getUri().split(","));
return GraphDatabase.driver(driverUrl, AuthTokens.basic(neo4jConfig.getUsername(), neo4jConfig.getPassword())).session();
}
@Override
public PooledObject<Session> wrap(Session session) {
return new DefaultPooledObject<>(session);
}
@Override
public void destroyObject(PooledObject<Session> p) throws Exception {
Session session = p.getObject();
if (session != null) {
session.close();
}
}
@Override
public boolean validateObject(PooledObject<Session> p) {
Session session = p.getObject();
if (session != null) {
return session.isOpen();
} else {
return false;
}
}
/**
* 随机负载均衡
* @param datas
* @return
*/
private static String chooseDriver(String[] datas) {
int length = datas.length;
int dataIndex = random.nextInt(length);
return datas[dataIndex];
}
}
根据自己的业务,创建模板类(节点创建、关系创建)
public class Neo4jConstant {
/**
* 创建账号节点
*/
public static final String ACCOUNT_NODE_TEMPLATE_BY_PIGEONHOLE = "UNWIND {batch} as row \n" +
"MERGE(accountEntity:AccountEntity{id:row.id}) ON \n" +
"CREATE SET accountEntity.account=row.account,accountEntity.accountType=row.accountType";
/**
* 通联关系创建模板
*/
public static final String LINK_RELATION_TEMPLATE = "UNWIND {batch} as row \n" +
" match (n:AccountEntity),(m:AccountEntity) where n.id = row.account1 and m.id = row.`account2` \n" +
" MERGE (n) -[r:LINKS]->(m) \n" +
" ON CREATE SET r.relationTimes=row.relationTimes,r.firstRelationTime=row.firstRelationTime,r.lastRelationTime=row.lastRelationTime,r.type=row.type,r.linkType=row.linkType,r.linkFrom=row.linkFrom\n" +
" ON MATCH SET r.relationTimes=r.relationTimes+row.relationTimes,r.lastRelationTime=row.lastRelationTime\n" +
" return r";
}
编写neo4j的java具体实现,包含(增删改查,连接,关闭)
一般的查询,都需要自己组装成DSL语句
@Slf4j
@Service
public class Neo4jService {
@Autowired
Neo4jSessionFactory neo4jSessionFactory;
@Autowired
Neo4jConfig neo4jConfig;
GenericObjectPool genericObjectPool;
@PostConstruct
public void init() {
GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
if (StringUtils.isNotBlank(neo4jConfig.getMaxTotal())) {
genericObjectPoolConfig.setMaxTotal(Integer.valueOf(neo4jConfig.getMaxTotal()));
genericObjectPoolConfig.setMinIdle(Integer.valueOf(neo4jConfig.getMaxTotal()));
genericObjectPoolConfig.setMaxIdle(Integer.valueOf(neo4jConfig.getMaxTotal()));
}
genericObjectPoolConfig.setTestOnBorrow(neo4jConfig.getTestOnBorrow());
genericObjectPoolConfig.setMaxWaitMillis(neo4jConfig.getMaxWaitMillis());
genericObjectPool = new GenericObjectPool(neo4jSessionFactory, genericObjectPoolConfig);
}
/**
* 禁止使用此方法,会导致资源泄露问题 !!!
*
* @return
*/
public Session createNewSession() {
try {
return (Session) genericObjectPool.borrowObject();
} catch (Exception e) {
log.error("获取 neo4j 连接出错 {}", e);
}
return null;
}
/**
* 使用完会话必须关闭 !!!
*
* @param session
*/
public void closeSession(Session session) {
try {
if (session != null) {
genericObjectPool.returnObject(session);
}
} catch (Exception e) {
log.error("归还连接出错 {}", e);
}
}
/**
* 批量执行 dsl
*
* @param template
* @param params
*/
public void executeBatch(String template, JSONArray params) {
Session session = null;
try {
if (params != null && params.size() > 0) {
session = createNewSession();
JSONObject batch = new JSONObject();
batch.put("batch", params);
log.info("开始执行批插入 模板:{} 参数: {} ", template, params);
session.run(template, batch);
}
} catch (Exception e) {
log.error("executeBatch error:{}", e);
} finally {
closeSession(session);
}
}
public void executeBatch(Session session, String template, JSONArray params) {
try {
if (params != null && !params.isEmpty()) {
JSONObject batch = new JSONObject();
batch.put("batch", params);
log.info("开始执行批插入 模板:{} 参数: {} ", template, params);
session.run(template, batch);
}
} catch (Exception e) {
log.error("executeBatch error:{}, error msg:{}", e, e.getMessage());
}
}
/**
* 单条查询
*
* @param template
* @param param
* @return
*/
public List<Record> queryBatch(String template, Map<String, Object> param) {
List<Record> records = new ArrayList<>();
Session session = null;
try {
if (param != null && param.size() > 0) {
session = createNewSession();
JSONObject batch = new JSONObject();
batch.put("batch", param);
log.debug("开始执行批插入 模板:{} 参数: {} ", template, param);
StatementResult result = session.run(template, batch);
records = extractRecordsFromResult(result);
return records;
}
} finally {
closeSession(session);
}
return records;
}
/**
* 执行单条
*
* @param template
* @param param
*/
public void execute(String template, Map<String, Object> param) {
Session newSession = null;
try {
if (param != null && param.size() > 0) {
newSession = createNewSession();
newSession.run(template, param);
}
} finally {
closeSession(newSession);
}
}
/**
* 查询
*
* @param template
* @param param
* @return
*/
public List<Record> query(String template, Map<String, Object> param) {
Session newSession = null;
try {
newSession = createNewSession();
StatementResult result = newSession.run(template, param);
return extractRecordsFromResult(result);
} finally {
closeSession(newSession);
}
}
/**
* 查询单条结果
*
* @param template
* @param param
* @return
*/
public Record queryOne(String template, Map<String, Object> param) {
Session newSession = null;
try {
newSession = createNewSession();
StatementResult result = newSession.run(template, param);
return extractOneRecordsFromResult(result);
} finally {
closeSession(newSession);
}
}
public Record queryOne(String dsl) {
Session newSession = null;
try {
newSession = createNewSession();
StatementResult result = newSession.run(dsl);
return extractOneRecordsFromResult(result);
} finally {
closeSession(newSession);
}
}
/**
* 执行单条
*
* @param dsl
* @return
*/
public List<Record> queryByDsl(String dsl) {
Session newSession = null;
try {
newSession = createNewSession();
StatementResult result = newSession.run(dsl);
return extractRecordsFromResult(result);
} finally {
closeSession(newSession);
}
}
/**
* 抽取 record 中的指定 key
*
* @param record
* @param key
* @return
*/
public Map<String, Object> extractRecordToMap(Record record, String key) {
if (record != null) {
return record.get(key).asEntity().asMap();
}
return null;
}
public String extractRecordToString(Record record, String key) {
if (record != null) {
return record.get(key).asString();
}
return null;
}
/**
* 从 statementResult 中 抽取 Record
*
* @param statementResult
* @return
*/
public List<Record> extractRecordsFromResult(StatementResult statementResult) {
ArrayList<Record> records = new ArrayList<>();
while (statementResult.hasNext()) {
Record record = statementResult.next();
records.add(record);
}
return records;
}
/**
* 抽取单条记录
*
* @param statementResult
* @return
*/
public Record extractOneRecordsFromResult(StatementResult statementResult) {
if (statementResult.hasNext()) {
return statementResult.next();
}
return null;
}
}
创建结点、关联关系 调用接口
#获取连接
Session session = neo4jService.createNewSession();
#执行模板操作 nodeParams:结点数据,根据自己业务而定。 创建结点
neo4jService.executeBatch(session, Neo4jConstant.ACCOUNT_NODE_TEMPLATE_BY_PIGEONHOLE, nodeParams);
#执行模板操作 nodeRelationParams:关联关系数据,根据自己业务而定。 创建结点之间的关联关系
neo4jService.executeBatch(session, Neo4jConstant.LINK_RELATION_TEMPLATE, nodeRelationParams);