1. 程式人生 > >spring data jpa 實現多條件複雜查詢及多表聯查

spring data jpa 實現多條件複雜查詢及多表聯查

最近發現JPA在處理單表時,很方便,但多表就比較複雜了。今天寫一下端午這兩天琢磨的多條件查詢,還有多表聯查。

文章比較長,大部分都是程式碼,不願意看的程式碼copy下去,直接可以實現;想交流的可以看完,然後留言交流。

maven依賴啊,配置,繼承寫法等知識點不展開說了,之前寫過一篇文章:

這裡說一下更新的地方:

JPA的配置

######################################################
###spring data JPA配置
######################################################
#指定JPA的DB
spring.jpa.database=MYSQL
#是否顯示SQL
spring.jpa.show-sql=true
#執行DDL語句時,是建立create,建立刪除create-drop,更新update
spring.jpa.hibernate.ddl-auto=update
#命名策略:當建立了entity,會在DB中建立一個表結構
#這個是駝峰命名法,遇到大寫加下劃線
#spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
#這個是預設寫法,以屬性名命名
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
#hibernate配置DB方言
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect

說一點,命名策略的配置更新了,以前是:

org.hibernate.cfg.DefaultNamingStrategy

org.hibernate.cfg.ImprovedNamingStrategy

但我發現配置了之後無論使用哪種都是帶下劃線的,所以查找了一下資料,發現現在使用上面的兩個配置:

PhysicalNamingStrategyStandardImpl  預設以屬性名作為欄位名;

SpringPhysicalNamingStrategy 以駝峰法拆分加下劃線為欄位名。

其他註解幾乎都寫明白了,不是重點不展開贅述了。

然後上例項

系統分4層:entity,repository,service,controller

模擬:一個使用者可以有多個地址,但是一條地址記錄,只能對應一個使用者。

entity:

兩個實體webuser和address

因為兩個實體有共性,都需要主鍵,建立時間,銷燬時間.....所以抽出來單獨寫一個類。

entity父類:

package com.wm.springboot.base;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;
import javax.persistence.Version;

import com.alibaba.fastjson.annotation.JSONField;

import lombok.Getter;
import lombok.Setter;

@MappedSuperclass //表明這是父類,可以將屬性對映到子類中使用JPA生成表
public abstract class BaseEntity extends BaseClass {
	
	@JSONField(ordinal=1) @Getter @Setter
	@Id @GeneratedValue(strategy=GenerationType.AUTO)
	@Column(name="id",columnDefinition="int(30) comment '無意義自增主鍵'")
	protected Integer id; //無意義自增主鍵

	
	@JSONField(ordinal=2,format="yyyy-MM-dd HH:mm:ss") @Getter @Setter 
	@Column(name="createTime",columnDefinition="DATETIME comment '建立時間'")
	protected Date createTime; //建立時間
	
	@JSONField(ordinal=3,format="yyyy-MM-dd HH:mm:ss") @Getter @Setter 
	@Column(name="destroyTime",columnDefinition="DATETIME comment '銷燬時間'")
	protected Date destroyTime; //銷燬時間
	
	
	@JSONField(ordinal=4) @Getter @Setter 
	@Version @Column(name="version",nullable=false,columnDefinition="int(20) comment '版本號'")
	protected Integer version;
	
	@JSONField(ordinal=5) @Getter @Setter 
	@Column(length=1,name="isValid",nullable=false,columnDefinition="int(1) comment '是否啟用,1:啟用     0:不啟用'")
	protected Integer isValid; //是否啟用 
	
	@Transient
	@JSONField(ordinal=5) @Getter @Setter 
	protected String createTimeStart;  //建立時間的開始點
	
	@Transient
	@JSONField(ordinal=6) @Getter @Setter 
	protected String createTimeEnd; //建立時間的結束點

}

1,由於是父類,所以不需要單獨例項化,所以寫成抽象類。

2,BaseClass不展開了,裡面是國際化;這裡只從baseEntity展開。

3,使用@MappedSuperclass註解,讓子類在JPA生成表時可以使用父類繼承來的屬性。

4,@Getter @Setter 使用了外掛lombok,自動生成getset方法,非常好用。多說一句:有人覺得這個東西改變了程式碼的寫法,造成不好影響,我覺得目前使用來看,沒給我造成什麼不好影響,反而提高了我的效率。

5,@JSONField(ordinal=1) 繼承了fastjson帶的註解,這裡僅做排序使用。其實可以不用寫,我寫習慣了。

6,@Id, 這個是生成表時的主鍵,按照資料庫設計原則,主鍵應該是無意義自增主鍵。所以我覺得可以抽象出來放到父類;使每個表的主鍵都叫ID也不是什麼問題;

7,@GeneratedValue(strategy=GenerationType.AUTO)  主鍵生成策略,自增;

8,@Column:網上一查一大堆。不過我的用法跟網上不太一樣。簡單說一下吧。

    a),name,對映到表時的欄位名,這個和上面講的命名策略相關。

如果使用之前的策略或者加下劃線的命名策略,這裡只要使用駝峰寫法的,都會自動加下劃線。我不想要這種命名策略,所以使用:PhysicalNamingStrategyStandardImpl,這樣其實name可以省略了,但是為了規範我還是寫上了。

    b),length  長度,比如String型別的屬性 length寫20,生成欄位為varchar(20),這裡注意:length的值要和後面寫的columnDefinition中的例如:varchar(32)的值一致,不然啟動時會報錯。所以如果配置columnDefinition,建議可以不寫length。

    c),nullable:能否為空  true:可以為空    false:不能為空。

這裡其實還有一個屬性:unique,唯一性約束,我這裡發現一個問題;如果設定了unique,那麼啟動時會報錯,但是啟動能成功!而且,去表中看,唯一性約束設定成功。報錯的大概意思好像是還沒表無法設定唯一性約束。這裡我覺得有可能涉及到底層原因。有時間再深究吧。但是我看著這個報錯又難受,我就退而求其次,使用columnDefinition設定欄位的唯一性約束,並且好處是還可以設定欄位的備註,或者對映到表中的欄位型別,以及長度。

    d),columnDefinition: 其實就是新增建表sql。例子程式碼都有。

9,@version  樂觀鎖,這個不是重點 不贅述了。

使用者子類BaseUserEntity:

package com.wm.springboot.base;

import javax.persistence.Column;
import javax.persistence.MappedSuperclass;

import com.alibaba.fastjson.annotation.JSONField;

import lombok.Getter;
import lombok.Setter;

@MappedSuperclass //表明這是父類,可以將屬性對映到子類中使用JPA生成表
public abstract class BaseUserEntity extends BaseEntity {
	
	@JSONField(ordinal=1)
	@Getter @Setter 
	@Column(length=32,name="userName",nullable=false,columnDefinition="varchar(32) unique comment '使用者名稱'")
	protected String userName; //使用者名稱
	
	@JSONField(ordinal=2) @Getter @Setter 
	@Column(length=32,name="password",nullable=false,columnDefinition="varchar(32) default '000000' comment '密碼'")
	protected String password; //密碼
	
	@JSONField(ordinal=3) @Getter @Setter 
	@Column(length=64,name="email",nullable=false,columnDefinition="varchar(64) unique comment '郵箱'")
	protected String email; //郵箱號
	
	@JSONField(ordinal=4) @Getter @Setter 
	@Column(length=11,name="phoneNo",nullable=false,columnDefinition="varchar(11) unique comment '電話號碼'")
	protected String phoneNo; //手機號
	
	@JSONField(ordinal=5)
	@Getter @Setter 
	@Column(length=32,name="realName",nullable=false,columnDefinition="varchar(32) comment '真實姓名'")
	protected String realName; //真實姓名
}

註解參考上面解釋。這麼寫的思路是,假設一個系統分管理用使用者,和網站使用者。這樣使用者也會有共同特性。所以再抽象一層。

WebUser類:

package com.wm.springboot.sc.entity;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.wm.springboot.base.BaseUserEntity;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
 * 使用者資訊表
 * 原則:ID,使用者名稱,郵箱號,手機號,微信ID都不可重複
 * @author maybe
 */
@NoArgsConstructor
@AllArgsConstructor
/*
 * @Entity說明這是一個實體bean,使用orm預設規則(類名=表名;屬性名=欄位名)關聯DB;
 * 如果想改變這種規則:1,可以配置@Entity的name來對應DB中的表名;@Entity(name="USER")
 * 				  2,使用@Table來改變class和DB表名的對映規則;@Column來改變屬性名和欄位名的對映規則
*/
@Entity(name="WEBUSER")
public class WebUser extends BaseUserEntity{

	@JSONField(ordinal=1) @Getter @Setter 
	@Column(length=32,name="nickName",columnDefinition="varchar(32) comment '暱稱'")
	private String nickName; //暱稱
	
	@JSONField(ordinal=2) @Getter @Setter 
	@Column(length=32,name="wxId",columnDefinition="varchar(32) unique comment '微訊號'")
	private String wxId; //微信ID
	
	@JSONField(ordinal=3) @Getter @Setter 
	@OneToMany(mappedBy="webUser",cascade=CascadeType.ALL,fetch=FetchType.LAZY)
	private Set<Address> addresses;
	
	public WebUser(String username) {
		this.userName = username;
	}
	
	@Override
	public String toString() {
		return JSONObject.toJSONString(this,true);
	}
}
@NoArgsConstructor  無參構造器

@AllArgsConstructor 全參構造器   不過只是本類的全部引數,如果需要使用父類引數,還需要自己寫構造器。

@Entity(name="WEBUSER")  將被此註解標註的實體,對映到資料庫,表名為name名。

@OneToMany(mappedBy="webUser",cascade=CascadeType.ALL,fetch=FetchType.LAZY)

    webuser是使用者實體,一個使用者對應多個地址,所以webuser是“一對多”中的“一”。在一的實體中,使用此註解標註。

mappedBy:標註該屬性對應“多”的實體中的屬性名。

cascade 表示級聯操作。

fetch  載入方式,預設都是lazy載入。

重寫toString方法,fastjson提供,將實體列印時,預設以json格式輸出。  true的意思是標準json格式。

Address類:

package com.wm.springboot.sc.entity;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.wm.springboot.base.BaseEntity;

import lombok.Getter;
import lombok.Setter;

@Entity(name="ADDRESS")
public class Address extends BaseEntity {

	@JSONField(ordinal=1) @Getter @Setter
	@Column(name="label",nullable=false,columnDefinition="varchar(16) comment '地址標籤(家、公司)'")
	private String label;

	@JSONField(ordinal=2) @Getter @Setter
	@Column(name="country",nullable=false,columnDefinition="varchar(16) comment '國家'")
	private String country;
	
	@JSONField(ordinal=3) @Getter @Setter
	@Column(name="province",nullable=false,columnDefinition="varchar(32) comment '省份'")
	private String province;
	
	@JSONField(ordinal=4) @Getter @Setter
	@Column(name="city",nullable=false,columnDefinition="varchar(32) comment '城市'")
	private String city;
	
	@JSONField(ordinal=5) @Getter @Setter
	@Column(name="address",nullable=false,columnDefinition="varchar(255) comment '具體地址'")
	private String address;
	
	@JSONField(ordinal=6) @Getter @Setter
	@ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY,optional=true)
	@JoinColumn(name="webUser_id",nullable=true)
	private WebUser webUser;
	
	@Override
	public String toString() {
		return JSONObject.toJSONString(this,true);
	}
	
}

@ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY,optional=true)

address表為“一對多”中的多,所以使用@ManyToOne註解,並且配合@JoinColumn註解使用。

如果單獨使用@ManyToOne,那麼會生成一張中間表來維護兩張表關係,如果不想使用中間表使用@JoinColumn來生成外來鍵維護兩張表關係。

name="webUser_id",表示生成的外來鍵名稱,並且欄位型別以webUser表的主鍵為準。

多表聯查的重點是:@[email protected]@OneToMany註解的使用。

==================================================

實體搞完了,下面搞一下repository層

比較簡單了,普通的增刪改JPA封裝的很好了。這裡重點說多條件查詢及多表聯查。先上程式碼。

WebUserRepository

package com.wm.springboot.sc.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.wm.springboot.sc.entity.WebUser;

@Repository
public interface WebUserRepository  extends JpaRepository<WebUser, Integer>{

	public Page<WebUser> findAll(Specification<WebUser> specification,Pageable pageable);
}

使用Specification來進行復雜條件查詢,還可以使用Pageable進行分頁查詢。具體實現我們再service進行實現。

AddressRepository

package com.wm.springboot.sc.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.wm.springboot.sc.entity.Address;

@Repository
public interface AddressRepository extends JpaRepository<Address, Integer> {
	
	public Page<Address> findAll(Specification<Address> specification,Pageable pageable);
}

=====================================

接下來是service層及repository的方法實現

面向介面變成我們先定義一下webuser的service

WebUserService

package com.wm.springboot.sc.service;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import com.wm.springboot.sc.entity.WebUser;

public interface WebUserService {

	/**
	 * 單表條件查詢
	 */
	public Page<WebUser> findAll(WebUser webUser,Pageable pageable);
	
	/**
	 * 批量新增
	 * @param list
	 * @return
	 */
	public WebUser save(WebUser webUser);
	
	/**
	 * 單個刪除
	 * @param user
	 */
	public void deleteOne(int id);
	
	/**
	 * 單個修改
	 */
	public WebUser update(WebUser webUser);
	
	/**
	 * 根據ID查詢
	 */
	public WebUser findOne(int id);
}

實現類WebUserServiceImpl

package com.wm.springboot.sc.service.impl;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.wm.springboot.sc.entity.WebUser;
import com.wm.springboot.sc.repository.WebUserRepository;
import com.wm.springboot.sc.service.WebUserService;

@Service("WebUserServiceImpl")
public class WebUserServiceImpl implements WebUserService {

	@Autowired
	private WebUserRepository webUserRepository;

	@Override
	public Page<WebUser> findAll(WebUser webUser, Pageable pageable) {
		Page<WebUser> page = webUserRepository
				.findAll((Root<WebUser> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
					List<Predicate> predicates = new ArrayList<Predicate>();
					predicates.add(cb.like(root.get("userName").as(String.class), "%"+webUser.getUserName() + "%"));
					predicates.add(cb.like(root.get("email").as(String.class), "%"+webUser.getEmail() + "%"));
					predicates.add(cb.like(root.get("phoneNo").as(String.class), "%"+webUser.getPhoneNo() + "%"));
					predicates.add(cb.equal(root.get("isValid").as(String.class), webUser.getIsValid()));
					
					SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
					try {
						if (null != webUser.getCreateTimeStart() && !"".equals(webUser.getCreateTimeStart()))
							predicates.add(cb.greaterThanOrEqualTo(root.get("createTime").as(Date.class),
									f.parse(webUser.getCreateTimeStart())));
						if (null != webUser.getCreateTimeEnd() && !"".equals(webUser.getCreateTimeEnd()))
							predicates.add(cb.lessThan(root.get("createTime").as(Date.class),
									new Date(f.parse(webUser.getCreateTimeEnd()).getTime() + 24 * 3600 * 1000)));
					} catch (ParseException e) {
						e.printStackTrace();
					}
					return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
				}, pageable);
		return page;

	}

	@Override
	@Transactional
	public WebUser save(WebUser webUser) {
		return webUserRepository.save(webUser);
	}

	@Override
	public void deleteOne(int id) {
		webUserRepository.delete(id);
	}

	@Override
	public WebUser update(WebUser webUser) {
		return webUserRepository.save(webUser);
	}

	@Override
	public WebUser findOne(int id) {
		return webUserRepository.findOne(id);

	}
}

重點說一下實現的findAll方法。因為使用jdk8,進入介面Specification,發現是函式式介面,直接使用lambda表達時進行書寫。關於lambda表示式:

簡述一下這段邏輯,有錯誤請指正:

進入相應的方法可以看到:

root應該就是來獲得欄位的。

CriteriaBuilder 是用來拼裝查詢條件的。 如like  equal greaterThanOrEqualTo ......

將每一個Predicate新增到list,然後使用CriteriaQuery進行查詢。

pageable,是用來分頁查詢的。

AddressService

package com.wm.springboot.sc.service;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import com.wm.springboot.sc.entity.Address;

public interface AddressService {

	public Address save(Address address);
	
	public Page<Address> findAll(Pageable pageable,Address address);
}

實現類AddressServiceImpl

package com.wm.springboot.sc.service.impl;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.wm.springboot.sc.entity.Address;
import com.wm.springboot.sc.entity.WebUser;
import com.wm.springboot.sc.repository.AddressRepository;
import com.wm.springboot.sc.service.AddressService;

@Service
public class AddressServiceImpl implements AddressService {

	@Autowired
	private AddressRepository addressRepository;
	
	@Override
	public Address save(Address address) {
		return addressRepository.save(address);
	}

	@Override
	public Page<Address> findAll(Pageable pageable,Address address) {
		return addressRepository.findAll((Root<Address> root, CriteriaQuery<?> query, CriteriaBuilder cb)->{
			List<Predicate> predicates = new ArrayList<Predicate>();
			if(null!=address.getId()&&!"".equals(address.getId()))
				predicates.add(cb.equal(root.get("id").as(Integer.class),address.getId()));
			if(null!=address.getWebUser()&&!"".equals(address.getWebUser()))
				predicates.add(cb.equal(root.<WebUser>get("webUser").<Integer>get("id"),address.getWebUser().getId()));
			return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
		},pageable);
	}

}
重點:root.<WebUser>get("webUser").<Integer>get("id")  通過獲取address類中的屬性webUser,得到一個webUser實體中的id,這個就是address中的外來鍵。也是多表聯查時的關鍵。=======================================

controller層,訪問控制器

先看一下webuserController

package com.wm.springboot.sc.controller;

import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.wm.springboot.modelUtils.Pages;
import com.wm.springboot.modelUtils.PagesUtils;
import com.wm.springboot.modelUtils.RespResult;
import com.wm.springboot.modelUtils.RespResultEnum;
import com.wm.springboot.modelUtils.RespResultUtil;
import com.wm.springboot.sc.entity.WebUser;
import com.wm.springboot.sc.service.WebUserService;

/**
 * 網站使用者控制器
 * @author maybe
 */
@RequestMapping("/WebUser")
@RestController
public class WebUserController {

	@Autowired
	private WebUserService webUserService;
	
	/**
	 * 分頁查詢所有使用者(動態頁數,每頁大小,排序方式,排序欄位)
	 * 包括動態條件查詢(使用者名稱,email,電話,是否啟用,建立時間)
	 * 規則:無輸入條件,預設查詢全部。預設返回第一頁 每頁5條,預設asc排序,預設id排序。
	 */
	@RequestMapping(value="/findAll.do",method={RequestMethod.POST,RequestMethod.GET})
	public RespResult<Page<WebUser>> findAll(Pages pages,WebUser webUser){
		return RespResultUtil.success(webUserService.findAll(webUser, PagesUtils.createPageRequest(pages)));
	}
	
	/**
	 * 新增
	 */
	@PostMapping(value="/save.do")
	public RespResult<?> save(WebUser webUser){
		webUser.setCreateTime(new Date());
		webUser.setIsValid(1);
		webUser.setVersion(2);
		System.out.println(webUser.toString());
		WebUser webUser2 = webUserService.save(webUser);
		if(webUser2!=null) return RespResultUtil.success();
		else return RespResultUtil.error(RespResultEnum.ERROR);
	}
	
	/**
	 * 單個刪除
	 */
	@RequestMapping(value="/deleteOne.do",method= {RequestMethod.POST,RequestMethod.GET})
	public RespResult<?> deleteOne(String id){
		try {
			webUserService.deleteOne(Integer.parseInt(id));
		} catch (Exception e) {
			return RespResultUtil.error(RespResultEnum.ERROR);
		}
		return RespResultUtil.success();
	}

	/**
	 * 修改
	 * @param webUser
	 * @return
	 */
	@RequestMapping(value="/update.do",method= {RequestMethod.POST,RequestMethod.GET})
	public RespResult<?> update(WebUser webUser){
		webUser.setVersion(webUserService.findOne(webUser.getId()).getVersion());
		System.out.println(webUser.toString());
		WebUser user = webUserService.update(webUser);
		if(user!=null) return RespResultUtil.success();
		else return RespResultUtil.error(RespResultEnum.ERROR);
	}
}

RespResult、RespResultUtil為我自己封裝的返回實體類。具體可參考:

哦,我還沒來得及寫。。。有空補上。

在說下Pages、PagesUtils類,這個也是我自己封裝的分頁相關的類:

pages類:

package com.wm.springboot.modelUtils;

import com.alibaba.fastjson.JSON;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@AllArgsConstructor
@NoArgsConstructor
public class Pages {

	@Getter @Setter
	private int page;//第幾頁
	@Getter @Setter
	private int size;//每頁顯示幾條內容
	@Getter @Setter
	private String sortColumn; //排序欄位
	@Getter @Setter
	private String direction; //排序方式
	
	@Override
	public String toString() {
		return JSON.toJSONString(this, true);
	}
}

PagesUtils類

package com.wm.springboot.modelUtils;

import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.util.StringUtils;

public class PagesUtils {

	//分頁大小
	private final static Integer SIZE = 5;
	//預設頁數  0開頭
	private final static Integer PAGE = 0;
	//預設排序欄位
	private final static String ID = "id";
	
	public static Pageable createPageRequest(Pages pages) {
		return new PageRequest(pages.getPage()<=0?PAGE:pages.getPage(), 
							   pages.getSize()<=0?SIZE:pages.getSize(),
				new Sort(null!=pages.getDirection()&&!"".equals(pages.getDirection())&&pages.getDirection().equals("desc")?Direction.DESC:Direction.ASC,
						StringUtils.isEmpty(pages.getSortColumn())?ID:pages.getSortColumn()));
	}
}

這樣的好處時,前臺將頁數,分頁大小,排序欄位,排序方式都從前臺傳入。靈活多變。還有一個沒來得及補全的就是多欄位排序。這個回來再新增。

然後是AddressController

package com.wm.springboot.sc.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.wm.springboot.modelUtils.Pages;
import com.wm.springboot.modelUtils.PagesUtils;
import com.wm.springboot.modelUtils.RespResult;
import com.wm.springboot.modelUtils.RespResultEnum;
import com.wm.springboot.modelUtils.RespResultUtil;
import com.wm.springboot.sc.entity.Address;
import com.wm.springboot.sc.service.AddressService;

@RestController
@RequestMapping(value="/address")
public class AddressController {

	@Autowired
	private AddressService addressService;
	
	
	@RequestMapping(value="/save.do")
	public RespResult<?> save(Address address){
		address.setVersion(0);
		address.setIsValid(1);
		Address address2 = addressService.save(address);
		System.out.println(address2);
		if(address2!=null) return RespResultUtil.success();
		return RespResultUtil.error(RespResultEnum.ERROR);
	}
	
	@RequestMapping(value="/findAll.do")
	public RespResult<?> findAll(Address address,Pages pages){
		System.out.println(address.toString());
		return RespResultUtil.success(addressService.findAll(PagesUtils.createPageRequest(pages), address));
	}
	
}
================================================================================================================到此,java部分已經完成。其實後面可以使用junit測試一下。但是我junit使用的不是很好,總感覺用的不是特別舒服。

還有就是前臺程式碼也可以稍微熟悉一下。所以下面看一下前臺,使用html+jq實現。

直接上程式碼吧,一目瞭然。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" 
	xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<script type="text/javascript" src="/webjars/jquery/jquery.js"></script>
<script type="text/javascript" src="/js/test.js"></script>
</head>
<body>
	<div>
			<p>id修改使用:<input type="text" name="id" id="id"/></p>
			<p>userName不能重複:<input type="text" name="userName" id="userName"/></p>
			<p>password:<input type="text" name="password" id="password"/></p>
			<p>email不能重複:<input type="text" name="email" id="email"/></p>
			<p>phoneNo不能重複:<input type="text" name="phoneNo" id="phoneNo"/></p>
			<p>realName:<input type="text" name="realName" id="realName"/></p>
			<input type="button" value="提交" id="submit"/>
			<input type="button" value="修改" id="update"/>
	</div>
	<div>
		<form>
			<p>label:<input type="text" name="label" id="label"/></p>
			<p>country:<input type="text" name="country" id="country"/></p>
			<p>province:<input type="text" name="province" id="province"/></p>
			<p>city:<input type="text" name="city" id="city"/></p>
			<p>address:<input type="text" name="address" id="address"/></p>
			<input type="button" value="提交address" id="submitAddress"/>
		</form>
	</div>
	
<!-- 	<div> -->
<!-- 		<form> -->
<!-- 			<p>sender:<input type="text" name="sender" id="sender"/></p> -->
<!-- 			<p>addressee:<input type="text" name="addressee" id="addressee"/></p> -->
<!-- 			<p>msgTitle:<input type="text" name="msgTitle" id="msgTitle"/></p> -->
<!-- 			<p>msgText:<input type="text" name="msgText" id="msgText"/></p> -->
<!-- 			<input type="button" value="提交email" id="submitEmails"/> -->
<!-- 		</form> -->
<!-- 	</div> -->
	
	<div>
			<input type="button" value="查詢所有" id="findAll"/>
			<input type="text" value="0" placeholder="頁數" id="page" name="page"/> 
			<input type="text" value="15" placeholder="大小" id="size" name="size"/> 
			<input type="text" value="id" placeholder="排序列" id="sortColumn" name="sortColumn"/> 
			<input type="text" value="asc" placeholder="排序方式" id="direction" name="direction"/> 
			<input type="text" value="1" placeholder="是否啟用" id="isValid" name="isValid"/>
			<input type="date" value="" placeholder="開始時間" id="createTimeStart" name="createTimeStart"/>
			<input type="date" value="" placeholder="結束時間" id="createTimeEnd" name="createTimeEnd"/>
			<input type="text" value="" placeholder="地址" id="addresses" name="addresses"/>
	</div>
	
	<h3>測試多表聯查</h3>
	<div>
		關聯ID:<input type="text" value="11" name="webUser" id="webUser"/>
		<input type="button" value="查詢所有2" id="findAllAddress"/>
	</div>
	
	<div>
		<table>
			<thead>
				<tr>
					<td>ID</td>
					<td>USERNAME</td>
					<td>EMAIL</td>
					<td>PHONENO</td>
					<td>REALNAME</td>
					<td>DELETE</td>
				</tr>
			</thead>
			<tbody id="content">
			</tbody>
		</table>
	</div>
</body>
<script type="text/javascript">
	
	$(function(){
		
		$("#findAllAddress").click(function(){
			alert("123");
			$.post("/address/findAll.do",{
				webUser:$("#webUser").val(),
				id:$("#id").val()
			},function(data){
				alert(data.data.content);
			});
		});
		
		$("#submit").on('click', function() {
			
			var _userName = $("#userName").val();
			var _password = $("#password").val();
			var _email = $("#email").val();
			var _phoneNo = $("#phoneNo").val();
			var _realName = $("#realName").val();
			
			$.post("/WebUser/save.do", {
				userName : _userName,
				password : _password,
				email : _email,
				phoneNo : _phoneNo,
				realName : _realName
			}, function(data) {
				alert(data.msg);
			});
		});
		
		
		$("#submitAddress").on('click', function() {
			
			var _label = $("#label").val();
			var _country = $("#country").val();
			var _province = $("#province").val();
			var _city = $("#city").val();
			var _address = $("#address").val();
			
			$.post("/address/save.do", {
				label : _label,
				country : _country,
				province : _province,
				city : _city,
				address : _address
			}, function(data) {
				alert(data.msg);
			});
		});

		$("#update").on('click', function() {
			
			var _id = $("#id").val();
			var _userName = $("#userName").val();
			var _password = $("#password").val();
			var _email = $("#email").val();
			var _phoneNo = $("#phoneNo").val();
			var _realName = $("#realName").val();
			
			$.post("/WebUser/update.do", {
				id : _id,
				userName : _userName,
				password : _password,
				email : _email,
				phoneNo : _phoneNo,
				realName : _realName
			}, function(data) {
				alert(data.msg);
			});
		});

		$("#findAll").on(
				'click',
				function() {
					$.post("/WebUser/findAll.do", {
						page : $("#page").val(),
						size : $("#size").val(),
						sortColumn : $("#sortColumn").val(),
						direction : $("#direction").val(),
						userName : $("#userName").val(),
						email : $("#email").val(),
						phoneNo : $("#phoneNo").val(),
						isValid : $("#isValid").val(),
						createTimeStart : $("#createTimeStart").val(),
						createTimeEnd : $("#createTimeEnd").val()
					}, function(data) {
						$("#content").html();
						var html = "";
						$.each(data.data.content, function(i, val) {
							html += "<tr><td>" + val.id + "</td>" + "<td>"
									+ val.userName + "</td>" + "<td>" + val.email
									+ "</td>" + "<td>" + val.phoneNo + "</td>"
									+ "<td>" + val.realName + "</td>"
									+ '<td><a href="/WebUser/deleteOne.do?id='
									+ val.id + '">刪除</a></td></tr>';
						});
						$("#content").html(html);
					});
				});
		
	})

	
</script>
</html>

啟動專案:

看到資料庫生成的表:


可以看到父類的欄位,備註等都有。

隨手添加了幾條資料。

然後測試兩個表的findAll是否可以成功的進行關聯查詢及webuser的複雜條件查詢。

測試結果:

複雜條件查詢:



多表聯查:

{
	"code":0,
	"msg":"處理成功",
	"remindMsg":"處理成功",
	"data":{
		"content":[
			{
				"id":1,
				"label":"1",
				"country":"1",
				"province":"1",
				"city":"1",
				"version":0,
				"address":"1",
				"isValid":1,
				"webUser":{
					"id":11,
					"userName":"123",
					"createTime":"2018-06-17 21:06:26",
					"password":"123",
					"addresses":[{
						"id":3,
						"label":"3",
						"country":"3",
						"province":"3",
						"city":"3",
						"version":0,
						"address":"3",
						"isValid":1,
						"webUser":{"$ref":"$.data.content[0].webUser"}
					},{
						"id":2,
						"label":"2",
						"country":"2",
						"province":"2",
						"city":"2",
						"version":0,
						"address":"2",
						"isValid":1,
						"webUser":{"$ref":"$.data.content[0].webUser"}
					},{"$ref":"$.data.content[0]"}],
					"email":"222",
					"phoneNo":"123",
					"version":2,
					"isValid":1,
					"realName":"123"
				}
			},
			{"$ref":"$.data.content[0].webUser.addresses[1]"},
			{"$ref":"$.data.content[0].webUser.addresses[0]"}
		],
		"first":true,
		"last":true,
		"number":0,
		"numberOfElements":3,
		"size":5,
		"sort":[{
			"ascending":true,
			"descending":false,
			"direction":"ASC",
			"ignoreCase":false,
			"nullHandling":"NATIVE",
			"property":"id"
		}],
		"totalElements":3,
		"totalPages":1
	}
}

相關推薦

spring data jpa 實現條件複雜查詢聯查

最近發現JPA在處理單表時,很方便,但多表就比較複雜了。今天寫一下端午這兩天琢磨的多條件查詢,還有多表聯查。文章比較長,大部分都是程式碼,不願意看的程式碼copy下去,直接可以實現;想交流的可以看完,然後留言交流。maven依賴啊,配置,繼承寫法等知識點不展開說了,之前寫過一

spring data jpa】帶有條件查詢後分頁和不帶條件查詢後分頁實現

一.不帶有動態條件的查詢 分頁的實現  例項程式碼: controller:返回的是Page<>物件 @Controller@RequestMapping(value = "/egg")  publicclass EggController {   @

SpringBoot中使用Spring Data Jpa 實現簡單的動態查詢的兩種方法

ppr eat value table 得到 blog .net ride integer 首先謝謝大佬的簡書文章:http://www.jianshu.com/p/45ad65690e33# 這篇文章中講的是spring中使用spring data jpa,使用了xml配

spring data jpa 利用JpaSpecificationExecutor做複雜查詢

spring data jpa 通過建立方法名來做查詢,只能做簡單的查詢,那如果我們要做複雜一些的查詢呢,多條件分頁怎麼辦,這裡,spring data jpa為我們提供了JpaSpecificationExecutor介面,只要簡單實現toPredicate方

Spring Data JPA 實現關聯查詢

多表查詢在spring data jpa中有兩種實現方式,第一種是利用hibernate的級聯查詢來實現,第二種是建立一個結果集的介面來接收連表查詢後的結果,這裡介紹第二種方式。 一、一對一對映 實體 UserInfo :使用者。 實體 Address

Spring Boot中使用Spring-data-jpa實現分頁查詢(轉)

分頁查詢 log def inpu database ext identity odin btn 在我們平時的工作中,查詢列表在我們的系統中基本隨處可見,那麽我們如何使用jpa進行多條件查詢以及查詢列表分頁呢?下面我將介紹兩種多條件查詢方式。 1、引入起步依賴

Spring Data JPA 實現聯查的另一種方式

       通過封裝實體類來實現多表聯查的方式,利用Spring Data JPA @Query定義中的SpEL中的 ( rd.orderId=:#{#orderPageReq.orderId} or :#{#orderPageReq.orderId} is null

Spring Boot中使用Spring-data-jpa實現分頁查詢

在我們平時的工作中,查詢列表在我們的系統中基本隨處可見,那麼我們如何使用jpa進行多條件查詢以及查詢列表分頁呢?下面我將介紹兩種多條件查詢方式。 1、引入起步依賴   <dependency> <groupId>org.springframe

spring data jpa實現增刪改查

一、引子: (1)finAll()方法: 二、增刪改查程式碼 (一)建立springboot框架 1.專案結構 2.apllication.properties配置mysql連線資訊 spring.jpa.hibernate.ddl

SpringBoot第二講利用Spring Data JPA實現資料庫的訪問(二)_分頁和JpaSpecificationExecutor介面介紹

我們繼續研究spring jpa data,首先看看分頁和排序的實現,在原來的程式碼中,我們如果希望實現分頁,首先得建立一個Pager的物件,在這個物件中記錄total(總數),totalPager(總頁數),pageSize(每頁多少條記錄),pageInde

使用Gradle構建SpringBoot工程系列:第八篇:使用spring-data-jpa 實現資料持久化

本篇文章是SpringBoot 系列文章的第八篇文章,由於本人工作原因,中斷了一段時間,接下來的一段時間裡,將陸續更新本系列的其他文章,迴歸Spring Boot技術體系,記錄本人學習和使用Gradle構建spring Boot工程的過程、技術要點以及在過程中遇到的各種問題,

Spring Data JPA 動態拼接條件的通用設計模式

記住官方文件永遠是首選 import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import javax.persistence.criteria.Crit

SpringBoot第二講 利用Spring Data JPA實現資料庫的訪問(一)

在基本瞭解了springboot的執行流程之後,我們需要逐個來突破springboot的幾個關鍵性問題,我們首先解決的是springboot訪問資料庫的問題。Java訪問資料庫經歷了幾個階段,第一個階段是直接通過JDBC訪問,這種方式工作量極大,而且會做大量的重複勞動,之

spring data jpa 實體類中欄位不與資料庫對映

當我們使用spring data jpa開發的時候,會將實體類中的成員變數與表中的欄位一一對應,當我們在實體類中加上一個不與資料庫表一一對應的成員變數的時候,此時我們只要在這個成員變數上加上註解@Transient @Transient private String[

spring-data-jpa動態拼接sql語句實現動態的條件查詢

** spring-data-jpa 動態拼接sql語句 ** spring-data-jpa對於簡單的資料操作確實使用起來比較方便,但是對於一些比較複雜的動態的多表條件查詢就不是那麼簡單了,對於需要些sql語句並且需要動態的新增條件的時候就得使用jpa的EntityManager來

Spring Data JPA 複雜/條件組合查詢

1: 編寫DAO類或介面  dao類/介面 需繼承 publicinterface JpaSpecificationExecutor<T>          介面;        如果需要分頁,還可繼承   publicinterface Pa

Spring Data JPA動態查詢條件and)

ica cat 滿足 from pos true ans let tid entity: @Entity @Table(name = "data_illustration") public class Test { @Id @Gen

Spring Data JPA 二:實現關聯分頁查詢

最近在對JPA的使用過程中發現對於單表的操作很是方便,但是當設計到多表聯查的時候就需要有一些特殊的操作了。 專案中有一個場景是後臺需要做一個分頁的列表查詢,所需要的資料分散在兩張表中,如果是用mybatis的話直接定義resultMap,然後手寫SQL就可以了。而在JPA中就需要用到JPQL

Spring Data JPA 一:實現關聯查詢

多表查詢在spring data jpa中有兩種實現方式,第一種是利用hibernate的級聯查詢來實現,第二種是建立一個結果集的介面來接收連表查詢後的結果,這裡介紹第二種方式。 一、一對一對映 實體 UserInfo :使用者。 實體 Address:家庭住址。 這裡通過外來鍵的方

快速學習Spring Data JPA -- 第六章JPA條件查詢

xl_echo編輯整理,交流學習請加1280023003 百戰不敗,依不自稱常勝,百敗不頹,依能奮力前行。——這才是真正的堪稱強大!! 在Springle Data JPA中,我們看到了JPA由於不需要寫SQL給我們簡化了很多的工作。同時JPA的特性,讓我們對資料層面的操作