[TOC]
SpringData JPA是spring基于ORM框架、JPA規(guī)范的基礎(chǔ)上封裝的一套JPA應(yīng)用框架诈乒,可以使開發(fā)者使用極簡(jiǎn)的代碼實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的訪問(wèn)和操作淑趾。它提供了包括增刪改查等在內(nèi)的基本功能,且易于擴(kuò)展岖圈。
springdata jpa鹦牛、jpa和hibernate三者關(guān)系
通俗來(lái)講springdata jpa是對(duì)jpa規(guī)范的一層封裝搞糕,hibernate實(shí)現(xiàn)了jpa規(guī)范。
java代碼----->springdata jpa ------>jpa規(guī)范------>hibernate------>jdbc ----->mysql數(shù)據(jù)庫(kù)
graph LR
A[java代碼] -->B(spring data jpa)
B --> |jpa規(guī)范| C(hibernate)
C -->|jdbc| D(mysql數(shù)據(jù)庫(kù))
我們使用java代碼調(diào)用springdata jpa的api曼追,springdata jpa封裝了jpa規(guī)范窍仰,并且內(nèi)部使用的是hibernate實(shí)現(xiàn),hibernate封裝了jdbc進(jìn)行數(shù)據(jù)庫(kù)操作礼殊。
入門案例
1驹吮、創(chuàng)建工程,導(dǎo)入依賴
compile group: 'org.hibernate', name: 'hibernate-core', version: '5.4.3.Final'
compile group: 'org.hibernate', name: 'hibernate-c3p0', version: '5.4.3.Final'
compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.16'
compile group: 'org.springframework.data', name: 'spring-data-jpa', version: '2.1.9.RELEASE'
testCompile group: 'org.springframework', name: 'spring-test', version: '5.1.8.RELEASE'
2晶伦、編寫spring配置文件
配置spring相關(guān)
數(shù)據(jù)源信息
jpa的實(shí)現(xiàn)方式
配置要用到的實(shí)體類
配置jpa實(shí)現(xiàn)方的配置信息
配置事務(wù)管理器
聲明式事務(wù)
<?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:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--spring-->
<!--配置spring的注解掃描-->
<context:component-scan base-package="com.lxf"/>
<!--spring data jpa-->
<!--整合spring data jpa-->
<jpa:repositories base-package="com.lxf.dao" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager" />
<!--創(chuàng)建實(shí)體管理器工廠,交給spring管理-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!--配置數(shù)據(jù)源-->
<property name="dataSource" ref="dataSource"/>
<!--配置要掃描的包碟狞,實(shí)體所在包-->
<property name="packagesToScan" value="com.lxf.entity"/>
<!--配置jpa的實(shí)現(xiàn)方-->
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
</property>
<!--jpa的實(shí)現(xiàn)方的配置-->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--數(shù)據(jù)庫(kù)類型-->
<property name="database" value="MYSQL"/>
<!--控制臺(tái)顯示sql語(yǔ)句-->
<property name="showSql" value="true"/>
<!--是否自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)表-->
<property name="generateDdl" value="true"/>
<!--數(shù)據(jù)庫(kù)方言-->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
</bean>
</property>
<!--jpa方言:高級(jí)特性-->
</bean>
<!--數(shù)據(jù)源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/study?serverTimezone=GMT"/>
<property name="user" value="root"/>
<property name="password" value="crystal1024"/>
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
</bean>
<!--配置事務(wù)管理器-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!--聲明式事務(wù)-->
</beans>
3、創(chuàng)建實(shí)體類婚陪,編寫實(shí)體類和數(shù)據(jù)庫(kù)表關(guān)系映射
參考JPA規(guī)范族沃。
4、編寫dao層接口
-
需要繼承兩個(gè)接口
- JpaRepository:封裝了增刪改查分頁(yè)排序等基本操作近忙,具體可以看JpaRepository的父類
graph TB A[Repository] -->B(CrudRepository) B --> C(PagingAndSortingRepository) C -->D(JpaRepository)
- JpaSpecificationExecutor:封裝了標(biāo)準(zhǔn)查詢
-
提供相應(yīng)的泛型
- JpaRepository
- 操作的實(shí)體類型
- 實(shí)體中主鍵類型
- JpaSpecificationExecutor
- 操作的實(shí)體類型
- JpaRepository
public interface UserDao extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
}
- 會(huì)通過(guò)動(dòng)態(tài)代理自動(dòng)生成相應(yīng)方法
5竭业、測(cè)試
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring_data_jpa.xml")
public class Test {
@Autowired
private UserDao userDao;
@org.junit.Test
public void textSave(){
User user = new User();
user.setName("小紅");
user.setAge(22);
User userResult = userDao.save(user);
System.out.println(userResult);
}
}
User{id=4, name='小紅', age=22, sex=null, address='null', phone='null'}
操作數(shù)據(jù)庫(kù)
調(diào)用spring data jpa的api
插入/更新
- save方法:傳入的實(shí)體對(duì)象有主鍵則更新智润,沒有主鍵則插入及舍。
@org.junit.Test
public void testSave(){
User user = new User();
user.setName("小紅");
user.setAge(22);
User userResult = userDao.save(user);
System.out.println(userResult);
}
刪除
- delete系列方法
@org.junit.Test
public void testDelete(){
userDao.deleteById(2);
}
查詢
count:統(tǒng)計(jì)
exists系列方法:數(shù)據(jù)庫(kù)中是否存在
find系列方法:立即加載
getOne:延遲加載,返回的是一個(gè)動(dòng)態(tài)代理對(duì)象
@org.junit.Test
public void testFindOne(){
// Optional<User> user = userDao.findById(2);
// System.out.println(user.get());
User user = userDao.getOne(2);
System.out.println(user);
}
@org.junit.Test
public void testApi(){
long count = userDao.count();
boolean b = userDao.existsById(2);
}
語(yǔ)句操作
除了調(diào)用spring data jpa內(nèi)置的api窟绷,我們也可以在dao接口中定義我們自己的方法锯玛,通過(guò)@Query聲明jpql或sql語(yǔ)句。
- @Query
- value:數(shù)據(jù)庫(kù)操作語(yǔ)句
- nativeQuery:是否是原生查詢,默認(rèn)false攘残,即默認(rèn)使用jpql查詢
- @Modifying:聲明當(dāng)前是一個(gè)更新操作拙友,需要修改數(shù)據(jù)庫(kù)數(shù)據(jù)。
- 只能用于void或int/Integer的返回類型
- 因?yàn)樾枰薷臄?shù)據(jù)庫(kù)數(shù)據(jù)歼郭,未防止修改失敗造成未知后果遗契,需要搭配事務(wù)管理來(lái)是使用
- @Transactional:添加事務(wù)管理支持
- 一般需要設(shè)置rollbackFor或者noRollbackFor,來(lái)表示什么情況下進(jìn)行事務(wù)回滾
- @Rollback:是否可以回滾病曾,默認(rèn)true
jpql查詢
public interface UserDao extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> {
@Query(value = "from User where name = :name and age = :age")
public User findUserByName(@Param("name") String userName,@Param("age") int age);
}
jpql更新
@Query(value = "update User set name = :name where id = :id")
@Modifying
public Integer updateNameById(@Param("id") int id,@Param("name") String userName);
@org.junit.Test
@Transactional(rollbackFor = Exception.class)
//@Rollback(value = false)//如果設(shè)置為fasle牍蜂,即使發(fā)生異常也不會(huì)回滾
public void testJpql(){
User user = userDao.findUserByName("lili",18);
System.out.println(user);
userDao.updateNameById(user.getId(),"lili_2");
}
原生sql語(yǔ)句查詢
public interface UserDao extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> {
@Query(value = "select * from user where name = :name and age = :age",nativeQuery = true)
public User findUserByName(@Param("name") String userName,@Param("age") int age);
}
約定規(guī)則查詢
spring data jpa制定了一些約定,如果按照這些約定來(lái)定義方法名泰涂,則會(huì)自動(dòng)解析出sql語(yǔ)句鲫竞。
findBy + 屬性名 + 查詢方式 + (And|Or) + 屬性名 + 查詢方式...
查詢方式 | 方法命名 | sql where字句 |
---|---|---|
And | findByNameAndPwd | where name= ? and pwd =? |
Or | findByNameOrSex | where name= ? or sex=? |
Is,Equals | findById,findByIdEquals | where id= ? |
Between | findByIdBetween | where id between ? and ? |
LessThan | findByIdLessThan | where id < ? |
LessThanEquals | findByIdLessThanEquals | where id <= ? |
GreaterThan | findByIdGreaterThan | where id > ? |
GreaterThanEquals | findByIdGreaterThanEquals | where id > = ? |
After | findByIdAfter | where id > ? |
Before | findByIdBefore | where id < ? |
IsNull | findByNameIsNull | where name is null |
isNotNull,NotNull | findByNameNotNull | where name is not null |
Like | findByNameLike | where name like ? |
NotLike | findByNameNotLike | where name not like ? |
StartingWith | findByNameStartingWith | where name like '?%' |
EndingWith | findByNameEndingWith | where name like '%?' |
Containing | findByNameContaining | where name like '%?%' |
OrderBy | findByIdOrderByXDesc | where id=? order by x desc |
Not | findByNameNot | where name <> ? |
In | findByIdIn(Collection<?> c) | where id in (?) |
NotIn | findByIdNotIn(Collection<?> c) | where id not in (?) |
True | findByAaaTue | where aaa = true |
False | findByAaaFalse | where aaa = false |
IgnoreCase | findByNameIgnoreCase | where UPPER(name)=UPPER(?) |
簡(jiǎn)單挑幾個(gè)示例:
public interface UserDao extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> {
public User findByName(String name);
public User findByNameLike(String name);
public User findByNameLikeAndAge(String name, int age);
public List<User> findByIdBetween(int idMin, int idMax);
}
@org.junit.Test
public void testName(){
User user1 = userDao.findByName("tom");
System.out.println(user1);
User user2 = userDao.findByNameLike("t%");
System.out.println(user2);
User user3 = userDao.findByNameLikeAndAge("tom",18);
System.out.println(user3);
List<User> users = userDao.findByIdBetween(1, 3);
users.forEach(new Consumer<User>() {
@Override
public void accept(User user) {
System.out.println(user);
}
});
}
Hibernate: select user0_.id as id1_0_, user0_.address as address2_0_, user0_.age as age3_0_, user0_.name as name4_0_, user0_.phone as phone5_0_, user0_.sex as sex6_0_ from user user0_ where user0_.name=?
User{id=3, name='tom', age=18, sex=1, address='null', phone='null'}
Hibernate: select user0_.id as id1_0_, user0_.address as address2_0_, user0_.age as age3_0_, user0_.name as name4_0_, user0_.phone as phone5_0_, user0_.sex as sex6_0_ from user user0_ where user0_.name like ? escape ?
User{id=3, name='tom', age=18, sex=1, address='null', phone='null'}
Hibernate: select user0_.id as id1_0_, user0_.address as address2_0_, user0_.age as age3_0_, user0_.name as name4_0_, user0_.phone as phone5_0_, user0_.sex as sex6_0_ from user user0_ where (user0_.name like ? escape ?) and user0_.age=?
User{id=3, name='tom', age=18, sex=1, address='null', phone='null'}
Hibernate: select user0_.id as id1_0_, user0_.address as address2_0_, user0_.age as age3_0_, user0_.name as name4_0_, user0_.phone as phone5_0_, user0_.sex as sex6_0_ from user user0_ where user0_.id between ? and ?
User{id=2, name='lili2', age=18, sex=1, address='null', phone='null'}
User{id=3, name='tom', age=18, sex=1, address='null', phone='null'}
標(biāo)準(zhǔn)查詢(Specification)
我們上面提到過(guò),springdata jpa的dao層一般繼承2個(gè)接口JpaRepository和JpaSpecificationExecutor逼蒙。JpaRepository封裝了crud从绘、統(tǒng)計(jì)、排序是牢、分頁(yè)的常見操作僵井,而JpaSpecificationExecutor基于JPA的criteria查詢封裝了另一種查詢方式,我們之前一直在使用JpaRepositoru中的方法驳棱,下面來(lái)看下JpaSpecificationExecutor接口驹沿,它里面只提供了5個(gè)方法:
public interface JpaSpecificationExecutor<T> {
//查詢一個(gè)
Optional<T> findOne(@Nullable Specification<T> spec);
//查詢?nèi)? List<T> findAll(@Nullable Specification<T> spec);
//查詢?nèi)? 提供分頁(yè)功能
Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);
//查詢?nèi)浚峁┡判蚬δ? List<T> findAll(@Nullable Specification<T> spec, Sort sort);
//統(tǒng)計(jì)
long count(@Nullable Specification<T> spec);
}
可以看到蹈胡,這5個(gè)方法有個(gè)共同點(diǎn)渊季,接收一個(gè)Specification參數(shù)。
Specification
Specification是對(duì)JPA規(guī)范中Root罚渐、CriteriaQuery却汉、CriteriaBuilder的一層封裝,用于構(gòu)建過(guò)濾條件荷并。實(shí)例化Specification需要實(shí)現(xiàn)它的toPerdicate方法:
//參數(shù)含義在我的另一文JPA規(guī)范中有介紹合砂,簡(jiǎn)單說(shuō)來(lái)Root用于獲得查詢屬性,CriteriaBuilder用于構(gòu)建過(guò)濾條件源织,CriteriaQuery用于指定最終查詢語(yǔ)句翩伪,這里一般不會(huì)使用,默認(rèn)為where語(yǔ)句谈息。
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);
注意這里創(chuàng)建出來(lái)的是where查詢語(yǔ)句缘屹。
來(lái)個(gè)簡(jiǎn)單示例僻孝,查詢表中年齡大于等于18的所有河南人:
@Test
public void test(){
Specification<User> specification = new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
//分別構(gòu)造各個(gè)單屬性的過(guò)濾條件
Predicate namePredicate = criteriaBuilder.like(root.get("address"), "河南%");
Predicate agePredicate = criteriaBuilder.ge(root.get("age"), 18);//大于等于
//組合成最終的過(guò)濾條件
Predicate predicate = criteriaBuilder.and(namePredicate, agePredicate);
return predicate;
}
};
//查詢
List<User> users = userDao.findAll(specification);
users.forEach(new Consumer<User>() {
@Override
public void accept(User user) {
System.out.println(user);
}
});
}
如果要添加排序和分頁(yè)蒿囤,可以使用Sort和Pageable。
- Sort:排序
Sort sort = new Sort(Sort.Direction.DESC,"id");//排序?qū)傩钥梢栽O(shè)置多個(gè)
List<User> users = userDao.findAll(specification,sort);
- Pageable:分頁(yè)袱蜡,是一個(gè)接口,可以通過(guò)PageRequest構(gòu)建實(shí)例互亮。
Sort sort = new Sort(Sort.Direction.DESC,"id");
//Pageable pageable = PageRequest.of(0,10);//pageIndex犁享,pageSize
Pageable pageable = PageRequest.of(0,10,sort);
Page<User> users = userDao.findAll(specification, pageable);
users.forEach(new Consumer<User>() {
@Override
public void accept(User user) {
System.out.println(user);
}
});
spring boot中的springdata jpa配置
application.yaml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/study?serverTimezone=GMT
username: root
password: crystal1024
jpa:
show-sql: true
hibernate:
ddl-auto: update