图数据库应用
架构图
MySQL表结构设计
-- 节点表
CREATE TABLE person_node (
id BIGINT PRIMARY KEY,
name VARCHAR(100),
gender VARCHAR(10),
birth_date DATE,
address TEXT,
create_time DATETIME,
update_time DATETIME
);
-- 关系表
CREATE TABLE person_relation (
id BIGINT PRIMARY KEY,
source_id BIGINT,
target_id BIGINT,
relation_type VARCHAR(50),
create_time DATETIME,
FOREIGN KEY (source_id) REFERENCES person_node(id),
FOREIGN KEY (target_id) REFERENCES person_node(id)
);
-- 画像表
CREATE TABLE person_profile (
person_id BIGINT PRIMARY KEY,
education VARCHAR(100),
occupation VARCHAR(100),
income_level VARCHAR(50),
hobby TEXT,
FOREIGN KEY (person_id) REFERENCES person_node(id)
);
数据同步实现
@Service
@Slf4j
public class DataSyncService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private Neo4jTemplate neo4jTemplate;
@Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行
public void syncData() {
try {
// 1. 同步节点数据
syncNodes();
// 2. 同步关系数据
syncRelations();
// 3. 构建图索引
buildGraphIndex();
} catch (Exception e) {
log.error("数据同步失败", e);
throw e;
}
}
private void syncNodes() {
String sql = "SELECT * FROM person_node";
List<Person> persons = jdbcTemplate.query(sql, new PersonRowMapper());
for (Person person : persons) {
String cypher = "MERGE (p:Person {id: $id}) " +
"SET p.name = $name, p.gender = $gender, " +
"p.birthDate = $birthDate, p.address = $address";
neo4jTemplate.query(cypher, Parameters.of(
"id", person.getId(),
"name", person.getName(),
"gender", person.getGender(),
"birthDate", person.getBirthDate(),
"address", person.getAddress()
));
}
}
}
后端服务实现
@Service
public class FamilyGraphService {
@Autowired
private Neo4jTemplate neo4jTemplate;
@Autowired
private JdbcTemplate jdbcTemplate;
public GraphResult getPersonRelations(Long personId, int depth) {
String cypher = """
MATCH path = (p:Person {id: $personId})-[r*1..%d]-(related)
RETURN path
""".formatted(depth);
return neo4jTemplate.query(cypher,
Parameters.of("personId", personId));
}
public PersonProfile getPersonProfile(Long personId) {
String sql = "SELECT * FROM person_profile WHERE person_id = ?";
return jdbcTemplate.queryForObject(sql,
new Object[]{personId},
new PersonProfileMapper());
}
}
前端React实现
const FamilyGraph: React.FC = () => {
const [graphData, setGraphData] = useState<any>(null);
const [selectedPerson, setSelectedPerson] = useState<any>(null);
const [depth, setDepth] = useState(2);
// 获取图数据
const fetchGraphData = async (personId: string, depth: number) => {
const response = await axios.get(
`/api/family/graph/${personId}?depth=${depth}`
);
setGraphData(response.data);
};
// 获取画像数据
const fetchProfile = async (personId: string) => {
const response = await axios.get(
`/api/family/profile/${personId}`
);
setSelectedPerson(response.data);
};
return (
<div className="family-graph">
<div className="graph-container">
<GraphVis
data={graphData}
onNodeClick={(node) => fetchProfile(node.id)}
/>
</div>
<div className="profile-panel">
{selectedPerson && (
<ProfileCard person={selectedPerson} />
)}
</div>
<div className="control-panel">
<DepthControl
value={depth}
onChange={setDepth}
/>
</div>
</div>
);
};