spring data jpa 查詢自定義欄位,轉換為自定義實體
目標:查詢資料庫中的欄位,然後轉換成 JSON 格式的資料,返回前臺。
環境:idea 2016.3.4, jdk 1.8, mysql 5.6, spring-boot 1.5.2
背景:首先建立 entity 對映資料庫(非專業 java 不知道這怎麼說)
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String userName; // 賬號
private String password; // 密碼
// getter setter 方法略過
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
然後建立與之對應的 model
public class UserModel implements Serializable {
// 一些屬性
}
- 1
- 2
- 3
這裡我們分情況討論
首先第一種情況:查詢的欄位與表中的欄位全部對應(就是查表裡所有的欄位,但是使用 Model 作為接收物件)
這種情況比較簡單,呼叫 Repository 提供的方法,返回一個 entity , 然後將 entity 的屬性複製到 model 中。像這樣
UserModel user = new UserModel();
User userEntity = new User();
// 一個工具類,具體使用方法請百度
BeanUtils.copyProperties(user, userEntity);
- 1
- 2
- 3
- 4
第二種情況:只查詢指定的幾個欄位
現在我有張表,有欄位如下:
@Entity
@Table(name = "user_info")
public class UserInfo {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name = "使用者"; // 暱稱
private String signature; // 個性簽名
private String gender = "未知"; // 性別
private String description; // 個人說明
private String avatar; // 頭像
private Long role; // 許可權
private Boolean disable; // 是否凍結
private Date createTime; // 建立時間
private Boolean isDelete; // 是否刪除
private Long userId; // 使用者 Id
// ...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
但是我只需要查詢指定的幾個欄位,然後轉換成 JSON,返回給前臺,咋辦呢?
第一種方法:使用 model 查詢時轉化
首先建立一個 model ,寫上自己想要查詢的欄位,然後寫上建構函式,這步很重要,因為spring jpa 轉化時會呼叫這個構造方法
public class MyModel implements Serializable {
private String userName;
private String name;
private String gender;
private String description;
public MyModel() {};
public MyModel(String userName, String name, String gender, String description) {
this.userName = userName;
this.name = name;
this.gender = gender;
this.description = description;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
然後在 dao 類中寫查詢方法
@Query(value = "select new pers.zhuch.model.MyModel(u.userName, ui.name, ui.gender, ui.description) from UserInfo ui, User u where u.id = ui.userId")
public List<MyModel> getAllRecord();
- 1
- 2
直接在查詢語句中 new model 框架底層會呼叫它,然後返回這個物件(這裡我寫了完整的類路徑,不寫的時候它報錯說找不到型別什麼的)
然後就可以獲得只有指定欄位的 model 了。然後就把它轉成 JSON 格式就 O 了。
第二種方法:在service 裡邊轉換成 JSON
原理其實和第一種方法差不多,只是處理結果的方式不太一樣,只是這種方法我們就不在 hql 中 new Model 了,直接寫查詢方法
@Query(value = "select new map(u.userName, ui.name, ui.gender, ui.description) from UserInfo ui, User u where u.id = ui.userId")
public List<Map<String, Object>> getCustomField();
- 1
- 2
直接new map(這裡得是小寫,不知道大寫有木有問題,反正沒試,編譯器提示是要小寫的)
然後返回的結果是這樣的
[
{
"0": "admin",
"1": "你猜",
"2": "男",
"3": "一段描述"
}, {
"0": "abc",
"1": "你猜人家",
"2": "女",
"3": "沒事先掛了"
}
]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
然後在 service 層裡直接封裝成 JSON 物件,返回
List<JsonObject> list = new ArrayList();
for(Map map : result) {
JsonObject j = new JsonObject();
j.addProperty(attrName, val);
...
list.add(j);
}
gson.toJson(list);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
還有一種返回結果,這樣寫:
@Query(value = "select u.userName, ui.name, ui.gender, ui.description from UserInfo ui, User u where u.id = ui.userId")
public List<Object> getCustomField();
- 1
- 2
返回結果是這樣的格式:
[
[
"admin",
"你猜",
"男",
"一段描述"
], [
"abc",
"你猜人家",
"女",
"沒事先掛了"
]
]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
返回的是陣列,也一樣可以通過上面的方法轉成 json ,這裡我的程式中出現了一點點 BUG,就是空值的欄位不會在陣列中,不知道為什麼。
這種方法必須明確的知道查詢了哪些欄位,靈活性比較差,雖然它解決了手頭的問題。還有就是版本的不同,有可能會出現丟失空欄位的情況,我個人特別的不喜歡這樣的方法,萬一我實體幾十個欄位,寫著寫著忘了寫到哪了,就 over 了
第三種方法:返回一個便於轉換成 json 格式的 list
其實和上面很相似,都是 dao 層返回一個 List < Map < String, Object >>,但是上面的結果集返回的 Map 的 key 只是列的下標,這種方式稍微理想一點點,就是 Map 的 key 就是查詢的列名。但是這種方式需要實現自定義 Repository( 這裡不詳細介紹,請自行百度 ),並且只是 jpa 整合 hibenate 的時候可以使用。
public List getCustomEntity() {
String sql = "select t.id, t.name, t.gender, t.is_delete, t.create_time, t.description from t_entity t";
Query query = em.createNativeQuery(sql);
// Query 介面是 spring-data-jpa 的介面,而 SQLQuery 介面是 hibenate 的介面,這裡的做法就是先轉成 hibenate 的查詢介面物件,然後設定結果轉換器
query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
return query.getResultList();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
這種方法返回的就是比較標準的 JSON 格式的 java 物件了,只需要用 jackson 或者 Gson 轉一下就是標準的 json 了
[
{
attr: val,
...
},
{
attr: val,
...
},
]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
這種方式其實已經比較理想了,因為直接就能返回到前臺,但是有時候,結果不是一條 sql 能夠解決的,得兩條或者以上的 sql 來解決一個複雜的查詢需求,這個過程中,結果比較需要轉換成 pojo,以便於組裝操作。
第四種方案:dao 中直接轉成 pojo 返回
這個方案還是依賴於 hibenate,有點操蛋,但是更明確一些。
public List getCustomEntity() {
String sql = "select t.id, t.name, t.gender, t.is_delete as isEnable, t.create_time as createTime, t.description from t_entity t";
Query query = em.createNativeQuery(sql);
query.unwrap(SQLQuery.class)
// 這裡是設定欄位的資料型別,有幾點注意,首先這裡的欄位名要和目標實體的欄位名相同,然後 sql 語句中的名稱(別名)得與實體的相同
.addScalar("id", StandardBasicTypes.LONG)
.addScalar("name", StandardBasicTypes.STRING)
.addScalar("gender", StandardBasicTypes.STRING)
.addScalar("isEnable", StandardBasicTypes.BOOLEAN)
.addScalar("createTime", StandardBasicTypes.STRING)
.addScalar("description", StandardBasicTypes.STRING)
.setResultTransformer(Transformers.aliasToBean(EntityModel.class));
return query.getResultList();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
這次返回的就是 List 了。這裡要注意的是 StandardBasicTypes這個常量類,在一些舊版本中,是 Hibenate 類,具體哪個包我不知道,我這個版本中是換成了前面的那個常量類