原創作品,可以轉載,但是請標注出處地址:https://www.cnblogs.com/V1haoge/p/9959865.html
SpringBoot整合JPA進行數據庫開發
步驟
第一步:添加必要的jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version>
</dependency>
第二步:添加必要的配置
spring.datasource.url = jdbc\:h2\:file\:D\:\\testdb;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username = sa
spring.datasource.password = sa
spring.datasource.driverClassName =org.h2.Driver
第三步:添加實體,並添加注解
@Entity
@Table(name = "USER")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int useId;
@Column
private String useName;
@Column
private UseSex useSex;
@Column
private int useAge;
@Column(unique = true)
private String useIdNo;
@Column(unique = true)
private String usePhoneNum;
@Column(unique = true)
private String useEmail;
@Column
private LocalDateTime createTime;
@Column
private LocalDateTime modifyTime;
@Column
private UseState useState;
}
第四步:添加持久層
@Repository
public interface UserRepository extends JpaRepository {
}
注意:
繼承自JpaRepository的持久層可以直接使用其定義好的CRUD操作,其實只有增刪查操作,關於修改的操作還是需要自定義的。
第五步:持久層的使用
@Service
public class UserService {
@Autowired
private UserRepository repository;
public ResponseEntity addUser(final User user) {
return new ResponseEntity<>(repository.save(user),HttpStatus.OK);
}
}
注意:其實在JpaRepository中已經定義了許多方法用於執行實體的增刪查操作。
JPA高級功能
方法名匹配
在UserRepository中定義按照規則命名的方法,JPA可以將其自動轉換為SQL,而免去手動編寫的煩惱,比如定義如下方法:
User getUserByUseIdNo(String useIdNo);
JPA會自動將其轉換為如下的SQL:
select * from USER where use_id_no = ?
下面簡單羅列方法命名規則:
| 關鍵字 | 例子 | sql |
|---|---|---|
| And | findByNameAndAge | ...where x.name=?1 and x.age=?2 |
| Or | findByNameOrAge | ...where x.name=?1 or x.age=?2 |
| Between | findByCreateTimeBetween | ...where x.create_time between ?1 and ?2 |
| LessThan | findByAgeLessThan | ...where x.age < ?1 |
| GreaterThan | findByAgeGreaterThan | ...where x.age > ?1 |
| IsNull | findByAgeIsNull | ...where x.age is null |
| IsNotNull,NotNull | findByAgeIsNotNull | ...where x.age not null |
| Like | findByNameLike | ...where x.name like ?1 |
| NotLike | findByNameNotLike | ...where x.name not like ?1 |
| OrderBy | findByAgeOrderByNameDesc | ...where x.age =?1 order by x.name desc |
| Not | findByNameNot | ...where x.name <>?1 |
| In | findByAgeIn | ...where x.age in ?1 |
| NotIn | findByAgeNotIn | ...where x.age not in ?1 |
@Query注解
使用@Query注解在接口方法之上自定義執行SQL。
@Modifying
@Query(value = "update USER set USE_PHONE_NUM = :num WHERE USE_ID= :useId", nativeQuery = true)
void updateUsePhoneNum(@Param(value = "num") String num, @Param(value = "useId") int useId);
上面的更新語句必須加上@Modifying注解,其實在JpaRepository中並未定義更新的方法,所有的更新操作均需要我們自己來定義,一般采用上面的方式來完成。
/**
* 表示一個查詢方法是修改查詢,這會改變執行的方式。只在@Query注解標注的方法或者派生的方法上添加這個注解,而不能
* 用於默認實現的方法,因為這個注解會修改執行方式,而默認的方法已經綁定了底層的APi。
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Documented
public @interface Modifying {
boolean flushAutomatically() default false;
boolean clearAutomatically() default false;
}
JPQL(SQL拼接)
使用JPQL需要在持久層接口的實現列中完成,即UserRepositoryImpl,這個類是UserRepository的實現類,我們在其中定義JPQL來完成復雜的SQL查詢,包括動態查詢,連表查詢等高級功能。
QBE(QueryByExampleExecutor)
使用QBE來進行持久層開發,需要用到兩個接口類,Example和ExampleMatcher,開發方式如下:
List users = repository.findAll(Example.of(user));
或者配合ExampleMarcher使用:
ExampleMatcher matcher = ExampleMatcher.matching().withIgnoreCase();
List users = repository.findAll(Example.of(user, matcher));
以上邏輯一般位於service之中。其中user模型中保存着查詢的條件值,null的字段不是條件,只有設置了值的字段才是條件。ExampleMatcher是用來自定義字段匹配模式的。
處理枚舉
使用Spring-Data-Jpa時,針對枚舉的處理有兩種方式,對應於EnumType枚舉的兩個值:
public enum EnumType {
/** Persist enumerated type property or field as an integer. */
ORDINAL,
/** Persist enumerated type property or field as a string. */
STRING
}
其中ORDINAL表示的是默認的情況,這種情況下將會將枚舉值在當前枚舉定義的序號保存到數據庫,這個需要是從0開始計算的,正對應枚舉值定義的順序。STRING表示將枚舉的名稱保存到數據庫中。
前者用於保存序號,這對枚舉的變更要求較多,我們不能隨便刪除枚舉值,不能隨意更改枚舉值的位置,而且必須以0開頭,而這一般又與我們定義的業務序號不一致,限制較多,一旦發生改變,極可能造成業務混亂;后者較為穩妥。
正常情況下,如果不在枚舉字段上添加@Enumerated注解的話,默認就以ORDINAL模式存儲,若要以STRING模式存儲,請在枚舉字段上添加如下注解:
@Enumerated(EnumType.STRING)
@Column(nullable=false) // 一般要加上非null約束
private UseState useState;
分頁功能
Spring-Data-Jpa中實現分頁使用到了Pageable接口,這個接口將作為一個參數參與查詢。
Pageable有一個抽象實現類AbstractPageRequest,是Pageable的抽象實現,而這個抽象類有兩個實現子類:PageRequest和QPageRequest,前者現已棄用,現在我們使用QPageRequest來定義分頁參數,其有三個構造器:
public QPageRequest(int page, int size) {
this(page, size, QSort.unsorted());
}
public QPageRequest(int page, int size, OrderSpecifier<?>... orderSpecifiers) {
this(page, size, new QSort(orderSpecifiers));
}
public QPageRequest(int page, int size, QSort sort) {
super(page, size);
this.sort = sort;
}
在這里面我們可以看到一個QSort,這個QSort是專門用於與QPageRequest相配合使用的類,用於定義排序規則。默認情況下采用的是無排序的模式,即上面第一個構造器中的描述。
要構造一個QSort需要借助querydsl來完成,其中需要OrderSpecifier來完成。
這里有個簡單的用老版本實現的分頁查詢:
public ResponseEntity<Page<User>> getUserPage(final int pageId) {
Sort sort = new Sort(new Sort.Order(Sort.Direction.DESC, "useId"));
Page<User> users = repository.findAll(new PageRequest(pageId,2, sort));
return new ResponseEntity<>(users, HttpStatus.OK);
}
至於新版本的分頁查詢和排序涉及內容較多較復雜,稍后再看。
注意:分頁首頁頁碼為0
