Spring data

Preview:

DESCRIPTION

spring data repository

Citation preview

M.C. Kang

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

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>

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.

@Entity

@Table(name="TUSR")

public class User {   

@Id   private String id;

@Column(name="fn")   private String name;   

private Date lastLogin;

...

}

@Document( collection="usr")

public class User {   

@Id   private String id;   

@Field("fn")   private String name;   

private Date lastLogin;

 ...

}

@NodeEntity

public class User {   

@GraphId   Long id;  

 private String name;   

private Date lastLogin;

...

}

JPA MongoDB Neo4j

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

Data Model

Customer Address

CREATE TABLE customer ( id BIGINT IDENTITY PRIMARY KEY, firstname VARCHAR(255), lastname VARCHAR(255), email_address VARCHAR(255));

CREATE UNIQUE INDEX ix_customer_email 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), country VARCHAR(255));

Domain Model

<<Entity>>

Customer

<<Entity>>

Address

<<Embeddable>

>

EmailAddress

<<MappedSuperClass>>

AbstractEntity

Step1. “Define Domain Model”

AbstractEntity@MappedSuperclasspublic class AbstractEntity {

@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;

public Long getId() {return id;

}

public boolean equals(Object obj) {if (this == obj) {return true;}

if (this.id == null || obj == null || !(this.getClass().equals(obj.getClass()))) {

return false;}

AbstractEntity that = (AbstractEntity) obj;

return this.id.equals(that.getId());}

public int hashCode() {return id == null ? 0 : id.hashCode();

}}

Customer@Entitypublic class Customer extends AbstractEntity {

private String firstname, lastname;

@Column(unique = true)private EmailAddress emailAddress;

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)@JoinColumn(name = "customer_id")private Set<Address> addresses = new HashSet<Address>();

public Customer(String firstname, String lastname) {

Assert.hasText(firstname);Assert.hasText(lastname);

this.firstname = firstname;this.lastname = lastname;

}

protected Customer() {

}

public void add(Address address) {

Assert.notNull(address);this.addresses.add(address);

}…

Step1. “Define Domain Model”

@MappedSuperclass to express that it is not a managedentity class on its own but rather will be extended by entity classes.

If there were demand to customize the names of the columns to which the properties would be persisted, you could use the @Column annotation.

Address@Entitypublic class Address extends AbstractEntity {

private String street, city, country;

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;

}

protected Address() {

} …

EmailAddress@Embeddablepublic class EmailAddress {

@Column(name = "email_address")private String value;

public EmailAddress(String emailAddress) {Assert.isTrue(isValid(emailAddress), "Invalid email address!");this.value = emailAddress;

}

protected EmailAddress() {

}…

public interface CustomerRepository extends CrudRepository<Customer, Long> {

Customer findByEmailAddress(EmailAddress emailAddress);Customer findById(Long id);List<Customer> findAll();

}

CustomerRepository

Step1. “Define Domain Model”

Step2. “Define Repository Interface”

-Just only interface

the EmailAddress class is an @Embeddable, which will cause the persistence provider to flatten out all properties of it into the table of the surrounding class.

@Configuration@EnableTransactionManagement@ComponentScan@EnableJpaRepositoriespublic class InfrastructureConfig {

@Beanpublic DataSource dataSource() {

return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).addScript("classpath:sql/schema.sql").addScript("classpath:sql/test-data.sql").build();

}

@Beanpublic 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;

}

@Beanpublic PlatformTransactionManager transactionManager() {

JpaTransactionManager txManager = new JpaTransactionManager();txManager.setEntityManagerFactory(entityManagerFactory().getObject());return txManager;

}}

SpringConfig

Step3. “Configure Spring Beans”

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes = { TestConfig.class })@Transactional@DirtiesContextpublic class TestMain {

@AutowiredCustomerRepository repository;

@AutowiredDataSource dataSource;

@Testpublic void testFindAll() {List<Customer> results = repository.findAll();assertThat(results, is(notNullValue()));assertThat(results, hasSize(3));assertThat(results.get(0), notNullValue());assertThat(results.get(1), notNullValue());assertThat(results.get(2), notNullValue());}

@Testpublic void testFindById() {Customer result = repository.findOne(100L);assertThat(result, is(notNullValue()));assertThat(result.getFirstname(), is("John"));}

@Testpublic void testFindByEmail() {Customer result = repository.findByEmailAddress(new EmailAddress("bob@doe.com"));assertThat(result, is(notNullValue()));assertThat(result.getFirstname(), is("Bob"));}

TestCase@Testpublic void saveNewCustomer() {Customer c = new Customer();//c.setFirstname("Sven");c.setLastname("Svensson");c.setEmailAddress(new EmailAddress("sven@svensson.org"));Address a = new Address("Storgaten 6", "Trosa", "Sweden");c.add(a);repository.save(c);System.out.println(repository.findAll());Customer result = repository.findOne(c.getId());assertThat(result, is(notNullValue()));//assertThat(result.getFirstName(), is("Sven"));assertThat(result.getEmailAddress().toString(), is(notNullValue()));}

@Test(expected = DataIntegrityViolationException.class)public void saveNewCustomerWithDuplicateEmail() {Customer c = new Customer("Bob","Doe");c.setEmailAddress(new EmailAddress("bob@doe.com"));Address a = new Address("66 Main St", "Middletown", "USA");c.add(a);repository.save(c);}

@Testpublic void deleteCustomer() {Customer c = repository.findOne(100L);repository.delete(c);Customer result = repository.findOne(100L);assertThat(result, is(nullValue()));}

}

Step4. “Test it…”

Document Model

Customer

(*)Address

{ firstname : "Dave", lastname : "Matthews", email : { email : "dave@dmband.com" }, addresses : [ { street : "Broadway", city : "New York", country : "United States" } ]}

Domain Model

<<Document>

>

Customer

<<Entity>>

Address

<<Embeddable>

>

EmailAddress

AbstractDocument

Step1. “Define Domain Model”

Email

AbstractDocumentpublic class AbstractDocument {

@Idprivate BigInteger id;

public BigInteger getId() {return id;

}

public boolean equals(Object obj) {

if (this == obj) {return true;}

if (this.id == null || obj == null || !(this.getClass().equals(obj.getClass()))) {return false;}

AbstractDocument that = (AbstractDocument) obj;

return this.id.equals(that.getId());}

public int hashCode() {return id == null ? 0 : id.hashCode();

}}

Customer@Documentpublic class Customer extends AbstractDocument {

private String firstname, lastname;

@Field("email")@Indexed(unique = true)private EmailAddress emailAddress;private Set<Address> addresses = new HashSet<Address>();

public Customer(String firstname, String lastname) {

Assert.hasText(firstname);Assert.hasText(lastname);

this.firstname = firstname;this.lastname = lastname;}

protected Customer() {

}…

Step1. “Define Domain Model”

Addresspublic class Address {

private final String street, city, country;

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;

} …

EmailAddresspublic class EmailAddress {

@Field("email")private final String value;

public EmailAddress(String emailAddress) {Assert.isTrue(isValid(emailAddress), "Invalid email address!");this.value = emailAddress;

}

protected EmailAddress() {

}…

public interface CustomerRepository extends CrudRepository<Customer, Long> {

Customer findByEmailAddress(EmailAddress emailAddress);Customer findById(Long id);List<Customer> findAll();

}

CustomerRepository

Step1. “Define Domain Model”

Step2. “Define Repository Interface”

-Just only interface

@Configuration@ComponentScan@EnableMongoRepositories(basePackages="com.mckang.springdata.mongo")class ApplicationConfig extends AbstractMongoConfiguration {

@Autowiredprivate 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 getMappingBasePackage() {return "com.mckang.springdata.mongo";

}}

SpringConfig

Step3. “Configure Spring Beans”

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes = { TestConfig.class })@Transactional@DirtiesContextpublic class TestMain {

@AutowiredCustomerRepository repository;

@AutowiredDataSource dataSource;

@Testpublic void testFindAll() {

List<Customer> results = repository.findAll();assertThat(results, is(notNullValue()));assertThat(results, hasSize(3));assertThat(results.get(0), notNullValue());assertThat(results.get(1), notNullValue());assertThat(results.get(2), notNullValue());

}

@Testpublic void testFindById() {

Customer result = repository.findOne(100L);assertThat(result, is(notNullValue()));assertThat(result.getFirstname(), is("John"));

}

@Testpublic void testFindByEmail() { Customer result = repository.findByEmailAddress(new EmailAddress("bob@doe.com"));}

TestCase@Testpublic void saveNewCustomer() {

Customer c = new Customer();//c.setFirstname("Sven");c.setLastname("Svensson");c.setEmailAddress(new EmailAddress("sven@svensson.org"));Address a = new Address("Storgaten 6", "Trosa", "Sweden");c.add(a);repository.save(c);

}

@Test(expected = DataIntegrityViolationException.class)public void saveNewCustomerWithDuplicateEmail() {

Customer c = new Customer("Bob","Doe");c.setEmailAddress(new EmailAddress("bob@doe.com"));Address a = new Address("66 Main St", "Middletown", "USA");c.add(a);repository.save(c);

}

@Testpublic void deleteCustomer() {

Customer c = repository.findOne(100L);repository.delete(c);

}

}

Step4. “Test it…”(Need Fixture…)

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.

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.

Graph Model Domain Model

<<NodeEntity>

>

Customer

<<NodeEntity>

>

Address

EmailAddress

AbstractEntity

Step1. “Define Domain Model”

Customer

Address

Address

AbstractDocumentpublic abstract class AbstractEntity {

@GraphIdprivate Long id;

public Long getId() {return id;

}

@Overridepublic boolean equals(Object obj) {

if (this == obj) {return true;

}

if (id == null || obj == null || !getClass().equals(obj.getClass())) { return false;} return id.equals(((AbstractEntity) obj).id);

}

@Overridepublic int hashCode() {

return id == null ? 0 : id.hashCode();}

}

Customer@NodeEntitypublic class Customer extends AbstractEntity {

private String firstName, lastName;

@Indexed(unique = true)private String emailAddress;

@RelatedTo(type = "ADDRESS")private Set<Address> addresses = new HashSet<Address>();

public Customer(String firstName, String lastName, String emailAddress) {

Assert.hasText(firstName); Assert.hasText(lastName); Assert.hasText(emailAddress);

this.firstName = firstName; this.lastName = lastName; this.emailAddress = emailAddress;}

protected Customer() {

} …

Step1. “Define Domain Model”

Address@NodeEntitypublic class Address extends AbstractEntity {

private String street, city;

public Address(String street, String city) {this.street = street;this.city = city;

}

public Address() {

} …

EmailAddresspublic class EmailAddress {

private final String value;

public EmailAddress(String emailAddress) {Assert.isTrue(isValid(emailAddress), "Invalid email address!");this.value = emailAddress;

}

protected EmailAddress() {

}…

public interface CustomerRepository extends GraphRepository<Customer> {

Customer findOne(Long id);

<C extends Customer> C save(C customer);

Customer findByEmailAddress(String emailAddress);}

CustomerRepository

Step1. “Define Domain Model”

Step2. “Define Repository Interface”

-Just only interface

@Configuration@ComponentScan@ImportResource("classpath:META-INF/spring/spring-data-context.xml")@EnableTransactionManagementclass ApplicationConfig {

@Bean(destroyMethod = "shutdown")public GraphDatabaseService graphDatabaseService() {

//return new EmbeddedGraphDatabase("target/graph.db");return new SpringRestGraphDatabase("http://localhost:7474/db/data");

}}

SpringConfig

Step3. “Configure Spring Beans”

<?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.xsdhttp://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j-2.0.xsd">

<!--neo4j:config storeDirectory="target/graph.db" /--><neo4j:config graphDatabaseService="graphDatabaseService" /><neo4j:repositories base-package="com.mckang.springdata.neo4j" />

</beans>

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes = { TestConfig.class })public class TestMain {

@AutowiredCustomerRepository repository;

@AutowiredDataSource dataSource;

@Testpublic void savesCustomerCorrectly() {

EmailAddress email = new EmailAddress("alicia@keys.com");Customer alicia = new Customer("Alicia", "Keys",email.getEmail()); // todoalicia.add(new Address("27 Broadway", "New York"));Customer result = repository.save(alicia);assertThat(result.getId(), is(notNullValue()));}

@Testpublic 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));}

TestCase

@Testpublic void preventsDuplicateEmail() {

final EmailAddress email = new EmailAddress("dave@dmband.com"); Customer dave = repository.findByEmailAddress(email.getEmail()); Customer anotherDave = new Customer("Dave", "Matthews",dave.getEmailAddress()); repository.save(anotherDave);}

}

Step4. “Test it…”(Need Fixture…)

Recommended