一、准备工作
1. 安装 Neo4j Python 驱动
pip install neo4j
✅ 推荐使用虚拟环境(如
venv或conda)隔离依赖。
2. 启动 Neo4j 数据库
确保你本地或远程的 Neo4j 实例正在运行(默认端口为 7687)。
默认用户名/密码为 neo4j / password(首次登录需修改密码)。
二、基础操作
1. 连接数据库
使用 GraphDatabase.driver() 创建驱动实例:
from neo4j import GraphDatabase URI = "bolt://localhost:7687" AUTH = ("neo4j", "your_password") # 替换为你的密码 driver = GraphDatabase.driver(URI, auth=AUTH)
🔒 安全提示:不要硬编码密码!建议使用环境变量或配置文件。
import os AUTH = ("neo4j", os.getenv("NEO4J_PASSWORD"))
2. 执行 Cypher 查询
使用 session.run() 执行查询:
with driver.session() as session: result = session.run("MATCH (n) RETURN count(n) AS nodeCount") record = result.single() print("Total nodes:", record["nodeCount"])
session是轻量级会话对象,自动管理连接池中的连接。- 使用
with语句确保会话正确关闭。
3. 参数化查询(防止注入)
永远不要拼接用户输入到 Cypher 字符串中!
✅ 正确做法:使用参数占位符 $param:
def get_person_by_name(tx, name): query = "MATCH (p:Person {name: $name}) RETURN p.age" result = tx.run(query, name=name) return [record["p.age"] for record in result] with driver.session() as session: ages = session.read_transaction(get_person_by_name, "Alice") print(ages)
💡 参数通过字典传入,驱动会自动转义,防止 Cypher 注入攻击。
4. 事务管理
Neo4j 驱动支持显式事务和隐式事务。
方式一:隐式事务(推荐)
使用 session.execute_write() 或 session.execute_read():
def create_person(tx, name, age): query = "CREATE (p:Person {name: $name, age: $age})" tx.run(query, name=name, age=age) with driver.session() as session: session.execute_write(create_person, "Bob", 30)
方式二:显式事务
with driver.session() as session: tx = session.begin_transaction() try: tx.run("CREATE (p:Person {name: 'Charlie'})") tx.commit() except Exception as e: tx.rollback() raise e
⚠️ 显式事务需手动
commit()或rollback(),否则可能挂起。
5. 结果处理(Record 与 Value)
session.run() 返回 Result 对象,可迭代获取 Record:
result = session.run("MATCH (p:Person) RETURN p.name AS name, p.age AS age") for record in result: print(record["name"], record["age"]) # 通过 key 访问 print(record[0], record[1]) # 通过索引访问 print(dict(record)) # 转为字典
Record类似命名元组,支持键和索引访问。- 节点/关系对象可通过
.get()或直接访问属性。
6. 错误处理
常见异常来自 neo4j.exceptions 模块:
from neo4j import ServiceUnavailable, TransientError, AuthError try: with driver.session() as session: session.run("MATCH (n) RETURN n LIMIT 1") except ServiceUnavailable: print("数据库不可用,请检查连接") except AuthError: print("认证失败,请检查用户名/密码") except TransientError as e: print(f"临时错误(如死锁): {e}") except Exception as e: print(f"其他错误: {e}")
ServiceUnavailable:网络或服务宕机。TransientError:可重试的错误(如并发冲突)。- 建议对
TransientError实现重试逻辑。
三、最佳实践
1. 使用连接池
GraphDatabase.driver() 默认启用连接池,无需额外配置。但可调整参数:
driver = GraphDatabase.driver( URI, auth=AUTH, max_connection_pool_size=50, connection_timeout=30.0, max_connection_lifetime=3600 # 连接最大存活时间(秒) )
📌 生产环境中根据负载调整池大小。
2. 封装为 DAO 类(数据访问对象)
将数据库操作封装为类,提高可维护性:
from neo4j import GraphDatabase import logging class PersonDAO: def __init__(self, uri, auth): self.driver = GraphDatabase.driver(uri, auth=auth) logging.basicConfig(level=logging.INFO) def close(self): self.driver.close() def _log_query(self, query, params): logging.info(f"Executing: {query} | Params: {params}") def create_person(self, name, age): def _create(tx, name, age): query = "CREATE (p:Person {name: $name, age: $age})" self._log_query(query, {"name": name, "age": age}) tx.run(query, name=name, age=age) with self.driver.session() as session: session.execute_write(_create, name, age) def find_person_by_name(self, name): def _find(tx, name): query = "MATCH (p:Person {name: $name}) RETURN p.name AS name, p.age AS age" self._log_query(query, {"name": name}) result = tx.run(query, name=name) return [dict(record) for record in result] with self.driver.session() as session: return session.execute_read(_find, name)
使用示例:
dao = PersonDAO("bolt://localhost:7687", ("neo4j", "password")) dao.create_person("David", 25) people = dao.find_person_by_name("David") print(people) dao.close()
3. 日志记录查询语句(便于调试)
如上例所示,在 DAO 中添加 _log_query() 方法,配合 logging 模块输出查询语句和参数。
🔍 开发阶段开启 INFO 日志,生产环境可关闭或降级为 WARNING。
四、完整示例:用户管理系统
# neo4j_dao.py from neo4j import GraphDatabase import logging import os logging.basicConfig(level=logging.DEBUG) class UserDAO: def __init__(self): uri = os.getenv("NEO4J_URI", "bolt://localhost:7687") user = os.getenv("NEO4J_USER", "neo4j") password = os.getenv("NEO4J_PASSWORD") if not password: raise ValueError("NEO4J_PASSWORD 环境变量未设置") self.driver = GraphDatabase.driver(uri, auth=(user, password)) def close(self): self.driver.close() def _run_query(self, tx, query, **params): logging.debug(f"Query: {query} | Params: {params}") return tx.run(query, **params) def create_user(self, username, email): def _create(tx): query = """ CREATE (u:User {username: $username, email: $email, created_at: timestamp()}) RETURN u """ return self._run_query(tx, query, username=username, email=email).single() with self.driver.session() as session: return session.execute_write(_create) def get_user_by_email(self, email): def _get(tx): query = "MATCH (u:User {email: $email}) RETURN u" return self._run_query(tx, query, email=email).single() with self.driver.session() as session: return session.execute_read(_get) # 使用 if __name__ == "__main__": dao = UserDAO() dao.create_user("alice", "alice@example.com") user = dao.get_user_by_email("alice@example.com") print(user["u"]["username"] if user else "Not found") dao.close()
五、总结
| 主题 | 关键点 |
|---|---|
| 连接 | 使用 GraphDatabase.driver(),避免硬编码凭证 |
| 查询 | 始终使用参数化查询 $param |
| 事务 | 优先使用 execute_read/write,避免手动事务 |
| 错误处理 | 捕获 ServiceUnavailable、TransientError |
| 最佳实践 | 封装 DAO、启用日志、利用连接池 |
通过以上方法,你可以安全、高效地在 Python 应用中集成 Neo4j 图数据库。
浙公网安备 33010602011771号