2. 22
背景と動機
§ RDBMS はいまだ重要かつ支配的
• ただ “one size fits all” ではなくなってきている
§ Spring はデータアクセスに常に優れたサポートを行ってきた
§ Spring Data project のゴールは Spring のデータサポートを “リフレッシュ”
すること
• Traditional: JDBC, JPA
• New: Grid, Document, Graph, K-V (aka NoSQL or NewSQL)
§ なじみの一貫性のある Spring ベースのプログラミングモデルを提供
• 個々の技術的特徴はいかしながら
§ データアクセス層から boiler-plate code をなくす
• 異なるテクノロジーをまたがって利用できる共通のインタフェース
3. 33
Spring Data は “umbrella project”
§ http://www.springframework.org/spring-data
§ JPA - Repositories
§ JDBC Extensions
§ MongoDB – Document Database
§ Neo4j – Graphs Database
§ Redis – Key Value Database
§ Gemfire – Distributed Data Grid
§ Commons – shared abstractions
• Repositories
• Object Mapping
§ QueryDSL – integration with type-safe query API
4. 44
Spring Data Repositories (1/3)
public interface CrudRepository<T, ID extends Serializable> extends
Repository<T, ID> {
T save(T entity);
Iterable<T> save(Iterable<? extends T> entities);
T findOne(ID id);
boolean exists(ID id);
Iterable<T> findAll();
long count();
void delete(ID id);
void delete(T entity);
void delete(Iterable<? extends T> entities);
void deleteAll();
}
public interface Repository<T, ID extends Serializable> {
// マーカーインタフェース ex.) Repository<Customer, Long>
}
5. 55
Spring Data Repositories (2/3)
public interface PagingAndSortingRepository<T, ID extends Serializable> extends
CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
§ “naming conventions” によるクエリメソッドの定義
public interface PersonRepository extends CrudRepository<Person,BigInteger> {
// Finder for a single entity
Person findByEmailAddress(String emailAddress);
// Finder for a multiple entities
List<Person> findByLastnameLike(String lastName);
// Finder with pagination
Page<Person> findByFirstnameLike(String firstName, Pageable page);
}
6. 66
Spring Data Repositories (3/3)
@NoRepositoryBean
public interface BaseRepository<T, ID extends Serializable> extends
Repository<T, ID> {
Iterable<T> findAll(Pageable sort);
<S extends T> S save(S entity);
<S extends T> S save(Iterable<S> entities);
}
§ Custom Repository インタフェースの定義
• CRUD は便利だが Read のみを提供したい、または Delete は提供したくない etc.
§ 定義方法
1. Repository を継承(or @RepositoryDefinition を付与)したインタフェースを定義
2. 公開したいメソッドのみをそのインタフェースに定義(ただし、メソッドシグネチャー
は Spring Data Repository と同一にする)
3. 出来上がったインタフェースを entity のベースに
7. 77
Spring Data JPA – Entity Mapping
§ インタフェースを定義するだけ:
• PersonRepository の実装は Spring (Data) が提供
<jpa:repositories base-package="com.acme.repository" />
@Entity
public class Person {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private BigInteger id;
private String firstname, lastname;
@Column(name="email")
private String emailAddress;
@OneToMany
private Set<Person> colleagues;
}
@EnableJpaRepositoriesJavaConfig
XML
8. 88
Spring Data JPA
§ transactional service layer (通常通り)
@Service
public class DefaultUserManagementService implements UserManagementService {
public PersonRepository personRepository;
public ShiftRepository shiftRepository;
@Autowired
public DefaultUserManagementService(PersonRepository personRepository,
ShiftRepository shiftRepository) {
this.personRepository = personRepository;
this.shiftRepository = shiftRepository;
}
@Transactional
public void assignToNightShift(String emailAddress) {
Person person = personRepository.findByEmailAddress(emailAddress);
// continue processing
}
}
9. 99
Spring Data JPA
§ クエリメソッド(naming conventions ベース)
• Query annotation でオーバーライド可能
• JPA named query (@NamedQuery) の参照も可.
public interface PersonRepository extends CrudRepository<Person,BigInteger> {
// previous methods omitted…
@Query("select p from Person p where p.emailAddress = ?1")
Person findByEmailAddress(String emailAddress);
@Query("select p from Person p where p.firstname = :firstname or p.lastname = :lastname")
Person findByLastnameOrFirstname(@Param("lastname") String lastname,
@Param("firstname") String firstname);
}
15. 1515
Spring Data MongoDB
§ MongoTemplate
• MongoDB 特有のオペレーションへのアクセス: geo, map/reduce etc.
• Fluent Query, Criteria, Update APIs
§ Object-Document マッピング
§ Repository サポート
§ Spring XML namespace (e.g. <mongo:XXX>)
§ QueryDSL サポート
§ JMX / Logging
16. 1616
Spring Data MongoDB – Entity Mapping (1/3)
@Document
public class Customer {
@Id
private BigInteger id;
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;
}
…
}
17. 1717
Spring Data MongoDB – Entity Mapping (2/3)
public final class EmailAddress {
private static final String EMAIL_REGEX = “…";
private static final Pattern PATTERN = Pattern.compile(EMAIL_REGEX);
@Field("email")
private final String value;
public EmailAddress(String emailAddress) {
Assert.isTrue(isValid(emailAddress), "Invalid email address!");
this.value = emailAddress;
}
… }
public 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!");
…
this.street = street;
this.city = city;
this.country = country;
}
… }
24. 2424
Spring Data Neo4j
§ Neo4jTemplate
• Neo4j 特有のオペレーションへのアクセス: get/create Node and Relationship,
query, traverse, fluent query Result handling
• トランザクション管理
• Exception translation to Spring’s DAO exception hierarchy
• Also works via REST with Neo4jServer
§ アノテーションベースの Entity 定義(@NodeEntity/@RelationshipEntity..)
§ Repository サポート
§ Cypher query language
§ Spring XML namespace (<neo4j:XXX>)
§ Neo4j Server 統合
<bean id="graphDatabaseService" class="org.springframework.data.neo4j.rest.SpringRestGraphDatabase”
/>
25. 2525
Classic Neo4j – Entity class
public class Person {
private final Node underlyingNode;
public Person(final Node node) {
underlyingNode = node;
}
public Node getUnderlyingNode() {
return underlyingNode;
}
public final String getName() {
return (String) underlyingNode.getProperty(“name”);
}
public void setName(final String name) {
underlyingNode.setProperty(“name”, name);
}
}
26. 2626
Spring Data Neo4j – Entity class
@NodeEntity
public class Actor {
@Indexed // Neo4jTemplate.getOrCreateNode() etc. で利用可能
private String id;
@Indexed(indexType=IndexType.FULLTEXT , indexName=“people")
private String name;
public Person(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() { return id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
27. 2727
Spring Data Neo4j – Entity class
@NodeEntity
public class Movie {
@Indexed
private String id;
@Indexed(indexType=IndexType.FULLTEXT , indexName=“search")
private String title;
// Collection of other nodes
@RelatedTo(type = "ACTS_IN", direction= INCOMING)
private Set<Actor> actors;
// Collection of relationships (actor -> (rating) -> movie)
@RelatedToVia(type = “RATED", direction= INCOMING)
private Set<Rating> rating;
public Movie(String id, String title) {
this.id = id;
this.title = title;
}
// Getters and Setters omitted
}
28. 2828
Spring Data Neo4j - Repositories
§ Spring Data Commons の Repository 同様
• インタフェースの定義(のみ) & naming convention
§ Support for
• CRUD
• IndexRepository (findAllByPropertyValue, findAllByQuery etc.)
• TraversalRepository (findAllByTraversal)
public interface MovieRepository extends GraphRepository<Movie>,
NamedIndexRepository<Movie>,
RelationshipOperationsRepository<Movie> {
Movie findById(String id);
Page<Movie> findByTitleLike(String title, Pageable page);
@Query("start user=node({0}) " +
" match user-[r:RATED]->movie<-[r2:RATED]-other-[r3:RATED]->otherMovie " +
" where r.stars >= 3 and r2.stars >= 3 and r3.stars >= 3 " +
" return otherMovie, avg(r3.stars) " +
" order by avg(r3.stars) desc, count(*) desc" +
" limit 10")
List<MovieRecommendation> getRecommendations(User user);
}
<neo4j:repositories base-package="org.neo4j.cineasts.repository"/>
29. 2929
Spring Data Neo4j – Neo4jTemplate
public void setUp() throws Exception {
dave = template.save(new Customer("Dave", "Matthews", "dave@dmband.com"));
template.save(new Customer("Carter","Beauford","carter@dmband.com"));
template.save(new Customer("Boyd","Tinsley","boyd@dmband.com"));
final Country usa = template.save(new Country("US", "United States"));
template.save(new Address("27 Broadway","New York",usa));
iPad = template.save(
new Product("iPad", "Apple tablet device")
.withPrice(BigDecimal.valueOf(499D)));
mbp = template.save(
new Product("MacBook Pro", "Apple notebook")
.withPrice(BigDecimal.valueOf(1299D)));
final Order order = new Order(dave);
order.add(iPad,2);
order.add(mbp,1);
template.save(order);
}
30. 3030
Redis
§ Advanced key-value store
• 軽量 & 高パフォーマンス (in-memory)
• Values: binary strings, Lists, Sets, Ordered Sets, Hash maps, ..
• データタイプごとの操作: e.g. list への追加 ([RL]PUSH), set への追加
(SADD), retrieving a slice of a list (LRANGE key start end), …
• 各操作がアトミック (e.g. INCR)
§ Very fast: ~100K operations/second on entry-level hardware
§ 永続化対応
• Periodic snapshots (point-in-time)
• Write コマンドのログファイルへの書き出し (append-based)
§ PUB/SUB
§ トランザクション対応 (MULTI <-> EXEC)
§ 多言語対応, all separate open source projects
… 続きは http://redis.io/ で
K1
K2
K3
V1
V2
V2
31. 3131
Spring Data Redis
§ Redis ドライバに依存しない統一された API を提供
§ RedisTemplate
• Redis の機能実装。各データタイプごとに専用のインタフェース
• Value/Hash/Set/Zset/ListOperations
• 名前は分かりやすいように変換: SETNX -> putIfAbsent()
• 自動で serialization と型変換を実施
• Fluent query API
§ 非同期 Publish-Subscribe サポート with message listener containers
§ JDK Atomic counters (AtomicLong etc.) backed by Redis
§ Spring 3.1 Cache abstraction provider
32. 3232
Spring Data Redis – RedisTemplate – 型変換
<bean id="conFactory“ class="o.s.d.r.connection.jedis.JedisConnectionFactory"/>
<bean id=“template“ class="o.s.d.redis.core.StringRedisTemplate"
p:connection-factory-ref="conFactory"/>
@Bean
public RedisTemplate<String, Long> longTemplate() {
RedisTemplate<String, Long> tmpl = new RedisTemplate<String, Long>();
tmpl.setConnectionFactory( redisConnectionFactory() );
tmpl.setValueSerializer(LongSerializer.INSTANCE);
return tmpl;
}
public static enum LongSerializer implements RedisSerializer<Long> {
INSTANCE;
@Override public byte[] serialize( Long aLong ) throws SerializationException {
if ( null != aLong ) { return aLong.toString().getBytes(); }
else { return new byte[0]; }
}
@Override public Long deserialize( byte[] bytes ) throws SerializationException {
if ( bytes.length > 0 ) {
return Long.parseLong( new String( bytes ) );
} else { return null; }
}
}
33. 3333
Spring Data Redis – RedisTemplate – アトミック操作
@Autowired RedisConnectionFactory connectionFactory;
@Test public void testAtomicCounters() {
RedisAtomicLong counter = new RedisAtomicLong("spring-data-book:counter-
test:hits", connectionFactory, 0);
Long l = counter.incrementAndGet();
assertThat(l, is(greaterThan(0L)));
}
34. 3434
Spring Data Redis – RedisTemplate – Pub/Sub
@Configuration
public class PubSubConfig extends ApplicationConfig {
public static final String DUMP_CHANNEL = ”pubsub-test:dump";
@Bean RedisMessageListenerContainer container() {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(redisConnectionFactory());
container.addMessageListener(dumpToConsoleListener(), new ChannelTopic(DUMP_CHANNEL));
return container;
}
@Bean MessageListener dumpToConsoleListener() {
return new MessageListener() {
@Override public void onMessage(Message message, byte[] pattern) {
System.out.println("FROM MESSAGE: " + new String(message.getBody()));
}
};
}
}
@Test public void testPubSubWithoutConversion() {
RedisConnection redis = connectionFactory.getConnection();
try {
redis.publish( PubSubConfig.DUMP_CHANNEL.getBytes(), "Hello World!".getBytes() );
} finally { redis.close(); }
}
37. 3737
• Cache がインメモリのストレージ、およびデータ管
理を提供 (RDBMS でいうところの Database)
• XML (cache.xml) and/or API calls の組合せで
Cache を構成
• 複数の Regions から構成される
Cache / Region
z
Application
Region
Region
Region
Cache
Cache
• Region class は java.util.Map interface を実装
• データの保存方法 (Replicated/Partition)や場所に
依らず一貫した API を提供
• Region 内でキーは一意である必要がある
Region
java.util.Map
Region
38. 3838
• クライアントが distributed system に接続する方法としてGemFire server に(直接)、
または “locator” 経由で接続する方法がある
• クライアントは独自にサーバーからのデータをローカルにコピー(キャッシュ)すること
が可能
• クライアントはサーバー側の変更に対し register 可能。その場合変更があった際にク
ライアントに通知されるようになる
Client Cache
Application
Application
Region
Region
Region
Cache
Client Cache
39. 3939
• GemFire distributed system に接続、および Cache を生成するプロセス
• Locator
• Cacheserver (Cache 保持)
• Agent
• 最小構成の GemFire プロセスは組み込みモードで実行される単一のノード
メンバー構成
Application
Region
Region
Region
Cache
Application
Region
Region
Region
Cache
Application
Region
Region
Region
Cache
Application ApplicationApplication
43. 4343
Creating a Cache - XML
Application
Region
Cache
<?xml version="1.0"?>
<!DOCTYPE cache PUBLIC
"-//GemStone Systems, Inc.//GemFire Declarative
Caching 6.6//EN"
"http://www.gemstone.com/dtd/cache6_6.dtd">
<cache>
<region name="Customer” refid="REPLICATE" />
</cache>
cache.xml
Load XML via Java
Cache cache = new CacheFactory()
.set("cache-xml-file", "xml/cache.xml")
.create();
Region<Integer, Customer> customers = cache.getRegion("Customer");
44. 4444
Creating a Cache - API
Application
Region
Cache
// Create the cache without using a cache xml file
Cache cache = new CacheFactory().create();
// Create a region dynamically using the APIs
Region<Integer, Customer> customers =
(Region<Integer, Customer>)cache.createRegionFactory()
.create("Customer”);
§ Classes in
com.gemstone.gemfire.cache
§ Call cache.close() when done
48. 4848
Spring Data Gemfire – Entity Mapping
@Region("myRegion")
public class Person {
@Id BigInteger id; // Cache キー
@Indexed String firstname;
@Transient String middleName; // persist されない
@PersistenceConstructor // コンストラクタの明示指定
public Person(String firstname, String lastname) { … }
}
49. 4949
Spring Data Gemfire - Repositories
interface PersonRepository extends CrudRepository<Person, BigInteger> {
// Finder for a single entity
Person findByEmailAddress(String emailAddress);
// Finder for multiple entities
List<Person> findByLastnameLike(String lastname);
// Finder with manually defined query
@Query("SELECT p FROM /Person p WHERE p.firstname = $1")
List<Person> findByFirstname(String firstname);
}
50. 5050
Spring Data Gemfire – Repositories
<gf:repositories base-package="com.acme.repositories"/>
@Service
public class MySimpleService {
@Autowired PersonRepository personRepository;
@Autowired AccountRepository accountRepository;
@Transactional
public List<Person> doSomething(Integer personId) {
Person person = personRepository.findOne(personId);
List<Person> persons = accountRepository.findByPerson(person);
}
}
public interface PersonRepository extends CrudRepository<Person, BigInteger> {
// Finder for a single entity
Person findByEmailAddress(String emailAddress);
// Finder for multiple entities
List<Person> findByLastnameLike(String lastname);
// Finder with manually defined query (OQL)
@Query("SELECT p FROM /Person p WHERE p.firstname = $1")
List<Person> findByFirstname(String firstname);
}