Spring data jpa 的使用與詳解(一):框架整合及基本使用

1 什麼是JPA

JPA全稱Java Persistence API,能夠經過註解或者XML描述【對象-關係表】之間的映射關係,並將實體對象持久化到數據庫中。JPA的出現主要是爲了簡化持久層開發以及整合ORM技術,結束Hibernate、TopLink、JDO等ORM框架各自爲營的局面。java

JAP爲咱們提供了ORM映射元數據,JPA的API,JPQL查詢語言等,但JPA僅僅是一種規範,也就是說JPA僅僅定義了一些接口,而接口是須要實現才能工做的。因此底層須要某種實現,而Hibernate就是實現了JPA接口的ORM框架。mysql

2 什麼是Hibernate框架

Hibernate是Java中的對象關係映射解決方案。對象關係映射或ORM是將應用程序域模型對象映射到關係數據庫表的編程技術。Hibernate是一個基於Java的ORM工具,它提供了一個框架,用於將應用程序域對象映射到關係數據庫表。web

Hibernate提供了Java Persistence API的參考實現,使其成爲具備鬆散耦合優點的ORM工具的絕佳選擇。spring

3 什麼是Spring Data JPA

Spring Data是Spring Framework的一部分。Spring Data存儲庫抽象的目標是顯著減小爲各類持久性存儲實現數據訪問層所需的代碼量。sql

Spring Data JPA不是JPA提供者。它是一個庫/框架,它在咱們的JPA提供程序(如Hibernate)的頂部添加了一個額外的抽象層。數據庫

4 Hibernate和Spring Data JPA的關係

Hibernate是一個JPA實現,而Spring Data JPA是一個JPA數據訪問抽象。Spring Data提供了GenericDao自定義實現的解決方案,它還能夠經過方法名稱約定表明您生成JPA查詢。編程

Spring Data JPA不是一個實現或JPA提供者,它只是一個抽象,用於顯著減小爲各類持久性存儲實現數據訪問層所需的代碼量。Spring Data JPA始終須要JPA提供程序,如Hibernate。bash

5 Spring data jpa概述

JPA Spring Data:致力於減小數據訪問層(DAO)的開發量,開發者惟一要作的,就只是聲明持久層的接口,其餘都交給Spring Data JPA來完成。mybatis

框架怎麼可能代替開發者實現業務邏輯呢?好比:當有一個 UserDao.findUserById() 這樣一個方法聲明,大體應該能判斷出這是根據給定條件的 ID 查詢出知足條件的 User 對象。Spring Data JPA 作的即是規範方法的名字,根據符合規範的名字來肯定方法須要實現什麼樣的邏輯。mvc

5 Spring data JPA使用

5.1 jar包引入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
複製代碼

5.2 Spring data jpa配置

@Configuration
// 藉助spring data實現自動化的jpa repository,只需編寫接口無需編寫實現類
// 至關於xml配置的<jpa:repositories base-package="com.example.repository" />
// repositoryImplementationPostfix默認就是Impl
// entityManagerFactoryRef默認就是entityManagerFactory
// transactionManagerRef默認就是transactionManager
@EnableJpaRepositories(basePackages = {"com.wtj.repository"},
        repositoryImplementationPostfix = "Impl",
        entityManagerFactoryRef = "entityManagerFactory",
        transactionManagerRef = "transactionManager")
// 啓用事務管理器
@EnableTransactionManagement
//審計功能 用來自動填充@CreateDate等
@EnableJpaAuditing(dateTimeProviderRef = "dateTimeProvider")
public class SpringDataJpaConfig {

    @Bean
    public DateTimeProvider dateTimeProvider(DateTimeService dateTimeService) {
        return dateTimeService::getNow;
    }

    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
        // 設置數據庫類型(可以使用org.springframework.orm.jpa.vendor包下的Database枚舉類)
        jpaVendorAdapter.setDatabase(Database.MYSQL);
        // 設置打印sql語句
        jpaVendorAdapter.setShowSql(true);
        // 設置不生成ddl語句
        jpaVendorAdapter.setGenerateDdl(false);
        // 設置hibernate方言
        jpaVendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQL5Dialect");
        return jpaVendorAdapter;
    }

    // 配置實體管理器工廠
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
        LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
        // 注入數據源
        emfb.setDataSource(dataSource);
        // 注入jpa廠商適配器
        emfb.setJpaVendorAdapter(jpaVendorAdapter);
        // 設置掃描基本包
        emfb.setPackagesToScan("com.wtj.entity");
        return emfb;
    }

    // 配置jpa事務管理器
    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        // 配置實體管理器工廠
        transactionManager.setEntityManagerFactory(emf);
        return transactionManager;
    }

}

複製代碼

啓用web支持還須要在Spring MVC配置類上添加@EnableSpringDataWebSupport註解

@Configuration
@ComponentScan(basePackages = {"com.wtj.controller"})
@EnableWebMvc   // 啓用spring mvc
@EnableSpringDataWebSupport     // 啓用springmvc對spring data的支持
public class WebMvcConfig extends WebMvcConfigurerAdapter {
}
複製代碼

配置文件

server:
  port: 20000
  servlet:
    context-path: /
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mytest1?useUnicode=true&serverTimezone=UTC&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false
    username: root
    password: root
  jpa:
    database: MySQL
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    show-sql: true
    hibernate:
      ddl-auto: update
複製代碼

ddl-auto 解釋:

  • create : 每次運行程序時,都會從新建立表,故而數據會丟失。
  • create-drop:每次運行程序時會先建立表結構,而後待程序結束時清空表。
  • upadte:每次運行程序,沒有表時會建立表,若是對象發生改變會更新表結構,原有數據不會清空,只會更新(推薦使用)。
  • validate:運行程序會校驗數據與數據庫的字段類型是否相同,字段不一樣會報錯。
  • none: 禁用DDL處理。

5.3 簡單的Spring dat jpa例子

例子簡單就不寫service層了,直接在controller中調用。
建立實體類

@Data
@Entity
@Table(name = "account")
@ToString
public class Account {

    @Id
    @GenericGenerator(name = "idGenerator", strategy = "uuid")
    @GeneratedValue(generator = "idGenerator")
    private String id;

    @Column(name = "username", unique = true, nullable = false, length = 64)
    private String username;

    @Column(name = "password", nullable = false, length = 64)
    private String password;

    @Column(name = "email", length = 64)
    private String email;

}
複製代碼

主鍵採用UUID策略 @GenericGenerator是Hibernate提供的主鍵生成策略註解,注意下面的@GeneratedValue(JPA註解)使用generator = "idGenerator"引用了上面的name = "idGenerator"主鍵生成策略。

JPA自帶的幾種主鍵生成策略
TABLE: 使用一個特定的數據庫表格來保存主鍵。
SEQUENCE: 根據底層數據庫的序列來生成主鍵,條件是數據庫支持序列。這個值要與generator一塊兒使用,generator 指定生成主鍵使用的生成器(多是orcale中本身編寫的序列)。
IDENTITY: 主鍵由數據庫自動生成(主要是支持自動增加的數據庫,如mysql)。
AUTO: 主鍵由程序控制,也是GenerationType的默認值。

dao層

@Repository
public interface AccountRepository extends JpaRepository<Account,String> {
}
複製代碼

controller層

@RestController
@RequestMapping(value = "/role")
public class AccountController {

    @Autowired
    private AccountRepository repository;

    @PostMapping()
    public Account save(@RequestBody Account account) {
        return repository.save(account);
    }

    @DeleteMapping("/{id}")
    public void delete(@PathVariable("id") String accountId) {
        repository.deleteById(accountId);
    }

    @PutMapping("/{id}")
    public Account update(@PathVariable("id") String accountId, @RequestBody Account account) {
        account.setId(accountId);
        return repository.saveAndFlush(account);
    }

    @GetMapping("/{id}")
    public Account getAccountInfo(@PathVariable("id") String accountId) {
        Optional<Account> optional = repository.findById(accountId);
        return optional.orElseGet(Account::new);
    }

}
複製代碼

最後在數據庫中造幾條假數據進行crud就能夠了。

5.4 Repository接口

Repository 接口概述:

  1. Repository 接口是 Spring Data 的一個核心接口,它不提供任何方法,是一個空接口,便是一個標記接口。開發者須要在本身定義的接口中聲明須要的方法 。
public interface Repository<T, ID extends Serializable> { } 
複製代碼
  1. Spring Data可讓咱們只定義接口,只要遵循Spring Data的規範,就無需寫實現類。
  2. 若咱們定義的接口繼承了Repository,則該接口會被IOC容器識別爲一個Repository Bean,歸入到IOC容器中,進而能夠在該接口中定義知足必定規範的方法。實際上在IOC容器中放的是該接口的實現類,只不過spring幫咱們實現了,實際上它是一個代理。
  3. 與繼承 Repository 等價的一種方式,就是在持久層接口上使用 @RepositoryDefinition 註解,併爲其指定 domainClass 和 idClass 屬性,指定對象和主鍵類型。

Repository 的子接口:
基礎的 Repository提供了最基本的數據訪問功能,其幾個子接口則擴展了一些功能。它們的繼承關係以下:

  • Repository:僅僅是一個標識,代表任何繼承它的均爲倉庫接口類.
  • CrudRepository:繼承Repository,實現了一組CRUD相關的方法.
  • PagingAndSortingRepository: 繼承 CrudRepository,實現了一組分頁排序相關的方法.
  • JpaRepository:繼承PagingAndSortingRepository,實現一組JPA規範相關的方法.
  • 自定義的 XxxxRepository 須要繼承 JpaRepository,這樣的XxxxRepository接口就具有了通用的數據訪問控制層的能力.
  • JpaSpecificationExecutor:不屬於Repository體系,實現一組JPACriteria 查詢相關的方法.

5.5 自定義條件查詢語句

按照 Spring Data的規範,查詢方法以find | read | get 開頭,涉及條件查詢時,條件的屬性用條件關鍵字鏈接,要注意的是:條件屬性以首字母大寫。 好比說如今要按照account的名稱進行查詢:  

在AccountRepository接口中新增方法

@Repository
public interface AccountRepository extends JpaRepository<Account,String> {
    Account findAccountByUsername(String username);
}
複製代碼

controller中新增方法:

@GetMapping("/name/{username}")
    public Account getAccountByName(@PathVariable("username") String userName){
        Account account = repository.findAccountByUsername(userName);
        return account;
    }
複製代碼

這樣就能夠根據名稱進行查詢了,固然你也可使用這種方法進行復雜查詢,spring data jpa中支持的關鍵字以下:

5.6 自定義sql語句

有些時候spring data jpa提供的查詢條件知足不了業務需求的時候,可使用自定義的sql來進行查詢。
想要使用自定義sql須要使用@Query註解,@Query註解使用起來很簡單,默認的屬性是value,就是當前寫的SQL語句,有時會用到nativeQuery屬性,這個屬性是用來標記當前的SQL是本地SQL,仍是符合JPA語法規範的SQL。這裏須要解釋一下本地SQL和JPA語法規範的SQL區別。

  • 本地SQL,是根據實際使用的數據庫類型寫的SQL,這種SQL中使用到的一些語法格式不能被JPA解析以及可能不兼容其餘數據庫,這種SQL稱爲本地SQL,此時須要將nativeQuery屬性設置爲true,不然會報錯。
  • JPA語法規範的SQL,每每這種SQL自己是不適用於任何數據庫的,須要JPA將這種SQL轉換成真正當前數據庫所須要的SQL語法格式。

JPA很好的一個特性就是用JPA語法規範寫的SQL,會根據當前系統使用的數據庫類型改變生成的SQL語法,兼容數據庫類型的切換,如以前使用的是MySQL,如今換成Oracle,因爲不一樣類型的數據庫,SQL語法會有區別,若是使用的是mybatis,就須要手動去改SQL兼容Oracle,而JPA就不用啦,無縫對接。
很大的時候使用JPA感受都是爲了兼容後期可能會有數據庫切換的問題,因此在使用JPA的時候,不要去使用本地SQL,這就違背了使用JPA的初衷,讓nativeQuery屬性保持默認值就能夠啦!

AccountRepository中新增方法

@Query("select a from Account a where a.username = :username")
Account findAccountByName(@Param("username")String name);

@Query("select a from Account a where a.email = ?1")
Account findAccountByEmail(String email);

@Query(value = "select * from account where username = ?1",nativeQuery = true)
Account getAccount(String username);
複製代碼
  • 在SQL上使用佔位符的兩種方式,第一種是使用":"後加變量的名稱,第二種是使用"?"後加方法參數的位置。若是使用":"的話,須要使用@Param註解來指定變量名;若是使用"?"就須要注意參數的位置。
  • 使用JPA語句中SQL語句中直接用實體類表明表名,由於在實體類中使用了@Table註解,將該實體類和表進行了關聯。 controller中新增方法(方法名還有url起的都比較隨意。。。。。)
@GetMapping("/sql/{username}")
    public Account findAccountByName(@PathVariable("username") String userName){
        Account account = repository.findAccountByName(userName);
        return account;
    }

    @GetMapping("/email/{email}")
    public Account findAccountByEmail(@PathVariable("email") String email){
        Account account = repository.findAccountByEmail(email);
        return account;
    }

    @GetMapping("/username/{username}")
    public Account getAccount(@PathVariable("username") String username){
        Account account = repository.getAccount(username);
        return account;
    }
複製代碼

最後經過請求就能夠得到數據而且在控制檯能夠看到打印出的響應的sql。
當自定義sql涉及到刪除,修改,插入的操做的時候須要加上@Modifying註解。註明當前方法是修改操做。

相關文章
相關標籤/搜索