Spring Data Jpa的詳細介紹
一、Jpa是什麼
JPA(Java Persistence API)
意即Java持久化API,是Sun官方在JDK5.0後提出的Java持久化規範,JPA的出現主要是爲了簡化持久層開發以及整合ORM技術,結束Hibernate、TopLink、JDO等ORM框架各自爲營的局面。JPA是在吸收現有ORM框架的基礎上發展而來,易於使用,伸縮性強。
總的來說,JPA包括以下3方面的技術:
ORM映射:支持XML和註解描述對象和表之間的映射關係
API:操作實體對象來執行CRUD(create read update delete)(增刪改查)操作
查詢語言:通過面向對象而非面向數據庫的查詢語言(JPQL)查詢數據
二、Spring Data Jpa 簡介
Spring Data JPA 是 Spring 基於 ORM 框架、JPA 規範的基礎上封裝的一套JPA應用框架,可使開發者用極簡的代碼即可實現對數據的訪問和操作。Spring Data JPA不需要過多的關心Dao層的實現,只需關注我們繼承的接口,按照一定的規則去編寫我們的接口即可,spring會按照規範動態生成我們接口的實現類進行注入,並且實現類裏包含了一些常規操作的方法實現。如果使用JPA提供的接口來操作ORM框架,可以不寫任何實現就能對數據庫進行CRUD操作,還可以進行簡單的分頁,排序操作。
三、Spring Data Jpa核心接口(API)
Repository:
所有接口的父接口,而且是一個空接口,目的是爲了統一所有Repository的類型,讓組件掃描的時候能進行識別。
CrudRepository:
是Repository的子接口,提供CRUD(增刪改查)的功能。
PagingAndSortingRepository:
是CrudRepository的子接口,添加分頁和排序的功能。
JpaRepository:
是PagingAndSortingRepository的子接口,增加了一些實用的功能,例如批量操作。
JpaSpecificationExecutor:用來做負責動態條件查詢的接口。
Specification:是Spring Data JPA提供的一個查詢規範,要做複雜的查詢,只需圍繞這個規範來設置查詢條件即可。
四、Spring Data Jpa的使用
**1.在項目的pom.xml中添加spring-data-jpa的依賴配置
<!-- c3p0數據庫連接池 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<!-- spring-data-jpa -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.9.0.RELEASE</version>
</dependency>
2.在spring核心配置文件中配置spring-data-jpa相關項
applicationContext.xml
與hibernate Mybatis配置一樣
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!-- 1、創建數據源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl"
value="jdbc:mysql://localhost:3306/1905a?useUnicode=true&characterEncoding=UTF-8" />
<property name="user" value="root" />
<property name="password" value="root" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!-- 數據源 -->
<property name="dataSource" ref="dataSource" />
<!-- 掃描實體類的包 -->
<property name="packagesToScan" value="com.fh.model" />
<!-- 指定jpa的提供商 -->
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider" />
</property>
<!--JPA的供應商適配器 -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!-- true: HBM2DDL_AUTO爲"update" ,有表就維護表,沒有表就創建表 false: 默認爲false -->
<property name="generateDdl" value="true" />
<property name="database" value="MYSQL" />
<!-- 數據庫方言:根據這個方言生成不同數據庫的sql -->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
<property name="showSql" value="true" />
</bean>
</property>
<!-- 可選:Jpa的方言 -->
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
</bean>
<!-- 3、配置事務 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- 事務通知和aop切面這裏就不配置: 今天是dao層的代碼 -->
<!-- 4、 配置SpringDataJpa的相關配置 base-package:指定基包 entity-manager-factory-ref:
引用實體類管理器工廠 transaction-manager-ref: 引用平臺事務管理器 -->
<jpa:repositories base-package="com.fh.dao"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager" />
<!-- 5、開啓組件的掃描 -->
<context:component-scan base-package="com.fh" />
<!-- 定義advice,配置傳播特性、事務隔離級別、只讀事務、回滾策略 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
<tx:method name="update*" propagation="REQUIRED"
rollback-for="java.lang.Exception" />
<tx:method name="delete*" propagation="REQUIRED"
rollback-for="java.lang.Exception" />
<tx:method name="save*" propagation="REQUIRED"
rollback-for="java.lang.Exception" />
<tx:method name="*" propagation="REQUIRED" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 切點配置 execution(* com.fh.service.impl.*.*(..)) 第一個*:任意返回值 第二個*:包下任意的類
第三個*:類中的所有方法 (..):任意參數 -->
<aop:config>
<aop:pointcut expression="execution(* com.fh.service.impl.*.*(..))"
id="servicePointcut" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut" />
</aop:config>
</beans>
3.編寫實體類並使用JPA註解配置實體類和數據庫表的映射關係
JPA不需要自己創建數據庫,只需要在實體類中通過註解配置好,就會自動創建出所需要的數據
注意:日期這塊要特別注意
User.class
//name="t_user" 數據庫的表名
@Table(name="t_user")
//@Entity表明是一個實體類
@Entity
public class User {
@Id
//指名這是一個主鍵,auto會自動根據方言來生成主鍵,IDENTITY mysql的主鍵生成
@GeneratedValue(strategy = GenerationType.AUTO)
//@Column 字段名,類型會自動根據屬性類型匹配,如果不寫,默認就是屬性名
@Column(name="user_id")
private Integer userId;
//定義字段名
@Column(name="user_name")
private String userName;
@Column(name="real_name")
private String realName;//真實姓名
@Column(name="user_sex")
private Integer userSex;//性別
//@DateTimeFormat是日期的展示格式
//加上後,格式爲:2020-01-01
//不加: 默認格式爲:2020-01-01 12:03:22
@DateTimeFormat(pattern = "yyyy-MM-dd")
//@Temporal設置數據庫的字段類型 ,
///加上爲:date類型(2020-01-01)
//不加默認:datetime(2020-01-01 12:03:22)
//與@DateTimeFormat一定要對應好
@Temporal(TemporalType.DATE)
@Column(name="user_birthday")
//json數據的展示
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
private Date userBirthday;//生日
//創建日期有時分秒
//不能省略
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name="create_date")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date createDate;//創建時間
//不能省略
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name="update_Date")
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
private Date updateDate;//修改時間
//生成getter setter方法
}
建立包結構
service serviceImpl
Repository 替代了dao層
通過JPA進行增刪改查
這裏通過junit進行測試,所以需要導入相關的jar包
<!-- java單元測試框架 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
Repository.java
/**
* Repository替代了dao類
* 需要繼承extends JpaRepository<User, Integer> Integer 是id的類型
* 只要繼承該類,就會自動有增刪改查 的方法,直接可以調用
* @author srj
*
*繼承JpaRepository(增刪改查),
*第一個泛型:我們要操作的實體類,
*第二個泛型:主鍵屬性的數據類型
*/
public interface UserRepository extends JpaRepository<User, Integer>{
//只要繼承JpaRepository,默認會有增刪改查的方法,直接調用就可以
}
測試類
UserRepositoryTest.java
//用於指定junit運行環境,是junit提供給其他框架測試環境接口擴展,爲了便於使用spring的依賴注入
//spring提供了org.springframework.test.context.junit4.SpringJUnit4ClassRunner作爲Junit測試環境
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
}
準備完成:
增加:
UserRepositoryTest.java
/**
* 新增
*/
@Test
public void addUser() {
User user = new User();
user.setUserName("李四111");
user.setUserSex(2);
user.setUserBirthday(new Date());
//直接調用新增方法即可
userRepository.saveAndFlush(user);
}
刪除
/**
* 刪除
*/
@Test
public void deleteUser() {
//直接調用即可
userRepository.delete(2);
}
修改
/**
* 修改
*/
@Test
public void updateUser() {
User user = userRepository.findOne(1);
user.setUserName("小紅");
user.setUserEmail("[email protected]");
user.setCreateDate(new Date());
userRepository.saveAndFlush(user);
}
查詢
/**
* 查詢
*/
@Test
public void queryUser() {
List<User> userList = userRepository.findAll();
//循環輸出所有值
for (Iterator iterator = userList.iterator(); iterator.hasNext();) {
User user = (User) iterator.next();
System.out.println(user.getUserName());
}
}
如果要進行特殊查詢等,需要繼承接口JpaSpecificationExecutor,根據規則自定方法
//繼承JpaSpecificationExecutor(複雜查詢,動態查詢),泛型: 就是我們操作的實體類
五、Spring Data Jpa查詢方法命名規則
Spring Data JPA爲自定義查詢提供了一些表達條件查詢的關鍵字,大致如下:
And:等價於 SQL 中的 and 關鍵字,比如 findByUsernameAndPassword(String user, Striang pwd);
Or :等價於 SQL 中的 or 關鍵字,比如 findByUsernameOrAddress(String user, String addr);
Between :等價於 SQL 中的 between 關鍵字,比如 findBySalaryBetween(int max, int min);
LessThan:等價於 SQL 中的 “<”,比如 findBySalaryLessThan(int max);
GreaterThan:等價於 SQL 中的">",比如 findBySalaryGreaterThan(int min);
IsNull :等價於 SQL 中的 “is null”,比如 findByUsernameIsNull();
IsNotNull:等價於 SQL 中的 “is not null”,比如 findByUsernameIsNotNull();
NotNull:與 IsNotNull 等價;
Like :等價於 SQL 中的 “like”,比如 findByUsernameLike(String user);
NotLike :等價於 SQL 中的 “not like”,比如 findByUsernameNotLike(String user);
OrderBy:等價於 SQL 中的 “order by”,比如 findByUsernameOrderBySalaryAsc(String user);
Not:等價於 SQL 中的 “!=”,比如 findByUsernameNot(String user);
In :等價於 SQL 中的 “in”,比如 findByUsernameIn(Collection userList) ,方法的參數可以是 Collection 類型,也可以是數組或者不定長參數;
NotIn:等價於 SQL 中的 “not in”,比如 findByUsernameNotIn(Collection userList) ,方法的參數可以是 Collection 類型,也可以是數組或者不定長參數;
public interface UserDao extends JpaRepository<User, Integer>,JpaSpecificationExecutor<User>{
}
And使用
//and
User findByUserNameAndUserSex(String userName,Integer userSex);
/**
* 通過userName和userSex查詢
*/
@Test
public void findByUsernameAndUserSexTest() {
User user = userRepository.findByUserNameAndUserSex("李四", 10);
System.out.println(user);
}
between使用
//between
List<User> findByUserSexBetween(int maxSex, int minSex);
/**
* between
*/
@Test
public void findByUserSexBetweenTest() {
List<User> userlist = userRepository.findByUserSexBetween(0, 4);
//循環輸出所有值
for (Iterator iterator = userlist.iterator(); iterator.hasNext();) {
User user = (User) iterator.next();
System.out.println(user.getUserName());
}
}
like使用
//like
List<User> findByUserNameLike(String user);
/**
* like
*/
@Test
public void findByUserNameLikeTest() {
//查詢姓李的人
List<User> userlist = userRepository.findByUserNameLike("李%");
//循環輸出所有值
for (Iterator iterator = userlist.iterator(); iterator.hasNext();) {
User user = (User) iterator.next();
System.out.println(user.getUserName());
}
}
OrderBy使用
//OrderBy:等價於 SQL 中的 "order by",比如
List<User> findByUserNameLikeOrderByUserIdAsc(String user);
/**
* 條件查詢+排序
*/
@Test
public void findByUserNameOrderByUserSexAscTest() {
//查詢姓李的人
//List<User> userlist = userRepository.findByUserNameOrderByUserIdAsc("");
List<User> userlist = userRepository.findByUserNameLikeOrderByUserIdAsc("李%");
//循環輸出所有值
for (Iterator iterator = userlist.iterator(); iterator.hasNext();) {
User user = (User) iterator.next();
System.out.println("name======"+user.getUserName()+"======sex=====");
}
}
JPA分頁使用
一.分頁
UserService .java
public interface UserService {
//分頁
public List<User> queryPage(Integer pageIndex,Integer pagesize);
}
UserServiceImpl .java
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserRepository userRepository;
/**
* 分頁
*/
public List<User> queryPage(Integer pageIndex,Integer pagesize){
//構造分頁對象
PageRequest pageRequest = new PageRequest(pageIndex, pagesize);
//開始分頁查詢獲取page對象
Page<User> page = userRepository.findAll(pageRequest);
//查詢
List<User> userList = page.getContent();
return userList;
}
}
UserServiceImplTest.java
//用於指定junit運行環境,是junit提供給其他框架測試環境接口擴展,爲了便於使用spring的依賴注入
//spring提供了org.springframework.test.context.junit4.SpringJUnit4ClassRunner作爲Junit測試環境
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class UserServiceImplTest {
@Autowired
private UserService userService;
/**
* 測試分頁
*/
@Test
public void queryPageTest() {
List<User> userList = userService.queryPage(0,2);
for (User user : userList) {
System.out.println(user);
System.out.println(user.getUserName());
}
}
}
二.分頁+排序 先排序再分頁
UserService .java
//分頁+排序
public List<User> queryPageAndSorting(Integer pageIndex,Integer pagesize,String name,Direction orderBy);
UserServiceImpl .java
/**
* 先排序再分頁
* 分頁+排序
* @param pageIndex 當前第幾頁 從0開始
* @param pagesize 每頁條數
* @param name 按照什麼屬性【字段名】分頁
* @param orderBy 排序規則:asc desc
* @return
*/
public List<User> queryPageAndSorting(Integer pageIndex,Integer pagesize,String name,Direction orderBy){
//排序
Sort sort = new Sort(orderBy,name);
//構建分頁對象
PageRequest pageRequest = new PageRequest(pageIndex, pagesize,sort);
//開始分頁查詢,獲取page對象
Page<User> page = userRepository.findAll(pageRequest);
//獲取分頁的list集合
List<User> userList = page.getContent();
return userList;
}
UserServiceImplTest.java
/**
* 測試分頁+排序
*/
@Test
public void queryPageAndSortingTest() {
List<User> userList = userService.queryPageAndSorting(0,2,"userId",Direction.DESC);
for (User user : userList) {
System.out.println(user);
}
}
三.先分頁再排序
UserService .java
//分頁+排序
public List<User> querySortingAndPage(Integer pageIndex,Integer pagesize,String name,Direction orderBy);
UserServiceImpl .java
/**
* 先分頁再排序
* @param pageIndex
* @param pagesize
* @param name
* @param orderBy
* @return
*/
public List<User> querySortingAndPage(Integer pageIndex,Integer pagesize,String name,Direction orderBy){
PageRequest pageRequest = new PageRequest(pageIndex, pagesize);
//開始分頁查詢,獲取page對象
Page<User> page = userRepository.findAll(pageRequest);
Sort sort = new Sort(orderBy,name);
//Sort sort2 = page.getSort();
//獲取分頁的list集合
List<User> userList = page.getContent();
//排序
PageRequest pageRequest1 = new PageRequest(pageIndex, pagesize,sort);
//開始分頁查詢,獲取page對象
Page<User> page1 = userRepository.findAll(pageRequest);
//獲取分頁的list集合
List<User> userList1 = page.getContent();
return userList1;
}
UserServiceImplTest.java
@Test
public void querySortingAndPage() {
List<User> userList = userService.querySortingAndPage(0,2,"userId",Direction.DESC);
for (User user : userList) {
System.out.println(user);
}
}