Spring?Data?Jpa?復(fù)雜查詢(xún)方式總結(jié)(多表關(guān)聯(lián)及自定義分頁(yè))
Spring Data Jpa復(fù)雜查詢(xún)總結(jié)
只是做一個(gè)總結(jié)所以就不多說(shuō)廢話了
實(shí)體類(lèi)
@Entity @Table(name = "t_hotel") @Data public class THotel { @Id private int id; private String name; private String address; /** * 城市id */ private String city; }
@Entity @Table(name = "t_city") @Data public class TCity { @Id private int id; private String name; private String state; private String country; private String map; }
在啟動(dòng)SpringBoot的時(shí)候 SpringDataJpa會(huì)自動(dòng)的在數(shù)據(jù)庫(kù)中生成表結(jié)構(gòu).
為了查詢(xún)要求,我隨便的增加了一些數(shù)據(jù),如下圖所示
新建接口
public interface TCityRepository extends JpaRepository<TCity, Integer>, JpaSpecificationExecutor<TCity> {? }
單元測(cè)試類(lèi)
@RunWith(SpringRunner.class) @SpringBootTest public class TCityRepositoryTest{ ? ? ?@Autowired ? ? private TCityRepository tCityRepository; }
1.查找出Id小于3,并且名稱(chēng)帶有shanghai的記錄
/** ?* 查找出Id小于3,并且名稱(chēng)帶有`shanghai`的記錄. ?* ?* @param id ? id ?* @param name 城市名稱(chēng) ?* @return 城市列表 ?*/ List<TCity> findByIdLessThanAndNameLike(int id, String name);
單元測(cè)試
@Test public void findByIdLessThanAndNameLike() throws Exception { ? ? List<TCity> shanghai = tCityRepository.findByIdLessThanAndNameLike(3, "%shanghai%"); ? ? Assert.assertTrue(shanghai.size() > 0); }
2.通過(guò)旅店名稱(chēng)分頁(yè)查詢(xún)旅店以及城市的所有信息
/** ?* 通過(guò)旅店名稱(chēng)分頁(yè)查詢(xún)旅店以及城市的信息 ?* ?* @param name ? ? 旅店名稱(chēng) ?* @param pageable 分頁(yè)信息 ?* @return Page<Object[]> ?*/ @Query(value = "select t1.name as cityName,t2.name as hotelName\n" + ? ? ? ? "from t_city t1\n" + ? ? ? ? " ?left join t_hotel t2 on t2.city = t1.id\n" + ? ? ? ? "where t2.name = :name", ? ? ? ? countQuery = "select count(*)" + ? ? ? ? ? ? ? ? "from t_city t1 \n" + ? ? ? ? ? ? ? ? " ?left join t_hotel t2 on t2.city = t1.id\n" + ? ? ? ? ? ? ? ? "where t2.name = :name" ? ? ? ? , nativeQuery = true) Page<Object[]> findCityAndHotel(@Param("name") String name, Pageable pageable);
為了節(jié)約時(shí)間 我只在select 與 from 之間 分別查詢(xún)了城市的名稱(chēng)以及旅店的名稱(chēng)如果要查所有的信息,可以換成t1.* ,t2.*
單元測(cè)試
@Test public void findCityAndHotel() throws Exception { ? ? Page<Object[]> cityAndHotel = tCityRepository.findCityAndHotel("酒店", new PageRequest(0, 10)); ? ? Assert.assertTrue(cityAndHotel.getTotalElements() > 0); } ? ?
關(guān)于把List<Object[]> 轉(zhuǎn)換成List<對(duì)象> 的方法 我已經(jīng)在上一篇JPA的文章中比較清楚的說(shuō)了,因此我就不再重復(fù)的敘述一遍了
3.HQL通過(guò)旅店名稱(chēng)查詢(xún)旅店以及城市的所有信息
3和2其實(shí)是一樣的,為了方便我就不作出分頁(yè)查詢(xún)了
HQL可以用map來(lái)接受返回的參數(shù),具體的用法如下所示:
/** ?* HQL通過(guò)旅店名稱(chēng)查詢(xún)旅店以及城市的所有信息 ?* ?* @return ?*/ @Query(value = "select new map(t1,t2) from ?TCity t1 left ?join THotel t2 on t1.id=t2.city where t2.name =:name") List<Map<String, Object>> findCityAndHotelByHQL(@Param("name") String name);
測(cè)試方法和2是差不多的 我就不粘貼了
Map<String.Object> 轉(zhuǎn)換實(shí)體類(lèi)的方法也挺多的我就不多說(shuō)了,如果是直接返回給前臺(tái)的話,也沒(méi)有必要轉(zhuǎn)換成對(duì)象.
4.HQL通過(guò)旅店名稱(chēng)查詢(xún)旅店以及城市的所有信息 直接返回實(shí)體類(lèi)
/** ?* 關(guān)聯(lián)查詢(xún) ?* ?* @return ?*/ @Query(value = "select new pers.zpw.domain.CityHohel(t1.name AS cityName,t2.name AS hotelName) from ?TCity t1 left ?join THotel t2 on t1.id=t2.city where t2.name =:name") List<CityHohel> findCityAndHotelByHQLResultObj(@Param("name") String name);
為了方便CityHohel我只封裝了2個(gè)屬性,這和HQL查詢(xún)的字段是完全一致的,也必須要保持一致.
/** * Created by ZhuPengWei on 2018/5/12. */ @Data public class CityHohel { ? ? ? ? private String cityName; ? ? ? ? private String hotelName; ? ? ? ?? ? ? ? ? public CityHohel(String cityName, String hotelName) { ? ? ? ? ? ? this.cityName = cityName; ? ? ? ? ? ? this.hotelName = hotelName; ? ? ? ? } }
當(dāng)然這個(gè)帶參的構(gòu)造方法是必須要寫(xiě)的,否則會(huì)拋出轉(zhuǎn)換實(shí)體的異常
單元測(cè)試
@Test public void findCityAndHotelByHQLResultObj() throws Exception { ? ? List<CityHohel> cityAndHotelByHQLResultObj = tCityRepository.findCityAndHotelByHQLResultObj("酒店"); ? ? Assert.assertTrue(cityAndHotelByHQLResultObj.size() > 0); }
4.HQL通過(guò)旅店名稱(chēng)分頁(yè)查詢(xún)旅店以及城市的所有信息 直接返回實(shí)體類(lèi)
/** ?* 關(guān)聯(lián)查詢(xún) ?* ?* @return ?*/ @Query(value = "select new pers.zpw.domain.CityHohel(t1.name AS cityName,t2.name AS hotelName) from ?TCity t1 left ?join THotel t2 on t1.id=t2.city where t2.name =:name", ? ? ? ? countQuery = "select count(*) from ?TCity t1 left ?join THotel t2 on t1.id=t2.city where t2.name =:name") Page<CityHohel> findCityAndHotelAllSelf(@Param("name") String name, Pageable pageable);
@Test public void findCityAndHotelAllSelf() throws Exception { ? ? Page<CityHohel> cityAndHotelAllSelf = tCityRepository.findCityAndHotelAllSelf("酒店", new PageRequest(0, 10)); ? ? Assert.assertTrue(cityAndHotelAllSelf.getTotalElements() > 0); }
5.動(dòng)態(tài)查詢(xún)旅店以及城市的所有信息 直接返回實(shí)體類(lèi)
如果是動(dòng)態(tài)查詢(xún)的話當(dāng)然首先得構(gòu)造一條sql去查詢(xún)了,當(dāng)然如果不是自定義實(shí)體對(duì)象的話這樣的網(wǎng)上一大堆我就不寫(xiě)了.
直接走測(cè)試
@Autowired @PersistenceContext private EntityManager entityManager; @Test public void testDynamic() throws Exception { ? ? ? ? String sql = "select new pers.zpw.domain.CityHohel(t1.name AS cityName,t2.name AS hotelName) from ?TCity t1 left ?join THotel t2 on t1.id=t2.city where t2.name ='酒店'"; ? ? ? ? Query query = entityManager.createQuery(sql); ? ? ? ? List resultList = query.getResultList(); ? ? ? ? Assert.assertTrue(resultList.size() > 0); }
這樣測(cè)試是通過(guò)的,因此可以知道在業(yè)務(wù)層的方法中我們可以動(dòng)態(tài)的構(gòu)造SQL語(yǔ)句. 比如說(shuō)可以在接口中這樣子來(lái)定義一個(gè)方法
? /** ?* 自定義查詢(xún) ?* @param sql ?* @param entityManager ?* @return ?*/ default List customQuery(String sql, EntityManager entityManager) { ? ? return entityManager.createQuery(sql).getResultList(); }
然后在測(cè)試類(lèi)中動(dòng)態(tài)的根據(jù)條件去拼接SQL語(yǔ)句去調(diào)用
JPA#復(fù)雜查詢(xún)#自定義查詢(xún)
編寫(xiě)自定義SQL基于下面信息
1. SpringData JPA 在為Repository接口生成實(shí)現(xiàn)的時(shí)候,會(huì)查找是否有 "接口名稱(chēng)"+"Impl"的類(lèi),如果有的話,就把這個(gè)類(lèi)的方法合并到要生成的實(shí)現(xiàn)當(dāng)中。
假設(shè):要為接口StudentRepository編寫(xiě)自定義sql查詢(xún)。
基于最前面的信息,要編寫(xiě)自定義SQL
需要下面三步:
1. 自定義一個(gè)接口,在在接口中聲明方法StudentCoustomRepository,這個(gè)自定義接口名稱(chēng)不重要;
2. 讓目標(biāo)接口繼承自定義接口,這樣目標(biāo)接口就有了相應(yīng)的方法;
3. 編寫(xiě)自定義方法的實(shí)現(xiàn)類(lèi),這個(gè)類(lèi)需要使用"目標(biāo)接口名稱(chēng)"+"Impl"為類(lèi)名,
即StudentRepositoryImpl,這樣SpringDataJpa 為StudentRepository生成實(shí)現(xiàn)的時(shí)候就會(huì)包含這里面的方法了。
public class StudentRepositoryImpl implements StudentCoustomRepository { // 這里可以寫(xiě)很復(fù)雜的SQL,作為演示之用,就不弄那么復(fù)雜 private static final String SQL = "select * from t_student where name like :name "; @PersistenceContext private EntityManager em; @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public List<StudentVO> findByName(String name) { Query query = em.createNativeQuery(SQL).setParameter("name", "%"+name+"%"); query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP); List queryList = query.getResultList(); if (queryList.size() == 0) { System.out.println("找不到Student name為" + name + "的記錄"); return null; } List<StudentVO> retVal = new ArrayList<>(); for(Object o : queryList) { Map student = (Map)o; StudentVO vo = new StudentVO(); try { // org.apache.commons.beanutils.BeanUtils; // 使用apaches的beanutil,直接吧map轉(zhuǎn)為實(shí)例 BeanUtils.populate(vo, student); retVal.add(vo); } catch (IllegalAccessException | InvocationTargetException e) { System.out.println("解析StudentVO bean時(shí)發(fā)生異常:" + e.getMessage()); } } return retVal; } }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- Spring Data JPA實(shí)現(xiàn)動(dòng)態(tài)查詢(xún)的兩種方法
- spring data jpa使用詳解(推薦)
- Spring Data Jpa的四種查詢(xún)方式詳解
- Spring Data JPA實(shí)現(xiàn)動(dòng)態(tài)條件與范圍查詢(xún)實(shí)例代碼
- Spring Data JPA 實(shí)現(xiàn)多表關(guān)聯(lián)查詢(xún)的示例代碼
- Spring Data JPA 復(fù)雜/多條件組合分頁(yè)查詢(xún)
- Spring Data JPA調(diào)用存儲(chǔ)過(guò)程實(shí)例代碼
- Spring Data JPA的作用和用法小結(jié)
相關(guān)文章
Java中id,pid格式數(shù)據(jù)轉(zhuǎn)樹(shù)和森林結(jié)構(gòu)工具類(lèi)實(shí)現(xiàn)
本文主要介紹了Java中id,pid格式數(shù)據(jù)轉(zhuǎn)樹(shù)和森林結(jié)構(gòu)工具類(lèi)實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05Maven項(xiàng)目讀取resources文件路徑問(wèn)題解決方案
這篇文章主要介紹了Maven項(xiàng)目讀取resources文件路徑問(wèn)題解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09Spring Boot Maven Plugin打包異常解決方案
這篇文章主要介紹了Spring Boot Maven Plugin打包異常解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11SpringBoot使用@Async注解實(shí)現(xiàn)異步調(diào)用
這篇文章主要介紹了SpringBoot使用@Async注解實(shí)現(xiàn)異步調(diào)用,異步調(diào)用是相對(duì)于同步調(diào)用而言的,同步調(diào)用是指程序按預(yù)定順序一步步執(zhí)行,每一步必須等到上一步執(zhí)行完后才能執(zhí)行,異步調(diào)用則無(wú)需等待,程序執(zhí)行完即可執(zhí)行,可以減少程序執(zhí)行時(shí)間,需要的朋友可以參考下2023-10-10java實(shí)現(xiàn)json字符串格式化處理的工具類(lèi)
這篇文章主要為大家詳細(xì)介紹了如何使用java實(shí)現(xiàn)json字符串格式化處理的工具類(lèi),文中的示例代碼簡(jiǎn)潔易懂,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01如何通過(guò)XML方式配置并實(shí)現(xiàn)Mybatis
這篇文章主要介紹了如何通過(guò)XML方式配置并實(shí)現(xiàn)Mybatis,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11如何解決redis的NOAUTH Authentication required異常
這篇文章主要介紹了Jedis異常解決:NOAUTH Authentication required,,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值2019-07-07