綜合概述
JPA是Java Persistence API的簡稱,是一套Sun官方提出的Java持久化規範。其設計目標主要是為了簡化現有的持久化開發工作和整合ORM技術,它為Java開發人員提供了一種ORM工具來管理Java應用中的關係資料。 簡而言之,JPA提供了使用物件導向的方式運算元據庫的功能。JPA充分吸收了現有Hibernate,TopLink,JDO等ORM框架的優勢,具有易於使用、伸縮性強等優點。
Spring Data JPA是Spring基於Spring Data框架對於JPA規範的一套具體實現方案,使用Spring Data JPA可以極大地簡化JPA 的寫法,幾乎可以在不寫具體實現的情況下完成對資料庫的操作,並且除了基礎的CRUD操作外,Spring Data JPA還提供了諸如分頁和排序等常用功能的實現方案。合理的使用Spring Data JPA可以極大的提高我們的日常開發效率和有效的降低專案開發成本。
實現案例
接下來,我們就通過實際案例來講解Spring Data JPA的整合,以及提供JPA相關操作的一些示例。
生成專案模板
為方便我們初始化專案,Spring Boot給我們提供一個專案模板生成網站。
1. 開啟瀏覽器,訪問:https://start.spring.io/
2. 根據頁面提示,選擇構建工具,開發語言,專案資訊等。
3. 點選 Generate the project,生成專案模板,生成之後會將壓縮包下載到本地。
4. 使用IDE匯入專案,我這裡使用Eclipse,通過匯入Maven專案的方式匯入。
新增相關依賴
清理掉不需要的測試類及測試依賴,新增 Maven 相關依賴,這裡需要新增上WEB和Swagger和JPA的依賴,Swagger的新增是為了方便介面測試。
<?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 http://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.1.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.louis.springboot</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!-- web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- swagger --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency> <!-- jpa --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> <!-- 打包時拷貝MyBatis的對映檔案 --> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/sqlmap/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.*</include> </includes> <filtering>true</filtering> </resource> </resources> </build> </project>
新增相關配置
1.新增資料來源配置
將application.properties檔案改名為application.yml ,並在其中新增MySQL資料來源連線資訊。
注意:
這裡需要首先建立一個MySQL資料庫,並輸入自己的使用者名稱和密碼。這裡的資料庫是springboot。
另外,如果你使用的是MySQL 5.x及以前版本,驅動配置driverClassName是com.mysql.jdbc.Driver。
application.yml
server: port: 8080 spring: datasource: driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=utf-8 username: root password: 123456 jpa: show-sql: true # 預設false,在日誌裡顯示執行的sql語句 database: mysql hibernate.ddl-auto: update #指定為update,每次啟動專案檢測表結構有變化的時候會新增欄位,表不存在時會新建,如果指定create,則每次啟動專案都會清空資料並刪除表,再新建 properties.hibernate.dialect: org.hibernate.dialect.MySQL5Dialect database-platform: org.hibernate.dialect.MySQL5Dialect hibernate: naming: implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl #指定jpa的自動錶生成策略,駝峰自動對映為下劃線格式 #physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
2. 新增swagger 配置
新增一個swagger 配置類,在工程下新建 config 包並新增一個 SwaggerConfig 配置類。
SwaggerConfig.java
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket createRestApi(){ return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()).build(); } private ApiInfo apiInfo(){ return new ApiInfoBuilder() .title("SpringBoot API Doc") .description("This is a restful api document of Spring Boot.") .version("1.0") .build(); } }
編寫業務程式碼
首先,編寫一個實體類,並新增相關注解,具體註解說明參見程式碼。
SysUser.java
package com.louis.springboot.demo.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Index; import javax.persistence.Table; @Entity // @Entity: 實體類, 必須 // @Table: 對應資料庫中的表, 必須, name=表名, Indexes是宣告表裡的索引, columnList是索引的列, 同時宣告此索引列是否唯一, 預設false @Table(name = "sys_user", indexes = {@Index(name = "id", columnList = "id", unique = true), @Index(name = "name", columnList = "name", unique = true)}) public class SysUser { @Id // @Id: 指明id列, 必須 @GeneratedValue(strategy = GenerationType.IDENTITY) // @GeneratedValue: 表明是否自動生成, 必須, strategy也是必寫, 指明主鍵生成策略, 預設是Oracle private Long id; @Column(name = "name", nullable = false) // @Column: 對應資料庫列名,可選, nullable 是否可以為空, 預設true private String name; private String password; private String email; 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 String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
然後,編寫一個SysUserDao並繼承JpaRepository,由此我們已經繼承了大部分可用的CURD操作,針對基礎操作,DAO完全不用寫任何方法。
SysUserDao.java
package com.louis.springboot.demo.dao; import java.io.Serializable; import org.springframework.data.jpa.repository.JpaRepository; import com.louis.springboot.demo.model.SysUser; public interface SysUserDao extends JpaRepository<SysUser, Long>, Serializable { }
使用Spring Data JPA,可以通過兩種方式使用 JPA 進行資料持久化。
方式一:使用Spring Data JPA 提供的介面預設實現,如上面我們的DAO實現。
方式二:自定義符合Spring Data JPA規則的查詢方法,由框架將其自動解析為SQL。
Spring Data JPA提供了一些實現了基本的資料庫操作的介面類,這些介面和類的關係如下。
其中CrudRepository是頂層CURD介面,提供了一些簡單的增刪查改功能,介面定義如下。
CrudRepository.java
package org.springframework.data.repository; import java.util.Optional; /** * Interface for generic CRUD operations on a repository for a specific type. * @author Oliver Gierke * @author Eberhard Wolff */ @NoRepositoryBean public interface CrudRepository<T, ID> extends Repository<T, ID> { /** * Saves a given entity. Use the returned instance for further operations as the save operation might have changed the * entity instance completely. * * @param entity must not be {@literal null}. * @return the saved entity will never be {@literal null}. */ <S extends T> S save(S entity); /** * Saves all given entities. * * @param entities must not be {@literal null}. * @return the saved entities will never be {@literal null}. * @throws IllegalArgumentException in case the given entity is {@literal null}. */ <S extends T> Iterable<S> saveAll(Iterable<S> entities); /** * Retrieves an entity by its id. * * @param id must not be {@literal null}. * @return the entity with the given id or {@literal Optional#empty()} if none found * @throws IllegalArgumentException if {@code id} is {@literal null}. */ Optional<T> findById(ID id); /** * Returns whether an entity with the given id exists. * * @param id must not be {@literal null}. * @return {@literal true} if an entity with the given id exists, {@literal false} otherwise. * @throws IllegalArgumentException if {@code id} is {@literal null}. */ boolean existsById(ID id); /** * Returns all instances of the type. * * @return all entities */ Iterable<T> findAll(); /** * Returns all instances of the type with the given IDs. * * @param ids * @return */ Iterable<T> findAllById(Iterable<ID> ids); /** * Returns the number of entities available. * * @return the number of entities */ long count(); /** * Deletes the entity with the given id. * * @param id must not be {@literal null}. * @throws IllegalArgumentException in case the given {@code id} is {@literal null} */ void deleteById(ID id); /** * Deletes a given entity. * * @param entity * @throws IllegalArgumentException in case the given entity is {@literal null}. */ void delete(T entity); /** * Deletes the given entities. * * @param entities * @throws IllegalArgumentException in case the given {@link Iterable} is {@literal null}. */ void deleteAll(Iterable<? extends T> entities); /** * Deletes all entities managed by the repository. */ void deleteAll(); }
PagingAndSortingRepository在繼承了CrudRepository基礎上實現了排序和分頁的方法。
PagingAndSortingRepository.java
package org.springframework.data.repository; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; /** * Extension of {@link CrudRepository} to provide additional methods to retrieve entities using the pagination and * sorting abstraction. * * @author Oliver Gierke * @see Sort * @see Pageable * @see Page */ @NoRepositoryBean public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> { /** * Returns all entities sorted by the given options. * * @param sort * @return all entities sorted by the given options */ Iterable<T> findAll(Sort sort); /** * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object. * * @param pageable * @return a page of entities */ Page<T> findAll(Pageable pageable); }
JpaRepository又在繼承PagingAndSortingRepository的基礎上,同時繼承了QueryByExampleExecutor介面,使其擁有了匹配指定樣例查詢的能力。
JpaRepository.java
package org.springframework.data.jpa.repository; import java.util.List; import javax.persistence.EntityManager; import org.springframework.data.domain.Example; import org.springframework.data.domain.Sort; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.QueryByExampleExecutor; /** * JPA specific extension of {@link org.springframework.data.repository.Repository}. * * @author Oliver Gierke * @author Christoph Strobl * @author Mark Paluch */ @NoRepositoryBean public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> { /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#findAll() */ List<T> findAll(); /* * (non-Javadoc) * @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort) */ List<T> findAll(Sort sort); /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable) */ List<T> findAllById(Iterable<ID> ids); /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable) */ <S extends T> List<S> saveAll(Iterable<S> entities); /** * Flushes all pending changes to the database. */ void flush(); /** * Saves an entity and flushes changes instantly. * * @param entity * @return the saved entity */ <S extends T> S saveAndFlush(S entity); /** * Deletes the given entities in a batch which means it will create a single {@link Query}. Assume that we will clear * the {@link javax.persistence.EntityManager} after the call. * * @param entities */ void deleteInBatch(Iterable<T> entities); /** * Deletes all entities in a batch call. */ void deleteAllInBatch(); /** * Returns a reference to the entity with the given identifier. Depending on how the JPA persistence provider is * implemented this is very likely to always return an instance and throw an * {@link javax.persistence.EntityNotFoundException} on first access. Some of them will reject invalid identifiers * immediately. * * @param id must not be {@literal null}. * @return a reference to the entity with the given identifier. * @see EntityManager#getReference(Class, Object) for details on when an exception is thrown. */ T getOne(ID id); /* * (non-Javadoc) * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example) */ @Override <S extends T> List<S> findAll(Example<S> example); /* * (non-Javadoc) * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Sort) */ @Override <S extends T> List<S> findAll(Example<S> example, Sort sort); }
上面因為我們的SysUserDao直接繼承了JpaRepository,所以上述所有的介面SysUserDao都是可以直接使用的,當然,除了可以直接使用預設提供的基礎介面外,Spring Data JPA還允許我們自定義查詢方法,對於符合以下命名規則的方法,Spring Data JPA能夠根據其方法名為其自動生成SQL,除了使用示例中的 find 關鍵字,還支援的關鍵字有:query、get、read、count、delete等。
只要按照以下命名規範的定義的方法,Spring Data JPA都能夠幫我們自動生成SQL,無需自己實現。
接著編寫一個服務介面,新增使用者儲存、刪除、查詢全部和分頁查詢的方法。
SysUserService.java
package com.louis.springboot.demo.service; import java.util.List; import com.louis.springboot.demo.model.SysUser; import com.louis.springboot.demo.util.PageQuery; public interface SysUserService { /** * 儲存使用者 * @param user */ public void save(SysUser user); /** * 刪除使用者 * @param id */ public void delete(SysUser user); /** * 查詢全部使用者 * @return */ public List<SysUser> findAll(); /** * 查詢分頁資料 * @return */ public Object findPage(PageQuery pageQuery); }
繼續編寫服務實現類並呼叫DAO實現相應功能,以下DAO方法都是繼承而來的,除此之後,JPA還提供了大量的API可用。
SysUserServiceImpl.java
package com.louis.springboot.demo.service.impl; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import com.louis.springboot.demo.dao.SysUserDao; import com.louis.springboot.demo.model.SysUser; import com.louis.springboot.demo.service.SysUserService; import com.louis.springboot.demo.util.PageQuery; @Service public class SysUserServiceImpl implements SysUserService { @Autowired private SysUserDao sysUserDao; @Override public void save(SysUser user) { sysUserDao.save(user); } @Override public void delete(SysUser user) { sysUserDao.delete(user); } @Override public List<SysUser> findAll() { return sysUserDao.findAll(); } @Override public Object findPage(PageQuery pageQuery) { return sysUserDao.findAll(PageRequest.of(pageQuery.getPage(), pageQuery.getSize())); } }
接著編寫一個使用者控制器,呼叫服務介面實現對應功能。
SysUserController.java
package com.louis.springboot.demo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.louis.springboot.demo.model.SysUser; import com.louis.springboot.demo.service.SysUserService; import com.louis.springboot.demo.util.PageQuery; @RestController @RequestMapping("user") public class SysUserController { @Autowired private SysUserService sysUserService; @PostMapping(value="/save") public Object save(@RequestBody SysUser user) { sysUserService.save(user); return 1; } @PostMapping(value="/delete") public Object delete(@RequestBody SysUser user) { sysUserService.delete(user); return 1; } @GetMapping(value="/findAll") public Object findAll() { return sysUserService.findAll(); } @PostMapping(value="/findPage") public Object findPage(@RequestBody PageQuery pageQuery) { return sysUserService.findPage(pageQuery); } }
上面對分頁請求進行了簡單的封裝,主要包含查詢頁碼和每頁數量兩個屬性。
PageQuery.java
package com.louis.springboot.demo.util; public class PageQuery { private int page; private int size; public int getPage() { return page; } public void setPage(int page) { this.page = page; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } }
編譯測試執行
1. 右鍵專案 -> Run as -> Maven install,開始執行Maven構建,第一次會下載Maven依賴,可能需要點時間,如果出現如下資訊,就說明專案編譯打包成功了。
2. 開啟資料庫,建立一個springboot資料庫,然後右鍵檔案 DemoApplication.java -> Run as -> Java Application,開始啟動應用,如果一開始資料庫沒有對應的表,在應用啟動時會建立,我們可以通過控制檯檢視到對應的SQL語句。
3. 開啟瀏覽器,訪問:http://localhost:8080/swagger-ui.html,進入swagger介面文件介面。
4. 首先訪問findAll介面,此時並沒有資料,所以返回結果為空。
然後呼叫save介面,分別插入以下三條資料。
{ "id": 1, "name": "111", "email": "111@qq.com", "password": "111" }
{ "id": 2, "name": "222", "email": "222@qq.com", "password": "222" }
{ "id": 3, "name": "333", "email": "333@qq.com", "password": "333" }
接著回來繼續呼叫findAll介面,可以看到我們已經成功的插入了三條資料。
接著測試分頁查詢介面findPage,輸入{ "page": 0, "size": 2 },標識查詢第一頁,每頁顯示兩條記錄,下面返回正確的分頁查詢資料。
最後我們測試一下刪除介面delete,刪除掉id為1的資料,再次呼叫findAll介面,我們發現目標記錄已經成功被刪除。
參考資料
專案主頁:https://spring.io/projects/spring-data-jpa
參考文件:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
網上資料:http://www.360doc.com/content/17/0801/09/16915_675758662.shtml
網上資料:https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-jpa/index.html
相關導航
原始碼下載
碼雲:https://gitee.com/liuge1988/spring-boot-demo.git
作者:朝雨憶輕塵
出處:https://www.cnblogs.com/xifengxiaoma/
版權所有,歡迎轉載,轉載請註明原文作者及出處。