第11章 使用对象-关系映射持久化数据
本章内容:
使用Spring和Hibernate
借助上下文Session,编写不依赖于Spring的Repository
通过Spring使用JPA
借助Spring Data实现自动化的JPA Repository
在数据持久化的世界中,JDBC就像自行车。对于份内的工作,它能很好地完成并且在一些特定的场景下表现出色。但随着应用程序变得越来越复杂,对持久化的需求也变得更复杂。我们需要将对象的属性映射到数据库的列上,并且需要自动生成语句和查询,这样我们就能从无休止的问号字符串中解脱出来。此外,我们还需要一些更复杂的特性:
延迟加载(Lazy loading):随着我们的对象关系变得越来越复杂,有时候我们并不希望立即获取完整的对象间关系。举一个典型的例子,假设我们在查询一组PurchaseOrder对象,而每个对象中都包含一个LineItem对象集合。如果我们只关心PurchaseOrder的属性,那查询出LineItem的数据就毫无意义。而且这可能是开销很大的操作。延迟加载允许我们只在需要的时候获取数据。
预先抓取(Eager fetching):这与延迟加载是相对的。借助于预先抓取,我们可以使用一个查询获取完整的关联对象。如果我们需要PurchaseOrder及其关联的LineItem对象,预先抓取的功能可以在一个操作中将它们全部从数据库中取出来,节省了多次查询的成本。
级联(Cascading):有时,更改数据库中的表会同时修改其他表。回到我们订购单的例子中,当删除Order对象时,我们希望同时在数据库中删除关联的LineItem。
一些可用的框架提供了这样的服务,这些服务的通用名称是对象/关系映射(object-relational mapping,ORM)。在持久层使用ORM工具,可以节省数千行的代码和大量的开发时间。ORM工具能够把你的注意力从容易出错的SQL代码转向如何实现应用程序的真正需求。 Spring对多个持久化框架都提供了支持,包括Hibernate、iBATIS、Java数据对象(Java Data Objects,JDO)以及Java持久化API(Java Persistence API,JPA)。与Spring对JDBC的支持那样,Spring对ORM框架的支持提供了与这些框架的集成点以及一些附加的服务:
支持集成Spring声明式事务;
透明的异常处理;
线程安全的、轻量级的模板类;
DAO支持类;
资源管理。
本章没有足够的篇幅介绍Spring支持的全部ORM框架。其实这并不会有什么问题,因为Spring对不同ORM解决方案的支持是很相似的。一旦掌握了Spring对某种ORM框架的支持后,你可以轻松地切换到另一个框架。
在本章中,我们将会看到Spring如何与最常用的两种ORM方案集成:Hibernate和JPA。同时还会通过Spring Data JPA了解一下Spring Data项目。借助这种方式,我们不仅可以学习到如何借助Spring Data JPA移除JPA Repository中的样板式代码,还能为下一章的如何将Spring Data用于无模式的存储打下基础。 让我们先来看看Spring是如何为Hibernate提供支持的。
11.1 在Spring中集成Hibernate
Hibernate是在开发者社区很流行的开源持久化框架。它不仅提供了基本的对象关系映射,还提供了ORM工具所应具有的所有复杂功能,比如缓存、延迟加载、预先抓取以及分布式缓存。 在本章中,我们会关注Spring如何与Hibernate集成,而不会涉及太多Hibernate使用时的复杂细节。如果你需要了解更多关于Hibernate如何使用的知识,我推荐你阅读Christian Bauer、GavinKing和Gary Gregory撰写的《Java Persistence with Hibernate,Second Edition》(Manning,2014,www.manning.com/bauer3/)或访问Hibernate的网站http://www.hibernate.org。
关于HIbernate,我想说,这是学生课本中的基础框架知识,在工作中基本被淘汰了,对于读者浏览一下便好了。
使用Hibernate所需的主要接口是org.hibernate.Session。Session接口提供了基本的数据访问功能,如保存、更新、删除以及从数据库加载对象的功能。通过Hibernate的Session接口,应用程序的Repository能够满足所有的持久化需求。
获取Hibernate Session对象的标准方式是借助于Hibernate SessionFactory接口的实现类。除了一些其他的任务,SessionFactory主要负责Hibernate Session的打开、关闭以及管理。
在Spring中,我们要通过Spring的某一个Hibernate Session工厂bean来获取HibernateSessionFactory。从3.1版本开始,Spring提供了三个Session工厂bean供我们选择:
org.springframework.orm.hibernate3.LocalSessionFactoryBean
org.springframework.orm.hibernate3.annotation.AnnotationSessionFactory
org.springframework.orm.hibernate4.LocalSessionFactoryBean
这些Session工厂bean都是Spring FactoryBean接口的实现,它们会产生一个HibernateSessionFactory,它能够装配进任何SessionFactory类型的属性中。这样的话,就能在应用的Spring上下文中,与其他的bean一起配置Hibernate Session工厂。
至于选择使用哪一个Session工厂,这取决于使用哪个版本的Hibernate以及你使用XML还是使用注解来定义对象-数据库之间的映射关系。如果你使用Hibernate 3.2或更高版本(直到Hibernate 4.0,但不包含这个版本)并且使用XML定义映射的话,那么你需要定义Spring的org.springframework.orm.hibernate3包中的LocalSessionFactoryBean:
@Repository
public class ProductDaoImpl implements ProductDao {
// class body here...
}
<beans>
<!-- Exception translation bean post processor -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean id="myProductDao" class="product.ProductDaoImpl"/>
</beans>
在配置LocalSessionFactoryBean时,我们使用了三个属性。属性dataSource装配了一个DataSource bean的引用。属性mappingResources列出了一个或多个的Hibernate映射文件,在这些文件中定义了应用程序的持久化策略。最后,hibernateProperties属性配置了Hibernate如何进行操作的细节。在本示例中,我们配置Hibernate使用H2数据库并且要按照H2Dialect来构建SQL。
如果你更倾向于使用注解的方式来定义持久化,并且你还没有使用Hibernate 4的话,那么需要使用AnnotationSessionFactoryBean来代替LocalSessionFactoryBean:
如果你使用Hibernate 4的话,那么就应该使用org.springframework.orm.hibernate4中的LocalSessionFactoryBean。尽管它与Hibernate 3包中的LocalSessionFactoryBean使用了相同的名称,但是Spring 3.1新引入的这个Session工厂类似于Hibernate 3中LocalSessionFactoryBean和 AnnotationSessionFactoryBean的结合体。它有很多相同的属性,能够支持基于XML的映射和基于注解的映射。如下的代码展现了如何对它进行配置,使其支持基于注解的映射: 在这两个配置中,dataSource和hibernateProperties属性都声明了从哪里获取数据库连接以及要使用哪一种数据库。这里不再列出Hibernate配置文件,而是使用packagesToScan属性告诉Spring扫描一个或多个包以查找域类,这些类通过注解的方式表明要使用Hibernate进行持久化,这些类可以使用的注解包括JPA的@Entity 或@MappedSuperclass以及Hibernate的@Entity。
如果愿意的话,你还可以使用annotatedClasses属性来将应用程序中所有的持久化类以全限定名的方式明确列出:
annotatedClasses属性对于准确指定少量的域类是不错的选择。如果你有很多的域类并且不想将其全部列出,又或者你想自由地添加或移除域类而不想修改Spring配置的话,那使用packagesToScan属性是更合适的。
在Spring应用上下文中配置完Hibernate的Session工厂bean后,那我们就可以创建自己的Repository类了。
省略以下内容.........
11.2 Spring与Java持久化API
Java持久化API(Java Persistence API,JPA)诞生在EJB 2实体Bean的废墟之上,并成为下一代Java持久化标准。JPA是基于POJO的持久化机制,它从Hibernate和Java数据对象(Java Data Object,JDO)上借鉴了很多理念并加入了Java 5注解的特性。
在Spring 2.0版本中,Spring首次集成了JPA的功能。具有讽刺意味的是,很多人批评(或赞赏)Spring颠覆了EJB。但是,当Spring支持JPA后,很多开发人员都推荐在基于Spring的应用程序中使用JPA实现持久化。实际上,有些人还将Spring-JPA的组合称为POJO开发的梦之队。
在Spring中使用JPA的第一步是要在Spring应用上下文中将实体管理器工厂(entity manager factory)按照bean的形式来进行配置。
11.2.1 配置实体管理器工厂
简单来讲,基于JPA的应用程序需要使用EntityManagerFactory的实现类来获取EntityManager实例。JPA定义了两种类型的实体管理器:
应用程序管理类型(Application-managed):当应用程序向实体管理器工厂直接请求实体管理器时,工厂会创建一个实体管理器。在这种模式下,程序要负责打开或关闭实体管理器并在事务中对其进行控制。这种方式的实体管理器适合于不运行在Java EE容器中的独立应用程序。
容器管理类型(Container-managed):实体管理器由Java EE创建和管理。应用程序根本不与实体管理器工厂打交道。相反,实体管理器直接通过注入或JNDI来获取。容器负责配置实体管理器工厂。这种类型的实体管理器最适用于Java EE容器,在这种情况下会希望在persistence.xml指定的JPA配置之外保持一些自己对JPA的控制。
以上的两种实体管理器实现了同一个EntityManager接口。关键的区别不在于EntityManager本身,而是在于EntityManager的创建和管理方式。应用程序管理类型的EntityManager是由EntityManagerFactory创建的,而后者是通过PersistenceProvider的createEntityManagerFactory()方法得到的。与此相对,容器管理类型的Entity ManagerFactory是通过PersistenceProvider的createContainerEntityManager Factory()方法获得的。
这对想使用JPA的Spring开发者来说又意味着什么呢?其实这并没太大的关系。不管你希望使用哪种EntityManagerFactory,Spring都会负责管理EntityManager。如果你使用的是应用程序管理类型的实体管理器,Spring承担了应用程序的角色并以透明的方式处理EntityManager。在容器管理的场景下,Spring会担当容器的角色。
这两种实体管理器工厂分别由对应的Spring工厂Bean创建:
LocalEntityManagerFactoryBean生成应用程序管理类型的EntityManagerFactory;
LocalContainerEntityManagerFactoryBean生成容器管理类型的EntityManagerFactory。
需要说明的是,选择应用程序管理类型的还是容器管理类型的EntityManager Factory,对于基于Spring的应用程序来讲是完全透明的。当组合使用Spring和JPA时,处理EntityManagerFactory的复杂细节被隐藏了起来,数据访问代码只需关注它们的真正目标即可,也就是数据访问。
应用程序管理类型和容器管理类型的实体管理器工厂之间唯一值得关注的区别是在Spring应用上下文中如何进行配置。让我们先看看如何在Spring中配置应用程序管理类型的LocalEntityManagerFactoryBean,然后再看看如何配置容器管理类型的LocalContainerEntityManagerFactoryBean。
配置应用程序管理类型的JPA对于应用程序管理类型的实体管理器工厂来说,它绝大部分配置信息来源于一个名为 persistence.xml的配置文件。这个文件必须位于类路径下的META-INF目录下。
persistence.xml的作用在于定义一个或多个持久化单元。持久化单元是同一个数据源下的一个或多个持久化类。简单来讲,persistence.xml列出了一个或多个的持久化类以及一些其他的配置如数据源和基于XML的配置文件。如下是一个典型的persistence.xml文件,它是用于Spittr应用程序的:
<persistence
xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="spitterPU">
<class>com.web.spittr.Spitter</class>
<class>com.web.spittr.Spittle</class>
<properties>
<property name="toplink.jdbc.url" value="jdbc:hsqldb:hsql://localhost/spitter" />
<property name="toplink.jdbc.user" value="sa" />
<property name="toplink.jdbc.password" value="123456" />
</properties>
</persistence-unit>
</persistence>
HSQLDB(HyperSQL DataBase)是一个开放源代码的JAVA数据库,其具有标准的SQL语法和JAVA接口,它可以自由使用和分发,非常简洁和快速的
容器管理的JPA:
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.MYSQL);
adapter.setShowSql(true);
adapter.setGenerateDdl(false);
adapter.setGenerateDdl(false);
adapter.setDatabasePlatform("org.hibernate.dialect.MYSQLDialect");
return adapter;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource dataSource,
JpaVendorAdapter jpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
emfb.setDataSource(dataSource);
emfb.setJpaVendorAdapter(jpaVendorAdapter);
// emfb.setPackagesToScan("com.web.spittr.data");
return emfb;
}
11.2.2 编写基于JPA的Repository
import com.web.spittr.Spittle;
import com.web.spittr.data.SpittleRepository;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import java.util.List;
/**
* @description:
* @author: Shenshuaihu
* @version: 1.0
* @data: 2019-05-06 10:56
*/
public class JpaSpitterRepository implements SpittleRepository {
@PersistenceUnit
private EntityManagerFactory emf;
@Override
public List<Spitter> findByUsername(String username) {
return (List<Spitter>) emf.createEntityManager().find(Spitter.class, username);
}
@Override
public Spitter findById(Long id) {
return emf.createEntityManager().find(Spitter.class, id);
}
}
11.3 借助Spring Data实现自动化的JPA Repository
借助Spring Data,以接口定义的方式创建Repository
import com.web.spittr.Spitter;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SpitterReposity extends JpaRepository<Spitter, Long> {
Spitter findByUsername(String username);
}
编写Spring Data JPA Repository的关键在于要从一组接口中挑选一个进行扩展。这里,SpitterRepository扩展了Spring Data JPA的JpaRepository(稍后,我会介绍几个其他的接口)。
通过这种方式,JpaRepository进行了参数化,所以它就能知道这是一个用来持久化Spitter对象的Repository,并且Spitter的ID类型为Long。另外,它还会继承18个执行持久化操作的通用方法,如保存Spitter、删除Spitter以及根据ID查询Spitter。
此时,你可能会想下一步就该编写一个类实现SpitterRepository和它的18个方法了。如果真的是这样的话,那本章就会变得乏味无聊了。其实,我们根本不需要编写SpitterRepository的任何实现类,相反,我们让Spring Data来为我们做这件事请。我们所需要做的就是对它提出要求。
为了要求Spring Data创建SpitterRepository的实现,我们需要在Spring配置中添加一个元素。
使用Java配置的话,那就不需要使用jpa:repositories元素了,而是要在Java配置类上添加@EnableJpaRepositories注解。如下就是一个Java配置类,它使用了@EnableJpaRepositories注解,并且会扫描com.web.spittr包:
@Configuration
@EnableJpaRepositories("com.web.spittr")
public class SpringDataJpaConfig {
......
}
让我们回到SpitterRepository接口,它扩展自JpaRepository,而JpaRepository又扩展自Repository标记接口(虽然是间接的)。因此,SpitterRepository就传递性地扩展了Repository接口,也就是Repository扫描时所要查找的接口。当Spring Data找到它后,就会创建SpitterRepository的实现类,其中包含了继承自JpaRepository、PagingAndSortingRepository和CrudRepository的18个方法。
很重要的一点在于Repository的实现类是在应用启动的时候生成的,也就是Spring的应用上下文创建的时候。它并不是在构建时通过代码生成技术产生的,也不是接口方法调用时才创建的。
很漂亮的技术,对吧?
Spring Data JPA很棒的一点在于它能为Spitter对象提供18个便利的方法来进行通用的JPA操作,而无需你编写任何持久化代码。但是,如果你的需求超过了它所提供的这18个方法的话,该怎么办呢?幸好,Spring Data JPA提供了几种方式来为Repository添加自定义的方法。让我们看一下如何为Spring Data JPA编写自定义的查询方法。
11.3.1 定义查询方法
现在,SpitterRepository需要完成的一项功能是根据给定的username查找Spitter对象。比如,我们将SpitterRepository接口修改为如下所示的样子:
public interface SpitterRepositoy extends JpaRepository<Spitter, Long> {
Spitter findByUsername(String username);
}
这个新的findByUserName()非常简单,但是足以满足我们的需求。现在,该如何让SpringData JPA提供这个方法的实现呢?
实际上,我们并不需要实现findByUsername()。方法签名已经告诉Spring Data JPA足够的信息来创建这个方法的实现了。
当创建Repository实现的时候,Spring Data会检查Repository接口的所有方法,解析方法的名称,并基于被持久化的对象来试图推测方法的目的。本质上,Spring Data定义了一组小型的领域特定语言(domain-specific language ,DSL),在这里,持久化的细节都是通过Repository方法的签名来描述的。
Spring Data能够知道这个方法是要查找Spitter的,因为我们使用Spitter对JpaRepository进行了参数化。方法名findByUsername确定该方法需要根据username属性相匹配来查找Spitter,而username是作为参数传递到方法中来的。另外,因为在方法签名中定义了该方法要返回一个Spitter对象,而不是一个集合,因此它只会查找一个 username属性匹配的Spitter。
findByUsername()方法非常简单,但是Spring Data也能处理更加有意思的方法名称。
Repository方法是由一个动词、一个可选的主题(Subject)、关键词By以及一个断言所组成。
在findByUsername()这个样例中,动词是find,断言是Username,主题并没有指定,暗含的主题是Spitter。
作为编写Repository方法名称的样例,我们参照名为readSpitterByFirstnameOrLastname()的方法,看一下方法中的各个部分是如何映射的。图11.1展现了这个方法是如何拆分的。
我们可以看到,这里的动词是read,与之前样例中的find有所差别。Spring Data允许在方法名中使用四种动词:get、read、find和count。其中,动词get、read和find是同义的,这三个动词对应的Repository方法都会查询数据并返回对象。而动词count则会返回匹配对象的数量,而不是对象本身。
readSpittersByFirstNameOrderByLastNameAsc
read | Spitters | ByFirstNameOrderByLastNameAsc
查询动词 | 主题 | 断言
Repository方法的命名遵循一种模式,有助于Spring Data生成针对数据库的查询
Repository方法的主题是可选的。它的主要目的是让你在命名方法的时候,有更多的灵活性。 如果你更愿意将方法称为readSpittersByFirstnameOrLastname()而不是readByFirstnameOrLastname()的话,那么你尽可以这么做。
对于大部分场景来说,主题会被省略掉。readSpittersByFirstnameOrLastname()与readPuppiesByFirstnameOrLastname()并没有什么差别,它们与readThoseThingsWeWantByFirstnameOrLastname()同样没有什么区别。要查询的对象类型是通 过如何参数化JpaRepository接口来确定的,而不是方法名称中的主题。
在省略主题的时候,有一种例外情况。如果主题的名称以Distinct开头的话,那么在生成查询的时候会确保所返回结果集中不包含重复记录。
断言是方法名称中最为有意思的部分,它指定了限制结果集的属性。
在readByFirstnameOrLastname()这个样例中,会通过firstname属性或lastname属性的值来限制结果。
在断言中,会有一个或多个限制结果的条件。每个条件必须引用一个属性,并且还可以指定一种比较操作。如果省略比较操作符的话,那么这暗指是一种相等比较操作。不过,我们也可以选择其他的比较操作,包括如下的种类:
IsAfter、After、IsGreaterThan、GreaterThan
IsGreaterThanEqual、GreaterThanEqual
IsBefore、Before、IsLessThan、LessThan
IsLessThanEqual、LessThanEqual
IsBetween、Between
IsNull、Null
IsNotNull、NotNull
IsIn、In
IsNotIn、NotIn
IsStartingWith、StartingWith、StartsWith
IsEndingWith、EndingWith、EndsWith
IsContaining、Containing、Contains
IsLike、Like
IsNotLike、NotLike
IsTrue、True
IsFalse、False
Is、Equals
IsNot、Not
要对比的属性值就是方法的参数。完整的方法签名如下所示:
List<Spitter> readSpittersByFirstNameOrLastNameOrderByLastNameAsc(String firstName, String lastName);
要处理String类型的属性时,条件中可能还会包含IgnoringCase或IgnoresCase,这样在执行对比的时候就会不再考虑字符是大写还是小写。例如,要在firstname和lastname属性上忽略大小写,那么可以将方法签名改成如下的形式:
需要注意,IgnoringCase和IgnoresCase是同义的,你可以随意挑选一个最合适的。作为IgnoringCase/IgnoresCase的替代方案,我们还可以在所有条件的后面添加AllIgnoringCase或AllIgnoresCase,这样它就会忽略所有条件的大小写:
没有找到提示,可能被废弃了
注意,参数的名称是无关紧要的,但是它们的顺序必须要与方法名称中的操作符相匹配。
最后,我们还可以在方法名称的结尾处添加OrderBy,实现结果集排序。例如,我们可以按照lastname属性升序排列结果集:
List<Spitter> readSpittersByFirstNameOrLastNameOrderByLastNameAsc(String firstName, String lastName);
如果要根据多个属性排序的话,只需将其依序添加到OrderBy中即可。例如,下面的样例中,首先会根据lastname升序排列,然后根据firstname属性降序排列:
List<Spitter> readSpittersByFirstNameOrderByFirstNameAscLastNameDesc(String firstName, String lastName);
可以看到,条件部分是通过And或者Or进行分割的。
List<Pet> findPetsByBreedIn(List<String> breed)
int countProductsByDiscontinuedTrue()
List<Order> findByShippingDateBetween(Date start, Date end)
我们只是初步体验了所能声明的方法种类,Spring Data JPA会为我们实现这些方法。现在,我们只需知道通过使用属性名和关键字构建Repository方法签名,就能让Spring Data JPA生成方法实现,完成几乎所有能够想象到的查询。
不过,Spring Data这个小型的DSL依旧有其局限性,有时候通过方法名称表达预期的查询很烦琐,甚至无法实现。如果遇到这种情形的话,Spring Data能够让我们通过@Query注解来解决问题。
11.3.2 声明自定义查询
假设我们想要创建一个Repository方法,用来查找E-mail地址是Gmail邮箱的Spitter。有一种方式就是定义一个findByEmailLike()方法,然后每次想查找Gmail用户的时候就将“%gmail.com”传递进来。不过,更好的方案是定义一个更加便利的findAllGmailSpitters()方法,这样的话,就不用将Email地址的一部分传递进来了:
List<Spitter> findAllByGEmail();
不过,这个方法并不符合Spring Data的方法命名约定。当Spring Data试图生成这个方法的实现时,无法将方法名的内容与Spitter元模型进行匹配,因此会抛出异常。 如果所需的数据无法通过方法名称进行恰当地描述,那么我们可以使用@Query注解,为Spring Data提供要执行的查询。对于findAllGmailSpitters()方法,我们可以按照如下的方式来使用@Query注解:
@Query("select s from spitter s where email like '%gmail.com'")
List<Spitter> findAllByGEmail();
我们依然不需要编写findAllGmailSpitters()方法的实现,只需提供查询即可,让SpringData JPA知道如何实现这个方法。 可以看到,当使用方法命名约定很难表达预期的查询时,@Query注解能够发挥作用。如果按照命名约定,方法的名称特别长的时候,也可以使用这个注解。例如,考虑如下的查询方法: 这真的是一个方法的名称!我不得不在返回类型后将其断开,这样才能适应本书页面的宽度。
我承认这是一个有点牵强的例子。但在现实世界中,确实存在这样的需求,使用Repository方法所执行的查询会得到一个很长的方法名。在这种情况下,你最好使用一个较短的方法名,并使用@Query来指定该方法要如何查询数据库。 对于Spring Data JPA的接口来说,@Query是一种添加自定义查询的便利方式。但是,它仅限于单个JPA查询。
混合自定义的功能 省略
11.4 小结
对于很多应用来讲,关系型数据库是主流的数据存储形式,并且这种情况已经持续了很多年。使用JDBC并且将对象映射为数据库表是很烦琐乏味的事情,像Hibernate和JPA这样的ORM方案能够让我们以更加声明式的模型实现数据持久化。尽管Spring没有为ORM提供直接的支持,但是它能够与多种流行的ORM方案集成,包括Hibernate与Java持久化API。
在本章中,我们看到了如何在Spring应用中使用Hibernate的上下文Session,这样我们的Repository就能包含很少甚至不包含Spring相关的代码。与之类似,我们还看到了如何将EntityManagerFactory或EntityManager注入到Repository实现中,从而实现不依赖于Spring的JPA Repository。
我们稍后初步了解了Spring Data,在这个过程中,只需声明JPA Repository接口即可,让SpringData JPA在运行时自动生成这些接口的实现。当我们需要的Repository方法超出了Spring DataJPA所提供的功能时,可以借助@Query注解以及编写自定义的Repository方法来实现。
但是,对于Spring Data的整体功能来说,我们只是接触到了皮毛。在下一章中,我们将会更加深入地学习Spring Data的方法命名DSL,以及Spring Data如何为关系型数据库以外的领域带来帮助。也就是说:我们将会看到Spring Data如何支持新兴的NoSQL数据库,这些数据库在最近几年变得越来越流行。
补充说明
作者本章内容粗略的讲述了JPA相关的数据库操作,对于我很遗憾的是没有跑起来,
WARN : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'emf' defined in class path resource [com/web/spittr/config/database/SpringDataJpaConfig.class]: Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/springframework/context/index/CandidateComponentsIndexLoader
ERROR: org.springframework.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'emf' defined in class path resource [com/web/spittr/config/database/SpringDataJpaConfig.class]: Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/springframework/context/index/CandidateComponentsIndexLoader
一直不能解决问题,只怪是使用的Java配置没有选择xml或者是springboot简化的方式,不过一些查询方式还是联系了一下,在springboot中整合jpa是相当简单的,之前看廖师兄的视频大多都是jpa,但现在常用的还是mybatis,因为半自动的,发杂的SQL可以独自自己写,不会被框架限制。
2019/5/7晚于成都