SpringBoot24 SpringDataJPA環境搭建、實體類註解、關聯查詢

 

1 版本說明

  JDK:1.8html

  MAVEN:3.5java

  SpringBoot:2.0.4mysql

  IDEA:旗艦版207.2git

  MySQL:5.5spring

 

2 SpringDataJPA環境搭建(SpringBoot版本)

  2.1 建立一個SrpingBoot項目

    須要引入的依賴以下圖所示sql

  2.2 配置數據庫相關

    》建立一個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;
student.sql

    》在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
application.yml

    2.2.1 利用IDEA鏈接數據

      參考博文springboot

    2.2.2 利用IDEA自動生成實體類

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;
    }
}
StudentDO.java

      參考博文

  2.3 建立持久層

    技巧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> {

}
StudentRepository.java

    技巧02:須要使用持久層實例時,只須要進行依賴注入便可

    技巧03:持久層接口上不用寫   @Repository  ,由於繼承了 JpaRepository 接口後就已是一個被Spring容器管理的持久層Bean啦

    技巧04:Repository相關接口

      

  2.4 測試持久層

    技巧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);
    }

}
View Code

    技巧03:jpa支持自定義SQL【藉助@Query註解實現】

 

 3 Projections 對查詢結果的擴展

  3.1 返回類型建模

    Spring JPA 對 Projections 的擴展的支持,我的以爲這是個很是好的東西,從字面意思上理解就是映射,指的是和 DB 的查詢結果的字段映射關係。通常狀況下,咱們是返回的字段和 DB 的查詢結果的字段是一一對應的,但有的時候,須要返回一些指定的字段,不須要所有返回,或者返回一些複合型的字段,還得本身寫邏輯。Spring Data 正是考慮到了這一點,容許對專用返回類型進行建模,以便更有選擇地將部分視圖對象。(聲明:來自gitChat)

    3.1.1 需求

      只須要查詢學生的姓名和年齡信息,其他信息不進行查詢

    3.1.2 思路

      聲明一個接口,包含咱們要返回的屬性的方法便可

    3.1.3 編程實現

      》根據實體類中屬性的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();

}
StudentBaseInfo.java

      》修改持久層接口中的方法

        技巧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);

}
StudentRepository.java

      》測試類

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

}
View Code

      》跳坑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);
    }

}
View Code

      》轉化後的結果爲

 

4 實體類註解

  待更新......  2018年8月14日20:21:24

 

5 關聯查詢

  說明:本博是利用原生的SQL進行關聯查詢

  5.1 需求

    現有兩張數據庫表:

      product -> 存放產品信息

      provider -> 存放供應商信息

      product 和 provider 的關係時多對一的關係,即:一個供應商可能和多個產品對應

  5.2 思路

    知己利用SQL的關聯查詢實現

  5.3 編程實現

    5.3.1 數據表

      技巧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', '重慶市渝北區');
View Code

    5.3.2 建立實體類

      技巧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;
    }
}
ProductDO.java
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;
    }
}
ProviderDO.java
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;

}
ProductInfoDTO.java

    5.3.3 建立持久層接口

      技巧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);

}
ProductDAO.java

    5.3.4 測試

      技巧01:多表關聯查詢時獲取到數據是Map類型的,須要進行一層轉化;本博文用的是阿里巴巴的 fastjson 進行轉化

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.1.46</version>
        </dependency>
View Code

      坑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);

    }

}
View Code

 

相關文章
相關標籤/搜索