`
liufei.fir
  • 浏览: 676561 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Spring Data —— 完全统一的API?

阅读更多
Spring Data 作为SpringSource的其中一个父项目, 旨在统一和简化对各类型持久化存储, 而不拘泥于是关系型数据库还是NoSQL 数据存储。
相关厂商内容

白皮书下载:JBoss Enterprise Application Platform 6迁移指南

白皮书下载:利用您的私有或混合云加速业务成果

Amazon五星敏捷图书作者、敏捷教练Amr Elssamadisy确认参加2012QCon杭州!

腾讯Web前端团队TAT(Tencent Alloy Team)负责人屈超确认参加2012QCon杭州!

去哪儿网自动化测试专家,《自动化软件测试实施指南》译者余昭辉确认参加2012QCon杭州!
相关赞助商

QCon杭州2012大会10月25~27,9月15日前报名享受8折优惠,5人以上团购享有更多优惠!

无论是哪种持久化存储, 数据访问对象(或称作为DAO,即Data Access Objects)通常都会提供对单一域对象的CRUD (创建、读取、更新、删除)操作、查询方法、排序和分页方法等。Spring Data则提供了基于这些层面的统一接口(CrudRepository,PagingAndSortingRepository)以及对持久化存储的实现。

你可能接触过某一种Spring 模型对象——比如JdbcTemplate——来编写访问对象的实现。基于Spring Data的数据访问对象, 我们只需定义和编写一些查询方法的接口(基于不同的持续化存储, 定义有可能稍有不同)。Spring Data会在运行时间生成正确的实现。 请看下面的例子:

public interface UserRepository extends MongoRepository<User, String> {         @Query("{ fullName: ?0 }")         List<User> findByTheUsersFullName(String fullName);         List<User> findByFullNameLike(String fullName, Sort sort); } ... Autowired UserRepository repo;

本文将比较两个JPA的子项目, MongoDB和Neo4j。JPA是J2EE的一部分, 它定义了一系列用于操作关系型数据库和O/R映射的API。 MongoDB是一种可扩展的、高性能的、开源的、面向文档的数据库。Neo4j则是一种图形数据库,一种完整的用于存储图形数据的事务性数据库。

所有这些Spring Data的子项目都支持:

    模板
    对象、数据存储映射
    对数据访问对象的支持

其他一些Spring Data 子项目,如Spring Data Redis和Spring Data Riak都只是提供模板, 这是由于其相应的数据存储都只支持非结构化的数据,而不适用于对象的映射和查询。

下面,我们来深入了解一下模板、对象数据映射和数据访问对象。
模板

Spring Data 模板的主要目的, 同时也是所有其他Spring 模板的目的, 就是资源分配和异常处理。

这里所说的资源就是数据存储资源, 通常来说会通过远程TCP/IP连接访问。下面的实例展示了如果配置MongoDB的模板:

<!-- 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>

首先我们需要定义连接工厂,MongoTemplate会引用这个连接工厂。这个例子中, Spring Data采用了较底层的数据库驱动,MongoDB Java driver。

一般来说, 这类较底层的数据库驱动会有自己的一套异常处理策略。 Spring的异常处理采用的是未检查异常(unchecked exception),因此开发人员可以自己决定是否须要捕获异常。MongoDB的模板的实现方式是把捕获到的底层的异常封装成未检查异常, 这些异常都是Spring里DataAccessException的子类。

模板提供了基于数据存储的操作, 诸如保存、更新、删除单一记录或执行查询的方法。但所有这些方法只能用于相应的底层数据存储。

由于JPA的实现本身已经是位于JDBC API上层的抽象层,Spring Data JPA 并没有提供模板。和模板概念相对应的是JPA的EntityManager, 而异常处理则是由数据访问对象来负责的。
对象、数据映射

JPA引入了O/R映射的标准(如关系型数据库中表和对象的映射)。Hibernate很有可能是被最为广泛使用的JPA标准的实现。

Spring Data采用类对象的方式将O/R映射的支持延伸到了NoSQL数据库。但在各种NoSQL数据库中, 数据结构差异较大, 所以很难形成一种通用的API。 每一种数据存储都有各自一套注释用以标注映射所需要的元信息。下面我们来看一个简单的例子,如何映射一个简单领域对象:
JPA MongoDB Neo4j
@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实体,不难看出这里用了标准的JPA注释。Spring Data复用了这些标准的注释, 而且没有引入其他新的内容。对象的映射正是由这些JPA的实现完成的。 MangoDB和Neo4j各种需要一套类似的注释。在上面的例子中, 我们使用了类级别的注释collection和nodetype. MangoDB中, collection就相当于关系型数据库的表, 而node和edge则是图形数据库(如Neo4j)的主要数据类型。

每个JPA实体都需要有唯一标识符,即便是MongoDB的文档和Neo4j的节点也是如此。

MongoDB使用@Id这个注释作为唯一标识符(这@Id是在org.springframework.data.annotation包中, 和JPA的@Id并不相同)。Neo4j则使用了@GraphId这个注释。这些属性的值是在域对象成功存储后被设置的。 当类属性的名称和MongoDB的文档中的属性名称不同时, 可以使用@Field注释标注。

同样这两种映射也支持对其他对象的引用,请看下面的例子:
JPA MongoDB Neo4j
@OneToMany
private List<Role> roles; private List<Role> roles; @RelatedTo( type = "has", direction = Direction.OUTGOING)
private List<Role> roles;

在JPA中, 我们使用@OneToMany来标识一对多的关系, 通常多的一端的数据存放在子表中, 通过联合查询获得。MongoDB并不支持文档间的联合查询,默认情况下, 被引用的对象和主对象存储在同一个文档中。当然, 我们也可以通过客户端的虚拟联合查询引用其他文档的数据。在Neo4j中, 关系被称作edges, 而edge也是一个基本的数据类型。

总结来说, MongoDB和Neo4j所使用的对象映射和我们大家所熟悉的JPA O/R映射非常类似, 但由于不同的数据结构,两者存在着显著的区别。但不管怎么说, 基本概念都是实现对象和数据结构的映射。
数据访问对象

你一定写过这样的DAO对象,针对单一记录的CRUD操作、针对多记录的CRUD操作, 基于各种查询条件的查询方法。

随着JPA的引入, 虽然EntityManager接口已经包含了CRUD操作,但 编写查询方法仍然是一件麻烦事, 为此, 完成一次查询需要创建命名查询, 设置参数, 执行查询。请看下面的例子:

@Entity @NamedQuery( name="myQuery", query = "SELECT u FROM User u where u.name = :name" ) public class User { ... } @Repository public class ClassicUserRepository { @PersistenceContext EntityManager em; public List<User> findByName(String Name) { TypedQuery<User> q = getEntityManger().createNamedQuery("myQuery", User.class); q.setParameter("name", fullName); return q.getResultList(); } ...

TypedQuery可以略微简化这个过程,如:

@Repository public class ClassicUserRepository { @PersistenceContext EntityManager em; public List<User> findByName(String name) { return getEntityManger().createNamedQuery("myQuery", User.class) .setParameter("name", fullName) .getResultList(); } ...

我们仍然须要编写类似这样的查询方法,为查询赋值, 执行查询。如果可以引入Spring Data JPA,要实现这类的查询, 编码就可以大大简化, 如下:

package repositories; public interface UserRepository extends JpaRepository<User, String> { List<User> findByName(String name); }

在Spring Data JPA中, 我们将不再需要在JPA实体类中定义@NamedQuerys, 来实现JPQL的查询。 相反, 我们可以为数据访问对象的各方法加上@Query这样的注释。如:

@Transactional(timeout = 2, propagation = Propagation.REQUIRED) @Query("SELECT u FROM User u WHERE u.name = 'User 3'") List<User> findByGivenQuery();

Spring Data MongoDB和Spring Data Neo4j可以使上述方法同样适用于MangoDB和Neo4j数据库。下面的例子的是通过Cipher查询语言来实现对Neo4j数据库的查询。

public interface UserRepository extends GraphRepository<User> { User findByLogin(String login); @Query("START root=node:User(login = 'root') MATCH root-[:knows]->friends RETURN friends") List<User> findFriendsOfRoot(); }

当然,各个持久化层的方的命名规则稍有差异。比如说, MongoDB支持一种叫geospatial queries的查询语言, 通常我们可以这样写查询:

public interface LocationRepository extends MongoRepository<Location, String> { List<Location> findByPositionWithin(Circle c); List<Location> findByPositionWithin(Box b); List<Location> findByPositionNear(Point p, Distance d); }

Spring Data也提供对分页和排序的通用方法,这需要在查询方法中加入特殊的参数。

支持数据访问对象的优势在于:

    减少相似代码的编写
    定义方法的同时, 可以定义查询语句,这也使文档变得更清晰
    另外一个优点是, 查询语句将和Spring上下文同时编译并组装,而不是在初次使用的时候编译, 这样可以大大减少代码编写中的语法错误。

总结

本文只是简单介绍了Spring Data 的这些新内容, 意在阐明和普通JPA的相似点和不同之处。下面这些链接可以帮助大家对Spring Data的相关项目做更输入的了解。

    Spring Data JPA
    Spring Data MongoDB
    Spring Data Neo4j

对标题中问题的回答显然是否定的, 不可能存在支持所有持久存储的通用API, 其原因也是不言而喻的。Spring Data 项目旨在为大家提供一种通用的编码模式。

数据访问对象实现了对物理数据层的抽象, 为编写查询方法提供了方便。通过对象映射, 实现域对象和持续化存储之间的转换, 而模板提供的是对底层存储实体的访问实现。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics