springDataJpa入門教程
-
springDataJpa入門教程(1)-基於springBoot的基本增刪改查
-
springDataJpa入門教程(2)-Specification動態條件查詢+排序+分頁
-
springDataJpa入門教程(3-1)-基於EntityManager原生sql多表聯合查詢+動態條件查詢+分頁
-
springDataJpa入門教程(3-2)-基於EntityManager原生sql多表聯合查詢+動態條件查詢+分頁返回自定義實體類對象
-
springDataJpa入門教程(4)-Example單表動態條件查詢+分頁
-
springDataJpa入門教程(5)-單表動態條件查詢+分頁
-
springDataJpa入門教程(6)-多表動態條件查詢+分頁
-
springDataJpa入門教程(7)-基於springDataJpa投影(Projection)返回自定義實體類對象
-
springDataJpa入門教程(8)-JPA EnableJpaAuditing 審計功能
-
springDataJpa入門教程(9)-spring jpa實體屬性類型轉換器AttributeConverter的用法
-
springDataJpa入門教程(10)-JPA使用過程中遇到的坑及解決方法
springDataJpa入門教程(7)-基於springDataJpa投影(Projection)返回自定義實體類對象
在使用springDataJpa做查詢操作時,如果查詢的結果集和實體類不對應,通常的做法有三種:
- 返回Map對象,然後再手動將Map對象裏的值取出來,一個一個set到自定義對象裏面。
- 返回Object數組,然後按照數組下標,將值一個一個set到自定義對象裏面。
- 在自定義對象類中,添加構造方法,然後通過類似於"@Query(value = “select new 包路徑.類名(args1,args2…) from tb_user”)"的方式,將查詢結果封裝到自定義實體類對象中。
首先說第三種方法,這種方法要創建構造方法,通常情況下構造方法的參數會比較多,構造方法顯示很臃腫,而且構造方法的參數順序要和查詢結果集的參數順序完全對應,否則就會報錯,這樣的話參數一多很容易出錯。再有,如果自定義實體類在多個查詢裏面使用,結果就是構造方法氾濫,難以維護。
第二種方法,Object數組方式,需要強轉才能set到對象裏面,有的情況還要做null值判斷,不是很方便。
第一種方法,個人認爲比第二種和第三種要理想一些,但是還是要自己手動從Map對象裏面get值,然後再set,也避免不了要進行類型強轉。
今天來給大家介紹另一種方法,springDataJpa的投影Projection,可以很容易實現將結果集封裝到自定義對象裏面,並且代碼也比較簡潔。由於本人水平有限,教程中難免出現錯誤,敬請諒解,歡迎大家批評指正。源碼地址:源碼下載地址。java學習交流羣:184998348,歡迎大家一起交流學習。下面來舉個例子。
下面是需要用到的實體類
package com.thizgroup.jpa.study.model;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "tb_user")
@Data//使用lombok生成getter、setter
@NoArgsConstructor//生成無參構造方法
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name",columnDefinition = "varchar(64)")
private String name;
@Column(name = "mobile",columnDefinition = "varchar(64)")
private String mobile;
@Column(name = "email",columnDefinition = "varchar(64)")
private String email;
@Column(name = "age",columnDefinition = "smallint(64)")
private Integer age;
@Column(name = "birthday",columnDefinition = "timestamp")
private Date birthday;
//地址
@Column(name = "address_id",columnDefinition = "bigint(20)")
private Long addressId;
@Column(name = "create_date",columnDefinition = "timestamp")
private Date createDate;
@Column(name = "modify_date",columnDefinition = "timestamp")
private Date modifyDate;
@Builder(toBuilder = true)
public User(Long id,String name, String mobile, String email, Integer age, Date birthday,
Long addressId) {
this.id = id;
this.name = name;
this.mobile = mobile;
this.email = email;
this.age = age;
this.birthday = birthday;
this.addressId = addressId;
}
}
package com.thizgroup.jpa.study.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Data;
@Entity
@Table(name="tb_address")
@Data//使用lombok生成getter、setter
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "country",columnDefinition = "varchar(64)")
private String country;
@Column(name = "province",columnDefinition = "varchar(64)")
private String province;
@Column(name = "city",columnDefinition = "varchar(64)")
private String city;
}
需求: 根據用戶id查詢用戶信息,用戶信息包括姓名、年齡、手機號、國家、城市、地址(地址=國家+城市)。
下面我們來自定義一個接口用於封裝查詢的結果集,注意:這裏定義的是interface,而不是Class,不需要給接口定義實現類。
package com.thizgroup.jpa.study.dto;
import org.springframework.beans.factory.annotation.Value;
/**
* 用戶信息 jpa 投影
*/
public interface UserProjection {
public Long getId();
public String getName();//姓名
public Integer getAge();//年齡
public String getMobile();//手機號
public String getCountry();//國家
public String getCity();//城市
@Value("#{target.country+target.city}")
public String getAddress();//地址
}
address這個字段比較特殊,在結果集中並沒有一個名爲address的字段,這個address是由country字段和city字段聚合得到的。
在IUserService接口中添加一個查詢方法,
/**
* 使用jpa投影查詢用戶信息
* @param id
* @return
*/
UserProjection findUserInfoById(Long id);
然後在UserServiceImpl實現類中實現該方法,
package com.thizgroup.jpa.study.service.impl;
import com.thizgroup.jpa.study.dao.UserRepository;
import com.thizgroup.jpa.study.dto.UserProjection;
import com.thizgroup.jpa.study.model.User;
import com.thizgroup.jpa.study.service.IUserService;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
@Service
@Transactional(readOnly = false,propagation = Propagation.REQUIRED)
public class UserServiceImpl implements IUserService {
@Autowired
private UserRepository userRepository;
@Override
public UserProjection findUserInfoById(Long id) {
return userRepository.findUserInfoById(id);
}
}
接下來在UserRepository中添加findUserInfoById查詢方法,
package com.thizgroup.jpa.study.dao;
import com.thizgroup.jpa.study.dto.UserProjection;
import com.thizgroup.jpa.study.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface UserRepository extends JpaRepository<User,Long>, JpaSpecificationExecutor<User> {
@Query("select u.id as id,u.name as name,u.age as age,u.mobile as mobile,a.country as country,"
+ " a.city as city from User u "
+ " left join Address a on u.addressId=a.id where u.id =:id")
UserProjection findUserInfoById(@Param("id") Long id);
}
注意:寫sql語句一定要給字段起別名,並且別名必須和自定義接口中的屬性名保持一致。
完成以上幾步,就能直接拿到一個UserProjection 對象,整個代碼看上去十分簡潔。
下面寫個單元測試來驗證一下,
package com.thizgroup.jpa.study.service;
import com.thizgroup.jpa.study.JpaApplication;
import com.thizgroup.jpa.study.dto.UserProjection;
import com.thizgroup.jpa.study.model.User;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@SpringBootTest(classes={JpaApplication.class})
@RunWith(SpringJUnit4ClassRunner.class)
@Transactional(readOnly = false,propagation = Propagation.REQUIRED)
public class UserServiceImplTest {
@Autowired
private IUserService userService;
@Test
public void findUserInfoByIdTest(){
UserProjection userProjection = userService.findUserInfoById(1L);
System.out.println("id:"+userProjection.getId()+",age:"
+userProjection.getAge()+",country:"+userProjection.getCountry()
+",address:"+userProjection.getAddress());
}
}
運行一下單元測試,結果如下:
id:1,age:35,country:中國,address:中國上海
至此,springDataJpa使用Projection返回自定義實體類對象就介紹完了,有需要源碼的朋友,請到git上下載源碼,源碼地址:源碼下載地址。java學習交流羣:184998348,歡迎大家一起交流學習。