SpringBoot基礎教程(四) | JPA篇

Spring Data Jpa介紹

JPA(Java persisten API),全稱為Java持久化API,是JAVAEE中的一套規范API。它推出的目的是對ORM框架進行大統一,它提供一套接口,讓廠商們(如hibernate)對JPA提供實現。JPA與hibernate的關系就像JDBC與Mysql驅動、Oracle驅動一樣的關系,只是它更加高度抽象,可以稱之為ORM框架的接口。
Spring Data JPA 是 Spring 基于 Spring Data 框架、在JPA 規范的基礎上開發的一個框架,使用 Spring Data JPA 可以極大地簡化JPA 的寫法,可以在幾乎不用寫實現的情況下實現對數據庫的訪問和操作,除了CRUD外,還包括分頁和排序等一些常用的功能。

Spring Data Jpa實戰

  • 創建項目

maven依賴詳情如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>training.springboot</groupId>
    <artifactId>jpa</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>jpa</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <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>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

配置文件詳情如下

# 數據庫相關配置
spring.datasource.username=root
spring.datasource.password=yzhroot
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=CTT

# JPA相關配置
# 數據庫類型
spring.jpa.database=mysql
# 日志輸出sql
spring.jpa.show-sql=true
# 建表語句設置
spring.jpa.hibernate.ddl-auto=update
# 解決spring-mvc與hibernate事務問題
spring.jpa.open-in-view=true
  • 實戰場景

這次練習主要涉及到三個實體,用戶,部門以及角色,其中用戶和部門是多對一,用戶和角色是多對多關系。

  • 編寫實體類

/**
 * 用戶實體類
 */
@Entity(name = "user")
public class User {

    //設置id為主鍵,生成策略是自增
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;
    private String username;

    //設置數據庫列名是create_date,格式為 yyyy-MM-dd
    @Temporal(value = TemporalType.DATE)
    @Column(name = "create_date")
    private Date createDate;

    //防止JSON序列化時循環引用
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "department_id")
    @JsonBackReference
    private Department department;

    //多對多
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "user_and_role",
            joinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "id")},
            inverseJoinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "id")})
    private List<Role> roles;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", createDate=" + createDate +
                ", department=" + department +
                ", roles=" + roles +
                '}';
    }
}
/**
 * 角色實體類
 */
@Entity(name = "role")
public class Role {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;
    private String name;

    @ManyToMany
    @JoinTable(name = "user_and_role",
            joinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "id")},
            inverseJoinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "id")})
    @JsonBackReference
    private List<User> users;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }
}
/**
 * 部門實體類
 */
@Entity(name = "department")
public class Department {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;
    private String name;
    //設置一對多的映射表
    @OneToMany(mappedBy="department",cascade = CascadeType.ALL)
    private List<User> users;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }
}

創建實體時涉及到了幾個Jpa的常用注解,下面來簡單解釋一下。

  • @Entity:表明這是一個實體類,name屬性的值代表與這個實體類映射的數據庫表。

  • @Id:表明這個屬性是數據庫中的主鍵。

  • @GeneratedValue:用來聲明主鍵是自動生成的,strategy的常見的值有四個;分別是TABLE、AUTO、IDENTITY、SEQUENCE。

    • TABLE:使用一個特定的數據庫表格來保存主鍵。
    • AUTO:主鍵由程序控制,是默認值。
    • IDENTITY:主鍵由數據庫自動生成(主要是支持自動增長的數據庫,如mysql)
    • SEQUENCE:根據底層數據庫的序列來生成主鍵,條件是數據庫支持序列。 這個值要與generator一起使用,generator 指定生成主鍵使用的生成器(可能是orcale中自己編寫的序列)。
  • @OneToMany:用于標識一對多關系,mappedBy屬性是One方的屬性名稱,cascade 屬性是代表著級聯操作。

  • ManyToMany:用于標識多對多關系。

  • @JoinTable:用于標識中間表,并需要制定另一個joinColumns標識本表主鍵在中間表的名稱,inverseJoinColumns標識另一個Many方的主鍵在中間表的名稱。

  • @JsonBackReference:防止Json轉換后循環調用。

  • @Temporal:標明屬性在數據庫中是date類型,TemporalType的值有三種;

    • TemporalType.DATE:實體類會封裝成日期“yyyy-MM-dd”的 Date類型。
    • TemporalType.TIME:實體類會封裝成時間“hh-MM-ss”的 Date類型。
    • TemporalType.TIMESTAMP:實體類會封裝成完整的時間“yyyy-MM-dd hh:MM:ss”的 Date類型。
  • 編寫實體類的CRUD

/**
 * 部門實體類的CRUD操作
 */
@Repository
public interface DepartmentDao extends JpaRepository<Department,Long> {
}
/**
 * 角色實體類的CRUD操作
 */
@Repository
public interface RoleDao extends JpaRepository<Role,Long> {
}
/**
 * 用戶實體類的CRUD操作
 */
@Repository
public interface UserDao extends JpaRepository<User,Long> {
}

可以看到我們只需編寫一個接口繼承JpaRepository接口便可以基本的CRUD操作,下面來簡單介紹下原理。
JpaRepository繼承自PagingAndSortingRepository和QueryByExampleExecutor,而
PagingAndSortingRepository繼承自CrudRepository。

  • CrudRepository
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);//根據ID查詢一個實體

    boolean existsById(ID var1);//根據ID判斷一個實體是否存在

    Iterable<T> findAll();//查詢所有實體

    Iterable<T> findAllById(Iterable<ID> var1);//根據ID批量查詢實體

    long count();//查詢所有實體的數量

    void deleteById(ID var1);    //根據ID刪除一個實體

    void delete(T var1);//根據一個實體的信息,刪除這個實體

    void deleteAll(Iterable<? extends T> var1);  //批量刪除實體

    void deleteAll();    //刪除全部實體
}
  • PagingAndSortingRepository
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
    Iterable<T> findAll(Sort var1);//根據規則排序

    Page<T> findAll(Pageable var1);//分頁查詢
}
  • JpaRepository
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
    List<T> findAll(); // 查詢所有實體
 
    List<T> findAll(Sort sort); // 查詢所有實體并排序
 
    List<T> findAll(Iterable<ID> ids); // 根據ID集合查詢實體
 
    <S extends T> List<S> save(Iterable<S> entities); // 保存并返回(修改后的)實體集合
 
    void flush(); // 提交事務
 
    <S extends T> S saveAndFlush(S entity); // 保存實體并立即提交事務
 
    void deleteInBatch(Iterable<T> entities); // 批量刪除實體集合
 
    void deleteAllInBatch();// 批量刪除所有實體
 
    T getOne(ID id); // 根據ID查詢實體
 
    @Override
    <S extends T> List<S> findAll(Example<S> example); // 查詢與指定Example匹配的所有實體
 
    @Override
    <S extends T> List<S> findAll(Example<S> example, Sort sort);// 查詢與指定Example匹配的所有實體并排序
}

簡單來說,SpringDataJpa在運行時,會以動態代理的形式生成實現這些方法的代理對象,然后就可以實現我們的增刪改查功能了。
另外,JPA還支持約定命名法,以下是約定規則(下圖來自https://blog.csdn.net/Phapha1996/article/details/78712597
):

Jpa約定規則

  • 測試

由于篇幅有限,只簡單寫出幾種測試,完整代碼見底部鏈接

@SpringBootTest
class JpaApplicationTests {

    @Autowired
    private UserDao userDao;

    @Autowired
    private DepartmentDao departmentDao;

    //添加一個USER
    @Test
    public void testAddUser(){
        User user = new User();
        user.setCreateDate(new Date());
        user.setUsername("小明");
        userDao.save(user);
    }

    //添加一個部門,級聯操作
    @Test
    public void testAddDepartment(){
        Department department = new Department();
        department.setName("研發部");
        department.setId((long) 1);

        User user = new User();
        user.setId((long) 2);
        user.setUsername("小紅2");
        user.setCreateDate(new Date());

        List<User> users = new ArrayList<>();
        users.add(user);
        department.setUsers(users);
        departmentDao.save(department);
    }

    //查詢所有USER
    @Test
    public void testQueryAllUser(){
        System.out.println(userDao.findAll());
    }

}

完整的工程代碼鏈接:https://github.com/youzhihua/springboot-training

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容