Spring?Data?Jpa?復雜查詢方式總結(多表關聯(lián)及自定義分頁)
Spring Data Jpa復雜查詢總結
只是做一個總結所以就不多說廢話了
實體類
@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;
}
在啟動SpringBoot的時候 SpringDataJpa會自動的在數據庫中生成表結構.
為了查詢要求,我隨便的增加了一些數據,如下圖所示


新建接口
public interface TCityRepository extends JpaRepository<TCity, Integer>, JpaSpecificationExecutor<TCity> {?
}單元測試類
@RunWith(SpringRunner.class)
@SpringBootTest
public class TCityRepositoryTest{
? ? ?@Autowired
? ? private TCityRepository tCityRepository;
}1.查找出Id小于3,并且名稱帶有shanghai的記錄
/** ?* 查找出Id小于3,并且名稱帶有`shanghai`的記錄. ?* ?* @param id ? id ?* @param name 城市名稱 ?* @return 城市列表 ?*/ List<TCity> findByIdLessThanAndNameLike(int id, String name);
單元測試
@Test
public void findByIdLessThanAndNameLike() throws Exception {
? ? List<TCity> shanghai = tCityRepository.findByIdLessThanAndNameLike(3, "%shanghai%");
? ? Assert.assertTrue(shanghai.size() > 0);
}2.通過旅店名稱分頁查詢旅店以及城市的所有信息
/**
?* 通過旅店名稱分頁查詢旅店以及城市的信息
?*
?* @param name ? ? 旅店名稱
?* @param pageable 分頁信息
?* @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é)約時間 我只在select 與 from 之間 分別查詢了城市的名稱以及旅店的名稱如果要查所有的信息,可以換成t1.* ,t2.*
單元測試
@Test
public void findCityAndHotel() throws Exception {
? ? Page<Object[]> cityAndHotel = tCityRepository.findCityAndHotel("酒店", new PageRequest(0, 10));
? ? Assert.assertTrue(cityAndHotel.getTotalElements() > 0);
} ? ?關于把List<Object[]> 轉換成List<對象> 的方法 我已經在上一篇JPA的文章中比較清楚的說了,因此我就不再重復的敘述一遍了
3.HQL通過旅店名稱查詢旅店以及城市的所有信息
3和2其實是一樣的,為了方便我就不作出分頁查詢了
HQL可以用map來接受返回的參數,具體的用法如下所示:
/**
?* HQL通過旅店名稱查詢旅店以及城市的所有信息
?*
?* @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);測試方法和2是差不多的 我就不粘貼了
Map<String.Object> 轉換實體類的方法也挺多的我就不多說了,如果是直接返回給前臺的話,也沒有必要轉換成對象.
4.HQL通過旅店名稱查詢旅店以及城市的所有信息 直接返回實體類
/**
?* 關聯(liá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個屬性,這和HQL查詢的字段是完全一致的,也必須要保持一致.
/**
* 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;
? ? ? ? }
}當然這個帶參的構造方法是必須要寫的,否則會拋出轉換實體的異常
單元測試
@Test
public void findCityAndHotelByHQLResultObj() throws Exception {
? ? List<CityHohel> cityAndHotelByHQLResultObj = tCityRepository.findCityAndHotelByHQLResultObj("酒店");
? ? Assert.assertTrue(cityAndHotelByHQLResultObj.size() > 0);
}4.HQL通過旅店名稱分頁查詢旅店以及城市的所有信息 直接返回實體類
/**
?* 關聯(liá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.動態(tài)查詢旅店以及城市的所有信息 直接返回實體類
如果是動態(tài)查詢的話當然首先得構造一條sql去查詢了,當然如果不是自定義實體對象的話這樣的網上一大堆我就不寫了.
直接走測試
@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);
}這樣測試是通過的,因此可以知道在業(yè)務層的方法中我們可以動態(tài)的構造SQL語句. 比如說可以在接口中這樣子來定義一個方法
? /**
?* 自定義查詢
?* @param sql
?* @param entityManager
?* @return
?*/
default List customQuery(String sql, EntityManager entityManager) {
? ? return entityManager.createQuery(sql).getResultList();
}然后在測試類中動態(tài)的根據條件去拼接SQL語句去調用
JPA#復雜查詢#自定義查詢
編寫自定義SQL基于下面信息
1. SpringData JPA 在為Repository接口生成實現(xiàn)的時候,會查找是否有 "接口名稱"+"Impl"的類,如果有的話,就把這個類的方法合并到要生成的實現(xiàn)當中。
假設:要為接口StudentRepository編寫自定義sql查詢。
基于最前面的信息,要編寫自定義SQL
需要下面三步:
1. 自定義一個接口,在在接口中聲明方法StudentCoustomRepository,這個自定義接口名稱不重要;
2. 讓目標接口繼承自定義接口,這樣目標接口就有了相應的方法;
3. 編寫自定義方法的實現(xiàn)類,這個類需要使用"目標接口名稱"+"Impl"為類名,
即StudentRepositoryImpl,這樣SpringDataJpa 為StudentRepository生成實現(xiàn)的時候就會包含這里面的方法了。
public class StudentRepositoryImpl implements StudentCoustomRepository {
// 這里可以寫很復雜的SQL,作為演示之用,就不弄那么復雜
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轉為實例
BeanUtils.populate(vo, student);
retVal.add(vo);
} catch (IllegalAccessException | InvocationTargetException e) {
System.out.println("解析StudentVO bean時發(fā)生異常:" + e.getMessage());
}
}
return retVal;
}
}以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Java中id,pid格式數據轉樹和森林結構工具類實現(xiàn)
本文主要介紹了Java中id,pid格式數據轉樹和森林結構工具類實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-05-05
Spring Boot Maven Plugin打包異常解決方案
這篇文章主要介紹了Spring Boot Maven Plugin打包異常解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-11-11
SpringBoot使用@Async注解實現(xiàn)異步調用
這篇文章主要介紹了SpringBoot使用@Async注解實現(xiàn)異步調用,異步調用是相對于同步調用而言的,同步調用是指程序按預定順序一步步執(zhí)行,每一步必須等到上一步執(zhí)行完后才能執(zhí)行,異步調用則無需等待,程序執(zhí)行完即可執(zhí)行,可以減少程序執(zhí)行時間,需要的朋友可以參考下2023-10-10
如何解決redis的NOAUTH Authentication required異常
這篇文章主要介紹了Jedis異常解決:NOAUTH Authentication required,,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值2019-07-07

