慢來比較快,虛心學技術
Ⅰ、Spring Data JPA簡介
Spring-Data:Spring 的一個子項目。用於簡化數據庫訪問,支持NoSQL 和 關係數據存儲。其主要目標是使數據庫的訪問變得方便快捷。
Spring-data系列產品:
Spring Data Common:提供共享的基礎框架,適合各個子項目使用,支持跨數據庫持久化
Spring Data JDBC:提供對關係數據庫的訪問,而無需處理JPA的複雜性
Spring Data JDBC Extensions:支持 Oracle RAD、高級隊列和高級數據類型
Spring Data JPA:簡化創建 JPA 數據訪問層和跨存儲的持久層功能
Spring Data Mongodb:提供對文檔數據庫的支持
Spring Data Redis:提供對鍵值對數據庫的支持
其中,Spring-data-jpa是 Spring 基於 ORM 框架、JPA 規範的基礎上封裝的一套 JPA 應用框架,底層使用了 Hibernate 的 JPA 技術實現,可使開發者用極簡的代碼即可實現對數據的訪問和操作。它提供了包括增刪改查等在內的常用功能,且易於擴展。
簡單來說,其目的在於簡化Spring未簡化的持久層業務工作,開發者僅需要完成的是聲明持久層接口,其餘則由Spring Data JPA來完成。
Ⅱ、Spring Data JPA架構體系分析
如上所述,我們知道Spring Data JPA依賴於接口即可實現持久層操作,那麼瞭解它提供的核心接口類,即可開始我們的使用:
Repository:最頂層的接口,是一個空的接口,目的是爲了統一所有Repository的類型,且能讓組件掃描的時候自動識別
Specification:Spring Data JPA提供的一個查詢規範,要做複雜的查詢,只需圍繞這個規範來設置查詢條件
CrudRepository接口:提供了CRUD的基本功能
PagingAndSortingRepository分頁排序接口:封裝了查詢分頁和排序的功能
JpaRepository接口:PagingAndSortingRepository和QueryByExampleExecutor的子接口,除了提供CRUD的功能之外,還額外提供了分頁和排序、過濾等功能
** JpaSpecificationExecutor接口**:提供了對JPA Criteria查詢(動態查詢)的支持
以下分析部分源碼:
①Repository
T:要操作的實體類類型
ID:實體類的主鍵類型class
@Indexed
public interface Repository<T, ID> {
}
②CrudRepository
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
//保存一個實體記錄,返回保存後的實體
<S extends T> S save(S var1);
//保存傳入的實體列表,返回保存後的實體列表
<S extends T> Iterable<S> saveAll(Iterable<S> var1);
//根據主鍵查詢實體,返回封裝實體信息的對象
Optional<T> findById(ID var1);
//根據主鍵查詢數據庫是否已存在該記錄,返回boolean值
boolean existsById(ID var1);
//獲取數據表所有記錄,返回實體列表
Iterable<T> findAll();
//根據主鍵列表獲取對應實體記錄,返回實體列表
Iterable<T> findAllById(Iterable<ID> var1);
//統計數據表記錄總數
long count();
//根據主鍵刪除記錄
void deleteById(ID var1);
//根據對象刪除記錄
void delete(T var1);
//根據傳入的對象列表刪除記錄
void deleteAll(Iterable<? extends T> var1);
//清空數據表
void deleteAll();
}
③PagingAndSortingRepository
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
//獲取數據表所有記錄並根據傳入的Sort對象進行排序,返回實體列表
Iterable<T> findAll(Sort var1);
//獲取數據表所有記錄並根據傳入的分頁對象進行分頁排序,返回實體列表
Page<T> findAll(Pageable var1);
}
④JpaSpecificationExecutor
public interface JpaSpecificationExecutor<T> {
//根據傳入的條件對象查詢某個實體,返回實體
Optional<T> findOne(@Nullable Specification<T> var1);
//根據傳入的條件對象查詢符合條件的實體,返回實體列表
List<T> findAll(@Nullable Specification<T> var1);
//根據傳入的條件對象和分頁對象查詢符合條件的實體,返回實體分頁列表
Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
//根據傳入的條件對象查詢實體並根據Sort對象排序,返回實體列表
List<T> findAll(@Nullable Specification<T> var1, Sort var2);
//根據傳入的條件對象查詢符合條件的實體個數,返回個數
long count(@Nullable Specification<T> var1);
}
⑤JpaRepository
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
//獲取數據表所有記錄,返回實體列表
List<T> findAll();
//獲取數據表所有記錄並根據Sort對象進行排序,返回實體列表
List<T> findAll(Sort var1);
//根據主鍵列表獲取對應實體記錄,返回實體列表
List<T> findAllById(Iterable<ID> var1);
//保存傳入的實體列表,返回保存後的實體列表
<S extends T> List<S> saveAll(Iterable<S> var1);
//清除session緩存
void flush();
//保存並清除session緩存
<S extends T> S saveAndFlush(S var1);
//根據傳入的實體對象批量刪除數據表記錄
void deleteInBatch(Iterable<T> var1);
//清空數據庫表
void deleteAllInBatch();
//根據主鍵獲取實體,返回實體
T getOne(ID var1);
//根據傳入的Example對象獲取符合條件的實體,返回實體列表
<S extends T> List<S> findAll(Example<S> var1);
//根據傳入的Example對象獲取符合條件的實體並根據Sort對象進行排序,返回實體列表
<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}
Ⅲ、Spring整合使用Sring Data JPA
① 引入依賴
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--logback日誌實現引入-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
<!--slf4j日誌門面引入-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<!--引入alibaba的數據庫連接池-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--引入Spring支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--引入Spring事務-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--引入Spring對ORM框架的支持依賴-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--引入jpa支持-->
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
<!--引入Hibernate對象管理器-->
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-entitymanager -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.1.Final</version>
</dependency>
<!--引入數據庫驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
②配置jpa
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!--開啓註解-->
<context:annotation-config/>
<!--配置組件掃描範圍-->
<context:component-scan base-package="com.my.spring"></context:component-scan>
<!-- 導入資源文件 -->
<context:property-placeholder location="classpath:datasource.properties"/>
<!--配置數據庫連接池-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="maxActive" value="${druid.maxActive}"></property>
<property name="maxWait" value="${druid.maxWait}"></property>
<property name="timeBetweenEvictionRunsMillis" value="${druid.timeBetweenEvictionRunsMillis}"></property>
</bean>
<!--配置JPA的持久化實現廠商類-->
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!-- 自動檢查註解的實體和數據表,如果數據庫不存在的標,會根據實體自動生成 -->
<property name="generateDdl" value="true" />
<property name="database" value="HSQL" />
</bean>
<!--配置Jpa Entity Manager-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" lazy-init="false">
<!--注入數據庫-->
<property name="dataSource" ref="dataSource" />
<!-- 指定Jpa持久化實現廠商類,這裏以Hibernate爲例 -->
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"></property>
<!-- 指定Entity實體類包路徑,使用註解方式進行映射 -->
<property name="packagesToScan" value="com.my.spring.bean" />
<!-- 指定JPA屬性;如Hibernate中指定是否顯示SQL的是否顯示、方言等 -->
<property name="jpaProperties">
<props>
<!-- 命名規則 My_NAME->MyName -->
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.DefaultComponentSafeNamingStrategy</prop>
<!-- 打印sql語句 -->
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
</props>
</property>
</bean>
<!--配置jpa的方言對象-->
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
<!-- 配置Spring聲明式事務 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="dataSource" ref="dataSource"></property>
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<!--開啓註解事務-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 重要配置:啓用掃描並自動創建代理的功能 -->
<jpa:repositories base-package="com.my.spring" transaction-manager-ref="transactionManager" entity-manager-factory-ref="entityManagerFactory" />
</beans>
③編寫實體類
@Entity//指定當前實體類需要被掃描
@Table(name = "basebean")//指定當前實體類映射到數據庫表名
public class BaseBean {
@Id//當前字段爲主鍵字段
@GeneratedValue(strategy = GenerationType.IDENTITY)//指定id自增
private Integer id;
private String name;
private Integer age;
}
④編寫Repository接口,繼承CrudRepository接口,獲得默認接口
public interface BaseRepository extends CrudRepository<BaseBean,Integer>{
}
⑤編寫Service和Service實現類
public interface BaseService {
/**
* 保存記錄
* @param baseBean
* @return
*/
void save(BaseBean baseBean);
/**
* 獲取所有記錄
* @return
*/
List<BaseBean> findAll();
/**
* 獲取指定id記錄
* @param id
* @return
*/
BaseBean findOne(Integer id);
}
@Component
public class BaseServiceImpl implements BaseService {
@Autowired
private BaseRepository baseRepository;
@Override
@Transactional(rollbackFor = RuntimeException.class)
public void save(BaseBean baseBean) {
this.baseRepository.save(baseBean);
}
@Override
public List<BaseBean> findAll() {
Iterable<BaseBean> iterable = this.baseRepository.findAll();
if(null!=iterable){
return (List<BaseBean>) iterable;
}
return null;
}
@Override
public BaseBean findOne(Integer id) {
Optional<BaseBean> optional = this.baseRepository.findById(id);
if(null!=optional){
return optional.get();
}
return null;
}
}
⑥編寫測試類
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:application.xml"})//以application.xml爲配置環境文件
public class JPATest {
@Autowired
private BaseService baseService;
@Autowired
private ApplicationContext applicationContext;
@Test
public void testJPA() {
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames){
System.out.println(beanDefinitionName);
}
}
@Test
public void testSave(){
BaseBean baseBean = new BaseBean();
baseBean.setName("jpa bean");
baseBean.setAge(50);
this.baseService.save(baseBean);
}
@Test
public void testFindAll(){
List<BaseBean> list = this.baseService.findAll();
if(null==list||list.isEmpty()){
System.err.println("空表");
}else{
for (BaseBean baseBean : list){
System.err.println(baseBean.toString());
}
}
}
}
運行測試,測試結果:
testJPA()
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.annotation.internalPersistenceAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
baseServiceImpl
org.springframework.context.support.PropertySourcesPlaceholderConfigurer#0
dataSource
jpaVendorAdapter
entityManagerFactory
jpaDialect
transactionManager
org.springframework.transaction.config.internalTransactionalEventListenerFactory
org.springframework.aop.config.internalAutoProxyCreator
org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0
org.springframework.transaction.interceptor.TransactionInterceptor#0
org.springframework.transaction.config.internalTransactionAdvisor
org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension#0
emBeanDefinitionRegistrarPostProcessor
jpaMappingContext
jpaContext
org.springframework.data.jpa.util.JpaMetamodelCacheCleanup
baseRepository
testSave()
insert into basebean (age, name) values (?, ?)
testFindAll()
BaseBean(id=1, name=jpa bean, age=50)