近期由于工作需要,需要做一个人口关系大图的存储及检索方案,我们主要的数据对象有:人口(年龄,身份证号码,性别。。) ;学校信息(学校地址,学校名称,学校级别,学校下边的年级班级。。);就职信息(公司名称,公司地址,公司企业信用代码。。)以及论文发布信息(论文主题,参与人,发布时间)

问题思考:

  1. 针对这类人口关系大图,或者叫图谱的数据一般都是非关系型,而且有多钟关系的存在,比如张三跟李四两个人,可能是同学,同事,老乡,配偶,夫妻,等多种关系,那一般如果采取关系型数据库(oracle)为例,可能就需要建立人员信息表, 同学关系表,同事关系表,老乡关系表,配偶关系表,夫妻关系表,这样一旦后续增加一个关系,就要增加一张表,增加一张表,而且后续数据库中就基本上只剩关系表了,后续的维护,扩充,代码的修改就机器的困难,简直就是噩梦。
  2. 通过上述的第一点,我们不难看出,采用关系型数据库来存储并不是最好的解决方案,那么就很容易想到采用关系型数据库存,那么目前比较流行的有哪些非关系型数据库么,redis,mongoDB,neo4j都能存储单个菲关系型的数据,但他们三者还是有区别的,前两者一般在项目中作为一个数据缓存的容器,将一些不经常变化的数据放在容器中,需者自取。而neoj4j专门用于网络图的存储,网络图,这个跟我们的项目需求不是不谋而合?于是心底暗自拍板,就你了 ,neo4j。

数据库设计:

通过关系表中存储的ID来进行关联;注意设置的时候是将与一个人所有关联的信息进行抽象出来并划分为五个对象,即上图的五个表单。 通过上图我们可以和明确的看出来有如下几种关系: 人 -就读->学校;

人 -发布论文->论文; 人-就职->企业; 人-居住->家庭;这四类的外部关系(以单个人节点为参照),另外一方面,人根根之间也是存在关系的,比如刚开始提到的同事,同学类的,那么这类属于内部关系,内部关系我们一般需要通过外部关系区分,比如张三和李四只有在同一个班级上学,那么菜算得上同学吧,只有在同一个公司就职才算得上同事吧.。这样的话,我们数据库设计出来,逻辑也梳理出来,尤其是内部关系外部关系。

实现方案:

我项目采用的是spring+springMvc的环境。

组件代码:

注意我是将neo4j的配置及实体放在一块的,至于其他的逻辑操作在其他的包中。

部分代码:

设置Configuration属性到neo4j中
package com.audaque.module.graphData.neo4j.config;

import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement; /***
*@ClassName AppConfiguration
*@desc TODO
*@Author xxxxxx
*@Date 2019/5/17 0017 下午 10:57
*@version 1.0.1
**/
@Configuration
@EnableNeo4jRepositories(basePackages = "com.audaque.module.graphData.neo4j.repos")
@EnableTransactionManagement
@ComponentScan("com.audaque.module.graphData.neo4j.config")
public class AppConfiguration extends Neo4jConfiguration {
@Autowired
private org.neo4j.ogm.config.Configuration config; @Bean
public org.neo4j.ogm.config.Configuration getConfiguration() {
return config;
}
@Override
@Bean
public SessionFactory getSessionFactory() {
// with domain entity base package(s)
return new SessionFactory(config,"com.audaque.module.graphData.neo4j.model");
}
@Override
@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
return super.getSession();
}
}

  

动态设置属性到Configuration中属性到中
package com.audaque.module.graphData.neo4j.config;

import org.neo4j.ogm.authentication.UsernamePasswordCredentials;
import org.neo4j.ogm.config.Configuration; import static org.neo4j.ogm.config.DriverConfiguration.CREDENTIALS;
import static org.neo4j.ogm.config.DriverConfiguration.DRIVER;
import static org.neo4j.ogm.config.DriverConfiguration.URI; /***
*@ClassName MyConfiguration
*@desc 自定义bean动态配置neo4j数据源。
*@Author xiaokang.ma@audaque.com
*@Date 2019/5/24 9:14
*@version 1.0.1
**/
public class MyConfiguration extends Configuration {
public MyConfiguration(String driverClass,String driverURL,String userName,String password){
super.set(DRIVER[0],driverClass);
super.set(URI[0],driverURL);
super.set(CREDENTIALS[0],new UsernamePasswordCredentials(userName, password)); } }
PaperInfoRepository配置(实体的保存):
package com.audaque.module.graphData.neo4j.repos;

import com.audaque.module.graphData.neo4j.model.PaperInfo;
import org.springframework.data.neo4j.repository.GraphRepository;
import org.springframework.stereotype.Repository; /***
*@ClassName PaperInfoRepository
*@desc 论文信息接口
*@Author xxxx
*@Date 2019/5/17 0017 下午 5:43
*@version 1.0.1
**/
@Repository
public interface PaperInfoRepository extends GraphRepository<PaperInfo> { }

内部关系的Repository我采用的自定义查询语句查询,如下:

package com.audaque.module.graphData.neo4j.repos;

import com.audaque.module.graphData.neo4j.model.Neo4jRelationInfo;
import org.neo4j.consistency.store.paging.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.GraphRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository; import java.util.Collection;
import java.util.List;
import java.util.Map; /***
*@ClassName Neo4jRelationInfoRepository
*@desc 八大关系型数据。
*@Author xxxx
*@Date 2019/5/17 0017 下午 5:43
*@version 1.0.1
**/
@Repository
public interface Neo4jRelationInfoRepository extends GraphRepository<Neo4jRelationInfo> { /**
* pe - pe 邻居关系
* @param fromId
* @param toId
* @return
*/
@Query(value = "match(a:pe),(b:pe) where a.ids={fromId} and b.ids={toId} merge (a) - [r:邻居{direction:{direction}}]->(b)")
Neo4jRelationInfo generateGraphDataRelation_LJ(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /**
* pe -> pe 同学关系
* @param fromId
* @param toId
* @return
*/
@Query(value = "match(a:pe),(b:pe) where a.ids={fromId} and b.ids={toId} merge (a) - [r:同学{direction:{direction}}]->(b)")
Neo4jRelationInfo generateGraphDataRelation_TX(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /**
* pe -> pe 校友关系
* @param fromId
* @param toId
* @return
*/
@Query(value = "match(a:pe),(b:pe) where a.ids={fromId} and b.ids={toId} merge (a) <- [r:校友{direction:{direction}}]->(b)")
Neo4jRelationInfo generateGraphDataRelation_XY(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /**
* pe -> li 居住关系
* @param fromId
* @param toId
* @return
*/
@Query(value = "match(a:pe),(b:li) where a.ids={fromId} and b.ids={toId} merge (a) - [r:居住{direction:{direction}}]->(b)")
Neo4jRelationInfo generateGraphDataRelation_JUZ(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /**
* pe -> pe 同事关系
* @param fromId
* @param toId
* @return
*/
@Query(value = "match(a:pe),(b:pe) where a.ids={fromId} and b.ids={toId} merge (a) - [r:同事{direction:{direction}}]-(b)")
Neo4jRelationInfo generateGraphDataRelation_TS(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /**
* pe -> wu 就职关系
* @param fromId
* @param toId
* @return
*/
@Query(value = "match(a:pe),(b:wu) where a.ids={fromId} and b.ids={toId} merge (a) - [r:就职{direction:{direction}}]->(b)")
Neo4jRelationInfo generateGraphDataRelation_JZGX(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /**
* pe -> sc 毕业院校关系
* @param fromId
* @param toId
* @return
*/
@Query(value = "match(a:pe),(b:sc) where a.ids={fromId} and b.ids={toId} merge (a) - [r:毕业{direction:{direction}}]->(b)")
Neo4jRelationInfo generateGraphDataRelation_BYYX(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /**
* pe -> pa 论文参与关系
* @param fromId
* @param toId
* @return
*/
@Query(value = "match(a:pe),(b:pa) where a.ids={fromId} and b.ids={toId} merge (a) - [r:参与者{direction:{direction}}]->(b)")
Neo4jRelationInfo generateGraphDataRelation_PA(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /**
* pe -> pa 论文合作关系
* @param fromId
* @param toId
* @return
*/
@Query(value = "match(a:pe),(b:pe) where a.ids={fromId} and b.ids={toId} merge (a) - [r:论文合作{direction:{direction}}]->(b)")
Neo4jRelationInfo generateGraphDataRelation_LWHZ(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /**
* web查询接口,查询与当前节点为1的数据---根据节点编号,适用于登录第二次节点点击之后的查询
*/
@Query(value = "match(a)-[r]-(b) where a.ids={qid} return a,r,b")
List<Map<String,String>> queryRelationDataByIds(@Param("qid") String qid); /**
* web查询接口,查询与当前节点为1的数据
*/
@Query(value = "match(a:pe)-[r]-(b) where a.cardNo={cardNo} return a,r,b")
List<Map<String,String>> queryRelationDataByCardNo(@Param("cardNo") String cardNo); }

bean配置:

<!--初始化配置的Config-->
<bean id="myConfiguration" class="com.audaque.module.graphData.neo4j.config.MyConfiguration">
<constructor-arg name="driverClass" value="org.neo4j.ogm.drivers.http.driver.HttpDriver" index="0" type="java.lang.String"/>
<constructor-arg name="driverURL" value="http://10.229.183.142:7474" index="1" type="java.lang.String"/>
<constructor-arg name="userName" value="neo4j" index="2" type="java.lang.String"/>
<constructor-arg name="password" value="123456" index="3" type="java.lang.String"/>
</bean>

通过上述的简单配置就在spring环境中搭建到了neo4j。

业务逻辑代码:

  1. 存储所有的节点,直接读取数据中的人员信息,学校信息,居住信息,教育信息,工作信息,5张表的数据直接save到neo4j,部分代码如下.

  

 /**
* 初始化图数据需要的人员节点数据
*
* @param personBaseInfoRepository
* @return
*/
@Override
public void resetNeo4jPersonBaseInfoNode(PersonBaseInfoRepository personBaseInfoRepository) {
//查询人员节点列表
List<PersonBaseInfo> personBaseInfos = graphDataDao.queryPersonBaseInfo(); for (PersonBaseInfo personBaseInfo :personBaseInfos){
try {
personBaseInfoRepository.save(personBaseInfo);
}
catch (Throwable t){
//生成节点异常时,继续跳过。
continue;
}
}
}

初始化内部外部关心数据,代码如下:

 /********************************************开始初始化节点间关系的数据**********************************************/

    /**
* 初始化五大节点间的关系数据。
* @param neo4jRelationInfoRepository
* @return
*/
@Override
public void resetAllRelationInfo(Neo4jRelationInfoRepository neo4jRelationInfoRepository) {
long start = System.currentTimeMillis();
//初始化人员-->工作关系,人员->学校;人员->论文;人员->住房信息4个主关系的数据。
List<Map<String,Object>> lists = graphDataDao.queryEntityRelationInfo();
//计算内部关系信息 ,同事,校友,邻居,合作者关系。
Map<String,List<String>> schoolFriendMap = new ConcurrentHashMap<>(); //校友 Map<String,List<String>> workTogetherMap = new ConcurrentHashMap<>(); //同事 Map<String,List<String>> neighborMap = new ConcurrentHashMap<>(); //邻居
Map<String,List<String>> collaboratorMap = new ConcurrentHashMap<>();//论文合作者
for(Map<String,Object> map :lists){ String userId = "";
//获取人员信息
if(StringUtils.isNoneEmpty((String)map.get("USERID")) && StringUtils.isNoneEmpty((String)map.get("USERNAME"))){
//取工作单位字段 UNIT_ID , UNIT_NAME
if(StringUtils.isNoneEmpty((String)map.get("UNIT_ID")) && StringUtils.isNoneEmpty((String)map.get("UNIT_NAME"))){
//插入就职关系
neo4jRelationInfoRepository.generateGraphDataRelation_JZGX((String)map.get("USERID"),(String)map.get("UNIT_ID"),(String)map.get("USERID")+"->"+(String)map.get("UNIT_ID")); //处理同事关系
if(workTogetherMap.containsKey((String)map.get("UNIT_ID"))){
List<String> list = workTogetherMap.get((String)map.get("UNIT_ID"));
if(!list.contains((String)map.get("USERID"))){
list.add((String)map.get("USERID"));
workTogetherMap.put((String)map.get("UNIT_ID"),list);
}
}else{
List<String> lis = new ArrayList<String>();
lis.add((String)map.get("USERID"));
workTogetherMap.put((String)map.get("UNIT_ID"),lis);
} }
//取学校字段 SCHOOL_ID , SCHOOL_NAME
if(StringUtils.isNoneEmpty((String)map.get("SCHOOL_ID")) && StringUtils.isNoneEmpty((String)map.get("SCHOOL_NAME"))){
//插入毕业院校
neo4jRelationInfoRepository.generateGraphDataRelation_BYYX((String)map.get("USERID"),(String)map.get("SCHOOL_ID"),(String)map.get("USERID")+"->"+(String)map.get("SCHOOL_ID")); //处理校友关系
if(schoolFriendMap.containsKey((String)map.get("SCHOOL_ID"))){
List<String> list = schoolFriendMap.get((String)map.get("SCHOOL_ID"));
if(!list.contains((String)map.get("USERID"))){
list.add((String)map.get("USERID"));
schoolFriendMap.put(this.schoolFriend,list);
}
}else{
List<String> lis = new ArrayList<String>();
lis.add((String)map.get("USERID"));
schoolFriendMap.put((String)map.get("SCHOOL_ID"),lis);
} } //取住房信息字段 HOUSE_ID , HOUSE_ADDR
if(StringUtils.isNoneEmpty((String)map.get("HOUSE_ID")) && StringUtils.isNoneEmpty((String)map.get("HOUSE_ADDR"))){
//插入毕业院校
neo4jRelationInfoRepository.generateGraphDataRelation_JUZ((String)map.get("USERID"),(String)map.get("HOUSE_ID"),(String)map.get("USERID")+"->"+(String)map.get("HOUSE_ID")); //处理邻居关系
if(neighborMap.containsKey((String)map.get("HOUSE_ID"))){
List<String> list = neighborMap.get((String)map.get("HOUSE_ID"));
if(!list.contains((String)map.get("USERID"))){
list.add((String)map.get("USERID"));
neighborMap.put((String)map.get("HOUSE_ID"),list);
}
}else{
List<String> lis = new ArrayList<String>();
lis.add((String)map.get("USERID"));
neighborMap.put((String)map.get("HOUSE_ID"),lis);
}
}
//取论文信息字段 LW_ID , LW_TITLE
if(StringUtils.isNoneEmpty((String)map.get("LW_ID")) && StringUtils.isNoneEmpty((String)map.get("LW_TITLE"))){
//插入毕业院校
neo4jRelationInfoRepository.generateGraphDataRelation_PA((String)map.get("USERID"),(String)map.get("LW_ID"),(String)map.get("USERID")+"->"+(String)map.get("LW_ID"));
//处理合作者关系
if(collaboratorMap.containsKey((String)map.get("LW_ID"))){
List<String> list = collaboratorMap.get((String)map.get("LW_ID"));
if(!list.contains((String)map.get("USERID"))){
list.add((String)map.get("USERID"));
collaboratorMap.put((String)map.get("LW_ID"),list);
}
}else{
List<String> lis = new ArrayList<String>();
lis.add((String)map.get("USERID"));
collaboratorMap.put((String)map.get("LW_ID"),lis);
}
} }
} //开始剔除长度为1的数据,没有对应的关系。
schoolFriendMap.forEach((key,value) -> {
//独立个体无法形成关系,剔除
if(((List<String>)value).size()<=1){
schoolFriendMap.remove(key);
}
});
workTogetherMap.forEach((key,value) -> {
//独立个体无法形成关系,剔除
if(((List<String>)value).size()<=1){
workTogetherMap.remove(key);
}
});
neighborMap.forEach((key,value) -> {
//独立个体无法形成关系,剔除
if(((List<String>)value).size()<=1){
neighborMap.remove(key);
}
});
collaboratorMap.forEach((key,value) -> {
//独立个体无法形成关系,剔除
if(((List<String>)value).size()<=1){
collaboratorMap.remove(key);
}
}); //开始遍历校友关系数据,生成节点。
schoolFriendMap.forEach((key,value) -> {
List<String> li = (List<String>)value;
for(int st = 0;st<li.size() ; st++){
for(int ss = li.size()-1;ss>st;ss--){
System.out.println("生成关系"+li.get(st)+"->"+li.get(ss));
neo4jRelationInfoRepository.generateGraphDataRelation_XY(li.get(st),li.get(ss),"<->"); }
}
});
//同事关系
workTogetherMap.forEach((key,value) -> {
List<String> li = (List<String>)value;
for(int st = 0;st<li.size() ; st++){
for(int ss = li.size()-1;ss>st;ss--){
System.out.println("生成关系"+li.get(st)+"->"+li.get(ss));
neo4jRelationInfoRepository.generateGraphDataRelation_TS(li.get(st),li.get(ss),"<->"); }
}
});
//邻居关系
neighborMap.forEach((key,value) -> {
List<String> li = (List<String>)value;
for(int st = 0;st<li.size() ; st++){
for(int ss = li.size()-1;ss>st;ss--){
System.out.println("生成关系"+li.get(st)+"->"+li.get(ss));
neo4jRelationInfoRepository.generateGraphDataRelation_LJ(li.get(st),li.get(ss),"<->"); }
}
});
//合作者关系
collaboratorMap.forEach((key,value) -> {
List<String> li = (List<String>)value;
for(int st = 0;st<li.size() ; st++){
for(int ss = li.size()-1;ss>st;ss--){
System.out.println("生成关系"+li.get(st)+"->"+li.get(ss));
neo4jRelationInfoRepository.generateGraphDataRelation_LWHZ(li.get(st),li.get(ss),"<->"); }
}
});
System.out.println("总共花费时间:" + (System.currentTimeMillis() - start) + "ms"); }

然后Control中对外暴露三个接口, 1.初始化节点,  2.初始化关系。 就可以了。

然后通过浏览器直接可以看到图数据的结构,分享下我做出来的效果。

至此一个从数据库存入到neo4j数据库中,然后可以对外开放一个查询的接口,返回与之有关系并且深度为1的数据,方便其他系统展示,接口如下:

/**
* web查询接口,查询与当前节点为1的数据---根据节点编号,适用于登录第二次节点点击之后的查询。
*/
@Query(value = "match(a)-[r]-(b) where a.ids={qid} return a,r,b")
List<Map<String,String>> queryRelationDataByIds(@Param("qid") String qid); /**
* web查询接口,用户登录之后第一次查询,需根据身份证号码查询。第二次查询就直接会调用上一个查询。
*/
@Query(value = "match(a:pe)-[r]-(b) where a.cardNo={cardNo} return a,r,b")
List<Map<String,String>> queryRelationDataByCardNo(@Param("cardNo") String cardNo);

基于neo4j图数据库,实现人口关系大图的基本思路及实现方案。的更多相关文章

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

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

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

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

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

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

  4. 使用neo4j图数据库的import工具导入数据 -方法和注意事项

    背景 最近我在尝试存储知识图谱的过程中,接触到了Neo4j图数据库,这里我摘取了一段Neo4j的简介: Neo4j是一个高性能的,NOSQL图形数据库,它将结构化数据存储在网络上而不是表中.它是一个嵌 ...

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. yii2 AR模型使用exists添加子查询与父查询关联

    有A,B两个表对应A_AR,B_AR两个模型B表interval_id对应A表id现在要查a表的数据,且没有code为a的子数据要求使用yii2的AR模型写查询: A_AR::find()->w ...

  2. Echarts同一页面多个图表自适应浏览器窗口大小——window.onresize

    当前做的一个项目中,频繁使用到百度团队的Echarts,发在一个页面同时出现多个图表时,只有最后一个图表触发了window.onresize事件,查询官方文档后得到解决. 方法如下: hwChart. ...

  3. linux增加/删除虚拟IP地址

    网卡上增加一个IP: ifconfig eth0:1 192.168.0.1 netmask 255.255.255.0 删除网卡的第二个IP地址: ip addr del 192.168.0.1 d ...

  4. Jedis使用工具类

    Redis 使用工具类: package com.qlwb.business.common.redis; import org.apache.log4j.Logger; import redis.cl ...

  5. 常见SQL优化方法

    SQL优化的一些方法 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否 ...

  6. Catch the moments of your life. Catch them while you're young and quick.

    Catch the moments of your life. Catch them while you're young and quick.趁你还年轻利落,把握住生活中的美好瞬间吧!

  7. js addEventListener调用传参函数

    先看这段代码 function abc(key){ console.log(key); } for(let i=0;i<oInput.length;i++){ oInput[i].addEven ...

  8. 解决The Network Adapter could not establish the connection

    解决1 主机与虚拟机ping不通 解决2 状态: 失败 -测试失败: IO 错误: The Network Adapter could not establish the connection 本次尝 ...

  9. 用C++/CLI搭建C++和C#之间的桥梁

    一.简单用法 C#和C++是非常相似的两种语言,然而我们却常常将其用于两种不同的地方,C#得益于其简洁的语法和丰富的类库,常用来构建业务系统.C++则具有底层API的访问能力和拔尖的执行效率,往往用于 ...

  10. Java开发工具IntelliJ IDEA本地历史记录的使用方法

    IntelliJ IDEA的本地历史记录可以帮助用户记录并跟踪本地项目的更改,防止项目的意外丢失或来源于IDE之外的项目更改.本教程将展示如何使用本地历史记录查看和恢复某些项目更改. 1 .从头开始创 ...