Spring Boot 整合 JPA

@[toc]java

前言:以前一直用的都是Mybatis,最近因爲工做緣由,要使用JPA,所以整理一下學習筆記防止忘記,也但願可以幫到須要使用這個技術的人mysql

1. Spring Data JPA 概念

  • JPA(Java Persistence API,Java持久層api) 是一套ORM規範,使得應用程序以統一的方式訪問持久層
  • JPA 是 Hibernate 的一個抽象,從功能上來講是 Hibernate 的一個子集
  • Spring Data JPA 是 Spring 的一個子項目,用於簡化數據庫訪問,支持 nosql(redis,mongoDB) 和關係型數據庫(jdbc,jpa)存儲

2. 使用

2.1 引入JPA依賴

  • 建立SpringBoot項目,引入 JPA,MySQL,Web依賴,和數據庫鏈接池依賴
  • 由於我本地的數據庫版本是5.7,所以引入mysql依賴的時候標註version 版本號
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
    <version>5.1.28</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
複製代碼

2.2 配置鏈接數據庫參數和jpa參數

  • 在resources 目錄下的 application.properties 文件中
# 配置數據庫鏈接池及數據庫驅動
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# 配置mysql的連接信息
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/jpaDemo?useUnicode=true&characterEncoding=utf-8

#jpa
spring.jpa.database=mysql
spring.jpa.database-platform=mysql
#是否自動生成dd
spring.jpa.generate-ddl=true
# 生成方式 update 運行時在數據庫生成表,如有更新則去更新數據
spring.jpa.hibernate.ddl-auto=update
# 格式化sql語句
spring.jpa.properties.hibernate-format_sql=true
# 控制檯展現 JPA 框架生成的sql語句
spring.jpa.show-sql=true
# 解決 hibernate multiple merge 問題
spring.jpa.properties.hibernate.event.merge.entity_copy_observer = allow  
# 使用JPA 建立表時,默認使用的存儲引擎是MyISAM,經過指定數據庫版本,可使用InnoDB
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
複製代碼

2.3 基本使用

2.3.1 建立實體類

package com.bisnow.demo.pojo;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String bookname;

    private String author;

    get + set + toString
}
複製代碼

2.3.2 對數據庫操做

須要一個接口繼承JpaRepository<實體類名,id類型>,在接口中有現成的方法或者自定義方法web

package com.bisnow.demo.repository;

import com.bisnow.demo.pojo.Book;
import org.springframework.data.jpa.repository.JpaRepository;

public interface BookRepository extends JpaRepository<Book,Integer> {
}
複製代碼
  • 運行建表插入數據

在這裏插入圖片描述

2.3.3 測試

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {

    @Autowired
    BookRepository bookRepository;

    @Test
    public void test1(){
        List<Book> books = bookRepository.findAll();
        books.forEach(b-> System.out.println(b));
    }
}
複製代碼

在這裏插入圖片描述

2.4 JPA 實體類經常使用註解

JPA項目運行的時候會根據實體類自動的去數據庫建表redis

2.4.1 @Entity

表示這是一個實體類,默認狀況下,類名就是表名spring

2.4.2 @Table

@Entity並列使用,自定義數據庫表名sql

2.4.3 @Id

標註主鍵數據庫

2.4.4 @GeneratedValue

  • 同@Id 共同使用,標註主鍵的生成策略,經過strategy屬性指定
  • IDENTITY 採用數據庫ID自增加的方式產生主鍵,oracle不支持
  • AUTO JPA自動選擇合適的策略,是默認選項
  • SEQUENCE Oracle不支持主鍵自增加,其提供了一種叫作"序列(sequence)"的機制生成主鍵,GenerationType.SEQUENCE就能夠做爲主鍵生成策略,不足之處是隻能使用於部分支持序列的數據庫(Oracle,PostgreSQL,DB2),通常與@SequenceGenerator一塊兒使用,@SequenceGenerator註解指定了生成主鍵的序列,不指定序列時自動生成一個序列SEQ_GEN_SEQUENCE
  • TABLE:經過表產生主鍵,框架藉由表模擬序列產生主鍵,使用該策略更易於作數據庫移植。
package com.bisnow.demo.pojo;

import javax.persistence.*;

@Entity 
@Table(name = "t_person")  
public class Person {

    @Id 
    
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    private Integer age;

    get + set 方法
}
複製代碼
  • 此時運行項目,能夠在控制檯看到建表語句,而且能夠在數據庫中查看錶及表關係

在這裏插入圖片描述

  • 刪除之間建好的表。若將 @Table 屬性註釋,更改 @GeneratedValue 策略,則
@GeneratedValue(strategy = GenerationType.IDENTITY)
複製代碼

在這裏插入圖片描述

2.4.5 @Column

用來描述實體屬性對應數據表字段,其中name、nullable、unique、length屬性用的較多,分別用來描述字段名、字段是否能夠null、字段是否惟1、字段的長度,實際開發中須要用其餘屬性時,可從參考文獻獲取api文檔地址,查閱文檔配置。api

@Column(length = 400,name = "p_name",nullable = false,unique = true)
private String name;
複製代碼

此時建表能看到oracle

在這裏插入圖片描述

2.4.6 @Transient

在數據庫中建表的時候忽略該屬性app

@Transient
private String name;
複製代碼

在這裏插入圖片描述

2.4.7 @OneToOne 單向關聯

兩個實體一對一關係,例如人和身份證就是一對一的關係

查詢包含關聯屬性的實體對象時,能同步從數據庫中獲取關聯的實體對象,反過來不行

  • 建立兩個實體類 Person,Card
package com.bisnow.demo.pojo;

import javax.persistence.*;

@Entity
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    @OneToOne
    //關聯的字段 name屬性能夠標註表中字段的值
    @JoinColumn(name = "card_id")
    private Card card;

    get + set + toString ...
}
複製代碼
package com.bisnow.demo.pojo;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Card {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String num;

    get + set + toString ... 
}
複製代碼
  • 建立repository 接口,測試查詢
@Autowired
    PersonRepository personRepository;

    @Autowired
    CardRepository cardRepository;

    @Test
    public void contextLoads() {
        Optional<Person> person = personRepository.findById(1);
        Person p = person.get();
        System.out.println(p);
        Optional<Card> card = cardRepository.findById(1);
        Card c = card.get();
        System.out.println(c);
    }
複製代碼

在這裏插入圖片描述

  • 此時能夠看到,查詢Person能同步獲取到對應的Card,可是查詢Card獲取不到
  • 數據庫中的表關係

在這裏插入圖片描述

2.4.8 @OneToMany和@ManyToOne 單向關聯

兩實體一對多關係,例如人和電話就是一對多關係,一我的能夠有多個電話,一個電話只能屬於一我的

2.4.8.1 @ManyToOne

  • 建立實體類 Person 和 Phone
package com.bisnow.demo.pojo;

import javax.persistence.*;

@Entity
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    get + set + toString ...
}

package com.bisnow.demo.pojo;

import javax.persistence.*;

@Entity
public class Phone {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private Integer number;

    @ManyToOne
    @JoinColumn(name = "person_id")
    private Person person;

    get + set + toString ...
}

複製代碼
  • 插入數據

在這裏插入圖片描述

  • 建立repository 接口,測試查詢
@Autowired
    PersonRepository personRepository;

    @Autowired
    PhoneRepository phoneRepository;

    @Test
    public void test2(){
        List<Person> all = personRepository.findAll();
        all.forEach(p-> System.out.println(p));
        List<Phone> all1 = phoneRepository.findAll();
        all1.forEach(p-> System.out.println(p));
    }
複製代碼

在這裏插入圖片描述

2.4.8.2 @OneToMany

  • 建立實體類 Person 和 Phone
package com.bisnow.demo.pojo;

import javax.persistence.*;

@Entity
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    @OneToMany(cascade = CascadeType.MERGE,fetch = FetchType.EAGER)
    @JoinColumn(name = "phone_id")
    private List<Phone> phone;

    get + set + toString ...
}

package com.bisnow.demo.pojo;

import javax.persistence.*;

@Entity
public class Phone {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private Integer number;

    get + set + toString ...
}
複製代碼
  • 插入數據

在這裏插入圖片描述

  • 建立repository 接口,測試查詢
@Autowired
    PersonRepository personRepository;

    @Autowired
    PhoneRepository phoneRepository;

    @Test
    public void test2(){
        List<Person> all = personRepository.findAll();
        all.forEach(p-> System.out.println(p));
        List<Phone> all1 = phoneRepository.findAll();
        all1.forEach(p-> System.out.println(p));
    }
複製代碼

在這裏插入圖片描述

2.4.8.3 fetch屬性指明數據抓取策略

  • EAGER即當即抓取
  • LAZY延遲加載

2.4.8.4 cascade屬性指明級聯特性

  • CascadeType.PERSIST:級聯持久化操做,當將實體保存至數據庫時,其對應的關聯實體也會被保存至數據庫
  • CascadeType.REMOVE:級聯刪除操做,從數據庫刪除當前實體時,關聯實體也會被刪除
  • CascadeType.DETACH:級聯脫管,當前實體被ORM框架脫管,關聯實體也被同步脫管,處於脫管狀態的實體,修改操做不能持久化到數據庫
  • CascadeType.MERGE:級聯合並操做,實體數據改變時,會相應的更新Course中的數據
  • CascadeType.REFRESH:級聯刷新,當前實體修改保存至數據庫時,關聯的實體狀態會從新從數據庫加載,忽略掉先前的狀態
  • CascadeType.ALL:包含上述全部級聯操做

2.4.9 @ManyToMany

多對多的實體關係,例如學生和所選課程的關係,一個學生能夠選多個課程,一個課程可被多個學生選擇

  • 建立實體類 Student 和 Course
@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    @ManyToMany(targetEntity = Course.class,fetch = FetchType.EAGER)
    private List<Course> courses;

    get + set + toString ...
}

@Entity
public class Course {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    get + set + toString
}
複製代碼
  • 插入數據

在這裏插入圖片描述

  • 建立repository 接口,測試查詢
@Autowired
    StudentRepository studentRepository;
    @Autowired
    CourseRepository courseRepository;

    @Test
    public void test3(){
        List<Student> all = studentRepository.findAll();
        System.out.println(all);
        List<Course> all1 = courseRepository.findAll();
        System.out.println(all1);
    }
複製代碼

在這裏插入圖片描述

2.5 對數據庫操做

須要一個接口繼承 JpaRepository<實體類名,id類型>,可在接口中寫現有方法或者自定義的方法

  • 自定義方法規範

在這裏插入圖片描述

public interface BookRepository extends JpaRepository<Book,Integer> {

    List<Book> getBookByBooknameContains(String bookname);

    List<Book> getBookByIdGreaterThanAndAuthorEndsWith(Integer id,String author);

    //表示自定義的 dml 操做方法
    @Query(value = "select * from book where id = (select max(id) from book)",nativeQuery = true)
    Book getMaxIdBook();

    // 兩種傳參方式 使用 ?1 的方式不須要 @Param 註解
    // @Query(value = "select * from book where id > ?1",nativeQuery = true)
    @Query(value = "select * from book where id > :id",nativeQuery = true)
    List<Book> idGreaterThan(@Param("id") Integer id);

    //自定義 ddl 操做方法 須要 @Modifying 註解,告知此操做會對數據庫的數據進行修改
    // 對數據庫的數據進行修改時 須要添加事務管理@Transactional(通常加在Service層)
    @Query(value = "update t_book set bookname = ?1 where id = ?2",nativeQuery = true)
    @Modifying
    int updateBookById(String bookname,Long id);
}
複製代碼
  • 插入數據並測試

在這裏插入圖片描述

@Autowired
    BookRepository bookRepository;

    @Test
    public void test4(){
        List<Book> b = bookRepository.getBookByBooknameContains("拾");
        System.out.println(b);
        List<Book> b2 = bookRepository.getBookByIdGreaterThanAndAuthorEndsWith(2, "夏達");
        System.out.println(b2);
        Book maxIdBook = bookRepository.getMaxIdBook();
        System.out.println(maxIdBook);
        List<Book> books = bookRepository.idGreaterThan(2);
        System.out.println(books);
    }
複製代碼

在這裏插入圖片描述

  • 經過 @Query 自定義 dml 和 ddl 方法
  • 自定義 dml 方法能夠經過 ?一、?2 或者 :s_id + (@Param("s_id")) 來傳參
  • 自定義ddl方法須要在自定義的方法上面加上 @Modifying 註解,而且在操做時須要添加事務管理@Transactional(通常加在Service層)`
@Service
@Transactional
public class BookService {
}
複製代碼
相關文章
相關標籤/搜索