Spring Boot+Spring Data JPA

Spring Boot+Spring Data JPA

1.JPA

JPA是什么?

  1. Java Persistence API:用于對(duì)象持久化的 API
  2. Java EE 5.0 平臺(tái)標(biāo)準(zhǔn)的 ORM 規(guī)范咬腕,使得應(yīng)用程序以統(tǒng)一的方式訪問持久層
image

JPA和Hibernate的關(guān)系

  1. JPA 是 Hibernate 的一個(gè)抽象(就像JDBC和JDBC驅(qū)動(dòng)的關(guān)系)月洛;
  2. JPA 是規(guī)范:JPA 本質(zhì)上就是一種 ORM 規(guī)范,不是ORM 框架蒋伦,這是因?yàn)?JPA 并未提供 ORM 實(shí)現(xiàn)溉知,它只是制訂了一些規(guī)范,提供了一些編程的 API 接口娜膘,但具體實(shí)現(xiàn)則由 ORM 廠商提供實(shí)現(xiàn)逊脯;
  3. Hibernate 是實(shí)現(xiàn):Hibernate 除了作為 ORM 框架之外,它也是一種 JPA 實(shí)現(xiàn)
  4. 從功能上來說劲绪, JPA 是 Hibernate 功能的一個(gè)子集

JPA的優(yōu)勢(shì)

  1. 標(biāo)準(zhǔn)化: 提供相同的 API男窟,這保證了基于JPA 開發(fā)的企業(yè)應(yīng)用能夠經(jīng)過少量的修改就能夠在不同的 JPA 框架下運(yùn)行。
  2. 簡單易用贾富,集成方便: JPA 的主要目標(biāo)之一就是提供更加簡單的編程模型歉眷,在 JPA 框架下創(chuàng)建實(shí)體和創(chuàng)建 Java 類一樣簡單,只需要使用 javax.persistence.Entity 進(jìn)行注解颤枪;JPA 的框架和接口也都非常簡單汗捡。
  3. 可媲美JDBC的查詢能力: JPA的查詢語言是面向?qū)ο蟮模琂PA定義了獨(dú)特的JPQL畏纲,而且能夠支持批量更新和修改扇住、JOIN、GROUP BY盗胀、HAVING 等通常只有 SQL 才能夠提供的高級(jí)查詢特性艘蹋,甚至還能夠支持子查詢。
  4. 支持面向?qū)ο蟮母呒?jí)特性: JPA 中能夠支持面向?qū)ο蟮母呒?jí)特性票灰,如類之間的繼承女阀、多態(tài)和類之間的復(fù)雜關(guān)系,最大限度的使用面向?qū)ο蟮哪P?/li>

JPA包含的技術(shù)

  1. ORM 映射元數(shù)據(jù):JPA 支持 XML 和 JDK 5.0 注解兩種元數(shù)據(jù)的形式屑迂,元數(shù)據(jù)描述對(duì)象和表之間的映射關(guān)系浸策,框架據(jù)此將實(shí)體對(duì)象持久化到數(shù)據(jù)庫表中。
  2. JPA 的 API:用來操作實(shí)體對(duì)象惹盼,執(zhí)行CRUD操作庸汗,框架在后臺(tái)完成所有的事情,開發(fā)者從繁瑣的 JDBC 和 SQL 代碼中解脫出來手报。
  3. 查詢語言(JPQL):這是持久化操作中很重要的一個(gè)方面蚯舱,通過面向?qū)ο蠖敲嫦驍?shù)據(jù)庫的查詢語言查詢數(shù)據(jù),避免程序和具體的 SQL 緊密耦合

2.Spring Data

Spring Data 是 Spring 的一個(gè)子項(xiàng)目掩蛤。用于簡化數(shù)據(jù)庫訪問枉昏,支持NoSQL 和 關(guān)系數(shù)據(jù)存儲(chǔ)。其主要目標(biāo)是使數(shù)據(jù)庫的訪問變得方便快捷盏档。Spring Data 具有如下特點(diǎn):

1.SpringData 項(xiàng)目支持 NoSQL 存儲(chǔ):
MongoDB (文檔數(shù)據(jù)庫)
Neo4j(圖形數(shù)據(jù)庫)
Redis(鍵/值存儲(chǔ))
Hbase(列族數(shù)據(jù)庫)

2.SpringData 項(xiàng)目所支持的關(guān)系數(shù)據(jù)存儲(chǔ)技術(shù):
JDBC
JPA

3.Spring Data Jpa 致力于減少數(shù)據(jù)訪問層(DAO)的開發(fā)量凶掰。開發(fā)者唯一要做的,就是聲明持久層的接口,其他都交給 Spring Data JPA 完成懦窘。比如:當(dāng)有一個(gè)UserDao.findUserById()這樣一個(gè)方法聲明前翎,大致應(yīng)該能判斷出這是根據(jù)給定條件的 ID 查詢出滿足條件的 User 對(duì)象。Spring Data JPA做的便是規(guī)范方法的名字畅涂,根據(jù)符合規(guī)范的名字來確定方法需要實(shí)現(xiàn)什么樣的邏輯港华。

3.Spring Boot整合Spring Data JPA

添加依賴、配置datasource和JPA

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
 </dependency>
 <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
 </dependency>
 <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
 </dependency>

spring.datasource.url=jdbc:mysql://localhost:3306/demo?serverTimezone=GMT%2B8&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

# JPA配置
spring.jpa.database=mysql
# 在控制臺(tái)打印SQL
spring.jpa.show-sql=true
# 數(shù)據(jù)庫平臺(tái)
spring.jpa.database-platform=mysql
# 每次啟動(dòng)項(xiàng)目時(shí)午衰,數(shù)據(jù)庫初始化策略
spring.jpa.hibernate.ddl-auto=update
# 指定默認(rèn)的存儲(chǔ)引擎為InnoDB
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect

創(chuàng)建實(shí)體類

ORM(Object Relational Mapping)框架表示對(duì)象關(guān)系映射立宜,使用ORM框架我們不必再去創(chuàng)建表,框架會(huì)自動(dòng)根據(jù)當(dāng)前項(xiàng)目中的實(shí)體類創(chuàng)建相應(yīng)的數(shù)據(jù)表臊岸。因此橙数,我這里首先創(chuàng)建一個(gè)User對(duì)象,如下:

@Data
@Entity(name = "t_book")
public class book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "name")
    private String name;
    private String author;
}

@Entity注解表示這是一個(gè)實(shí)體類帅戒,那么在項(xiàng)目啟動(dòng)時(shí)會(huì)自動(dòng)針對(duì)該類生成一張表灯帮,默認(rèn)的表名為類名,@Entity注解的name屬性表示自定義生成的表名逻住。@Id注解表示這個(gè)字段是一個(gè)id钟哥,@GeneratedValue注解表示主鍵的自增長策略,對(duì)于類中的其他屬性瞎访,默認(rèn)都會(huì)根據(jù)屬性名在表中生成相應(yīng)的字段腻贰,字段名和屬性名相同,如果開發(fā)者想要對(duì)字段進(jìn)行定制扒秸,可以使用@Column注解播演,去配置字段的名稱,長度鸦采,是否為空等等宾巍。

之后咕幻,啟動(dòng)Spring Boot項(xiàng)目渔伯,數(shù)據(jù)庫中就會(huì)自動(dòng)創(chuàng)建一個(gè)名為t_user的表


image

針對(duì)該表的操作,則需要我們提供一個(gè)Repository肄程,如下:

package com.example.jpa.dao;

import com.example.jpa.bean.Book;
import org.springframework.data.jpa.repository.JpaRepository;

public interface BookDao extends JpaRepository<Book,Integer> {
}

這里锣吼,自定義UserDao接口繼承自JpaRepository,JpaRepository提供了一些基本的數(shù)據(jù)操作方法蓝厌,例如保存玄叠,更新,刪除拓提,分頁查詢等读恃。

普通增刪改查

    @Test
    void insert() {
        Book book = new Book();
        book.setName("百年孤獨(dú)");
        book.setAuthor("馬爾克斯");
        bookDao.save(book);
    }
    @Test
    public void update(){
        Book book = new Book();
        book.setAuthor("馬爾克斯");
        book.setName("霍亂時(shí)期的愛情");
        book.setId(1);
        bookDao.saveAndFlush(book);
    }
    @Test
    public void delete(){
        bookDao.deleteById(1);
    }
    @Test
    void find(){
        Optional<Book> byId = bookDao.findById(2);
        System.out.println(byId.get());
        List<Book> all = bookDao.findAll();
        System.out.println(all);
    }

JPA實(shí)現(xiàn)分頁查詢:

Pageable pageable = PageRequest.of(0,2);
        Page<Book> page = bookDao.findAll(pageable);
        System.out.println("記錄數(shù)"+page.getTotalElements());
        System.out.println("當(dāng)前頁記錄數(shù)"+page.getNumberOfElements());
        System.out.println("每頁記錄數(shù)"+page.getSize());
        System.out.println("獲取總頁數(shù)"+page.getTotalPages());
        System.out.println("查詢結(jié)果"+page.getContent());
        System.out.println("當(dāng)前頁(從零開始)"+page.getNumber());

查詢結(jié)果如下:


image

開發(fā)者也可以在接口中自己聲明相關(guān)的方法,只需要方法名稱符合規(guī)范即可,在Spring Data中寺惫,只要按照既定的規(guī)范命名方法疹吃,Spring Data Jpa就知道你想干嘛,這樣就不用寫SQL了西雀,那么規(guī)范參考下圖:

image

如查詢id大于等于3的記錄

List<Book> findBookByIdGreaterThan(Integer id);

查詢結(jié)果如下


image

這種方法命名主要是針對(duì)查詢萨驶,但是一些特殊需求,可能并不能通過這種方式解決艇肴,例如想要查詢id最大的用戶腔呜,這時(shí)就需要開發(fā)者自定義查詢SQL了,如上代碼所示再悼,自定義查詢SQL核畴,使用@Query注解,在注解中寫自己的SQL冲九,默認(rèn)使用的查詢語言不是SQL膛檀,而是JPQL,這是一種數(shù)據(jù)庫平臺(tái)無關(guān)的面向?qū)ο蟮牟樵冋Z言娘侍,有點(diǎn)定位類似于Hibernate中的HQL咖刃,在@Query注解中設(shè)置nativeQuery屬性為true則表示使用原生查詢,即大伙所熟悉的SQL憾筏。上面代碼中的只是一個(gè)很簡單的例子嚎杨,還有其他一些點(diǎn),例如如果這個(gè)方法中的SQL涉及到數(shù)據(jù)操作氧腰,則需要使用@Modifying和@Transactional注解枫浙。

如查詢id最大的記錄

    @Query(value = "select * from t_book where id=(select max(id) from t_book)",nativeQuery = true)
    Book getMaxIdBook();
    
    @Query(value = "insert into t_book(name,author) value (:name,:author)",nativeQuery = true)
    @Modifying
    @Transactional
    Integer addBook(@Param("name") String name,@Param("author") String author);

查詢結(jié)果如下


image

4.多數(shù)據(jù)源查詢

創(chuàng)建項(xiàng)目

創(chuàng)建項(xiàng)目時(shí)選擇Spring Web、Spring Data JPA以及MySQL Driver

手動(dòng)添加druid-spring-boot依賴

 <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
 </dependency>

多數(shù)據(jù)源配置

接下來配置多數(shù)據(jù)源古拴,這里基本上還是和JdbcTemplate多數(shù)據(jù)源的配置方式一致箩帚,首先在application.properties中配置數(shù)據(jù)庫基本信息,然后提供兩個(gè)DataSource黄痪,再加上jpa的配置即可紧帕。與單數(shù)據(jù)源配置不同,多數(shù)據(jù)源的jpa配置中要加上.properties,表示這些屬性最終都要放在properties中。

application.properties中的配置如下:

spring.datasource.one.url=jdbc:mysql://localhost:3306/demo?serverTimezone=GMT%2B8&useSSL=false
spring.datasource.one.username=root
spring.datasource.one.password=admin
spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource

spring.datasource.url=jdbc:mysql://localhost:3306/demo2?serverTimezone=GMT%2B8&useSSL=false
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

spring.jpa.properties.hibernate.ddl-auto=update
spring.jpa.properties.database-platform=mysql
spring.jpa.properties.database=mysql
spring.jpa.properties.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect

創(chuàng)建實(shí)體類和配置類

@Data
@Entity(name = "t_book")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    private String author;
}

配置DataSource
這里的配置和Mybatis以及jdbc的多數(shù)據(jù)源配置基本一致存谎,但是多了一個(gè)在Spring中使用較少的注解@Primary,@Primary表示當(dāng)某一個(gè)類存在多個(gè)實(shí)例時(shí)鹅搪,優(yōu)先使用哪個(gè)實(shí)例。這個(gè)注解一定不能少遭铺,否則在項(xiàng)目啟動(dòng)時(shí)會(huì)出錯(cuò)丽柿,

@Configuration
public class DataSourceConfig {
    @Bean
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.one")
    DataSource dsOne(){
        return DruidDataSourceBuilder.create().build();
    }
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.two")
    DataSource dsTwo(){
        return DruidDataSourceBuilder.create().build();
    }
}

配置JPA

@Configuration
@EnableJpaRepositories(basePackages = "com.example.jpa2.dao1",
        entityManagerFactoryRef = "localContainerEntityManagerFactoryBean1",
        transactionManagerRef = "platformTransactionManager1")
public class JpaConfig1 {
    @Autowired
    @Qualifier("dsOne")
    DataSource dsOne;

    @Autowired
    JpaProperties jpaProperties;
    
    @Bean
    @Primary
    LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean1(EntityManagerFactoryBuilder builder){
        return builder.dataSource(dsOne)
                .properties(jpaProperties.getProperties())
                .persistenceUnit("pu1")
                .packages("com.example.jpa2.bean")
                .build();
    }
    
    //事務(wù)管理器
    @Bean
    PlatformTransactionManager platformTransactionManager(EntityManagerFactoryBuilder builder){
        return new JpaTransactionManager(localContainerEntityManagerFactoryBean1(builder).getObject());
    }
}

首先這里注入dsOne恢准,再注入JpaProperties,JpaProperties是系統(tǒng)提供的一個(gè)實(shí)例甫题,里邊的數(shù)據(jù)就是我們?cè)?a target="_blank">application.properties中配置的jpa相關(guān)的配置顷歌。然后我們提供兩個(gè)Bean,分別是LocalContainerEntityManagerFactoryBean和PlatformTransactionManager事務(wù)管理器幔睬,不同于MyBatis和JdbcTemplate眯漩,在Jpa中,事務(wù)一定要配置麻顶。在提供LocalContainerEntityManagerFactoryBean的時(shí)候赦抖,需要指定packages,這里的packages指定的包就是這個(gè)數(shù)據(jù)源對(duì)應(yīng)的實(shí)體類所在的位置辅肾,另外在這里配置類上通過@EnableJpaRepositories注解指定dao所在的位置队萤,以及LocalContainerEntityManagerFactoryBean和PlatformTransactionManager分別對(duì)應(yīng)的引用的名字。這樣第一個(gè)就配置好了矫钓,第二個(gè)基本和這個(gè)類似要尔,主要有幾個(gè)不同點(diǎn):

  • dao的位置不同
  • persistenceUnit不同
  • 相關(guān)bean的名稱不同
  • 實(shí)體類可以共用。

代碼如下:

@Configuration
@EnableJpaRepositories(basePackages = "com.example.jpa2.dao2",
        entityManagerFactoryRef = "localContainerEntityManagerFactoryBean2",
        transactionManagerRef = "platformTransactionManager2")
public class JpaConfig2 {
    @Autowired
    @Qualifier("dsTwo")
    DataSource dsTwo;

    @Autowired
    JpaProperties jpaProperties;

    @Bean
    LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean2(EntityManagerFactoryBuilder builder){
        return builder.dataSource(dsTwo)
                .properties(jpaProperties.getProperties())
                .persistenceUnit("pu2")
                .packages("com.example.jpa2.bean")
                .build();
    }
    //事務(wù)管理器
    @Bean
    PlatformTransactionManager platformTransactionManager2(EntityManagerFactoryBuilder builder){
        return new JpaTransactionManager(localContainerEntityManagerFactoryBean2(builder).getObject());
    }
}

接下來新娜,在對(duì)應(yīng)位置分別提供相關(guān)的實(shí)體類和dao即可赵辕。

數(shù)據(jù)源一的dao如下:

public interface BookDao extends JpaRepository<Book,Integer> {}

數(shù)據(jù)源二的dao如下:

public interface BookDao2 extends JpaRepository<Book,Integer> {}

到此,所有的配置就算完成了概龄,接下來就可以在Service中注入不同的UserDao还惠,不同的UserDao操作不同的數(shù)據(jù)源。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末私杜,一起剝皮案震驚了整個(gè)濱河市蚕键,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌衰粹,老刑警劉巖锣光,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異铝耻,居然都是意外死亡誊爹,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門田篇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來替废,“玉大人箍铭,你說我怎么就攤上這事泊柬。” “怎么了诈火?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵兽赁,是天一觀的道長状答。 經(jīng)常有香客問我,道長刀崖,這世上最難降的妖魔是什么惊科? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮亮钦,結(jié)果婚禮上馆截,老公的妹妹穿的比我還像新娘。我一直安慰自己蜂莉,他們只是感情好蜡娶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著映穗,像睡著了一般窖张。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚁滋,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天宿接,我揣著相機(jī)與錄音,去河邊找鬼辕录。 笑死睦霎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的走诞。 我是一名探鬼主播碎赢,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼速梗!你這毒婦竟也來了肮塞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤姻锁,失蹤者是張志新(化名)和其女友劉穎枕赵,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體位隶,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拷窜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涧黄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片篮昧。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖笋妥,靈堂內(nèi)的尸體忽然破棺而出懊昨,到底是詐尸還是另有隱情,我是刑警寧澤春宣,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布酵颁,位于F島的核電站嫉你,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏躏惋。R本人自食惡果不足惜幽污,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望簿姨。 院中可真熱鬧距误,春花似錦、人聲如沸扁位。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贤牛。三九已至惋鹅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間殉簸,已是汗流浹背闰集。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留般卑,地道東北人武鲁。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像蝠检,于是被迫代替她去往敵國和親沐鼠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容