使用 Spring Data Neo4j
文章目录
maven 依赖
pom依赖中只要加入如下即可:
注意:SDN默认使用 Bolt Driver 连接的方式,若要使用 embedded driver 或 HTTP driver 需要加入相关依赖
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
<version>{version}</version>
</dependency>
配置文件属性
在application.properties 中配置neo4j 的相关属性:
# NEO4J (Neo4jProperties)
spring.data.neo4j.auto-index=none # Auto index mode.
spring.data.neo4j.embedded.enabled=true # Whether to enable embedded mode if the embedded driver is available.
spring.data.neo4j.open-in-view=true # Register OpenSessionInViewInterceptor. Binds a Neo4j Session to the thread for the entire processing of the request.
spring.data.neo4j.password= # Login password of the server.
spring.data.neo4j.repositories.enabled=true # Whether to enable Neo4j repositories.
spring.data.neo4j.uri= # URI used by the driver. Auto-detected by default.
spring.data.neo4j.username= # Login user of the server.
属性文件配置的形式只有这几个属性可以设置,通过查看 org.springframework.boot.autoconfigure.data.neo4j.Neo4jProperties
的源码可以知道:
@ConfigurationProperties(prefix = "spring.data.neo4j")
public class Neo4jProperties implements ApplicationContextAware {
static final String EMBEDDED_DRIVER = "org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver";
static final String HTTP_DRIVER = "org.neo4j.ogm.drivers.http.driver.HttpDriver";
static final String DEFAULT_BOLT_URI = "bolt://localhost:7687";
static final String BOLT_DRIVER = "org.neo4j.ogm.drivers.bolt.driver.BoltDriver";
/**
* URI used by the driver. Auto-detected by default.
*/
private String uri;
/**
* Login user of the server.
*/
private String username;
/**
* Login password of the server.
*/
private String password;
/**
* Auto index mode.
*/
private AutoIndexMode autoIndex = AutoIndexMode.NONE;
/**
* Register OpenSessionInViewInterceptor. Binds a Neo4j Session to the thread for the
* entire processing of the request.",
*/
private Boolean openInView;
private final Embedded embedded = new Embedded();
private ClassLoader classLoader = Neo4jProperties.class.getClassLoader();
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.classLoader = ctx.getClassLoader();
}
/**
* Create a {@link Configuration} based on the state of this instance.
* @return a configuration
*/
public Configuration createConfiguration() {
Builder builder = new Builder();
configure(builder);
return builder.build();
}
private void configure(Builder builder) {
if (this.uri != null) {
builder.uri(this.uri);
}
else {
configureUriWithDefaults(builder);
}
if (this.username != null && this.password != null) {
builder.credentials(this.username, this.password);
}
builder.autoIndex(this.getAutoIndex().getName());
}
private void configureUriWithDefaults(Builder builder) {
if (!getEmbedded().isEnabled()
|| !ClassUtils.isPresent(EMBEDDED_DRIVER, this.classLoader)) {
builder.uri(DEFAULT_BOLT_URI);
}
}
public static class Embedded {
/**
* Whether to enable embedded mode if the embedded driver is available.
*/
private boolean enabled = true;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
//省略相关的 getter 和 setter
}
Java Config 配置
如果要自定义配置属性,则需要使用 Java Config 的形式,本人在测试 SDN 的并发写入属性时遇到连接池资源不足的情况,需要设置连接池大小,OGM(Object-Graph Mapping)中提供了这个属性,但是在SDN中并没用对外暴露设置,所以使用application.properties
没用默认可配置属性,可以使用如下Java Config的方法来实现一些特殊属性的配置:@Value
注解的属性在application.properties
中进行声明管理就可以使用了
Java Config:
/**
* Neo4j OGM 配置
*/
@Configuration
public class Neo4jConfig {
@Value("${neo4j.uri}")
private String uri;
@Value("${neo4j.username}")
private String username;
@Value("${neo4j.password}")
private String password;
@Value("${neo4j.connection.pool.size}")
private Integer connectionPoolSize;
@Bean
public SessionFactory getSessionFactory() {
return new SessionFactory(configuration(), "com.kay.model.neo4j");
}
@Bean
public Neo4jTransactionManager transactionManager(){
return new Neo4jTransactionManager(getSessionFactory());
}
@Bean
public org.neo4j.ogm.config.Configuration configuration() {
return new org.neo4j.ogm.config.Configuration.Builder()
.uri(uri)
.connectionPoolSize(connectionPoolSize) //配置连接池大小
.credentials(username,password) //用户密码
.autoIndex("update") //索引策略
.build();
}
}
Neo4j 与 对象实体间的映射
节点映射
实体对象的配置:比如我在 Neo4j 中有 一个 User 节点,添加 OGM 提供的注解进行如下映射即可:
/**
* Created by kay on 2018/6/20
*/
@Data //这是lombok 的注解
@NodeEntity
public class User {
@Id
@GeneratedValue
private Long id; // neo4j 自动生成的唯一ID
private String userId;
private String phone;
private String nickName;
@DateLong //将日期类型转换为时间戳,或者用 @DateString 转换为String
private Date createTime;
}
关系映射
比如一个用户 User
,他可能参演了一部电影 Movie
, 那么这之间就有一种关系,演员参演电影:
(u:User)-[ACTED_IN]->(m:Movie) , 用对象来表示就是:
// 参演
@RelationshipEntity(type = "ACTED_IN")
public class Role {
@Id
@GeneratedValue
private Long id;
private List<String> roles = new ArrayList<>();
@StartNode // 代表关系的起点
private User user;
@EndNode //代表关系的终点
private Movie movie;
public Role() {
}
public Role(Movie movie, Person actor) {
this.movie = movie;
this.person = actor;
}
//getter setter
}
在 User
类里面再加上一个Movie 的集合,代表参演的电影:
@Relationship(type = "ACTED_IN")
private List<Movie> movies = new ArrayList<>();
这样一个 用户参演多部电影的关系就出来了。
给节点添加 Lable
一个 User
参演了电影,那么他还是一个演员 Actor
,此时他就应该有2个Lable,分别代表他的不同身份:
在User
类里面添加
@Labels
private List<String> labels = new ArrayList<>();
@Lables 代表这个String 集合是User的所有标签,每当User有一个新的Label,就可以 add 进入即可。
添加索引
如果在进行图遍历的时候,需要给属性添加索引,可按如下方式添加:
// @Index(unique=true) //可设置唯一约束
@Index
private String userId;
使用Spring Data Neo4j
SDN 提供了类似于 JPA 接口,只要继承 Neo4jRepository
接口即可使用:
public interface UserNeo4jRepository extends Neo4jRepository<User,Long> {}
其默认提供了很多查询方法的实现,只要遵循Spring Data 的规范即可使用,例如
findBy+属性名
findLike+属性名
当然也可以自己写Cypher
语句来进行操作,只要加上@Query
注解即可
@Query("match (u:User) where u.userId={0} return u ")
User findByUserId(String userId);
其他使用 和 Spring Data 其他模块使用方法一样,不再赘述。
OGM 索引策略
在使用 Java Config 配置 Neo4j 的时候使用了如下代码:
@Bean
public org.neo4j.ogm.config.Configuration configuration() {
return new org.neo4j.ogm.config.Configuration.Builder()
.uri(uri)
.connectionPoolSize(connectionPoolSize) //配置连接池大小
.credentials(username,password) //用户密码
.autoIndex("update") //索引策略
.build();
}
其中 autoIndex("update")
是 OGM 提供的一种索引策略,其规则如下:
Option | Description | Properties Example | Java Example |
---|---|---|---|
none (default) | Nothing is done with index and constraint annotations. | - | - |
validate | 确保连接的数据库在启动前具有所有索引和约束 | indexes.auto=validate | config.setAutoIndex(“validate”); |
assert | 在启动时删除所有约束和索引,然后根据@Index在OGM中表示的内容构建索引。方便在开发过程中 | indexes.auto=assert | config.setAutoIndex(“assert”); |
update | 根据@Index在OGM中表示的任何内容构建索引。如果数据库中的定义与元数据不同,索引将更改为约束,反之亦然。方便在开发过程中 | indexes.auto=update | config.setAutoIndex(“update”); |
dump | 将生成的约束和索引转储到文件。很适合建立环境。 none:默认。只需将该字段标记为使用索引即可 | indexes.auto=dump indexes.auto.dump.dir= indexes.auto.dump.filename= | config.setAutoIndex(“dump”); config.setDumpDir(“XXX”); config.setDumpFilename(“XXX”); |
其源码如下,通过注释也可以看出各种策略的作用:
package org.neo4j.ogm.config;
/**
* Denotes the types of auto indexing that can be done by the OGM at startup.
*
* @author Mark Angrish
*/
public enum AutoIndexMode {
/**
* No indexing will be performed.
*/
NONE("none"),
/**
* Removes all indexes and constraints on startup then creates all indexes and constraints defined in metadata.
*/
ASSERT("assert"),
/**
* Creates all missing indexes and constraints.
* <p>
* If there is an index in the database and constraint in the metadata the index is dropped and constraint created.
* (and vise versa).
* <p>
* Other indexes and constraints are left untouched.
* <p>
* NOTE: When a field with index or constraint is renamed new index or constraint will be created.
* Existing for the old name will be left untouched.
*/
UPDATE("update"),
/**
* Ensures that all constraints and indexes exist on startup or will throw a Runtime exception.
*/
VALIDATE("validate"),
/**
* Runs validate then creates a file (in same dir where launched) with the cypher used to build indexes and constraints.
*/
DUMP("dump");
/**
* Parses an option name into the Enumeration type it represents.
*
* @param name The lowercase name to parse.
* @return The <code>AutoIndexMode</code> this name represents.
*/
public static AutoIndexMode fromString(String name) {
if (name != null) {
for (AutoIndexMode mode : AutoIndexMode.values()) {
if (name.equalsIgnoreCase(mode.name)) {
return mode;
}
}
}
return null;
}
private final String name;
AutoIndexMode(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
小结
Spring Data Neo4j 的使用方式类似于 Spring Data JPA等其他模块,不过其实现也并没用JPA提供的那么多,具体的支持可以参考 SDN的官方文档参看,需要注意的是,如果 节点关系映射处理的不好,可能在查询的时候会带来级联查询,也就是 1+N 问题,查询一个节点带出 N 层关系,可以通过在下层终止的节点上加上 @JsonIgnore 注解取消属性的向下级联。当然这个问题也可以通过自定义Cypher 来实现,而不使用 SDN 提供的默认方法。
参考文档:
https://docs.spring.io/spring-data/neo4j/docs/5.0.7.RELEASE/reference/html/#reference