Spring Boot+Spring Data JPA
1.JPA
JPA是什么?
- Java Persistence API:用于對(duì)象持久化的 API
- Java EE 5.0 平臺(tái)標(biāo)準(zhǔn)的 ORM 規(guī)范咬腕,使得應(yīng)用程序以統(tǒng)一的方式訪問持久層
JPA和Hibernate的關(guān)系
- JPA 是 Hibernate 的一個(gè)抽象(就像JDBC和JDBC驅(qū)動(dòng)的關(guān)系)月洛;
- JPA 是規(guī)范:JPA 本質(zhì)上就是一種 ORM 規(guī)范,不是ORM 框架蒋伦,這是因?yàn)?JPA 并未提供 ORM 實(shí)現(xiàn)溉知,它只是制訂了一些規(guī)范,提供了一些編程的 API 接口娜膘,但具體實(shí)現(xiàn)則由 ORM 廠商提供實(shí)現(xiàn)逊脯;
- Hibernate 是實(shí)現(xiàn):Hibernate 除了作為 ORM 框架之外,它也是一種 JPA 實(shí)現(xiàn)
- 從功能上來說劲绪, JPA 是 Hibernate 功能的一個(gè)子集
JPA的優(yōu)勢(shì)
- 標(biāo)準(zhǔn)化: 提供相同的 API男窟,這保證了基于JPA 開發(fā)的企業(yè)應(yīng)用能夠經(jīng)過少量的修改就能夠在不同的 JPA 框架下運(yùn)行。
- 簡單易用贾富,集成方便: JPA 的主要目標(biāo)之一就是提供更加簡單的編程模型歉眷,在 JPA 框架下創(chuàng)建實(shí)體和創(chuàng)建 Java 類一樣簡單,只需要使用 javax.persistence.Entity 進(jìn)行注解颤枪;JPA 的框架和接口也都非常簡單汗捡。
- 可媲美JDBC的查詢能力: JPA的查詢語言是面向?qū)ο蟮模琂PA定義了獨(dú)特的JPQL畏纲,而且能夠支持批量更新和修改扇住、JOIN、GROUP BY盗胀、HAVING 等通常只有 SQL 才能夠提供的高級(jí)查詢特性艘蹋,甚至還能夠支持子查詢。
- 支持面向?qū)ο蟮母呒?jí)特性: JPA 中能夠支持面向?qū)ο蟮母呒?jí)特性票灰,如類之間的繼承女阀、多態(tài)和類之間的復(fù)雜關(guān)系,最大限度的使用面向?qū)ο蟮哪P?/li>
JPA包含的技術(shù)
- ORM 映射元數(shù)據(jù):JPA 支持 XML 和 JDK 5.0 注解兩種元數(shù)據(jù)的形式屑迂,元數(shù)據(jù)描述對(duì)象和表之間的映射關(guān)系浸策,框架據(jù)此將實(shí)體對(duì)象持久化到數(shù)據(jù)庫表中。
- JPA 的 API:用來操作實(shí)體對(duì)象惹盼,執(zhí)行CRUD操作庸汗,框架在后臺(tái)完成所有的事情,開發(fā)者從繁瑣的 JDBC 和 SQL 代碼中解脫出來手报。
- 查詢語言(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的表
針對(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é)果如下:
開發(fā)者也可以在接口中自己聲明相關(guān)的方法,只需要方法名稱符合規(guī)范即可,在Spring Data中寺惫,只要按照既定的規(guī)范命名方法疹吃,Spring Data Jpa就知道你想干嘛,這樣就不用寫SQL了西雀,那么規(guī)范參考下圖:
如查詢id大于等于3的記錄
List<Book> findBookByIdGreaterThan(Integer id);
查詢結(jié)果如下
這種方法命名主要是針對(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é)果如下
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ù)源。