2. Overview
Spring Data is a high level
SpringSource project whose purpose is to
unify and ease the access to different
kinds of persistence stores, both
relational database systems and NoSQL
data stores.
Source http://www.infoq.com/articles/spring-data-intro
Spring Data projects support the followings aspects:
Templating
Object/Datastore mapping
Repository support
3. Overview - Templates
The main purpose of a Spring Data template (and all other Spring templates) is resource
allocation and exception translation.
A template offers store specific operations like saving, updating and deleting a single
record or for executing queries or map/reduce jobs. But all these methods work only for the
corresponding underlying datastore.
MongoDb Template Configuration Example
<!-- Connection to MongoDB server -->
<mongo:db-factory host="localhost" port="27017" dbname="test" />
<!-- MongoDB Template -->
<bean id="mongoTemplate“
class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
</bean>
4. Overview - Object/Datastore Mapping
With Spring Data, this support is extended to NoSQL datastores with object-like data structures. But
these data structures can be quite different from each other, so it would be difficult to
make up a common API for object/datastore mapping. Each type of datastore comes with its own set of
annotations to provide the needed meta information for the mapping.
JPA MongoDB Neo4j
@Entity @Document( collection="usr") @NodeEntity
@Table(name="TUSR") public class User { public class User {
public class User { @Id private String id; @GraphId Long id;
@Id private String id; @Field("fn") private String name; private String name;
@Column(name="fn") private String name; private Date lastLogin; private Date lastLogin;
private Date lastLogin; ... ...
... } }
}
5. Overview - Repository Support
generic support for CRUD and paging , sorting by providing special parameters to the finder
methods for all persistence stores
The main advantages of repository support are:
The developer writes a lot less boilerplate code
Queries can by defined alongside the finder method and its documentation
As a bonus, the JPQL queries are compiled as soon as the Spring context is assembled,
not the first time you use the query, which makes it easier to detect syntax errors
6. Data Model Domain Model
Customer Address
<<MappedSuperClass>>
AbstractEntity
CREATE TABLE customer (
id BIGINT IDENTITY PRIMARY KEY,
firstname VARCHAR(255),
lastname VARCHAR(255),
email_address VARCHAR(255));
<<Entity>>
CREATE UNIQUE INDEX ix_customer_email Customer
ON CUSTOMER (email_address ASC);
CREATE TABLE address (
id BIGINT IDENTITY PRIMARY KEY,
customer_id BIGINT CONSTRAINT
address_customer_ref REFERENCES
customer (id),
street VARCHAR(255),
city VARCHAR(255), <<Entity>> <<Embeddable>>
country VARCHAR(255));
Address EmailAddress
Step1. “Define Domain Model”
7. AbstractEntity Customer
@M appedSupercl ass @Entity
public class AbstractEntity { public class Customer extends AbstractEntity {
@Id private String firstname, lastname;
@GeneratedValue(strategy = GenerationT ype. AUTO)
private Long id; @Col umn(unique = true)
private EmailAddress emailAddress;
public Long getId() {
return id; @OneToMany(cascade = CascadeType.ALL, orphanRemoval =
} true)
@JoinColumn(nam e = "custom er_id")
public boolean equals(Object obj) { private Set<Address> addresses = new HashSet<Address>();
if (this == obj) {
return true; public Customer(String firstname, String lastname) {
}
Assert.hasText(firstname);
if (this.id == null || obj == null || ! Assert.hasText(lastname);
(this.getClass().equals(obj.getClass()))) {
return false; this.firstname = firstname;
} this.lastname = lastname;
}
AbstractEntity that = (AbstractEntity) obj;
protected Customer() {
return this.id.equals(that.getId());
} }
public int hashCode() { public void add(Address address) {
return id == null ? 0 : id.hashCode();
} Assert.notNull(address);
} this.addresses.add(address);
}
…
@MappedSuperclass to express that it is not a If there were demand to customize the names of the
managed columns to which the properties would be persisted,
entity class on its own but rather will be extended you could use the @Column annotation.
by entity classes.
Step1. “Define Domain Model”
8. Address CustomerRepository
@Entity public interface CustomerRepository
public class Address extends AbstractEntity { ext ends CrudRepos itory<Customer, Long> {
Customer findByEmailAddress(EmailAddress emailAddress);
private String street, city, country; Customer findById(Long id);
List<Customer> findAll();
public Address(String street, String city, String country) { }
Assert.hasText(street, "Street must not be null or empty!");
Assert.hasText(city, "City must not be null or empty!");
Assert.hasText(country, "Country must not be null or empty!");
this.street = street;
this.city = city;
this.country = country;
Step2. “Define Repository
} Interface”
protected Address() {
-Just only interface
}
…
EmailAddress
@Em beddabl e
public class EmailAddress {
@Colum n(nam e = "email_address")
private String value;
public EmailAddress(String emailAddress) {
Assert.isTrue(isValid(emailAddress), "Invalid email address!");
this.value = emailAddress;
}
the EmailAddress class is an @Embeddable, which
protected EmailAddress() { will cause the persistence provider to flatten out
all properties of it into the table of the
}
… surrounding class.
Step1. “Define Domain Model”
9. SpringConfig
@Configuration
@EnableT ransactionManagement
@ComponentS can
@EnableJ paReposi tories
public class InfrastructureConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).addScript("classpath:sql/schema.sql")
.addScript("classpath:sql/test-data.sql").build();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.HSQL);
//vendorAdapter.setGenerateDdl(true);
vendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan(getClass().getPackage().getName());
factory.setDataSource(dataSource());
return factory;
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory().getObject());
return txManager;
}
}
Step3. “Configure Spring Beans”
10. TestCase
@RunWit h(SpringJUnit4Cl assRunner.class) @Test
@Cont extConfiguration(classes = { TestConfig.cl ass }) public void saveNewCustomer() {
@T ransactional Customer c = new Customer();
@Di rtiesContext //c.setFirstname("Sven");
public class TestMain { c.setLastname("Svensson");
c.setEmailAddress(new EmailAddress("sven@svensson.org"));
@A utowired Address a = new Address("Storgaten 6", "Trosa", "Sweden");
CustomerRepository repository; c.add(a);
repository.save(c);
@Autowired System.out.println(repository.findAll());
DataSource dataSource; Customer result = repository.findOne(c.getId());
assertThat(result, is(notNullValue()));
@Test //assertThat(result.getFirstName(), is("Sven"));
public void testFindAll() { assertThat(result.getEmailAddress().toString(), is(notNullValue()));
List< Custom er> results = reposi tory.findAl l(); }
assertThat(results, is(notNullValue()));
assertThat(results, hasSize(3));
assertThat(results.get(0), notNullValue()); @Test(expected = DataIntegrityViolationException.class)
assertThat(results.get(1), notNullValue()); public void saveNewCustomerWithDuplicateEmail() {
assertThat(results.get(2), notNullValue()); Customer c = new Customer("Bob","Doe");
} c.setEmailAddress(new EmailAddress("bob@doe.com"));
Address a = new Address("66 Main St", "Middletown", "USA");
@Test c.add(a);
public void testFindById() { repository.save(c);
Customer res ult = repository.findOne(100L); }
assertThat(result, is(notNullValue()));
assertThat(result.getFirstname(), is("John")); @Test
} public void deleteCustomer() {
Customer c = repository.findOne(100L);
@Test repository.delete(c);
public void testFindByEmail() { Customer result = repository.findOne(100L);
assertThat(result, is(nullValue()));
Customer result = repository.fi ndByEm ail Address(new EmailA ddress("bob@doe.com"));
assertThat(result, is(notNullValue())); }
assertThat(result.getFirstname(), is("Bob"));
} }
Step4. “Test it…”
11. Document Model Domain Model
Customer
Email (*)Address
AbstractDocument
{
firstname : "Dave",
lastname : "Matthews",
email : {
email : "dave@dmband.com"
<<Document>>
},
addresses : [ Customer
{
street : "Broadway",
city : "New York",
country : "United
States"
}
]
}
<<Entity>> <<Embeddable>>
Address EmailAddress
Step1. “Define Domain Model”
12. AbstractDocument Customer
public class AbstractDocument { @Document
public class Customer extends AbstractDocument {
@Id
private BigInteger id; private String firstname, lastname;
public BigInteger getId() { @Fi eld("em ail ")
return id; @Indexed(unique = true)
} private EmailAddress emailAddress;
private Set<Address> addresses = new HashSet<Address>();
public boolean equals(Object obj) {
public Customer(String firstname, String lastname) {
if (this == obj) {
return true; Assert.hasText(firstname);
} Assert.hasText(lastname);
if (this.id == null || obj == null || ! this.firstname = firstname;
(this.getClass().equals(obj.getClass()))) { this.lastname = lastname;
return false; }
}
protected Customer() {
AbstractDocument that = (AbstractDocument) obj;
}
return this.id.equals(that.getId()); …
}
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
}
Step1. “Define Domain Model”
13. Address CustomerRepository
public class Address { public interface CustomerRepository
ext ends CrudRepos itory<Customer, Long> {
private final String street, city, country; Customer findByEmailAddress(EmailAddress emailAddress);
Customer findById(Long id);
public Address(String street, String city, String country) { List<Customer> findAll();
}
Assert.hasText(street, "Street must not be null or empty!");
Assert.hasText(city, "City must not be null or empty!");
Assert.hasText(country, "Country must not be null or
empty!");
this.street = street;
this.city = city;
this.country = country;
Step2. “Define Repository
…
} Interface”
-Just only interface
EmailAddress
public class EmailAddress {
@F iel d("emai l")
private final String value;
public EmailAddress(String emailAddress) {
Assert.isTrue(isValid(emailAddress), "Invalid email address!");
this.value = emailAddress;
}
protected EmailAddress() {
}
…
Step1. “Define Domain Model”
14. SpringConfig
@Configuration
@ComponentS can
@EnableM ongoRepositories(baseP ackages="com.mck ang.springdata.mongo")
class ApplicationConfig extends AbstractMongoConfiguration {
@Autowired
private List<Converter<?, ?>> converters;
protected String getDatabaseName() {
return "e-store";
}
public Mongo mongo() throws Exception {
Mongo mongo = new Mongo("127.0.0.1");
mongo.setWriteConcern(WriteConcern.FSYNC_SAFE);
return mongo;
}
public CustomConversions customConversions() {
return new CustomConversions(converters);
}
protected String get MappingBasePackage () {
return "com.mckang.springdata.mongo";
}
}
Step3. “Configure Spring Beans”
15. TestCase
@RunWith(S pringJUnit4ClassRunner.class ) @Test
@ContextConfigurati on(cl asses = { T es tConfi g.class }) public void saveNewCustomer() {
@T ransactional Customer c = new Customer();
@DirtiesContext //c.setFirstname("Sven");
public class TestMain { c.setLastname("Svensson");
c.setEmailAddress(new EmailAddress("sven@svensson.org"));
@Autowi red Address a = new Address("Storgaten 6", "Trosa", "Sweden");
Custom erRepository reposi tory; c.add(a);
repository.save(c);
@Autowired }
DataSource dataSource;
@Test @Test(expected = DataIntegrityViolationException.class)
public void testFindAll() { public void saveNewCustomerWithDuplicateEmail() {
List<Custom er> results = repository.f indAll (); Customer c = new Customer("Bob","Doe");
assertThat(results, is(notNullValue())); c.setEmailAddress(new EmailAddress("bob@doe.com"));
assertThat(results, hasSize(3)); Address a = new Address("66 Main St", "Middletown", "USA");
assertThat(results.get(0), notNullValue()); c.add(a);
assertThat(results.get(1), notNullValue()); repository.save(c);
assertThat(results.get(2), notNullValue()); }
}
@Test
@Test public void deleteCustomer() {
public void testFindById() { Customer c = repository.findOne(100L);
Custom er result = repos it ory.findOne(100L); repository.delete(c);
assertThat(result, is(notNullValue())); }
assertThat(result.getFirstname(), is("John"));
} }
@Test
public void testFindByEmail() {
Customer result = repository.f indByEm ai lAddres s(new Em ail Address("bob@doe.com"));
}
Step4. “Test it…”
(Need Fixture…)
16. Graph Database
Neo4j is the leading implementation of a property graph database. It is written
predominantly in Java and leverages a custom storage format and the facilities of the Java
Transaction Architecture (JTA) to provide XA transactions.
Neo4j integrates a transactional, pluggable indexing subsystem that uses Lucene as the
default. The index is used primarily to locate starting points for traversals. Its second use
is to support unique entity creation.
17. Cypher statement
With the declarative Cypher query language, Neo4j makes it easier to get started for
everyone who knows SQL from working with relational databases.
enabling users to define sophisticated queries like “find me all the customers who have
friends who have recently bought similar products.”
Like other query languages, it supports filtering, grouping, and paging. Cypher allows easy
creation, deletion, update, and graph construction.
18.
19. Graph Model Domain Model
AbstractEntity
Customer
Address
<<NodeEntity>>
Customer
Address
<<NodeEntity>>
EmailAddress
Address
Step1. “Define Domain Model”
20. AbstractDocument Customer
public abstract class AbstractEntity { @NodeEnti ty
public class Customer extends AbstractEntity {
@GraphId
private Long id; private String firstName, lastName;
public Long getId() { @Indexed(unique = true)
return id; private String emailAddress;
}
@Rel atedT o(type = "ADDRESS")
@Override private Set<Address> addresses = new HashSet<Address>();
public boolean equals(Object obj) {
public Customer(String firstName, String lastName, String emailAddress) {
if (this == obj) {
return true; Assert.hasText(firstName);
} Assert.hasText(lastName);
Assert.hasText(emailAddress);
if (id == null || obj == null || !
getClass().equals(obj.getClass())) { this.firstName = firstName;
return false; this.lastName = lastName;
} this.emailAddress = emailAddress;
return id.equals(((AbstractEntity) obj).id); }
} protected Customer() {
@Override }
public int hashCode() { …
return id == null ? 0 : id.hashCode();
}
}
Step1. “Define Domain Model”
21. Address CustomerRepository
@NodeEntity public interface CustomerRepository extends GraphRepository<Customer> {
public class Address extends AbstractEntity {
Customer findOne(Long id);
private String street, city;
<C extends Customer> C save(C customer);
public Address(String street, String city) { Customer findByEmailAddress(String emailAddress);
this.street = street; }
this.city = city;
}
public Address() {
}
Step2. “Define Repository
… Interface”
-Just only interface
EmailAddress
public class EmailAddress {
private final String value;
public EmailAddress(String emailAddress) {
Assert.isTrue(isValid(emailAddress), "Invalid email address!");
this.value = emailAddress;
}
protected EmailAddress() {
}
…
Step1. “Define Domain Model”
22. SpringConfig
@Configuration
@ComponentS can
@ImportResource("cl asspath:ME TA-INF /spring/spring-data-context.xml ")
@EnableT ransactionManagement
class ApplicationConfig {
@Bean(destroyMethod = "shutdown")
public GraphDatabaseService graphDatabaseService() {
//return new EmbeddedGraphDatabase("target/graph.db");
return new SpringRestGraphDatabase("http://localhost:7474/db/data");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:neo4j="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/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j-2.0.xsd">
<!--neo4j:config storeDi rectory="target/graph.db" /-->
<neo4j :c onfig graphDatabaseService= "graphDatabaseService" />
<neo4j :repositori es base-package="com. mckang.springdata.neo4j" />
</beans>
Step3. “Configure Spring Beans”
23. TestCase
@RunWith(SpringJUni t4ClassRunner.cl ass)
@Context Confi guration(classes = { TestConfig.class }) @Test
public class TestMain { public void preventsDuplicateEmail() {
@Autowired final EmailAddress email = new EmailAddress("dave@dmband.com");
CustomerReposi tory repository; Customer dave = repository.findByEmailAddress(email.getEmail());
Customer anotherDave = new Customer("Dave", "Matthews",dave.getEmailAddress());
@Autowired repository.save(anotherDave);
DataSource dataSource; }
@Test }
public void savesCustomerCorrectly() {
EmailAddress email = new EmailAddress("alicia@keys.com");
Customer alicia = new Customer("Alicia", "Keys",email.getEmail()); // todo
alicia.add(new Address("27 Broadway", "New York"));
Customer result = repository.save(alicia);
assertThat(result.getId(), is(notNullValue()));
}
@Test
public void readsCustomerByEmail() {
EmailAddress email = new EmailAddress("alicia@keys.com");
Customer alicia = new Customer("Alicia", "Keys",email.getEmail());
repository.save(alicia);
Customer result = repository.findByEmailAddress(email.getEmail());
assertThat(result, is(alicia));
}
Step4. “Test it…”
(Need Fixture…)