Spring Data Neo4j主要分为两个主要组件.
1. OGM SUpport
2. Spring Data Repository Support
3. maven
3.1 SDN 连接neo4j 通过 BOLT driver
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
</dependency>
<!-- add this dependency if you want to use the embedded driver -->
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-embedded-driver</artifactId>
</dependency>
<!-- add this dependency if you want to use the HTTP driver -->
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-http-driver</artifactId>
</dependency>
</dependency>
3.2SDN仅支持JavaConfig
@Configuration
@EnableNeo4jRepositories(basePackages = "org.neo4j.example.repository")
@EnableTransactionManagement
public class MyConfiguration {
@Bean
public SessionFactory sessionFactory() {
// with domain entity base package(s)
return new SessionFactory(configuration(), "org.neo4j.example.domain");
}
@Bean
public org.neo4j.ogm.config.Configuration configuration() {
ConfigurationSource properties = new ClasspathConfigurationSource("ogm.properties");
org.neo4j.ogm.config.Configuration configuration = new org.neo4j.ogm.config.Configuration.Builder(properties).build();
return configuration;
}
@Bean
public Neo4jTransactionManager transactionManager() {
return new Neo4jTransactionManager(sessionFactory());
}
}
通过ogm.properties在类路径的根部提供一个文件或传入一个org.neo4j.ogm.config.Configuration对象来改变这些默认值。这里声明的最后一个基础设施组件是Neo4jTransactionManager。我们最终使用@EnableNeo4jRepositories注释激活Spring Data Neo4j存储库。如果没有配置基础软件包,它将使用配置类所在的软件包。
请注意,您必须@EnableTransactionManagement显式激活才能在正常工作中获取基于注释的配置,Neo4jTransactionManager并使用bean名称定义此实例transactionManager。上面的示例假定您正在使用组件扫描。
为了让您的查询方法成为事务性的,只需在您定义的存储库接口上使用@Transactional即可
- Transactions :@Transactional
- Neo4J Repositories
@Repository
public interface PersonRepository extends Neo4jRepository<Person, Long> {}
public class MySpringBean {
@Autowired
private PersonRepository repo;
...
}
// then you can use the repository as you would any other object
Person michael = repo.save(new Person("Michael", 36));
Optional<Person> dave = repo.findById(123);
long numberOfPeople = repo.count();
- Annotated queries(注解查询)
@Query("MATCH (:Actor {name:{name}})-[:ACTED_IN]->(m:Movie) return m")
public interface MovieRepository extends Neo4jRepository<Movie, Long> {
// returns the node with id equal to idOfMovie parameter
@Query("MATCH (n) WHERE id(n)={0} RETURN n")
Movie getMovieFromId(Integer idOfMovie);
// returns the nodes which have a title according to the movieTitle parameter
@Query("MATCH (movie:Movie {title={0}}) RETURN movie")
Movie getMovieFromTitle(String movieTitle);
// same with optional result
@Query("MATCH (movie:Movie {title={0}}) RETURN movie")
Optional<Movie> getMovieFromTitle(String movieTitle);
// returns a Page of Actors that have a ACTS_IN relationship to the movie node with the title equal to movieTitle parameter.
@Query(value = "MATCH (movie:Movie {title={0}})<-[:ACTS_IN]-(actor) RETURN actor", countQuery= "MATCH (movie:Movie {title={0}})<-[:ACTS_IN]-(actor) RETURN count(actor)")
Page<Actor> getActorsThatActInMovieFromTitle(String movieTitle, PageRequest page);
// returns a Page of Actors that have a ACTS_IN relationship to the movie node with the title equal to movieTitle parameter with an accurate total count
@Query(value = "MATCH (movie:Movie {title={0}})<-[:ACTS_IN]-(actor) RETURN actor", countQuery = "MATCH (movie:Movie {title={0}})<-[:ACTS_IN]-(actor) RETURN count(*)")
Page<Actor> getActorsThatActInMovieFromTitle(String movieTitle, Pageable page);
// returns a Slice of Actors that have a ACTS_IN relationship to the movie node with the title equal to movieTitle parameter.
@Query("MATCH (movie:Movie {title={0}})<-[:ACTS_IN]-(actor) RETURN actor")
Slice<Actor> getActorsThatActInMovieFromTitle(String movieTitle, Pageable page);
// returns users who rated a movie (movie parameter) higher than rating (rating parameter)
@Query("MATCH (movie:Movie)<-[r:RATED]-(user) " +
"WHERE id(movie)={movieId} AND r.stars > {rating} " +
"RETURN user")
Iterable<User> getUsersWhoRatedMovieFromTitle(@Param("movieId") Movie movie, @Param("rating") Integer rating);
// returns users who rated a movie based on movie title (movieTitle parameter) higher than rating (rating parameter)
@Query("MATCH (movie:Movie {title:{0}})<-[r:RATED]-(user) " +
"WHERE r.stars > {1} " +
"RETURN user")
Iterable<User> getUsersWhoRatedMovieFromTitle(String movieTitle, Integer rating);
@Query(value = "MATCH (movie:Movie) RETURN movie;")
Stream<Movie> getAllMovies();
}
- Queries derived from finder-method names
Some examples of methods and corresponding Cypher queries of a PersonRepository
public interface PersonRepository extends Neo4jRepository<Person, Long> {
// MATCH (person:Person {name={0}}) RETURN person
Person findByName(String name);
// MATCH (person:Person)
// WHERE person.age = {0} AND person.married = {1}
// RETURN person
Iterable<Person> findByAgeAndMarried(int age, boolean married);
// MATCH (person:Person)
// WHERE person.age = {0}
// RETURN person ORDER BY person.name SKIP {skip} LIMIT {limit}
Page<Person> findByAge(int age, Pageable pageable);
// MATCH (person:Person)
// WHERE person.age = {0}
// RETURN person ORDER BY person.name
List<Person> findByAge(int age, Sort sort);
//Allow a custom depth as a parameter
Person findByName(String name, @Depth int depth);
//Fix the depth for the query
@Depth(value = 0)
Person findBySurname(String surname);
}
- Mapping Query Results(将查询的结果映射成pojo通过注解@QueryResult)
Example of query result mapping
public interface MovieRepository extends Neo4jRepository<Movie, Long> {
@Query("MATCH (movie:Movie)-[r:RATING]\->(), (movie)<-[:ACTS_IN]-(actor:Actor) " +
"WHERE movie.id={0} " +
"RETURN movie as movie, COLLECT(actor) AS 'cast', AVG(r.stars) AS 'averageRating'")
MovieData getMovieData(String movieId);
@QueryResult
public class MovieData {
Movie movie;
Double averageRating;
Set<Actor> cast;
}
}
分页和排序
Projections
@NodeEntity
public class Cinema {
private Long id;
@Value("#{target.name}")
private String name,
private Stringlocation;
@Relationship(type = "VISITED", direction = Relationship.INCOMING)
private Set<User> visited = new HashSet<>();
@Relationship(type = "BLOCKBUSTER", direction = Relationship.OUTGOING)
private Movie blockbusterOfTheWeek;
…
}
This Cinema has several attributes:
1. id is the graph id
2. name and location are data attributes
3. visited and blockbusterOfTheWeek are links to other domain objects
4. @Value("#{target.name}"):重命名
- Transactions
Using a facade to define transactions for multiple repository calls
@Service
class UserManagementImpl implements UserManagement {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
@Autowired
public UserManagementImpl(UserRepository userRepository,
RoleRepository roleRepository) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
}
@Transactional
public void addRoleToAllUsers(String roleName) {
Role role = roleRepository.findByName(roleName);
for (User user : userRepository.findAll()) {
user.addRole(role);
userRepository.save(user);
}
}
- 事务绑定事件
Spring框架目前的结构使得上下文不知道事务支持,并且具有开放的基础结构以允许注册额外的组件并影响事件监听器的创建方式。
事务模块实现了一个EventListenerFactory查找新@TransactionalEventListener注释的工具。当存在这个时,注意到事务的扩展事件监听器被注册而不是缺省。
@Component
public class MyComponent {
@TransactionalEventListener(condition = "#creationEvent.awesome")
public void handleOrderCreatedEvent(CreationEvent<Order> creationEvent) {
...
}
}