JDK:1.8html
MAVEN:3.5java
SpringBoot:2.0.4mysql
IDEA:旗艦版207.2git
MySQL:5.5spring
須要引入的依賴以下圖所示sql
》建立一個mysql數據庫testdemo數據庫
》在testdemo中建立一個student表編程
/* Navicat MySQL Data Transfer Source Server : mysql5.4 Source Server Version : 50540 Source Host : localhost:3306 Source Database : testdemo Target Server Type : MYSQL Target Server Version : 50540 File Encoding : 65001 Date: 2018-08-13 21:13:51 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `student` -- ---------------------------- DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `id` int(50) NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, `age` int(10) NOT NULL, `address` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4;
》在springboot項目中配置數據庫信息json
spring: datasource: url: jdbc:mysql://127.0.0.1/testdemo?characterEncoding=utf-8&useSSL=false username: root password: 182838 jpa: properties: hibernate: format_sql: true show_sql: true
參考博文springboot
package cn.xiangxu.jpa_demo03.domain.domain_do; import javax.persistence.*; /** * @author 王楊帥 * @create 2018-08-13 21:36 * @desc **/ @Entity @Table(name = "student", schema = "testdemo", catalog = "") public class StudentDO { private int id; private String name; private int age; private String address; @Id @Column(name = "id") public int getId() { return id; } public void setId(int id) { this.id = id; } @Basic @Column(name = "name") public String getName() { return name; } public void setName(String name) { this.name = name; } @Basic @Column(name = "age") public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Basic @Column(name = "address") public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public boolean equals(Object object) { if (this == object) return true; if (object == null || getClass() != object.getClass()) return false; StudentDO studentDO = (StudentDO) object; if (id != studentDO.id) return false; if (age != studentDO.age) return false; if (name != null ? !name.equals(studentDO.name) : studentDO.name != null) return false; if (address != null ? !address.equals(studentDO.address) : studentDO.address != null) return false; return true; } @Override public int hashCode() { int result = id; result = 31 * result + (name != null ? name.hashCode() : 0); result = 31 * result + age; result = 31 * result + (address != null ? address.hashCode() : 0); return result; } }
技巧01:持久層接口只須要繼承 JpaRepository 接口便可
package cn.xiangxu.jpa_demo03.domain.repository; import cn.xiangxu.jpa_demo03.domain.domain_do.StudentDO; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; /** * @author 王楊帥 * @create 2018-08-13 21:53 * @desc 學生持久層接口 **/ public interface StudentRepository extends JpaRepository<StudentDO, Integer> { }
技巧02:須要使用持久層實例時,只須要進行依賴注入便可
技巧03:持久層接口上不用寫 @Repository ,由於繼承了 JpaRepository 接口後就已是一個被Spring容器管理的持久層Bean啦
技巧04:Repository相關接口
技巧01:繼承 JpaRepository 接口後就能夠有很過方法可使用
技巧02:能夠直接使用的緣由是:攔截 + JDK動態代理
package cn.xiangxu.jpa_demo03.domain.repository; import cn.xiangxu.jpa_demo03.domain.domain_do.StudentDO; import lombok.extern.slf4j.Slf4j; 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.context.junit4.SpringRunner; import java.util.List; import static org.junit.Assert.*; @RunWith(SpringRunner.class) @SpringBootTest @Slf4j public class StudentRepositoryTest { @Autowired private StudentRepository studentRepository; @Test public void findall() { List<StudentDO> all = studentRepository.findAll(); System.out.println(all); } }
技巧03:jpa支持自定義SQL【藉助@Query註解實現】
Spring JPA 對 Projections 的擴展的支持,我的以爲這是個很是好的東西,從字面意思上理解就是映射,指的是和 DB 的查詢結果的字段映射關係。通常狀況下,咱們是返回的字段和 DB 的查詢結果的字段是一一對應的,但有的時候,須要返回一些指定的字段,不須要所有返回,或者返回一些複合型的字段,還得本身寫邏輯。Spring Data 正是考慮到了這一點,容許對專用返回類型進行建模,以便更有選擇地將部分視圖對象。(聲明:來自gitChat)
只須要查詢學生的姓名和年齡信息,其他信息不進行查詢
聲明一個接口,包含咱們要返回的屬性的方法便可
》根據實體類中屬性的get方法建立接口
技巧01:接口中的方法就是須要獲取的字段在實體類中對應的get方法
package cn.xiangxu.jpa_demo03.domain.domain_do; /** * @author 王楊帥 * @create 2018-08-13 22:16 * @desc **/ public interface StudentBaseInfo { String getName(); Integer getAge(); }
》修改持久層接口中的方法
技巧01:返回值用StudentBaseInfo類型
package cn.xiangxu.jpa_demo02.repository; import cn.xiangxu.jpa_demo02.domain.domain_do.StudentBaseInfo; import cn.xiangxu.jpa_demo02.domain.domain_do.StudentDO; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; /** * @author 王楊帥 * @create 2018-08-13 10:00 * @desc **/ public interface StudentRepository extends JpaRepository<StudentDO, Integer> { /** * 根據姓名和年齡 * @param name 姓名 * @param age 年齡 * @return */ List<StudentDO> findByNameAndAge(String name, Integer age); /** * 根據年齡段查詢【PS: 開區間】 * @param start 最小值 * @param end 最大值 * @return */ List<StudentDO> findByAgeBetween(Integer start, Integer end); /** * 小於給定年齡【PS: 開區間】 * @param age 年齡 * @return */ List<StudentDO> findByAgeLessThan(Integer age); /** * 小於給定年齡【PS: 閉區間】 * @param age 年齡 * @return */ List<StudentDO> findByAgeLessThanEqual(Integer age); List<StudentDO> findByAgeGreaterThan(Integer age); List<StudentDO> findByAgeGreaterThanEqual(Integer age); List<StudentDO> findByName(String name); List<StudentDO> findByNameEquals(String name); List<StudentDO> findByNameIs(String name); List<StudentDO> findByAgeAfter(Integer age); List<StudentDO> findByAgeBefore(Integer age); List<StudentDO> findByAgeAfterOrAgeEquals(Integer age, Integer age2); // List<StudentDO> findByAddress(String address); List<StudentBaseInfo> findByAddress(String address); }
》測試類
package cn.xiangxu.jpa_demo03.domain.repository; import cn.xiangxu.jpa_demo03.domain.domain_do.StudentBaseInfo; import cn.xiangxu.jpa_demo03.domain.domain_do.StudentDO; import cn.xiangxu.jpa_demo03.repository.StudentRepository; import lombok.extern.slf4j.Slf4j; 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.context.junit4.SpringRunner; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest @Slf4j public class StudentRepositoryTest { @Autowired private StudentRepository studentRepository; @Test public void findall() { List<StudentDO> all = studentRepository.findAll(); System.out.println(all); } @Test public void findByName() { List<StudentBaseInfo> byNameIs = studentRepository.findByNameIs("楊玉林"); System.out.println(byNameIs); } }
》跳坑01:經過這種方式獲取到的數據是一個Map對象,如何轉化成一個實體類列表呢
》填坑01:建立一個實體類,這個實體類的字段要和建立的接口中方法對應實體類類的字段名保持一致【PS:本博文就繼續使用以前的實體類】,將查詢到的數據轉化成流,而後利用peek這個中間操做進行轉化
package cn.xiangxu.jpa_demo03.domain.repository; import cn.xiangxu.jpa_demo03.domain.domain_do.StudentBaseInfo; import cn.xiangxu.jpa_demo03.domain.domain_do.StudentDO; import cn.xiangxu.jpa_demo03.repository.StudentRepository; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.ArrayList; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest @Slf4j public class StudentRepositoryTest { @Autowired private StudentRepository studentRepository; @Test public void findall() { List<StudentDO> all = studentRepository.findAll(); System.out.println(all); } @Test public void findByName() { List<StudentBaseInfo> byNameIs = studentRepository.findByNameIs("楊玉林"); System.out.println(byNameIs); } @Test public void findBYName02() { List<StudentBaseInfo> byNameIs = studentRepository.findByNameIs("王楊帥"); List<StudentDO> studentDOList = new ArrayList<>(); byNameIs.stream() .peek( i -> { StudentDO studentDO = new StudentDO(); BeanUtils.copyProperties(i, studentDO); studentDOList.add(studentDO); } ) .forEach( System.out::println ); System.out.println(studentDOList); } }
》轉化後的結果爲
待更新...... 2018年8月14日20:21:24
說明:本博是利用原生的SQL進行關聯查詢
現有兩張數據庫表:
product -> 存放產品信息
provider -> 存放供應商信息
product 和 provider 的關係時多對一的關係,即:一個供應商可能和多個產品對應
知己利用SQL的關聯查詢實現
技巧01:product中的provider_id字段和provider的id字段做爲關聯查詢的橋樑
技巧02:理論上說,product中的provider_id字段應該設置成外鍵,可是爲了開發方便,此處不進行設置【PS: 開發人員知道這是外鍵便可】
/* Navicat MySQL Data Transfer Source Server : mysql5.4 Source Server Version : 50540 Source Host : localhost:3306 Source Database : testdemo Target Server Type : MYSQL Target Server Version : 50540 File Encoding : 65001 Date: 2018-08-14 20:25:59 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `product` -- ---------------------------- DROP TABLE IF EXISTS `product`; CREATE TABLE `product` ( `id` int(11) NOT NULL AUTO_INCREMENT, `product_name` varchar(255) NOT NULL, `product_price` double NOT NULL, `provider_id` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of product -- ---------------------------- INSERT INTO `product` VALUES ('1', '天友純牛奶', '3', '1'); INSERT INTO `product` VALUES ('2', '天友核桃花生奶', '2', '1'); INSERT INTO `product` VALUES ('3', '福特福克斯', '13', '2'); INSERT INTO `product` VALUES ('4', '福特嘉年華', '9', '2'); -- ---------------------------- -- Table structure for `provider` -- ---------------------------- DROP TABLE IF EXISTS `provider`; CREATE TABLE `provider` ( `id` int(11) NOT NULL AUTO_INCREMENT, `provider_name` varchar(255) NOT NULL, `provider_phone` varchar(255) NOT NULL, `provider_address` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of provider -- ---------------------------- INSERT INTO `provider` VALUES ('1', '天友乳業', '13282882818', '重慶市大足區'); INSERT INTO `provider` VALUES ('2', '長安製造', '13212112121', '重慶市渝北區');
技巧01:利用IDEA自動生成
技巧02:ProductInfoDTO這個實體類是用來封裝產品信息和供應商信息的
package cn.xiangxu.jpa_demo04.domain.domain_do; import lombok.ToString; import javax.persistence.*; /** * @author 王楊帥 * @create 2018-08-14 19:54 * @desc **/ @Entity @ToString @Table(name = "product", schema = "testdemo", catalog = "") public class ProductDO { private int id; private String productName; private double productPrice; private int providerId; @Id @Column(name = "id") public int getId() { return id; } public void setId(int id) { this.id = id; } @Basic @Column(name = "product_name") public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } @Basic @Column(name = "product_price") public double getProductPrice() { return productPrice; } public void setProductPrice(double productPrice) { this.productPrice = productPrice; } @Basic @Column(name = "provider_id") public int getProviderId() { return providerId; } public void setProviderId(int providerId) { this.providerId = providerId; } @Override public boolean equals(Object object) { if (this == object) return true; if (object == null || getClass() != object.getClass()) return false; ProductDO productDO = (ProductDO) object; if (id != productDO.id) return false; if (Double.compare(productDO.productPrice, productPrice) != 0) return false; if (providerId != productDO.providerId) return false; if (productName != null ? !productName.equals(productDO.productName) : productDO.productName != null) return false; return true; } @Override public int hashCode() { int result; long temp; result = id; result = 31 * result + (productName != null ? productName.hashCode() : 0); temp = Double.doubleToLongBits(productPrice); result = 31 * result + (int) (temp ^ (temp >>> 32)); result = 31 * result + providerId; return result; } }
package cn.xiangxu.jpa_demo04.domain.domain_do; import lombok.ToString; import javax.persistence.*; /** * @author 王楊帥 * @create 2018-08-14 19:54 * @desc **/ @Entity @ToString @Table(name = "provider", schema = "testdemo", catalog = "") public class ProviderDO { private int id; private String providerName; private String providerPhone; private String providerAddress; @Id @Column(name = "id") public int getId() { return id; } public void setId(int id) { this.id = id; } @Basic @Column(name = "provider_name") public String getProviderName() { return providerName; } public void setProviderName(String providerName) { this.providerName = providerName; } @Basic @Column(name = "provider_phone") public String getProviderPhone() { return providerPhone; } public void setProviderPhone(String providerPhone) { this.providerPhone = providerPhone; } @Basic @Column(name = "provider_address") public String getProviderAddress() { return providerAddress; } public void setProviderAddress(String providerAddress) { this.providerAddress = providerAddress; } @Override public boolean equals(Object object) { if (this == object) return true; if (object == null || getClass() != object.getClass()) return false; ProviderDO that = (ProviderDO) object; if (id != that.id) return false; if (providerName != null ? !providerName.equals(that.providerName) : that.providerName != null) return false; if (providerPhone != null ? !providerPhone.equals(that.providerPhone) : that.providerPhone != null) return false; if (providerAddress != null ? !providerAddress.equals(that.providerAddress) : that.providerAddress != null) return false; return true; } @Override public int hashCode() { int result = id; result = 31 * result + (providerName != null ? providerName.hashCode() : 0); result = 31 * result + (providerPhone != null ? providerPhone.hashCode() : 0); result = 31 * result + (providerAddress != null ? providerAddress.hashCode() : 0); return result; } }
package cn.xiangxu.jpa_demo04.domain.domain_do; import lombok.Builder; import lombok.Data; /** * @author 王楊帥 * @create 2018-08-14 20:03 * @desc **/ @Data @Builder public class ProductInfoDTO { private int id; private String productName; private double productPrice; private int providerId; private String providerName; private String providerPhone; private String providerAddress; }
技巧01:這裏使用@Query進行原生的SQL查詢,因此直接在ProductDAO中就能夠查詢出產品和供應商的信息
package cn.xiangxu.jpa_demo04.repository; import cn.xiangxu.jpa_demo04.domain.domain_do.ProductDO; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import java.util.List; import java.util.Map; /** * @author 王楊帥 * @create 2018-08-14 19:55 * @desc **/ public interface ProductDAO extends JpaRepository<ProductDO, Integer> { @Query( nativeQuery = true, value = "SELECT product_name, product_price, provider_name, provider_phone, provider_address\n" + "FROM product\n" + "JOIN provider \n" + "ON product.provider_id = provider.id\n" + "WHERE product.provider_id = ?1" ) List<Map<String, Object>> findTest(Integer providerId); }
技巧01:多表關聯查詢時獲取到數據是Map類型的,須要進行一層轉化;本博文用的是阿里巴巴的 fastjson 進行轉化
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.1.46</version> </dependency>
坑01:fastjson 轉化時 map 的 key 要和 實體類的屬性 保持一致,不一致時只有經過本辦法實現了
package cn.xiangxu.jpa_demo04.repository; import cn.xiangxu.jpa_demo04.JpaDemo04ApplicationTests; import cn.xiangxu.jpa_demo04.domain.domain_do.DoctorInfoDetail; import cn.xiangxu.jpa_demo04.domain.domain_do.ProductDO; import cn.xiangxu.jpa_demo04.domain.domain_do.ProductInfoDTO; import com.alibaba.fastjson.JSONObject; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; import java.util.Map; @Component public class ProductDaoTest extends JpaDemo04ApplicationTests { @Autowired private ProductDAO productDAO; @Test public void test01() { List<ProductDO> all = productDAO.findAll(); System.out.println(all); } @Test public void test02() { List<Map<String, Object>> test = productDAO.findTest(2); System.out.println(test); List<ProductInfoDTO> productInfoDTOS = new ArrayList<>(); for (Integer i = 0; i < test.size(); i++) { Map<String, Object> info = test.get(i); // ProductInfoDTO productInfoDTO = JSONObject.parseObject(JSONObject.toJSONString(test.get(i)), ProductInfoDTO.class); ProductInfoDTO productInfoDTO = ProductInfoDTO .builder() .productName((String)info.get("product_name")) .productPrice((Double)info.get("product_price")) .providerName((String)info.get("provider_name")) .providerPhone((String)info.get("provider_phone")) .providerAddress((String)info.get("provider_address")) .build(); System.out.println(productInfoDTO); productInfoDTOS.add(productInfoDTO); } System.out.println(productInfoDTOS); } }