什么是 Jpa
JPA(Java Persistence API)是 Java 標(biāo)準(zhǔn)中的一套 ORM 規(guī)范(提供了一些編程的 API 接口,具體實現(xiàn)由 ORM 廠商實現(xiàn),如Hiernate、TopLink 、Eclipselink等都是 JPA 的具體實現(xiàn)),借助 JPA 技術(shù)可以通過注解或者 XML 描述【對象-關(guān)系表】之間的映射關(guān)系,并將實體對象持久化到數(shù)據(jù)庫中(即Object Model與Data Model間的映射)。
什么是 Spring Data Jpa
Spring Data JPA 是在實現(xiàn)了 JPA 規(guī)范的基礎(chǔ)上封裝的一套 JPA 應(yīng)用框架(Criteria API還是有些復(fù)雜)。雖然 ORM 框架都實現(xiàn)了 JPA 規(guī)范,但是在不同的 ORM 框架之間切換仍然需要編寫不同的代碼,而使用 Spring Data JPA 能夠方便的在不同的 ORM 框架之間進行切換而不需要更改代碼。Spring Data JPA 旨在通過統(tǒng)一 ORM 框架的訪問持久層的操作,來提高開發(fā)人的效率。
Spring Data JPA 是一個 JPA 數(shù)據(jù)訪問抽象。也就是說 Spring Data JPA 不是一個實現(xiàn)或 JPA 提供的程序,它只是一個抽象層,主要用于減少為各種持久層存儲實現(xiàn)數(shù)據(jù)訪問層所需的樣板代碼量。但是它還是需要 JPA 提供實現(xiàn)程序,其實 Spring Data JPA 底層就是使用的 Hibernate 實現(xiàn)。
什么是 Hibernate
Hibernate對數(shù)據(jù)庫結(jié)構(gòu)提供了較為完整的封裝,Hibernate的O/R Mapping實現(xiàn)了POJO 和數(shù)據(jù)庫表之間的映射,以及SQL 的自動生成和執(zhí)行。往往只需定義好了POJO 到數(shù)據(jù)庫表的映射關(guān)系,即可通過Hibernate 提供的方法完成持久層操作。甚至不需要對SQL 的熟練掌握, Hibernate/OJB 會根據(jù)制定的存儲邏輯,自動生成對應(yīng)的SQL 并調(diào)用JDBC 接口加以執(zhí)行。
hibernate對 JPA 的支持,不是另提供了一套專用于 JPA 的注解。一些重要的注解如@Column, @OneToMany等,hibernate并沒有提供,這說明 JPA 的注解已經(jīng)是hibernate 的核心,hibernate只提供了一些補充,而不是兩套注解。JPA 和hibernate都提供了的注解(例如@Entity),若 JPA 的注解夠用,就直接用,若 JPA 的注解不夠用,直接使用hibernate的即可。
JPA、Spring Data Jpa、Hibernate 之間的關(guān)系
集成 Spring Data Jpa
POM 依賴
<!--刪掉 jdbc 依賴-->
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>-->
<!-- 添加 jpa 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
配置文件
spring:
jpa:
# 控制臺顯示SQL
show-sql: true
hibernate:
# 程序啟動后自動更新或者創(chuàng)建數(shù)據(jù)表結(jié)構(gòu)
ddl-auto: update
properties:
hibernate:
# 格式化打印 sql
format_sql: true
UserEntity
@Data
@Entity
@Table(name = "sys_user")
public class UserEntity {
/**
* Id 表示為表 ID
* GenerationType.IDENTITY 使用自增長主鍵
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String nickname;
private Integer age;
private String email;
private String password;
private String createUser;
private String updateUser;
private Date createTime;
private Date updateTime;
}
啟動程序
做完這些操作之后我們先啟動程序看看情況,先給大家看看我的數(shù)據(jù)庫下現(xiàn)在是沒有任何一張表的。
ok,啟動程序,查看一下結(jié)果,程序啟動成功,但是我先日志里面打印了一段 SQL 語句。
很明顯是一個建表語句,并且字段名和類型跟我們剛才新建的 UserEitity 一模一樣,猜測是 Jpa 自動根據(jù)實體類幫我們建表了,我們查看一下數(shù)據(jù)庫,果然多了一張 sys_user 表。
這是因為我們剛才配置了 spring.jpa.hibernate.ddl-auto=update
,update 默認(rèn)會根據(jù)添加了@Entity的映射實體類進行表結(jié)構(gòu)的創(chuàng)建或更新,生產(chǎn)上我們應(yīng)該關(guān)閉這個功能,配置為 none。
spring.jpa.hibernate.ddl-auto
配置比較重要,表示建表的策略,可選的枚舉值如下:
create
:不管表是否存在,每次啟動都會重新建表(會導(dǎo)致數(shù)據(jù)丟失)。create-drop
:啟動的時候創(chuàng)建表,程序退出(SessionFactory
關(guān)閉)的時候刪除表。none
:不進行任何操作。update
:如果數(shù)據(jù)表不存在則創(chuàng)建,在實體對象被修改后,下次啟動重新修改表結(jié)構(gòu)(不會刪除已經(jīng)存在的數(shù)據(jù))。validate
:啟動的時候驗證數(shù)據(jù)表的結(jié)構(gòu)。
Jpa 配置
Spring Data JPA已經(jīng)提供了一些獨立于供應(yīng)商的配置選項(例如SQL日志),Spring Boot將這些選項以及一些針對Hibernate的選項作為外部配置屬性公開。
屬性 | 描述 | 備注 |
---|---|---|
spring.jpa.database | 要操作的目標(biāo)數(shù)據(jù)庫,默認(rèn)自動檢測 | 可選配置 |
spring.jpa.database-platform | 要操作的目標(biāo)數(shù)據(jù)庫的名稱,默認(rèn)情況下是自動檢測的 | 可以使用"Database"枚舉 |
spring.jpa.defer-datasource-initialization | datasource初始化延遲 | 默認(rèn)false |
spring.jpa.generate-ddl | 啟動時是否初始化數(shù)據(jù)庫schema | 默認(rèn)false |
spring.jpa.show-sql | 是否啟用SQL語句日志記錄 | 默認(rèn)false |
spring.jpa.mapping-resources | 資源映射(等價于persistence.xml中的“mapping-file”條目) | |
spring.jpa.open-in-view | OpenEntityManagerInViewInterceptor注冊,將JPA EntityManager綁定到線程,用于整個請求處理 | 默認(rèn)true |
spring.jpa.properties | 要在JPA提供程序上設(shè)置的其他本地屬性 | 例如:spring.jpa.properties.hibernate.connection.autocommit |
spring.data.jpa.repositories.enabled | 是否啟用JPA Repository | 默認(rèn)true |
spring.data.jpa.repositories.bootstrap-mode | JPA Repository的引導(dǎo)模式 | 三種模式:DEFAULT(默認(rèn)), DEFERRED ,LAZY |
spring.jpa.hibernate.ddl-auto | DDL模式 | hibernate.hbm2ddl的快捷方式,當(dāng)使用嵌入式數(shù)據(jù)庫時,默認(rèn)為create-drop,否則默認(rèn)值為 none |
spring.jpa.hibernate.naming.implicit-strategy | 全限定名的隱式命名策略 | 例如:org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy |
spring.jpa.hibernate.naming.physical-strategy | 物理命名策略的完全限定名 | |
spring.jpa.hibernate.use-new-id-generator-mappings | 是否使用Hibernate更新的IdentifierGenerator AUTO, TABLE和SEQUENCE。 | 默認(rèn)true。hibernate.id.new_generator_mappings快捷方式 |
Jpa 注解
看完了 Spring Boot 中對于 Jpa 的配置,我們再大致的了解一下 Jpa 提供的一些注解,后續(xù)我們需要經(jīng)常用到這些注解來進行開發(fā)。
注解 | 解釋 |
---|---|
@Entity | 聲明類為實體或表。 |
@Table | 聲明表名。 |
@Basic | 指定非約束明確的各個字段。 |
@Embedded | 指定類或它的值是一個可嵌入的類的實例的實體的屬性。 |
@ld | 指定的類的屬性,用于識別(一個表中的主鍵)。 |
@GeneratedValue | 指定如何標(biāo)識屬性可以被初始化,例如自動、手動、或從序列表中獲得的值。 |
@Transient | 指定的屬性,它是不持久的,即:該值永遠(yuǎn)不會存儲在數(shù)據(jù)庫中。 |
@Column | 指定持久屬性欄屬性。 |
@SequenceGenerator | 指定在@GeneratedValue注解中指定的屬性的值。它創(chuàng)建了一個序列. |
@TableGenerator | 指定在@GeneratedValue批注指定屬性的值發(fā)生器。它創(chuàng)造了的值生成的表。 |
@AccessType | 這種類型的注釋用于設(shè)置訪問類型。如果設(shè)置@AccessType(FIELD),則可以直接訪問變量并目不需要getter和setter,但必須為public。如果設(shè)置@AccessType(PROPERTY),通過getter和setter方法訪問Entity的變量。 |
@JoinColumn | 指定一個實體組織或?qū)嶓w的集合。這是用在多對一和一對多關(guān)聯(lián)。 |
@UniqueConstraint | 指定的字段和用于主要或輔助表的唯一約束。 |
@ColumnResult | 參考使用select-子句的SQL查詢中的列名。 |
@ManyToMany | 定義了連接表之間的多對多對多的關(guān)系。 |
@ManyToOne | 定義了連接表之間的多對一的關(guān)系。 |
@OneToMany | 定義了連接表之間存在一個一對多的關(guān)系。 |
@OneToOne | 定義了連接表之間有一個一對一的關(guān)系。 |
@NamedQueries | 指定命名查詢的列表。 |
@NamedQuery | 指定使用靜態(tài)名稱的查詢。 |
UserRepository
要創(chuàng)建一個 repository 接口,你首先需要定義一個實體類專用的 repository 接口。該接口必須繼承 Repository
,并將其泛型設(shè)置為實體類和ID類型。
# 表示這是一個 Repository 接口
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long>, Serializable {
}
定義好了 UserRepository
接口之后,我們就可以對 sys_user 表進行增刪改查了,我們編寫一個測試類來測試一下。
@Slf4j
@SpringBootTest
class ApplicationTests {
@Resource
UserRepository userRepository;
@Test
void saveUserTest() {
UserEntity user = new UserEntity();
user.setName("張三");
user.setNickname("法外狂徒");
user.setAge(18);
user.setPassword("666");
// 保存用戶并返回
UserEntity result = userRepository.save(user);
log.info("用戶添加成功:{}", result);
}
@Test
void contextLoads() {
}
}
運行程序后,程序執(zhí)行成功,查看日志發(fā)現(xiàn)執(zhí)行了一條插入語句,數(shù)據(jù)庫中也是正常的存在一條數(shù)據(jù),說明Spring Data Jpa 已經(jīng)集成成功,是不是很簡單,只需要定義一個 Entity 和一個 Repository,什么增刪改查的方法都不用定義就可以了。那么這是為什么呢,咱們繼續(xù)往下看。
其實就是我們集成的 JpaRepository
接口中已經(jīng)預(yù)定義了各種 CRUD 方法,我們只需要在集成的時候插入相應(yīng)的泛型對象就可以。 JpaRepository
的泛型對象是一個實體類型和 ID 類型,他繼承的接口是 ListCrudRepository
、ListPagingAndSortingRepository
以及 QueryByExampleExecutor
接口。
繼續(xù)點進去 ListCrudRepository
發(fā)現(xiàn)繼承的是 CrudRepository
,他兩其實是提供了同等的方法,但ListCrudRepository
返回 List
,而 CrudRepository
的方法返回 Iterable
。
ListPagingAndSortingRepository
是可以進行分頁和排序操作的接口。
最后點進去CrudRepository
發(fā)現(xiàn)繼承的是Repository
接口,他是Spring Data repository 抽象的中心接口,它把要管理的 domain 類以及 domain 類的ID類型作為泛型參數(shù)。這個接口主要是作為一個標(biāo)記接口,用來捕捉工作中的類型,并幫助你發(fā)現(xiàn)擴展這個接口的接口。 CrudRepository
和 ListCrudRepository
接口為被管理的實體類提供復(fù)雜的CRUD功能。
repository 接口的繼承關(guān)系
CrudRepository
接口提供的一些方法
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S entity); 1
Optional<T> findById(ID primaryKey); 2
Iterable<T> findAll(); 3
long count(); 4
void delete(T entity); 5
boolean existsById(ID primaryKey); 6
// … more functionality omitted.
}
1、保存給定的實體。
2、根據(jù)ID返回實體。
3、返回所有實體。
4、返回實體數(shù)量。
5、刪除給定的實體。
6、根據(jù)ID判斷實體是否存在。
ok,我們接著完善一下代碼,編寫完整的業(yè)務(wù)邏輯吧。
UserService
public interface UserService {
/**
* 根據(jù)ID查詢用戶
*
* @param id 用戶ID
* @return UserEntity 用戶信息
*/
UserEntity get(Long id);
/**
* 查詢?nèi)坑脩? *
* @return List<UserEntity> 用戶集合
*/
List<UserEntity> lists();
/**
* 保存用戶
*
* @param user 用戶信息
*/
void save(UserEntity user);
/**
* 修改用戶
*
* @param user 用戶信息
*/
void update(UserEntity user);
/**
* 刪除用戶
*
* @param id 用戶id
*/
void delete(Long id);
/**
* 分頁查詢用戶
*
* @param pageable 分頁參數(shù)
* @return Page<UserEntity> 分頁用戶
*/
Page<UserEntity> page(Pageable pageable);
}
UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserRepository userRepository;
@Override
public UserEntity get(Long id) {
return userRepository.getReferenceById(id);
}
@Override
public List<UserEntity> lists() {
return userRepository.findAll();
}
@Override
public void save(UserEntity user) {
userRepository.save(user);
}
@Override
public void update(UserEntity user) {
userRepository.save(user);
}
@Override
public void delete(Long id) {
userRepository.deleteById(id);
}
@Override
public Page<UserEntity> page(Pageable pageable) {
return userRepository.findAll(pageable);
}
}
UserController
@RestController
@RequestMapping("/sys/user")
public class UserController {
@Resource
private UserService userService;
@GetMapping("/{id}")
public UserEntity get(@PathVariable Long id) {
return userService.get(id);
}
@GetMapping("/list")
public List<UserEntity> lists() {
return userService.lists();
}
@GetMapping("/page")
public Page<UserEntity> page(int page, int size) {
return userService.page(PageRequest.of(page - 1, size));
}
@PostMapping
public void save(@RequestBody UserEntity user) {
userService.save(user);
}
@PutMapping
public void update(@RequestBody UserEntity user) {
userService.update(user);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) {
userService.delete(id);
}
}
ok,啟動程序,使用 postman 進行測試一下,下一節(jié)我們集成 Swagger,就可以不用 postman 測試接口了。
證明接口是OK滴,這些方法都是一些基礎(chǔ)的方法,復(fù)雜的用法我們后面遇到了再說。
BaseEntity
我們編寫這個 BaseEntity 的目的就是,將所有的 Entity 中共有的屬性給他抽取出來,像是createUser、createTIme、updateUser以及updateTime 這些字段,同時進行一個數(shù)據(jù)的自動填充,我們在插入數(shù)據(jù)的時候就不需要關(guān)注這幾個字段的值了。
1、添加@EnableJpaAuditing注解,啟用jpa的審計功能
@EnableJpaAuditing
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2、在基礎(chǔ)類上添加jpa實體偵聽器@EntityListeners(AuditingEntityListener.class),并且在具體屬性上添加@CreatedBy、@CreatedDate、@LastModifiedBy、@LastModifiedBy注解。
@Data
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {
@CreatedBy
@Column(name = "create_user", updatable = false)
private String createUser;
@LastModifiedBy
@Column(name = "update_user")
private String updateUser;
@CreatedDate
@Column(name = "create_time", updatable = false)
private Date createTime;
@LastModifiedDate
@Column(name = "update_time")
private Date updateTime;
}
3、配置jpa自動填充用戶,因為jpa是不知道當(dāng)前的操作用戶是誰的
@Configuration
public class JpaAuditorConfig implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
// TODO 先寫死,等集成了Spring Security后再獲取實際用戶
return Optional.of("admin");
}
}
4、修改 UserEntity 集成 BaseEntity
@Data
@Entity
@Table(name = "sys_user")
public class UserEntity extends BaseEntity implements Serializable {
/**
* Id 表示為表 ID
* GenerationType.IDENTITY 使用自增長主鍵
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String nickname;
private Integer age;
private String email;
private String password;
}
5、啟動測試
插入成功我們,看一下數(shù)據(jù),沒得任何問題,數(shù)據(jù)填充成功,不過這個時間格式還得再調(diào)整調(diào)整。再配置文件里面添加 Jackson 的配置就行了。文章來源:http://www.zghlxwxcb.cn/news/detail-758233.html
spring:
jackson:
# 全局日期格式化
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
本節(jié)內(nèi)容到這里就結(jié)束啦。文章來源地址http://www.zghlxwxcb.cn/news/detail-758233.html
到了這里,關(guān)于從零開始搭建企業(yè)管理系統(tǒng)(三):集成 Spring Data Jpa的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!