The Spring Data Neo4j project support cross-store persistence for the advanced mapping mode, which allows for parts of the data to be stored in a traditional JPA data store (RDBMS), and other parts in a graph store. This means that an entity can be partially stored in e.g. MySQL, and partially stored in Neo4j.
This allows existing JPA-based applications to embrace NOSQL data stores for evolving certain parts of their data model. Possible use cases include adding social networking or geospatial information to existing applications.
Partial graph persistence is achieved by restricting the Spring Data Neo4j aspects to manage only
explicitly annotated parts of the entity. Those fields will be made @Transient
by the
aspect so that JPA ignores them.
A backing node in the graph store is only created when the entity has been assigned a JPA ID. Only then will the association between the two stores be established. Until the entity has been persisted, its state is just kept inside the POJO (in detached state), and then flushed to the backing graph database on the persist operation.
The association between the two entities is maintained via a FOREIGN_ID field in the node, that
contains the JPA ID. Currently only single-value IDs are supported. The entity class can be resolved
via the TypeRepresentationStrategy
that manages the Java type hierarchy within the graph
database. Given the ID and class, you can then retrieve the appropriate JPA entity for a given node.
The other direction is handled by indexing the Node with the FOREIGN_ID index which contains a concatenation of the fully qualified class name of the JPA entity and the ID. The matching node can then be found using the indexing facilities, and the two entities can be reassociated.
Using these mechanisms and the Spring Data Neo4j aspects, a single POJO can contain some fields handled by JPA and others handles by Spring Data Neo4j. This also includes relationship fields persisted in the graph database.
Cross-store persistence only requires the use of one additional annotation: @GraphProperty
.
See below for details and an example.
When annotating an entity with partial = true
, this marks it as a cross-store entity.
Spring Data Neo4j will thus only manage fields explicitly annotated with @GraphProperty
.
Fields of primitive or convertible types do not normally have to be annotated in order to be
persisted by Spring Data Neo4j. In cross-store mode, Spring Data Neo4j only
persists fields explicitly annotated with @GraphProperty
. JPA will ignore these fields.
The following example is taken from the Spring Data Neo4j examples myrestaurants-social project:
Example 22.1. Cross-store node entity
@Entity @Table(name = "user_account") @NodeEntity(partial = true) public class UserAccount { private String userName; private String firstName; private String lastName; @GraphProperty String nickname; @RelatedTo Set<UserAccount> friends; @RelatedToVia(type = "recommends") Iterable<Recommendation> recommendations; @Temporal(TemporalType.TIMESTAMP) @DateTimeFormat(style = "S-") private Date birthDate; @ManyToMany(cascade = CascadeType.ALL) private Set<Restaurant> favorites; @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; public void knows(UserAccount friend) { relateTo(friend, "friends"); } public Recommendation rate(Restaurant restaurant, int stars, String comment) { Recommendation recommendation = relateTo(restaurant, Recommendation.class, "recommends"); recommendation.rate(stars, comment); return recommendation; } public Iterable<Recommendation> getRecommendations() { return recommendations; } }
Configuring cross-store persistence is done similarly to the default Spring Data Neo4j configuration.
All you need to do is to specify an entityManagerFactory
in the XML namespace
config
element, and Spring Data Neo4j will configure itself for cross-store use.
Example 22.2. Cross-store Spring configuration
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:datagraph="http://www.springframework.org/schema/data/neo4j" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd "> <context:annotation-config/> <neo4j:config storeDirectory="target/config-test" entityManagerFactory="entityManagerFactory"/> <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory"> <property name="dataSource" ref="dataSource"/> <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml"/> </bean> </beans>