springDataJpa入門教程(7)-基於springDataJpa投影(Projection)返回自定義實體類對象

springDataJpa入門教程

springDataJpa入門教程(7)-基於springDataJpa投影(Projection)返回自定義實體類對象

在使用springDataJpa做查詢操作時,如果查詢的結果集和實體類不對應,通常的做法有三種:

  1. 返回Map對象,然後再手動將Map對象裏的值取出來,一個一個set到自定義對象裏面。
  2. 返回Object數組,然後按照數組下標,將值一個一個set到自定義對象裏面。
  3. 在自定義對象類中,添加構造方法,然後通過類似於"@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,歡迎大家一起交流學習。

上一篇:springDataJpa入門教程(6)-多表動態條件查詢+分頁
下一篇:springDataJpa入門教程(8)-JPA EnableJpaAuditing 審計功能
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章