• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • spring data jpa 實現多條件復雜查詢及多表聯查

    標簽: spring data jpa  多表聯查  復雜條件查詢  spring boot  hibernate

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

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

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

    spring boot 配置及使用 spring data jpa

    這里說一下更新的地方:

    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表的主鍵為準。


    多表聯查的重點是:@ManyToOne@JoinColumn@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表達式:

    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
    	}
    }


    版權聲明:本文為qq_34083066原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/qq_34083066/article/details/80733512

    智能推薦

    spring data jpa實現分頁查詢功能

    spring data jpa實現分頁查詢功能 HTML代碼部分: Action類: Service代碼: dao層:...

    【Spring Data 系列學習】Spring Data JPA 基礎查詢

    【Spring Data 系列學習】Spring Data JPA 基礎查詢 前面的章節簡單講解了 了解 Spring Data JPA 、 Jpa 和 Hibernate ,本章節開始通過案例上手 Spring boot Jpa 。 spring data Spring Data 庫的核心接口是 Repository。首先需要定義實體類的接口,接口必須繼承 repository 并且輸入實體類型...

    JPA與Spring Data JPA

    前言 這是在對慕課網的SpringBoot博客站實戰課程學習中遇到JpaRepository接口時就想要寫的,但是一直沒動手,畢竟懶癌,最近延畢了,所以有點空閑時間就來開篇了。僅僅是匆忙地做個記錄,還不清楚到底主旨是要表達什么,所以先用“JPA與Spring-data-Jpa”這個標題好了,后面可能再做修改。 1. JPA JPA是Java Persistence API的...

    JPA(2)Spring Data JPA

    目錄 一 概述 推薦組合 二 特性 和其他框架的關系 二 搭建環境 依賴 dao 測試 三 CRUD 查詢 保存/更新 刪除 根據id刪除 查詢所有 四 執行過程 五 復雜查詢 查詢總數 查詢id是否存在 getOne JPQL 占位符 實現更新 sql查詢 查詢Map 方法命名規則查詢 參考 一 概述 Spring Data JPA 是 Spring 基于 ORM 框架、JPA 規范的基礎上封裝...

    JPA & Spring Data JPA詳解

    JPA & Spring Data JPA 一、JPA 1. JPA是什么 JPA(Java Persistence API)Java持久化 API,是一套基于ORM思想的規范。 ORM(Object Relational Mapping)對象關系映射。實現了實體類對象和數據庫中表的一個映射關系。我們可以通過操作實體類對象(JavaBean)來操作數據庫表,從而實現數據庫的CRUD操作并且...

    猜你喜歡

    Unity_Shader高級篇_13.1_Unity Shader入門精要

    13.4 再談邊緣檢測 在12.3中,我們曾使用Sobel算子對屏幕圖像進行邊緣測試,實現描邊的效果。但是,這種直接利用顏色信息進行邊緣檢測的方法會產生很對我們不希望得到的邊緣線,如圖13.8所示。 可以看出,物體的紋理、陰影等位置也被描上黑邊,而這往往不是我們希望看到的。在本節中,我們將學習如何在深度和法線上進行邊緣檢測,這些圖像不會受紋理和光照的影響,而僅僅保存了當前渲染物體的模型信息,通過這...

    Seata AT 模式 原理詳解

    目錄 前提 整體機制 寫隔離 讀隔離 工作機制 一階段 二階段-回滾 二階段-提交 附錄 回滾日志表 前提 基于支持本地 ACID 事務的關系型數據庫。 Java 應用,通過 JDBC 訪問數據庫。 整體機制 兩階段提交協議的演變: 一階段:業務數據和回滾日志記錄在同一個本地事務中提交,釋放本地鎖和連接資源。 二階段: 提交異步化,非常快速地完成。 回滾通過一階段的回滾日志進行反向補償。 寫隔離 ...

    Python爬蟲 | 滑動驗證碼**

    極驗驗證碼:需要手動拼合滑塊來完成的驗證,相對圖形驗證碼識別難度上升了幾個等級。下面用程序識別并通過極驗驗證碼的驗證,其中有分析識別思路、識別缺口位置、生成滑塊拖動、模擬實現滑塊拼合通過驗證等步驟。需要用到Chrome 瀏覽器,并配置 ChromeDriver ,要用到的 Python 庫是 Selenium。 1、 對極驗驗證碼了解   極驗驗證碼官網:http://www.geetest.co...

    MobaXterm root用戶連接虛擬機時出現Access denied

    1.linux打開ssh服務 2.新建連接 首先在romote host中填入要連接的主機ip specify username中填入連接的用戶名 port為連接端口默認為22 輸入連接用戶的密碼 linux默認不顯示密碼 發現密碼正確但是連接不上 問題解決 /etc/ssh/sshd_config 配置問題: #PermitRootLogin prohibit-password將該行改為Perm...

    Linux C 預處理命令

    預處理命令 一、宏定義 C語言標準允許在程序中用一個標識符來表示一個字符串,成為宏。標識符為宏名 ,在編譯預處理時,將程序中所有的宏名用相應的字符串來替換,這個過程稱為宏替換,宏分為兩種:無參數的宏和有參數的宏。 1.無參數的宏 無參數宏定義的一般形式為:#define 標識符字符串 “#”代表本行是編譯預處理命令。define是宏定義的關鍵詞,標識符是宏名。字符串是宏名所...

    精品国产乱码久久久久久蜜桃不卡