復育紐約綠能 非營利組織盼捐助_貨運

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

摘錄自2020年05月17日世界新聞網報導

受新冠疫情影響,紐約市何時重啟海灘仍未知,但預料市內公園將迎大批夏日人潮;對此,城市公園基金會(CPF)近日宣布設立「紐約市綠色救濟與恢復基金」,希望通過籌款維護公園與復育環境綠能。

城市公園基金會是由社區家庭、團體成員等組成的非營利組織,500多名正職員工與上百名合約工為市內1萬5000畝綠地提供美化與照護。但在新冠疫情中,基金會收入大減60%,導致不少員工被迫放假或遭解雇,同時有數十個季節性職位被取消,造成高達15萬小時的景觀維護和園藝護理工作機會的流失。

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

對此,城市公園基金會日前宣布設立「紐約市綠色救濟與恢復基金」(NYC Green Relief & Recovery Fund),希望透過籌款舒緩緊張資金,保留必要工作與人力。

生活環境
國際新聞
紐約
CPF

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

疑似 iPad mini 6 渲覽圖現身!採相機挖孔、螢幕下 Touch ID 技術_貨運

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

除了 iPad Pro,新一代 iPad mini 系列也是很多人非常感興趣的新品,去年 Apple 沒有更新,因此今年就蠻有機會的,而就在稍早國外出現一組疑似 iPad mini 6 的渲染圖,不僅顯示螢幕邊框變更窄,還採螢幕下 Touch ID 與鏡頭(挖孔)設計,感覺超級讚。

疑似 iPad mini 6 渲覽圖現身

稍早外媒 Pigtou 分享一組 Apple iPad mini 6 的渲染圖,外觀跟現行的 iPad mini 5 有非常大不同,甚至用大躍進來形容也不為過。

螢幕部分邊框大幅變窄,佔比變更高,看起來有點類似 2020 iPad Pro,但正面自拍鏡頭變成嵌在螢幕下方,如果真是這樣,那 iPad mini 6 應該是目前首款採用這類面板的 Apple 行動裝置產品:

此外,iPad mini 6 圖片下方也顯示,這款會採用螢幕下指紋辨識技術,就跟謠傳的 iPhone 13 一樣,這點相信很多人也非常樂見。

尺寸部分,目前獲得的資訊是 203.2mm(長)x 134.8mm(寬)x 6.25mm(厚度),螢幕大約為 195mm x 126.6mm,根據他們計算,大約是 9.15 吋,這我就覺得怪怪的,iPad mini 的定位應該是小尺寸平版,iPad mini 5 也僅 7.9 吋,iPad mini 6 一下變到 9.15 吋,這幅度似乎有點太大。

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

其餘硬體規格 Pigtou 就沒有說明,不過 iPad Air 4 搭載 A14 Bionic 晶片,iPad mini 6 也有很大機會使用相同的晶片。推出時間有可能會落在三月份,如果這渲染圖是真的,那應該下個月有機會看到實機間諜圖現身,我們也會隨時追蹤。

話說回來,iPad mini 6 假設真的變成這種設計(螢幕下鏡頭 + Touch ID),那價格我猜一定會提升不少。

資料來源:Pigtou

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

實戰| 配置DataDog監控Apache Hudi應用指標_貨運

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

1. 可用性

在Hudi最新master分支,由Hudi活躍貢獻者Raymond Xu貢獻了DataDog監控Hudi應用指標,該功能將在0.6.0 版本發布,也感謝Raymond的投稿。

2. 簡介

Datadog是一個流行的監控服務。在即將發布的Apache Hudi 0.6.0版本中,除已有的報告者類型(Graphite和JMX)之外,我們將引入通過Datadog HTTP API報告Hudi指標的功能。

3. 配置

類似於其他支持的報告者,啟用Datadog報告者需要以下兩個屬性。

hoodie.metrics.on=true
hoodie.metrics.reporter.type=DATADOG

下面的屬性用來配置Datdog API站點。它會決定請求被發送給api.datadoghq.eu (EU) 還是 api.datadoghq.com (US)。根據你的Datadog賬號作相應配置。

hoodie.metrics.datadog.api.site=EU # 或者 US

hoodie.metrics.datadog.api.key可以讓你配置API密匙。

hoodie.metrics.datadog.api.key=<你的API密匙>
hoodie.metrics.datadog.api.key.supplier=<你的API密匙提供者>

出於安全性考慮,你可能會選擇在運行時返回API密匙。要使用這個方法,需要實現java.util.function.Supplier<String>。並把實現類的完整類名設置到hoodie.metrics.datadog.api.key.supplier。由於hoodie.metrics.datadog.api.key有更高的優先級,也要確保它沒有設置。

下面的屬性用來配置指標前綴,從而區分不同job的指標。

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

hoodie.metrics.datadog.metric.prefix=<你的指標前綴>

注意這裏.會被用來隔離前綴和指標名。比如,如果前綴是foo,則foo.會被加在指標名稱前。

其他的可選屬性在配置參考頁里有相關解釋。

4. 示例演示

在這個示例中,我們運行了一個HoodieDeltaStreamer,啟用了指標收集並做了相應的配置。

如圖所示,我們能收集到Hudi操作相關的指標,比如

  • <前綴>.<表名>.commit.totalScanTime
  • <前綴>.<表名>.clean.duration
  • <前綴>.<表名>.index.lookup.duration

以及HoodieDeltaStreamer相關的指標。

  • <前綴>.<表名>.deltastreamer.duration
  • <前綴>.<表名>.deltastreamer.hiveSyncDuration

. 總結

Hudi提供了多種報告者,方便監控Hudi應用運行時的各項指標,及時發現系統中的問題。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

5℃變21.7℃ 挪威北極圈群島氣溫飆升創歷史新高_貨運

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

摘錄自2020年07月26日蘋果日報報導

挪威氣象研究所通報,位於北極圈內的斯瓦巴(Svalbard)群島氣溫25日創下歷史新高,來到攝氏21.7°C,當地每年此時氣溫通常都在攝氏5°C到8°C之間。根據科學研究,北極圈內全球暖化速度是地表其他地方的兩倍。

挪威氣候研究中心(Norwegian Centre for Climate Studies)去年2月表示,斯瓦巴群島從70年代初期開始,平均氣溫已躍升攝氏3到5°C,若全球溫室氣體排放量持續增加,預料到了2100年氣溫恐上升攝氏10°C。

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

以北極熊棲息地聞名的斯瓦巴群島上有一座煤礦場,開挖所有能源中最會助長全球暖化的煤礦,也有為了「世界末日」作準備的種子庫,從2008年開始收集並儲存世界上的農作物,以防全球發生大災難。

氣候變遷
國際新聞
挪威
北極圈
全球暖化

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

容器技術之Dockerfile(二)_貨運

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

  前文我們聊到了什麼是dockerfile,它的主要作用以及dockerfile的一些基本指令的使用方法,回顧請參考https://www.cnblogs.com/qiuhom-1874/p/13019411.html;今天我們在來聊一聊dockerfile餘下指令的用法和作用;

  1、RUN:該指令用於docker build 過程中運行的程序,可以是任何命令;語法格式RUN <command> 或RUN [“<executable>”, “<param1>”, “<param2>”];第一種格式中,<command>通常是一個shell命令,且以“/bin/sh -c”來運行它,這意味着此進程在容器中的PID不為1,不能接收Unix信號,因此,當使用docker stop <container>命令停止容器時,此進程接收不到SIGTERM信號; 第二種語法格式中的參數是一個JSON格式的數組,其中<executable>為要運行的命令,後面的<paramN>為傳遞給命令的選項或參數;然而,此種格式指定的命令不會以“/bin/sh -c”來發起,因此常見的shell操作如變量替換以及通配符(?,*等)替換將不會進行;不過,如果要運行的命令依賴於此shell特性的話,可以將其替換為 RUN [“/bin/sh”, “-c”, “<executable>”, “<param1>”]這樣的格式;注意:json數組中,要使用雙引號;

  示例:

[root@node1 test]# cat Dockerfile 
FROM centos:7 

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

ARG web_home

COPY html ${web_home:-"/data/htdoc/"}

VOLUME ${web_home:-"/data/htdoc/"}

EXPOSE 80/tcp 443/tcp

RUN mkdir -p /aaa/bbb/t{1..4}



[root@node1 test]# 

  提示:以上Dockerfile中,用RUN指令運行了mkdir命令,這種運行命令的方式在就可以利用shell的特性,如上大括號展開功能;

  驗證:build 該dockerfile后,運行該鏡像為容器,看看容器內部是否創建了/aaa/bbb/t1 t2 t3 t4?

[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
busybox             latest              1c35c4412082        16 hours ago        1.22MB
centos              7                   b5b4d78bc90c        4 weeks ago         203MB
[root@node1 test]# docker build . -t myimg:v1
Sending build context to Docker daemon   1.05MB
Step 1/9 : FROM centos:7
 ---> b5b4d78bc90c
Step 2/9 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Running in 64c792ce6750
Removing intermediate container 64c792ce6750
 ---> 604899ef29f9
Step 3/9 : LABEL version="1.0"
 ---> Running in 6a3f9b4a9058
Removing intermediate container 6a3f9b4a9058
 ---> d9edea71fa22
Step 4/9 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Running in b191ab5e19f9
Removing intermediate container b191ab5e19f9
 ---> ee027bbdc04b
Step 5/9 : ARG web_home
 ---> Running in a4c86febf616
Removing intermediate container a4c86febf616
 ---> 5b25bb7421dd
Step 6/9 : COPY html ${web_home:-"/data/htdoc/"}
 ---> 7c7a667149fa
Step 7/9 : VOLUME ${web_home:-"/data/htdoc/"}
 ---> Running in f9ec02d8f736
Removing intermediate container f9ec02d8f736
 ---> 86c7226f6b21
Step 8/9 : EXPOSE 80/tcp 443/tcp
 ---> Running in ad82d389ac25
Removing intermediate container ad82d389ac25
 ---> 28dadea40aff
Step 9/9 : RUN mkdir -p /aaa/bbb/t{1..4}
 ---> Running in 1013a212d3f2
Removing intermediate container 1013a212d3f2
 ---> 7f109a34a4a5
Successfully built 7f109a34a4a5
Successfully tagged myimg:v1
[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v1                  7f109a34a4a5        4 seconds ago       203MB
busybox             latest              1c35c4412082        16 hours ago        1.22MB
centos              7                   b5b4d78bc90c        4 weeks ago         203MB
[root@node1 test]# docker run --name test --rm -it myimg:v1 /bin/bash
[root@fc89ca934ed5 /]# ls /
aaa                bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
anaconda-post.log  data  etc  lib   media  opt  root  sbin  sys  usr
[root@fc89ca934ed5 /]# ls /aaa/
bbb
[root@fc89ca934ed5 /]# ls /aaa/bbb/
t1  t2  t3  t4
[root@fc89ca934ed5 /]# exit
exit
[root@node1 test]# 

  提示:底層基礎鏡像的shell如果不支持大括號展開,那麼我們基於這種鏡像做出來的鏡像運行以上命令也就不支持shell的大括號展開功能;

  示例:

[root@node1 test]# cat Dockerfile
FROM centos:7

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

ARG web_home

COPY html ${web_home:-"/data/htdoc/"}

VOLUME ${web_home:-"/data/htdoc/"}

EXPOSE 80/tcp 443/tcp

RUN mkdir -p /aaa/bbb/t{1..4}

RUN ["mkdir","-p","/ccc/ddd/f{1..4}"]

[root@node1 test]# 

  提示:以json數組格式的方式去運行命令,它默認是不支持shell的任何特性,這意味着運行該命令時,不是基於shell子進程的方式在執行命令,通常是內核直接執行了;所以上面的命令它會把大括號處理成字符,而不會展開;

  驗證:build成鏡像運行成容器,看看是否把大括號處理成字符了?

[root@node1 test]# docker build . -t myimg:v1.1
Sending build context to Docker daemon   1.05MB
Step 1/10 : FROM centos:7
 ---> b5b4d78bc90c
Step 2/10 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> 604899ef29f9
Step 3/10 : LABEL version="1.0"
 ---> Using cache
 ---> d9edea71fa22
Step 4/10 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> ee027bbdc04b
Step 5/10 : ARG web_home
 ---> Using cache
 ---> 5b25bb7421dd
Step 6/10 : COPY html ${web_home:-"/data/htdoc/"}
 ---> Using cache
 ---> 7c7a667149fa
Step 7/10 : VOLUME ${web_home:-"/data/htdoc/"}
 ---> Using cache
 ---> 86c7226f6b21
Step 8/10 : EXPOSE 80/tcp 443/tcp
 ---> Using cache
 ---> 28dadea40aff
Step 9/10 : RUN mkdir -p /aaa/bbb/t{1..4}
 ---> Using cache
 ---> 7f109a34a4a5
Step 10/10 : RUN ["mkdir","-p","/ccc/ddd/f{1..4}"]
 ---> Running in 9da1e6bab59f
Removing intermediate container 9da1e6bab59f
 ---> ae463ec8cbd9
Successfully built ae463ec8cbd9
Successfully tagged myimg:v1.1
[root@node1 test]# docker run --name test --rm -it myimg:v1.1 /bin/bash
[root@02ec6e404100 /]# ls /
aaa                bin  data  etc   lib    media  opt   root  sbin  sys  usr
anaconda-post.log  ccc  dev   home  lib64  mnt    proc  run   srv   tmp  var
[root@02ec6e404100 /]# ls /ccc/ddd/
f{1..4}
[root@02ec6e404100 /]# 

  提示:可以看到在/ccc/ddd/目錄下並沒有把大括號展開,而是直接把它當成了字符處理了;如果我們想要用json數組這種方式運行命令,又想讓使用shell特性,我們可以使用”/bin/sh -c”來明確聲明後面的命令用shell子進程的方式運行;如下所示

[root@node1 test]# cat Dockerfile
FROM centos:7

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

ARG web_home

COPY html ${web_home:-"/data/htdoc/"}

VOLUME ${web_home:-"/data/htdoc/"}

EXPOSE 80/tcp 443/tcp

RUN mkdir -p /aaa/bbb/t{1..4}

RUN ["/bin/bash","-c","mkdir -p /ccc/ddd/f{1..4}"]

[root@node1 test]# 

  提示:以上運行命令的方式就明確聲明使用shell子進程的方式運行命令;這裏需要注意一點的是,如果使用json數組的方式運行命令,後面真正執行的命令要一個整體當作參數傳給”/bin/bash”

  驗證:看看是否會把大括號展開?

[root@node1 test]# docker build . -t myimg:v1.2
Sending build context to Docker daemon   1.05MB
Step 1/10 : FROM centos:7
 ---> b5b4d78bc90c
Step 2/10 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> 604899ef29f9
Step 3/10 : LABEL version="1.0"
 ---> Using cache
 ---> d9edea71fa22
Step 4/10 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> ee027bbdc04b
Step 5/10 : ARG web_home
 ---> Using cache
 ---> 5b25bb7421dd
Step 6/10 : COPY html ${web_home:-"/data/htdoc/"}
 ---> Using cache
 ---> 7c7a667149fa
Step 7/10 : VOLUME ${web_home:-"/data/htdoc/"}
 ---> Using cache
 ---> 86c7226f6b21
Step 8/10 : EXPOSE 80/tcp 443/tcp
 ---> Using cache
 ---> 28dadea40aff
Step 9/10 : RUN mkdir -p /aaa/bbb/t{1..4}
 ---> Using cache
 ---> 7f109a34a4a5
Step 10/10 : RUN ["/bin/bash","-c","mkdir -p /ccc/ddd/f{1..4}"]
 ---> Running in a5785a139e1f
Removing intermediate container a5785a139e1f
 ---> 30a5f5594104
Successfully built 30a5f5594104
Successfully tagged myimg:v1.2
[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v1.2                30a5f5594104        5 seconds ago       203MB
myimg               v1.1                ae463ec8cbd9        9 minutes ago       203MB
myimg               v1                  7f109a34a4a5        21 minutes ago      203MB
busybox             latest              1c35c4412082        16 hours ago        1.22MB
centos              7                   b5b4d78bc90c        4 weeks ago         203MB
[root@node1 test]# docker run --name test --rm -it myimg:v1.2 /bin/bash
[root@549f875aa4de /]# ls /
aaa                bin  data  etc   lib    media  opt   root  sbin  sys  usr
anaconda-post.log  ccc  dev   home  lib64  mnt    proc  run   srv   tmp  var
[root@549f875aa4de /]# ls /ccc/ddd/
f1  f2  f3  f4
[root@549f875aa4de /]# 

  提示:可以看到用”/bin/bash -c” 是可以明確聲明後面的命令用shell子進程的方式運行,這樣一來就可以在後面的命令使用shell特性的語法;

  2、CMD:該指令類似於RUN指令,CMD指令也可用於運行任何命令或應用程序,不過,二者的運行時間點不同; RUN指令運行於映像文件構建過程中,而CMD指令運行於基於Dockerfile構建出的新映像文件啟動一個容器時; CMD指令的首要目的在於為啟動的容器指定默認要運行的程序,且其運行結束后,容器也將終止;不過,CMD指定的命令其可以被docker run的命令行選項所覆蓋;在Dockerfile中可以存在多個CMD指令,但僅最後一個會生效;語法格式 CMD <command> 或 CMD [“<executable>”, “<param1>”, “<param2>”] 或 CMD [“<param1>”,”<param2>”];前兩種語法格式的意義同RUN,第三種則用於為ENTRYPOINT指令提供默認參數;

  示例:

[root@node1 test]# cat Dockerfile
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

ARG web_home

COPY html ${web_home:-"/data/htdoc/"}

VOLUME ${web_home:-"/data/htdoc/"}

EXPOSE 80/tcp 443/tcp

CMD httpd -f -h /data/htdoc/
[root@node1 test]# 

  提示:docker容器內部運行的程序必須運行為前台;CMD是指定容器運行時要運行的命令;通常該命令或程序是以前台方式運行;如果不是前台運行,我們的容器就會存在一啟動就退出的情況;以上命令就表示前台運行httpd程序 並指定httpd 的工作目錄為${web_home}變量所指定的目錄;

  驗證:build后看看啟動為容器是否提供80訪問服務?

[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v1.2                30a5f5594104        23 minutes ago      203MB
myimg               v1.1                ae463ec8cbd9        32 minutes ago      203MB
myimg               v1                  7f109a34a4a5        44 minutes ago      203MB
busybox             latest              1c35c4412082        16 hours ago        1.22MB
centos              7                   b5b4d78bc90c        4 weeks ago         203MB
[root@node1 test]# docker build . -t myimg:v1.3
Sending build context to Docker daemon   1.05MB
Step 1/9 : FROM busybox:latest
 ---> 1c35c4412082
Step 2/9 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Running in deb5e54eef87
Removing intermediate container deb5e54eef87
 ---> baf170e0c586
Step 3/9 : LABEL version="1.0"
 ---> Running in 433669185e0d
Removing intermediate container 433669185e0d
 ---> d96fb4ae3d58
Step 4/9 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Running in b5da74e27c69
Removing intermediate container b5da74e27c69
 ---> 62372d19daf3
Step 5/9 : ARG web_home
 ---> Running in 3f65a67bb15a
Removing intermediate container 3f65a67bb15a
 ---> 1ce797c7cde0
Step 6/9 : COPY html ${web_home:-"/data/htdoc/"}
 ---> 15848dea21b9
Step 7/9 : VOLUME ${web_home:-"/data/htdoc/"}
 ---> Running in 868f4c10e00f
Removing intermediate container 868f4c10e00f
 ---> f3ec40d1cb5e
Step 8/9 : EXPOSE 80/tcp 443/tcp
 ---> Running in 7f72c2612e92
Removing intermediate container 7f72c2612e92
 ---> 5ccfc6d604cc
Step 9/9 : CMD httpd -f -h /data/htdoc/
 ---> Running in 95a4fd578821
Removing intermediate container 95a4fd578821
 ---> 2e296b4f4500
Successfully built 2e296b4f4500
Successfully tagged myimg:v1.3
[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v1.3                2e296b4f4500        3 seconds ago       1.22MB
myimg               v1.2                30a5f5594104        23 minutes ago      203MB
myimg               v1.1                ae463ec8cbd9        33 minutes ago      203MB
myimg               v1                  7f109a34a4a5        44 minutes ago      203MB
busybox             latest              1c35c4412082        16 hours ago        1.22MB
centos              7                   b5b4d78bc90c        4 weeks ago         203MB
[root@node1 test]# docker run --name b1 -d myimg:v1.3
c3514f782cffd8140aa7c612293029f4d0302e8d697887dfc2696eea44a31700
[root@node1 test]# docker ps 
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
c3514f782cff        myimg:v1.3          "/bin/sh -c 'httpd -…"   4 seconds ago       Up 3 seconds        80/tcp, 443/tcp     b1
[root@node1 test]# curl http://172.17.0.2/test1.html
this is test1 html
[root@node1 test]# 

  提示:可以看到httpd是可以正常提供服務的;從上面的信息我們也可以了解到運行容器后,它默認是把我們寫的命令當作shell子命令的方式在運行;

  示例:以json數組方式運行命令

[root@node1 test]# cat Dockerfile 
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

ARG web_home

COPY html ${web_home:-"/data/htdoc/"}

VOLUME ${web_home:-"/data/htdoc/"}

EXPOSE 80/tcp 443/tcp

CMD ["httpd","-f","-h","/data/htdoc/"]

[root@node1 test]# 

  提示:用json數組格式運行命令,需要把後面的每個選項當作參數傳給httpd;

  驗證:運行容器看看容器是否退出,是否能夠正常提供httpd服務?

[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v1.3                2e296b4f4500        24 minutes ago      1.22MB
myimg               v1.2                30a5f5594104        47 minutes ago      203MB
myimg               v1.1                ae463ec8cbd9        57 minutes ago      203MB
myimg               v1                  7f109a34a4a5        About an hour ago   203MB
busybox             latest              1c35c4412082        17 hours ago        1.22MB
centos              7                   b5b4d78bc90c        4 weeks ago         203MB
[root@node1 test]# docker build . -t myimg:v1.4
Sending build context to Docker daemon   1.05MB
Step 1/9 : FROM busybox:latest
 ---> 1c35c4412082
Step 2/9 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> baf170e0c586
Step 3/9 : LABEL version="1.0"
 ---> Using cache
 ---> d96fb4ae3d58
Step 4/9 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> 62372d19daf3
Step 5/9 : ARG web_home
 ---> Using cache
 ---> 1ce797c7cde0
Step 6/9 : COPY html ${web_home:-"/data/htdoc/"}
 ---> Using cache
 ---> 15848dea21b9
Step 7/9 : VOLUME ${web_home:-"/data/htdoc/"}
 ---> Using cache
 ---> f3ec40d1cb5e
Step 8/9 : EXPOSE 80/tcp 443/tcp
 ---> Using cache
 ---> 5ccfc6d604cc
Step 9/9 : CMD ["httpd","-f","-h","/data/htdoc/"]
 ---> Running in 5bebdabfe2b7
Removing intermediate container 5bebdabfe2b7
 ---> 58e3b4c40ae7
Successfully built 58e3b4c40ae7
Successfully tagged myimg:v1.4
[root@node1 test]# docker run --name b1 -d myimg:v1.4
a32a05033a6dcb735363906bfcd2b84cfb290ca1b60c17d3ac2f81cdeceee705
[root@node1 test]# docker ps 
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
a32a05033a6d        myimg:v1.4          "httpd -f -h /data/h…"   6 seconds ago       Up 5 seconds        80/tcp, 443/tcp     b1
[root@node1 test]# curl http://172.17.0.2/test1.html
this is test1 html
[root@node1 test]# 

  提示:可以看到httpd服務可以正常提供訪問,說明我們用json數組方式運行命令是正確的;總結一點,用CMD或RUN指令運行命令時,如果直接在CMD或RUN指令後面接命令,這種方式通常會被解釋為啟動一個shell子進程運行命令,RUN指令表現形式就是後面的命令可以使用shell特性的語法格式的命令,比如大括號展開等等;而CMD指令表現形式就是啟動為容器后,它默認會把我們指定運行的命令當作參數傳給“/bin/sh”來運行;CMD或RUN指令加中括號的形式就表示使用json數組格式方式運行命令;這種方式運行命令在CMD中表現形式是我們運行的命令的選項都要當作參數傳給該命令;RUN指令表現形式是不能使用shell特性的命令;如果非要使用shell特性的命令格式,我們需要把我們的命令當作參數傳給“/bin/sh”,當然前提是我們的基礎鏡像shell支持shell特性的語法;

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

  3、ENTRYPOINT:該指令類似CMD指令的功能,用於為容器指定默認運行程序,從而使得容器像是一個單獨的可執行程序;與CMD不同的是,由ENTRYPOINT啟動的程序不會被docker run命令行指定的參數所覆蓋,而且,這些命令行參數會被當作參數傳遞給ENTRYPOINT指定的程序(不過,docker run命令的–entrypoint選項的參數可覆蓋ENTRYPOINT指令指定的程序);語法格式 ENTRYPOINT <command>或 ENTRYPOINT [“<executable>”, “<param1>”, “<param2>”];docker run命令傳入的命令參數會覆蓋CMD指令的內容並且附加到ENTRYPOINT命令最後做為其參數使用;Dockerfile文件中也可以存在多個ENTRYPOINT指令,但僅有最後一個會生效;

  示例:

[root@node1 test]# cat Dockerfile
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

ARG web_home

COPY html ${web_home:-"/data/htdoc/"}

VOLUME ${web_home:-"/data/htdoc/"}

EXPOSE 80/tcp 443/tcp

ENTRYPOINT httpd -f -h /data/htdoc/
[root@node1 test]# 

  提示:以上dockerfile中用ENTRYPOINT 來指定容器默認運行程序,它和CMD不同的是,CMD指定運行的命令,我們可以使用docker run 命令加要運行的的命令替代容器里默認運行的命令,而ENTRYPOINT指定的命令我們是不可隨便替換的,如果要替換必須要使用–entrypoint選項來指定;

  驗證:build成鏡像,我們啟動為容器直接運行/bin/sh 看看是否可行?

[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v1.4                58e3b4c40ae7        23 minutes ago      1.22MB
myimg               v1.3                2e296b4f4500        47 minutes ago      1.22MB
myimg               v1.2                30a5f5594104        About an hour ago   203MB
myimg               v1.1                ae463ec8cbd9        About an hour ago   203MB
myimg               v1                  7f109a34a4a5        2 hours ago         203MB
busybox             latest              1c35c4412082        17 hours ago        1.22MB
centos              7                   b5b4d78bc90c        4 weeks ago         203MB
[root@node1 test]# docker build . -t myimg:v1.5
Sending build context to Docker daemon   1.05MB
Step 1/9 : FROM busybox:latest
 ---> 1c35c4412082
Step 2/9 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> baf170e0c586
Step 3/9 : LABEL version="1.0"
 ---> Using cache
 ---> d96fb4ae3d58
Step 4/9 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> 62372d19daf3
Step 5/9 : ARG web_home
 ---> Using cache
 ---> 1ce797c7cde0
Step 6/9 : COPY html ${web_home:-"/data/htdoc/"}
 ---> Using cache
 ---> 15848dea21b9
Step 7/9 : VOLUME ${web_home:-"/data/htdoc/"}
 ---> Using cache
 ---> f3ec40d1cb5e
Step 8/9 : EXPOSE 80/tcp 443/tcp
 ---> Using cache
 ---> 5ccfc6d604cc
Step 9/9 : ENTRYPOINT httpd -f -h /data/htdoc/
 ---> Running in de274d68686c
Removing intermediate container de274d68686c
 ---> 5825c2ec655f
Successfully built 5825c2ec655f
Successfully tagged myimg:v1.5
[root@node1 test]# docker run --name b1 --rm -it myimg:v1.5 /bin/sh

  提示:運行以上命令后,不會給我們一個shell終端,也不報錯;但是我們直接訪問httpd服務是可以正常訪問的;這意味我們用docker run 命令是不能替換我們用entrypoint指定指定的命令的;

  測試:用–entrypoint 選項來看看是否能夠覆蓋ENTRYPOINT指定所指定的命令程序?

[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v1.5                5825c2ec655f        12 minutes ago      1.22MB
myimg               v1.4                58e3b4c40ae7        35 minutes ago      1.22MB
myimg               v1.3                2e296b4f4500        About an hour ago   1.22MB
myimg               v1.2                30a5f5594104        About an hour ago   203MB
myimg               v1.1                ae463ec8cbd9        2 hours ago         203MB
myimg               v1                  7f109a34a4a5        2 hours ago         203MB
busybox             latest              1c35c4412082        17 hours ago        1.22MB
centos              7                   b5b4d78bc90c        4 weeks ago         203MB
[root@node1 test]# docker run --name b1 --rm -it --entrypoint "/bin/sh" myimg:v1.5
/ # ls
bin   data  dev   etc   home  proc  root  sys   tmp   usr   var
/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh
    7 root      0:00 ps
/ # 

  提示:可以看到使用docker run 必須要加–entrypoint 選項才可以覆蓋ENTRYPOINT指令指定的命令;

  示例:使用json數組格式來指定命令

[root@node1 test]# cat Dockerfile 
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

ARG web_home

COPY html ${web_home:-"/data/htdoc/"}

VOLUME ${web_home:-"/data/htdoc/"}

EXPOSE 80/tcp 443/tcp

ENTRYPOINT ["httpd","-f","-h","/data/htdoc/"]

[root@node1 test]# 

  提示:使用json數組格式來指定命令時,都需要將後面的選項和參數當作該命令的參數傳進去;

  測試:使用docker run 直接加命令 看看是否能夠覆蓋ENTRYPOINT指令指定的命令?

  提示:可以看到我們直接使用命令是無法覆蓋ENTRYPOINT指令說指定的命令;

  示例:

[root@node1 test]# cat Dockerfile 
FROM centos:7

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

RUN yum install -y httpd

EXPOSE 80/tcp 

ENTRYPOINT ["/usr/sbin/httpd","-DFOREGROUND"]

[root@node1 test]# 

  測試:用docker run 命令覆蓋ENTRYPOINT指定的默認命令,看看是否可行?

[root@node1 test]# docker build . -t myimg:v1.7
Sending build context to Docker daemon  1.051MB
Step 1/7 : FROM centos:7
 ---> b5b4d78bc90c
Step 2/7 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> 604899ef29f9
Step 3/7 : LABEL version="1.0"
 ---> Using cache
 ---> d9edea71fa22
Step 4/7 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> ee027bbdc04b
Step 5/7 : RUN yum install -y httpd
 ---> Running in 164240645e39
Loaded plugins: fastestmirror, ovl
Determining fastest mirrors
 * base: mirrors.aliyun.com
 * extras: mirrors.aliyun.com
 * updates: mirrors.aliyun.com
Resolving Dependencies
--> Running transaction check
---> Package httpd.x86_64 0:2.4.6-93.el7.centos will be installed
--> Processing Dependency: httpd-tools = 2.4.6-93.el7.centos for package: httpd-2.4.6-93.el7.centos.x86_64
--> Processing Dependency: system-logos >= 7.92.1-1 for package: httpd-2.4.6-93.el7.centos.x86_64
--> Processing Dependency: /etc/mime.types for package: httpd-2.4.6-93.el7.centos.x86_64
--> Processing Dependency: libaprutil-1.so.0()(64bit) for package: httpd-2.4.6-93.el7.centos.x86_64
--> Processing Dependency: libapr-1.so.0()(64bit) for package: httpd-2.4.6-93.el7.centos.x86_64
--> Running transaction check
---> Package apr.x86_64 0:1.4.8-5.el7 will be installed
---> Package apr-util.x86_64 0:1.5.2-6.el7 will be installed
---> Package centos-logos.noarch 0:70.0.6-3.el7.centos will be installed
---> Package httpd-tools.x86_64 0:2.4.6-93.el7.centos will be installed
---> Package mailcap.noarch 0:2.1.41-2.el7 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package             Arch          Version                    Repository   Size
================================================================================
Installing:
 httpd               x86_64        2.4.6-93.el7.centos        base        2.7 M
Installing for dependencies:
 apr                 x86_64        1.4.8-5.el7                base        103 k
 apr-util            x86_64        1.5.2-6.el7                base         92 k
 centos-logos        noarch        70.0.6-3.el7.centos        base         21 M
 httpd-tools         x86_64        2.4.6-93.el7.centos        base         92 k
 mailcap             noarch        2.1.41-2.el7               base         31 k

Transaction Summary
================================================================================
Install  1 Package (+5 Dependent packages)

Total download size: 24 M
Installed size: 32 M
Downloading packages:
warning: /var/cache/yum/x86_64/7/base/packages/apr-util-1.5.2-6.el7.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID f4a80eb5: NOKEY
Public key for apr-util-1.5.2-6.el7.x86_64.rpm is not installed
--------------------------------------------------------------------------------
Total                                              7.8 MB/s |  24 MB  00:03     
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Importing GPG key 0xF4A80EB5:
 Userid     : "CentOS-7 Key (CentOS 7 Official Signing Key) <security@centos.org>"
 Fingerprint: 6341 ab27 53d7 8a78 a7c2 7bb1 24c6 a8a7 f4a8 0eb5
 Package    : centos-release-7-8.2003.0.el7.centos.x86_64 (@CentOS)
 From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : apr-1.4.8-5.el7.x86_64                                       1/6 
  Installing : apr-util-1.5.2-6.el7.x86_64                                  2/6 
  Installing : httpd-tools-2.4.6-93.el7.centos.x86_64                       3/6 
  Installing : centos-logos-70.0.6-3.el7.centos.noarch                      4/6 
  Installing : mailcap-2.1.41-2.el7.noarch                                  5/6 
  Installing : httpd-2.4.6-93.el7.centos.x86_64                             6/6 
  Verifying  : mailcap-2.1.41-2.el7.noarch                                  1/6 
  Verifying  : apr-util-1.5.2-6.el7.x86_64                                  2/6 
  Verifying  : httpd-2.4.6-93.el7.centos.x86_64                             3/6 
  Verifying  : apr-1.4.8-5.el7.x86_64                                       4/6 
  Verifying  : httpd-tools-2.4.6-93.el7.centos.x86_64                       5/6 
  Verifying  : centos-logos-70.0.6-3.el7.centos.noarch                      6/6 

Installed:
  httpd.x86_64 0:2.4.6-93.el7.centos                                            

Dependency Installed:
  apr.x86_64 0:1.4.8-5.el7                                                      
  apr-util.x86_64 0:1.5.2-6.el7                                                 
  centos-logos.noarch 0:70.0.6-3.el7.centos                                     
  httpd-tools.x86_64 0:2.4.6-93.el7.centos                                      
  mailcap.noarch 0:2.1.41-2.el7                                                 

Complete!
Removing intermediate container 164240645e39
 ---> 63db91f4fe6a
Step 6/7 : EXPOSE 80/tcp
 ---> Running in 6585da71fc3b
Removing intermediate container 6585da71fc3b
 ---> eb671cf67f52
Step 7/7 : ENTRYPOINT ["/usr/sbin/httpd","-DFOREGROUND"]
 ---> Running in f6e7297025af
Removing intermediate container f6e7297025af
 ---> bac03b20761a
Successfully built bac03b20761a
Successfully tagged myimg:v1.7
[root@node1 test]# docker run --name m1 --rm -it myimg:v1.7 /bin/sh
Usage: /usr/sbin/httpd [-D name] [-d directory] [-f file]
                       [-C "directive"] [-c "directive"]
                       [-k start|restart|graceful|graceful-stop|stop]
                       [-v] [-V] [-h] [-l] [-L] [-t] [-T] [-S] [-X]
Options:
  -D name            : define a name for use in <IfDefine name> directives
  -d directory       : specify an alternate initial ServerRoot
  -f file            : specify an alternate ServerConfigFile
  -C "directive"     : process directive before reading config files
  -c "directive"     : process directive after reading config files
  -e level           : show startup errors of level (see LogLevel)
  -E file            : log startup errors to file
  -v                 : show version number
  -V                 : show compile settings
  -h                 : list available command line options (this page)
  -l                 : list compiled in modules
  -L                 : list available configuration directives
  -t -D DUMP_VHOSTS  : show parsed vhost settings
  -t -D DUMP_RUN_CFG : show parsed run settings
  -S                 : a synonym for -t -D DUMP_VHOSTS -D DUMP_RUN_CFG
  -t -D DUMP_MODULES : show all loaded modules 
  -M                 : a synonym for -t -D DUMP_MODULES
  -t                 : run syntax check for config files
  -T                 : start without DocumentRoot(s) check
  -X                 : debug mode (only one worker, do not detach)
[root@node1 test]# 

  提示:可以看到我們用docker run指定命令去覆蓋ENTRYPOINT指令指定的命令,它給我們打印了httpd命令的用法,這說明我們後面傳遞的/bin/sh 當作參數傳遞給ENTRYPOINT說指定的命令;這裏還需要說一下,上面的示例用docker run 去覆蓋ENTRYPOINT指令指定的命令,沒有報錯的原因應該是busybox里的httpd程序支持傳遞/bin/sh當作參數;

  示例:CMD指令同ENTRYPOINT一起使用

[root@node1 test]# cat Dockerfile 
FROM centos:7

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

RUN yum install -y httpd

ADD entrypoint.sh /bin/

EXPOSE 80/tcp 

CMD ["/usr/sbin/httpd","-DFOREGROUND"]

ENTRYPOINT ["/bin/entrypoint.sh"]

[root@node1 test]# 

  提示:以上dockerfile使用了CMD和ENTRYPOINT指令來指定容器默認運行程序;此時CMD所指定的命令默認會以參數的形式傳給ENTRYPOINT指令所指定的命令;而上面ENTRYPOINT指定指定的是一個腳本,也就說上面dockerfile最終運行的命令是/bin/entrypoint.sh /usr/sbin/httpd -DFOREGROUND;這裏的腳本就相當於中間層,通過腳本設定一些參數,然後把CMD指定的命令當作參數傳給腳本,最終腳本運行起來;

  entrypoint腳本

[root@node1 test]# ll
total 1032
-rw-r--r-- 1 root root     307 Jun  3 11:28 Dockerfile
-rwxr-xr-x 1 root root     300 Jun  3 11:22 entrypoint.sh
drwxr-xr-x 2 root root      42 May 31 01:51 html
-rw-r--r-- 1 root root 1043748 May 26 11:07 nginx-1.19.0.tar.gz
-rw-r--r-- 1 root root      22 May 31 01:52 test.html
[root@node1 test]# cat entrypoint.sh 
#!/bin/bash

doc_root=${DOC_ROOT:-/var/www/html}
cat > /etc/httpd/conf.d/myweb.conf <<EOF
        <virtualhost *:80>
                servername "localhost"
                documentroot "${doc_root}"
                <directory "${doc_root}">
                        options none
                        allowoverride none
                        require  all granted
                </directory>
        </virtualhost>
EOF

exec "$@"
[root@node1 test]# 

  提示:這個腳本很簡單就是在/etc/httpd/conf.d/生成一個myweb.conf的配置文件,然後最後引用腳本的參數運行;exec “$@” 表示把腳本的所有參數獨立運行成一個守護進程;默認不使用exec就表示以shell子進程的方式運行,exec就表示運行為單獨的守護進程,不再是shell子進程的方式;

  測試:

[root@node1 test]# docker build . -t httpd:v1
Sending build context to Docker daemon  1.051MB
Step 1/9 : FROM centos:7
 ---> b5b4d78bc90c
Step 2/9 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> 604899ef29f9
Step 3/9 : LABEL version="1.0"
 ---> Using cache
 ---> d9edea71fa22
Step 4/9 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> ee027bbdc04b
Step 5/9 : RUN yum install -y httpd
 ---> Using cache
 ---> 63db91f4fe6a
Step 6/9 : ADD entrypoint.sh /bin/
 ---> 49d1270c3aa3
Step 7/9 : EXPOSE 80/tcp
 ---> Running in 3dacf6acf23b
Removing intermediate container 3dacf6acf23b
 ---> edced77af5b5
Step 8/9 : CMD ["/usr/sbin/httpd","-DFOREGROUND"]
 ---> Running in 23bb32def296
Removing intermediate container 23bb32def296
 ---> 169a5e164ba5
Step 9/9 : ENTRYPOINT ["/bin/entrypoint.sh"]
 ---> Running in f3bf0c267c7b
Removing intermediate container f3bf0c267c7b
 ---> 0801db092665
Successfully built 0801db092665
Successfully tagged httpd:v1
[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
httpd               v1                  0801db092665        35 seconds ago      307MB
myimg               v1.7                bac03b20761a        12 minutes ago      307MB
myimg               v1.6                5370df4238eb        2 hours ago         1.22MB
myimg               v1.5                5825c2ec655f        2 hours ago         1.22MB
myimg               v1.4                58e3b4c40ae7        2 hours ago         1.22MB
myimg               v1.3                2e296b4f4500        3 hours ago         1.22MB
myimg               v1.2                30a5f5594104        3 hours ago         203MB
myimg               v1.1                ae463ec8cbd9        3 hours ago         203MB
myimg               v1                  7f109a34a4a5        3 hours ago         203MB
busybox             latest              1c35c4412082        19 hours ago        1.22MB
centos              7                   b5b4d78bc90c        4 weeks ago         203MB
[root@node1 test]# docker run --name h1 -d httpd:v1
cee14b04912822c33e7deeee361e1ce0c20d7daf6c0666bff319bf3f1bc69bdc
[root@node1 test]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
cee14b049128        httpd:v1            "/bin/entrypoint.sh …"   9 seconds ago       Up 9 seconds        80/tcp              h1
[root@node1 test]# 

  提示:可以看到我們build成鏡像后,直接運行為容器,容器正常;我們進入容器內部看看它到底運行的說明命令

[root@node1 test]# docker exec -it h1 /bin/bash
[root@cee14b049128 /]# ls /etc/httpd/conf.d/myweb.conf 
/etc/httpd/conf.d/myweb.conf
[root@cee14b049128 /]# cat /etc/httpd/conf.d/myweb.conf
        <virtualhost *:80>
                servername "localhost"
                documentroot "/var/www/html"
                <directory "/var/www/html">
                        options none
                        allowoverride none
                        require  all granted
                </directory>
        </virtualhost>
[root@cee14b049128 /]# ps aux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.1 224080  5016 ?        Ss   16:26   0:00 /usr/sbin/httpd -D
apache        7  0.0  0.0 224212  2960 ?        S    16:26   0:00 /usr/sbin/httpd -D
apache        8  0.0  0.0 224212  2960 ?        S    16:26   0:00 /usr/sbin/httpd -D
apache        9  0.0  0.0 224212  2960 ?        S    16:26   0:00 /usr/sbin/httpd -D
apache       10  0.0  0.0 224212  2960 ?        S    16:26   0:00 /usr/sbin/httpd -D
apache       11  0.0  0.0 224212  2960 ?        S    16:26   0:00 /usr/sbin/httpd -D
root         12  0.0  0.0  11828  1932 pts/0    Ss   16:35   0:00 /bin/bash
root         27  0.0  0.0  51756  1720 pts/0    R+   16:36   0:00 ps aux
[root@cee14b049128 /]# httpd -t -D DUMP_VHOSTS
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
VirtualHost configuration:
*:80                   localhost (/etc/httpd/conf.d/myweb.conf:1)
[root@cee14b049128 /]# 

  提示:可以看到容器內部運行的就是/usr/sbin/httpd -DFOREGROUND這個命令;其實這個命令不是CMD直接運行的命令,而是通過腳本獲取參數而來的;我們通過腳本添加的配置文件都在對應的位置,並且也都生效了;總結一點,通常CMD和ENTRYPOINT應用在通過entrypoint腳本做中間層向容器內運行的程序提供配置文件的場景,通常這些應用程序不是雲原生的;

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

微軟黑科技幫你跟亡者聊天?新專利將能讓AI 學會已故親人的說話口氣與外貌_貨運

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

有句話這麼說:「世界上最遙遠的距離,不是生與死,而是我就站在你面前,你卻不知道我愛你」。然而,生離死別的痛苦仍然不亞於「你卻不知道我愛你」的痛苦。不論是親人還是愛人,死亡強硬的切開了彼此之間的距離與情感。台灣民間信仰中,有一種請所謂的仙姑、靈姑尋找已逝者的亡魂,並藉由上身這個儀式來讓亡魂藉由仙姑,與在世的親人交談的特殊宗教服務。有意思的是,許多科技公司也正在試著讓 AI 也能達到類似的效果。不過他們並非真的請亡魂來上「電腦」的身。以微軟最近的專利來說,就是利用 AI 去學習已故親人說話語氣、用詞用語等資料,並透過分析親人的肖像來製作 3D 模型。如此一來,一個能在電腦中與人對話,又有著與親人相似外貌的聊天機器人就誕生了:

▲微軟的專利居然有與亡者交談的部分,真的是太奇葩了(圖片來源)

微軟不是第一家想做「亡者聊天機器人」的公司,但不過在微軟之前提出類似想法的公司規模都相對較小,因此當微軟這方面的專利技術被嗅覺靈敏的媒體挖掘出來時,才讓人發現原來軟體巨頭不光只是想做個像 Cortana 那樣的語音助理而已。憑微軟的規模與財力,有心要做的話理論上是很容易實現的。

讓 AI 學習一切親人的相關資料,就能真的「仿生」他的音容笑貌

言歸正傳,這項專利是由微軟公司的 Dustin Abramson 和 Joseph Johnson jr. 兩位研究者於 2017 年註冊的。根據專利的敘述,他們將設計一個可以學習亡者生前所發表的社交貼文、語音資料、電子資料與個人圖像等資料的 AI,這個 AI 將能依照資料的完整程度,模仿出一個幾乎接近亡者在生時的模樣、語氣的聊天機器人。根據技術的成熟度,可能達到旁人不覺得說話的是個機器的程度:

▲這設定其實很多科幻作品都有類似的橋段,例如漫畫作品「人形電腦天使心(Chobits)」中,就有為了思念親人而將人形電腦製作成親人面貌的設定(圖片來源)

這個技術或許讓人想起很多科幻影劇或小說之類的內容,畢竟類似的題材相信好萊塢拍得夠多了。如果按照這個專利來看,人與生化人之間的差異性或許會進一步的縮小到難以察覺差異的程度。不過在這個技術正式實體化前,或許這些論調都只是杞人憂天:

▲即便是電腦模擬出來的,能跟已故的親人筆談一下也能夠撫慰人心(圖片來源)

▲肯伊威斯特曾在去年買下岳父生前樣貌的全息投影作為生日禮物送給妻子金卡戴珊,這或許是這項專利可能的呈現形式(圖片翻攝自 Twitter)

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

對於一些平常會找仙姑、巫醫之類的民俗宗教人士來跟陰間親人溝通或問些事情的人來說,這項技術或許就是用現代科學創造出一個思想邏輯上幾乎與本人差不多的生化人。這種生化人不太可能有靈魂上身,但一樣可以對它「問事」,某種意義上甚至可以當作當事人思想的一種參考。

消息來源:Gizmodo、USPTO

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

svg高級應用及動畫_貨運

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

canvas 和 webGL 這兩項圖形技術結合 css3 可以說能完成絕大部分的動畫和需求。但 canvas 和 webGL 畢竟是偏向底層的繪製引擎,某些場景使用起來還是過於繁瑣的,不分場合一律使用鎚子解決的行為不值得提倡。svg 在解決排版,圖標,相關動畫還是非常高效的,而且 svg 還是矢量圖形,高清還原各種屏幕尺寸的設計簡直就是神器。

svg 基礎知識不再講解,結合svg特點和應用場景,主要介紹如下幾方面的內容

  • svg sprite
  • css background-image 插入svg
  • 文字路徑
  • 路徑動畫

SVG sprite

SVG sprite 之於 font-face 的優勢就是矢量圖的抗鋸齒效果,再也不需要為適配高清屏而操心。使用方式就是 svg 標籤結合 symbol元素即可,其中 svg 是容器,而symbol 則是組件模板。怎麼把svg組件显示出來呢?使用 use 元素指向組件的id即可,如下所示:

<svg>
    <symbol id="ok">
        <!-- svg元素或組合 -->
    </symbol>
    <!-- ... -->
</svg>

<use href="#ok"/>

當然網上有很多工具可以幫我們把圖標自動sprite,比如:iconfot,gulp-svg-symbols

在css中插入svg

SVG 添加到網頁中有多種方法,最常見的是:

  1. 在 HTML 中內聯
  2. objectiframeembed 標籤插入
  3. img 標籤
  4. CSS background-image 屬性

html 內聯方式

<svg>
  <title>line</title>
  <line x1="20" y1="20" x2="100" y2="100">
</svg>

使用 object、iframe 或 embed 標籤插入

<object data="flag.svg" type="image/svg+xml"></object>
<iframe src="flag.svg" frameborder="0"></iframe>
<embed src="flag.svg" type="" />

使用img標籤插入

<img src="flag.svg"/>

現在重點介紹一下使用 css background屬性插入的方式;既可以使用圖片路徑也可以使用 base64 格式的data URI 方式

.svg-bg {
  background-image: url("./bg.svg");
}
.svg-background {
  background-image: url("data:image/svg+xml;<DATA>");
}

我認為使用 background base64 引入svg是最具靈活性的一種方式,還可以加入svg動畫,不需要額外加載圖標,只需引入css即可。同時跟普通圖片相比它又有着抗鋸齒的優勢,因此元素尺寸隨意調整不擔心失真。比如下面使用background data URI 引入的加載動畫css:

<style>
.loading{
  width: 50px;
  height: 50px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='80'%3E%3Cpath d='M10 40a30 30 0 1130 30' stroke-width='4' stroke='hsl(220,90%,60%)' fill='none'%3E%3CanimateTransform attributeName='transform' begin='0s' dur='1.4s' type='rotate' from='0 40 40' to='360 40 40' repeatCount='indefinite'/%3E%3C/path%3E%3C/svg%3E")
  no-repeat center / cover;
}
</style>

<body>
<div class="loading"></div>
</body>

文字路徑

svg 除了繪製圖形,插入文字也是很方便的,方式也簡單,使用 text 元素包裹文本內容即可

<text fill="hsla(260,80%,50%,1)" x="10" y="40" font-size="30"> I Love SVG </text>

還有一種很實用的效果就是文字路徑,也就是文字按照定義好的 path 進行排列显示,這需要使用到另一個標籤 textPath。實現方式:首先定義一個path元素(這裏使用三次貝塞爾曲線),然後 textPath元素使用 xlink:href 屬性引入path,最後再用 text標籤包裹。

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

<g>
  <title> 文字路徑 </title>
  <path id="path" d="M 10 100 C 100 0 150 300 250 100" fill="none" stroke="#555"/>
  <text x="10" y="100" stroke="hsl(280,100%,50%)">
    <textPath xlink:href="#path">I Love SVG I Love SVG I Love SVG</textPath>
  </text>
</g>

文字路徑 I Love SVG I Love SVG I Love SVG I Love SVG

svg路徑動畫

svg動畫既可以使用 css3 相關屬性實現,也可以使用自己獨有的SMIL實現。SMIL主要用以下標籤:

  • animate:基礎動畫元素,實現單屬性的動畫過渡效果

  • animateTransform: 實現transform變換動畫效果

  • animateMotion:路徑動畫效果

這裏只介紹路徑動畫,基礎動畫和變換動畫與css動畫相似,就不再介紹。來看一個基於 css3 的 animation 實現百分比加載的動畫。這裏用到了兩個關鍵屬性:

  • stroke-dasharray:設置線條斷開為虛線,數值越大,線就越長

  • stroke-dashoffset:設置線條的偏移,設置后,線段就會偏移相應的值,實現線條動畫只要動態改變這個偏移值就好

首先需要通過js獲取路徑的總長度

//獲取路徑長度
const arc = document.getElementById('circle');
console.log(arc.getTotalLength()); //250.92141723632812

接着編寫svg相關

<style>
  circle {
    fill: transparent;
    stroke-width: 10px;
  }
  #circle {
    stroke-dasharray: 250.921;
    stroke-dashoffset: 250.921;
    animation: round 3s linear infinite;
  }
  @keyframes round {
    to {
      stroke-dashoffset: 0;
    }
  }
</style>
<g>
  <circle cx="100" cy="60" r="40" stroke="#ccc" />
  <circle id="circle" cx="100" cy="60" r="40" stroke="hsl(160,80%,50%)" />
</g>

當然其他stroke線條動畫也類似

Click Me

接着我們再基於svg animateMotion標籤來實現path路徑動畫,其中path是運動路徑,dur 是持續時間,repeatCount設置是否循環

<g id="pathAnimate">
  <title> 路徑動畫 </title>
  <path stroke="hsl(0,0%,60%)" d="M 10 300 Q 150 100 300 300 T 490 300Z" stroke-width="2" fill="none"/>
  <circle cx="0" cy="0" r="8" stroke-width="1" stroke="hsl(300,10%,40%)" fill="hsl(300,100%,50%)">
    <animateMotion path="M 10 300 Q 150 100 300 300 T 500 300Z" dur="4s" repeatCount="indefinite"/>
  </circle>
</g>

路徑動畫

使用svg實現路徑動畫真是性價比超高。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

不怕去探索,Apple 新春微電影《阿年》來了_貨運

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

雖然核心還是以「用 iPhone 拍攝」來展現蘋果智慧型手機拍攝的威能,但每次農曆春節觀看蘋果的微電影,好像也多少變成了一種習慣。今年,隨著全面升級到杜比視界的 iPhone 12 系列推出,Apple 也正式拿出了號稱「用 iPhone 12 Pro Max 重新定義電影拍攝」的新作品。除了讓人開始感受到年味以外,片中更包含了不少相當引人省思的意涵。繼續閱讀不怕去探索,Apple 新春微電影《阿年》來了報導內文。

▲ 圖片來源:Apple

不怕去探索,Apple 新春微電影《阿年》來了

即將迎接農曆新年,蘋果準備的不僅是有著超應景鐫刻的限量 AirPods Pro,更帶來了由 2020 金球獎提名最佳外語片《別告訴她》導演王子逸,與好萊塢原班製作團隊和 Apple 打造的奇幻賀歲片《阿年》。

想看的朋友可直接前往傳送門觀看(建議使用 Safari 瀏覽器打開,或是點入官網首頁,更能觀看到幕後花絮的片段)。

這部靈感源自於中國傳統年獸而來的故事,最令小編印象深刻的,不僅是片中小女孩天真蹦出口中的「為什麼我們要怕年獸?」,這樣讓人反思看待事物觀點的話語。更多是在於表情活靈活現的年獸本人(本獸?)的演出 — 原本還以為是動畫!

除了新年的主題外,這支標榜用 iPhone 12 Pro Max 所拍攝的影片,自然也因為今年升級到了搭載杜比視界等各項強大新規格的幫助下,使整體微電影的畫質、穩定度還有豐富的色彩表現都又有升級。

雖說影片有標示了此片有使用額外器材與應用,不過《阿年》拍攝的場景巧思也非常之多。包括父母威嚇小孩與爭吵的房間場景,就有點類似皮影戲感覺。也充分展現以強大運算式攝影為基礎的新世代 iPhone,在動態範圍方面所擁有的強大拍攝表現。

此外,拍攝團隊更充分利用行動攝影所帶來的輕巧特性,不僅可以將 iPhone 放在身上(甚至是嘴裡,嗯… 年獸的嘴裡)直接拍攝特殊鏡位。甚至在拍攝大自然翻滾玩樂畫面時,還很有創意地請出了輪椅,並將 iPhone 固定在輪軸來達到同步旋轉的拍攝手法。

就此片攝影指導所分享的心得,他認為 iPhone 12 Pro Max 的防震功能解鎖了更多攝影技術的可能性,而杜比視界則帶給他極大的震撼。導演王子逸則是希望通過這部影片來鼓勵觀眾,「只要想去拍就可以用 iPhone 去拍,無需多想什麼。」

這樣的精神,不僅在劇情中充分傳達到我們心中,透過手上的行動裝置就可以拍出這樣等級的作品,其實也真的可以激勵不少創作者。

畢竟,就連年獸都懂得拍 Vlog 了,你還有什麼理由不拿起手邊的工具開始創作呢(咦!)。

延伸閱讀:

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

用 iPhone 突破創作界線 ,《怪胎》導演廖明毅分享新電影創作心得

iPhone 破十億活躍用戶,還刷了一波史上最大升級潮紀錄

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

Spring Data JPA入門及深入_網頁設計公司

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

一:Spring Data JPA簡介

  Spring Data JPA 是 Spring 基於 ORM 框架、JPA 規範的基礎上封裝的一套JPA應用框架,可使開發者用極簡的代碼即可實現對數據庫的訪問和操作。它提供了包括增刪改查等在內的常用功能,且易於擴展!學習並使用 Spring Data JPA 可以極大提高開發效率!

  Spring Data JPA 讓我們解脫了DAO層的操作,基本上所有CRUD都可以依賴於它來實現,在實際的工作工程中,推薦使用Spring Data JPA + ORM(如:hibernate)完成操作,這樣在切換不同的ORM框架時提供了極大的方便,同時也使數據庫層操作更加簡單,方便解耦

1:Spring Data JPA與JPA和hibernate三者關係

  我在接下面的文章中都對它們三者進行擴展及應用,以及三者的封裝關係及調用關係,我下面也會以一張圖說明,如果此時有對JPA還一竅不通的可以參考我之前的一篇關於JPA文章的介紹

  關係:其實看三者框架中,JPA只是一種規範,內部都是由接口和抽象類構建的;hibernate它是我們最初使用的一套由ORM思想構建的成熟框架,但是這個框架內部又實現了一套JPA的規範(實現了JPA規範定義的接口),所有也可以稱hibernate為JPA的一種實現方式我們使用JPA的API編程,意味着站在更高的角度上看待問題(面向接口編程);Spring Data JPA它是Spring家族提供的,對JPA規範有一套更高級的封裝,是在JPA規範下專門用來進行數據持久化的解決方案。

   其實規範是死的,但是實現廠商是有很多的,這裏我對hibernate的實現商介紹,如其它的實現廠商大家可以自行去理解,因為規範在這,實現類可以更好別的,面向接口編程。

二:SpringDataJPA快速入門(完成簡單CRUD)

1:環境搭建及簡單查詢

-- 刪除庫
-- drop database demo_jpa;
-- 創建庫
create database if not exists demo_jpa charset gbk collate gbk_chinese_ci;
-- 使用庫
use demo_jpa;
-- 創建表
create table if not exists student(
    sid int primary key auto_increment,     -- 主鍵id
    sname varchar(10) not null,             -- 姓名
    sage tinyint unsigned default 22,        -- 年齡
    smoney decimal(6,1),                    -- 零花錢
    saddress varchar(20)                    -- 住址
)charset gbk collate gbk_chinese_ci;
insert into student values
(1,"螞蟻小哥",23,8888.8,"安徽大別山"),
(2,"王二麻",22,7777.8,"安徽大別山"),
(3,"李小二",23,6666.8,"安徽大別山"),
(4,"霍元甲",23,5555.8,null),
(5,"恭弘=叶 恭弘問",22,4444.8,"安徽大別山"),
(6,"李連傑",23,3333.8,"安徽大別山"),
(7,"馬克思",20,2222.8,"安徽大別山");

MySQL簡單建表語句   重要【後面都參照這個建表語句進行】

<dependencies>
        <!--單元測試坐標  4.12為最穩定-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <!--Spring核心坐標  注:導入此坐標也同時依賴導入了一些其它jar包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!--Spring對事務管理坐標-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!--Spring整合ORM框架的必須坐標 如工廠/事務等交由Spring管理-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!--Spring單元測試坐標-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!--Spring Data JPA 核心坐標-->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>1.10.4.RELEASE</version>
        </dependency>
        <!--導入AOP切入點表達式解析包-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>

        <!--Hibernate核心坐標-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.4.10.Final</version>
        </dependency>
        <!--hibernate對持久層的操作坐標-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.4.10.Final</version>
        </dependency>

        <!--這下面的2個el坐標是使用Spring data jpa 必須導入的,不導入則報el異常-->
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>2.2.4</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>javax.el</artifactId>
            <version>2.2.4</version>
        </dependency>

        <!--C3P0連接池坐標-->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>

        <!--MySQL驅動坐標-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.32</version>
        </dependency>

        <!--JAXB API是java EE 的API,因此在java SE 9.0 中不再包含這個 Jar 包。
            java 9 中引入了模塊的概念,默認情況下,Java SE中將不再包含java EE 的Jar包
            而在 java 6/7 / 8 時關於這個API 都是捆綁在一起的
            拋出:java.lang.ClassNotFoundException: javax.xml.bind.JAXBException異常加下面4個坐標
            -->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
    </dependencies>

pom.xml坐標導入

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/data/jpa
        http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

    <!--配置註解掃描-->
    <context:component-scan base-package="cn.xw"></context:component-scan>

    <!--配置C3P0連接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/demo_jpa"></property>
        <property name="user" value="root"></property>
        <property name="password" value="123"></property>
    </bean>

    <!--創建EntityManagerFactory交給Spring管理,讓Spring生成EntityManager實體管理器操作JDBC-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <!--配置一個連接池,後期獲取連接的Connection連接對象-->
        <property name="dataSource" ref="dataSource"></property>
        <!--配置要掃描的包,因為ORM操作是基於實體類的-->
        <property name="packagesToScan" value="cn.xw.domain"></property>
        <!--配置JPA的實現廠家 實現了JPA的一系列規範-->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"></bean>
        </property>
        <!--JPA供應商適配器 因為我上面使用的是hibernate,所有適配器也選擇hibernate-->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!--指定當前操作的數據庫類型 必須大寫,底層是一個枚舉類-->
                <property name="database" value="MYSQL"></property>
                <!--是否自動創建數據庫表-->
                <property name="generateDdl" value="false"></property>
                <!--是否在運行的時候 在控制台打印操作的SQL語句-->
                <property name="showSql" value="true"></property>
                <!--指定數據庫方言:支持的語法,如Oracle和Mysql語法略有不同 org.hibernate.dialect下面的類就是支持的語法-->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"></property>
                <!--設置是否準備事務休眠會話的底層JDBC連接,即是否將特定於事務的隔離級別和/或事務的只讀標誌應用於底層JDBC連接。-->
                <property name="prepareConnection" value="false"></property>
            </bean>
        </property>
        <!--JPA方言:高級特性 我下面配置了hibernate對JPA的高級特性 如一級/二級緩存等功能-->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean>
        </property>
        <!--注入JPA的配置信息
            加載JPA的基本配置信息和JPA的實現方式(hibernate)的配置信息
            hibernate.hbm2ddl.auto:自動創建數據庫表
                create:每次讀取配置文件都會創建數據庫表
                update:有則不做操作,沒有則創建數據庫表 -->
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

    <!--配置事務管理器  不同的事務管理器有不同的類 如我們當初使用這個事務管理器DataSourceTransactionManager-->
    <bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <!--把配置好的EntityManagerFactory對象交由Spring內部的事務管理器-->
        <property name="entityManagerFactory" ref="entityManagerFactory"></property>
        <!--因為entityManagerFactory內部設置數據庫連接了  所有後面不用設置-->
        <!--<property name="dataSource" ref="dataSource"></property>-->
    </bean>

    <!--配置tx事務-->
    <tx:advice id="txAdvice" transaction-manager="jpaTransactionManager">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="insert*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="update*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="delete*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
            <!--如果命名規範直接使用下面2行控制事務-->
            <!--<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>-->
            <!--<tx:method name="*" propagation="REQUIRED" read-only="false"/>-->
        </tx:attributes>
    </tx:advice>

    <!--配置AOP切面-->
    <aop:config>
        <!--在日常業務中配置事務處理的都是Service層,因為這裡是案例講解,所有我直接在測試類進行
            所有我把事務配置這,也方便後期拷貝配置到真項目中-->
        <aop:pointcut id="pt1" expression="execution(* cn.xw.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>

    <!--整合SpringDataJPA-->
    <!--base-package:指定持久層接口
        entity-manager-factory-ref:引用其實體管理器工廠
        transaction-manager-ref:引用事務    -->
    <jpa:repositories base-package="cn.xw.dao" entity-manager-factory-ref="entityManagerFactory"
                      transaction-manager-ref="jpaTransactionManager"></jpa:repositories>
</beans>

applicationContext.xml 配置文件(重要)

@Entity
@Table(name = "student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "sid")
    private Integer id;
    @Column(name = "sname")
    private String name;
    @Column(name = "sage")
    private Integer age;
    @Column(name = "smoney")
    private Double money;
    @Column(name = "saddress")
    private String address;
    //下面get/set/構造/toString都省略
    //注意:我上面的都使用包裝類,切記要使用包裝類,
    // 原因可能數據庫某個字段查詢出來的值為空 null    
}

Student實體類及映射關係

//@Repository("studentDao") 這裏不用加入IOC容器 Spring默認幫我們注入
public interface StudentDao extends JpaRepository<Student, Integer>, JpaSpecificationExecutor<Student> {
    /*
    JpaRepository<T,ID>:T:當前表的類型  ID:當前表主鍵字段類型 
        功能:用來完成基本的CRUD操作 ,因為內部定義了很多增刪改查操作
    JpaSpecificationExecutor<T>:T:當前表的類型
        功能:用來完成複雜的查詢等一些操作
    */
}

StudentDao接口

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {
    //注入數據
    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;

    @Test
    public void test() {
        //查詢id為4學生
        Student student = sd.findOne(4);
        System.out.println("開始打印:" + student);
        //開始打印:Student{id=4, name='霍元甲', age=23, money=5555.8, address='null'}
    }
}

測試類

2:SpringDataJPA簡單單表接口方法查詢圖 

3:針對上面圖的一些方法示例

  在針對上面方法進行操作的時候,我們的Dao接口必須繼承JpaRepository<T,ID>和JpaSpecificationExecutor<T>(後面複雜查詢使用),大家也可以去研究一下CrudRepository類,這個類在這裏就不說了,JpaRespository類間接實現了它

①:Repository:最頂層的接口,是一個空的接口,目的是為了統一所有Repository的類型,且能讓組件掃描的時候自動識別。
②:CrudRepository :是Repository的子接口,提供CRUD的功能
③:PagingAndSortingRepository:是CrudRepository的子接口,添加分頁和排序的功能
④:JpaRepository:是PagingAndSortingRepository的子接口,增加了一些實用的功能,比如:批量操作等。
⑤:JpaSpecificationExecutor:用來做負責查詢的接口
⑥:Specification:是Spring Data JPA提供的一個查詢規範,要做複雜的查詢,只需圍繞這個規範來設置查詢條件
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {
    //注入數據
    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;

    /****************************查詢操作*****************************/
    @Test   //查詢並排序
    public void findtestA() {
        //查詢全部 先對age排序后在進行id排序
        Sort sort = new Sort(Sort.Direction.DESC, "age", "id");
        List<Student> students = sd.findAll(sort);
        //打印省略
    }

    @Test   //查詢並分頁
    public void findtestB() {
        //查詢分頁  page是第幾頁 size 每頁個數  當前是第2頁查詢3個   相當limit 6,3
        Pageable pageable = new PageRequest(2, 3);
        Page<Student> page = sd.findAll(pageable);
        System.out.println("當前頁:"+page.getNumber());
        System.out.println("每頁显示條目數:"+page.getSize());
        System.out.println("總頁數:"+page.getTotalPages());
        System.out.println("結果集總數量:"+page.getTotalElements());
        System.out.println("是否是首頁:"+page.isFirst());
        System.out.println("是否是尾頁:"+page.isLast());
        System.out.println("是否有上一頁:"+page.hasPrevious());
        System.out.println("是否有下一頁:"+page.hasNext());
        System.out.println("結果集:"+page.getContent());
        /*
            當前頁:2
            每頁显示條目數:3
            總頁數:3
            結果集總數量:7
            是否是首頁:false
            是否是尾頁:true
            是否有上一頁:true
            是否有下一頁:false
            結果集:[Student{id=7, name='馬克思', age=20, money=2222.8, address='安徽大別山'}]
         */
        //總結:以後做分頁操作變容易了呀
    }

    @Test   //查詢指定學生
    public void findtesC() {
        //創建一個集合   List就是一個Iterable可迭代容器對象,因為實現了Iterable接口
        List<Integer> list = new ArrayList<Integer>() {{
            add(1);add(5);add(3);
        }};
        List<Student> students = sd.findAll(list);
        //打印省略
    }

    /****************************更新操作*****************************/
    @Test   //更新多個學生 更新學生全部地址為 安徽合肥
    @Transactional
    @Rollback(value = false)
    public void savetestA() {
        //創建2個集合  第一個集合查詢全部數據,然後把集合里對象地址改為新的放入新集合上,後面迭代更新
        List<Student> list = sd.findAll();
        System.out.println(list.size());
        List<Student> newlist = sd.findAll();
        for (int i = 0; i < list.size(); i++) {
            Student student = list.get(i);
            student.setAddress("安徽合肥");
            System.out.println(student);
            newlist.add(student);
        }
        List<Student> save = sd.save(newlist);
        //打印略
    }

    /****************************刪除操作*****************************/
    @Test   //刪除指定學生
    public void deletetest() {
        List<Integer> list = new ArrayList<Integer>() {{
            add(1);add(5);add(3);
        }};
        List<Student> all = sd.findAll(list);
        sd.deleteInBatch(all);
    }
    /**
     *  刪除方法介紹:
     *  void deleteAll():底層會一條一條刪除
     *  void delete(Iterable<? extends T> entities):底層會一條一條刪除
     *  void deleteAllInBatch():底層會生成一個帶or語句刪除
     *  void deleteInBatch(Iterable<T> entities):底層會生成一個帶or語句刪除
     *  如:Hibernate: delete from student where sid=? or sid=? or sid=?
     */
}

針對圖上面的一些方法操作

提取注意點:【添加/更新/查詢單個】

①:查詢單個getOne/findOne 【延遲加載/立即加載】

@Test   //查詢單個  立即加載
    public void findOneTest(){
        Student student = sd.findOne(3);
        System.out.println("-------分割線-------");
        System.out.println("打印對象:"+student);
        //控制台打印:
        /*
            Hibernate: select student0_.sid as sid1_0_0_, student0_.saddress as saddress2_0_0_,
            student0_.sage as sage3_0_0_, student0_.smoney as smoney4_0_0_, student0_.sname as
            sname5_0_0_ from student student0_ where student0_.sid=?    【SQL語句】
            -------分割線-------
            打印對象:Student{id=3, name='李小二', age=23, money=6666.8, address='安徽大別山'}
        */
        //說明:在一執行到方法時就會去數據庫查詢,然後把數據封裝到實體類對象上
        //底層調用: EntityManager裏面的find()方法
    }

    @Test   //查詢單個  懶加載【延遲加載】
    @Transactional  //此包下import org.springframework.transaction.annotation.Transactional;
    public void getOneTest(){
        Student student=sd.getOne(3);
        System.out.println("-------分割線-------");
        System.out.println("打印對象:"+student);
        //控制台打印
        /*
            -------分割線-------
            Hibernate: select student0_.sid as sid1_0_0_, student0_.saddress as saddress2_0_0_,
            student0_.sage as sage3_0_0_, student0_.smoney as smoney4_0_0_, student0_.sname as
            sname5_0_0_ from student student0_ where student0_.sid=?
            打印對象:Student{id=3, name='李小二', age=23, money=6666.8, address='安徽大別山'}
         */
        //說明:在執行到getOne方法時並不會去數據庫查詢數據,而只是把此方法交給了代理對象,在後面
        //      某個代碼要對查詢的對象student操作時才會去數據庫做真正查詢
        //底層調用:EntityManager裏面的getReference方法
    }

getOne【延遲加載】/findOne【立即加載】

這裏說明一下:在使用延遲加載的時候必須在方法上面加事務註解

②:添加/更新操作

    @Test   //添加
    @Transactional  //開啟事務
    @Rollback(value = false)    //不自動回滾
    public void savetestA(){
        //注意這裏 我調用了一個不帶 ID的構咋函數
        Student student=new Student("蒼井空",25,555.6,"日本東京");
        Student save = sd.save(student);
        System.out.println("添加的數據是:"+save);
    }

    @Test   //更新
    public void updatetestA(){
        //更新操作的時候 首先要知道更新的ID 后通過把數據全部查詢出來,然後再通過set方法修改
        Student student = sd.findOne(5);    //查詢
        student.setName("小次郎");
        student.setAddress("日本");
        //更新
        Student save = sd.save(student);
        System.out.println("更新后的數據是:"+save);
    }
    /**
     * 總結:更新/添加都是save
     * 區別:傳入帶ID的屬性就是更新  不傳入ID屬性就是添加
     * 注意:添加必須添加事務和回滾機制
     */

更新/添加

   綜上總結:在了解了前面的一些簡單的增刪改查,它都是內部提供好的方法(前提實現指定接口),可以滿足我們開發的大部分需求,可是多表查詢、多條件查詢等等是系統沒有定義的,接下來我和大家介紹一下複雜的查詢及動態查詢等等

 三:SpringDataJPA的複雜自定義CRUD操作

 1:使用JPQL語法進行操作(寫接口方法

  其實我們在使用SpringDataJPA提供的方法就可以解決大部分需求了,但是對於某些業務來說是要有更複雜的查詢及操作,這裏我就要使用自定義接口和註解@Query來完成JPQL的一系列操作,關於JPQL操作我在JPA的一章已經說過大概了

//@Repository("studentDao") //這裏不用加入IOC容器 Spring默認幫我們注入
public interface StudentDao extends JpaRepository<Student, Integer>, JpaSpecificationExecutor<Student> {

    //查詢全部 根據年齡和地址查詢
    @Query("from Student where age=?1 and address=?2")
    List<Student> findByAgeAndAddress(Integer age,String address);

    //查詢全部 根據姓名模糊查詢
    @Query("from Student where name like ?1")
    List<Student> findByLikeName(String name);

    //查詢全部 根據地址為空的返回
    @Query("from Student where address is null ")
    List<Student> findByAddressIsNull();

    //查詢全部學生零花錢大於平均零花錢的所有學生
    @Query("from Student where money > (select avg(money) from Student)")
    List<Student> findByMoneyBigAvgMoney();

    //查詢零花錢在某個範圍內並且降序
    @Query("from Student where money between ?1 and ?2 order by money desc")
    List<Student> findByMoneyBetweenToOrderBy(Integer begin ,Integer end);

    //查詢全部個數
    @Query("select count(id) from Student")
    Long totalCount();

    /*********************刪改操作************************/
    //根據姓名刪除
    @Query("delete from Student where name=?1")
    @Modifying
    void deleteByName(String name);

    //更新數據
    @Query("update Student set name=?2 , address=?3 where id=?1")
    @Modifying
    void updateNameAndAddressById(Integer id,String name,String address);
}

使用JPQL自定義方法完成刪改查 【普通常用】

@Test
@Transactional
@Rollback(value = false)
public void test() {
    //裏面調用JPQL自定義的刪改操作
}
說明: 在使用@Query註解裏面定義的JPQL語法時 查詢不需要事務支持,但是刪改必須有事務及回滾操作,
   而且在定義JPQL語句下面要註明@Modify代表是刪改 JPQL 裏面不存在添加insert操作,所有大家要添加操作的話 調用原來存在的方法
//@Repository("studentDao") //這裏不用加入IOC容器 Spring默認幫我們注入
public interface StudentDao extends JpaRepository<Student, Integer>, JpaSpecificationExecutor<Student> {
    //使用排序   地址模糊查詢后 再按照id降序排列
    @Query("from Student where address like ?1 ")
    List<Student> findByLikeAddressOrderById(String address, Sort sort);

    //使用命名參數  查詢年齡和地址模糊查詢
    @Query("select s from Student as s where address like :address and age=:age")
    List<Student> findByLikeAddressAndAge(@Param(value="age") Integer s_age,@Param("address") String s_address);
}
/***下面測試方法****/

    @Test   //查詢學生帶排序
    public void test() {
        //說在前面:Sort如果不指定DESC或者ASC 如 new Sort("id");說明默認對id ASC升序排列
        //設置id為DESC降序排列
        Sort sort = new Sort(Sort.Direction.DESC, "id");
        List<Student> students = sd.findByLikeAddressOrderById("%大%", sort);
        //打印略
    }
    @Test   //使用命名參數
    public void testA() {
        List<Student> students = sd.findByLikeAddressAndAge(23, "%大別山%");
        //打印略
    }

使用JPAL查詢 【其它查方式】

2:使用原生SQL語句進行操作(寫接口方法

  這種寫SQL語句的在SpringDataJPA中並不常用,因為SpringDataJPA對封裝性那麼高,而且是ORM思想,有各種方法及JPQL語句支持,所有我們本地SQL查詢也只要了解會用即可,SQL語句和我們平常寫的都一樣

//@Repository("studentDao") //這裏不用加入IOC容器 Spring默認幫我們注入
public interface StudentDao extends JpaRepository<Student, Integer>, JpaSpecificationExecutor<Student> {

    //查詢指定的年齡和地址
    @Query(value = "select * from student where sage=?2 and saddress=?1",nativeQuery = true)
    List<Student> findByAgeAndAddress(String address,Integer age);
}

3:方法命名規則查詢

  在Spring Data JPA裏面有一種很不錯的操作,那就是在接口中定義方法而不用實現也不用註解就可以實現其查詢操作,但是寫方法名查詢是有一套規範的,這裏我在官方文檔裏面整理出來了分享給大家【注意:方法名命名規則不能對其增加(insert)和修改(update)】

 ①:方法名查詢的具體規則

//示例場景 //當前域為Student 就是對Student上註解了@Entity,表示對於ORM思想它是一張表 //Student域屬性有name、age、address、dog //dog裏面又是一個封裝類 封裝了Dog的一些屬性如 name、color等

①:按照Spring data的定義規則,查詢的方法必須為【find/findBy/read/readBy/get/getBy】種方式開頭,後面則寫條件屬性關鍵字,條件屬性的
    首字母必須大寫,因為框架在進行方法名解析的時候,會把方法名前面多餘的前綴截取掉,然後對剩下的進行解析
例:
    截取前:getByNameAndAge  截取后:NameAndAge
    截取前:findByAddressLikeAndAge 截取后:AddressLikeAndAge
    截取前:findByDogName 截取后:DogName
問題:findByNameOrderByDesc 這種最後攜帶一個 OrderByDesc 排序的怎麼辦呢? 其實最後面攜帶這種對數據進行最後的排序等等操作,框架會對其另外拆分

②:對已經分割好的方法名如 ‘DogName’ ,根據POJO規範會把首字母變為小寫 ‘dogName’,然後用 ‘dogName’這個屬性去Student域里
    面查詢是否存在此屬性,如果是裏面的某個屬性則進行查詢。

③:如果查詢不到此屬性,則會對‘dogName’從右向左來截取,截取到從右向左的第一個大寫字母(所有截取到了Name),然後框架會把剩下
    的字符‘dog’去Student域裏面查詢,找到此屬性則查詢,沒有會重複前面的截取操作,(重要步驟)

④:現在框架提取到了到了‘dog’,然後去Student域查詢到了,則表示方法最終是根據Student.dog.name方式進行查詢,假設name屬性後面
    還是一個封裝類,框架會一直截取,根據③方式截取。

注意:在寫上面這類方法名是,而且查詢的裏面會有一層一層的封裝類,所有強烈建議使用findByDog_Name();

②:注意一些細節(必看)

1:查詢的條件為null時
    實體介紹:Student裡面包含 address為String類型 age為Integer類型 
    查詢方法定義:List<Student> findByAddressAndAge(String address, Integer age);
    調用時傳入值:List<Student> students = sd.findByAddressAndAge(null,23);
    後台生成的SQL:where (student0_.saddress is null) and student0_.sage=?
 
    結論:當我們傳入了一個null值時,表示會去查詢數據庫中address中字段為null的數據其實在編寫此代碼傳入null值以為會跳過此判斷,
         但是恰恰相反,框架會去查詢為null的數據。(實現此方式的只能去動態查詢,這種簡單的查詢不能滿足要求)

2:排序功能
    List<Student> findByAddressOrderByAge(String address); //名稱正序(正序時,推薦此方式,簡單)
    List<Student> findByAddressOrderByAgeAsc(String address); //名稱正序(效果同上)
    List<Student> findByAddressOrderByAgeDesc(String address); //名稱倒序

3:限定查詢結果集大小
    ①:Student findFirstByOrderByAgeAsc();
    ②:Student findTopByOrderByAgeAsc();
    說明:對錶的全部數據根據age進行Asc(升序)排序后再選擇第一條數據返回
    相應SQL:select .... from student student0_ order by student0_.sage asc limit ?  (這裡是limit 1)
    注意但是我如果寫出:List<Student> findTop3ByOrderByAgeAsc(); 則就是每次返回3條 limit 3
    
    ③:List<Student> findFirst2ByAddress(String address,Sort sort);
    ④:List<Student> findTop2ByAddress(String address,Sort sort);
    說明:首先進行數據查詢並通過Sort進行排序后 再篩選數據列表中最前面的2行數據返回 limit 2
    
    ⑤:Page<Student> queryFirst2ByAddress(String address, Pageable pageable)
    ⑥:List<Student> queryFirst2ByAddress(String address, Pageable pageable)
    說明:首先進行數據查詢 查詢全部指定的address後進行分頁,分頁完後進行數據控制
    控制說明:關於帶分頁的控制是,假設分頁過後查詢的數據id為3,4,5 查詢出這三條數據後進行數據控制,
              本案例是控製為2條,那返回的id數據就是3,4兩條記錄,id為5的就捨棄,那麼如果數據控制是5條,
              那麼就會打印3,4,5另外再去添加6,7補充數據長度
    
    總結:這裏的這裏的First和Top意思相同都是最前面,query也和find一樣;
    
    關於一個小點:
        Page<Student> students = sd.queryFirst1ByAddress("安徽大別山",new PageRequest(1,2));
        這裏我是返回分頁后的第一條數據,可是返回了分頁數據的前一個,分頁后id是3,4控制數據后First1
        返回了id為2 

4:計數 返回總數
    Long countByAddress(String address);

5:刪除
    void deleteByAddress(String address);
    //或 void removeByAddress(String address);
    說明:必須添加事務和回滾,這樣根據條件找到幾條就刪幾條
    @Test
    @Transactional
    @Rollback(value=false)
    public void test() {
        sd.deleteByAddress("安徽大別山");
    }
    //對應的SQL語句
    //Hibernate: select .... from student student0_ where student0_.saddress=?
    //Hibernate: delete from student where sid=?
    //Hibernate: delete from student where sid=?
    //Hibernate: delete from student where sid=?
    //Hibernate: delete from student where sid=?
    //Hibernate: delete from student where sid=?
    //Hibernate: delete from student where sid=?

一些注意事項及其它操作

 ③:列舉幾個常用的方法定義查詢

//根據地址查詢後進行姓名模糊匹配
List<Student> findByAddressAndNameLike(String address ,String likeName);

//查詢指定範圍類的個數
List<Student> countByAgeBetween(Integer ... arg);

//根據對應的Address查詢后對查詢出的數據根據age排序后返回第一條數據
Student findFirstByAddressOrderByAgeDesc(String address);

 四:SpringDataJpa的源碼快速分析

   通過前幾節的增刪改查大家會發現,我們不用寫Dao接口實現類而是繼承JpaRepository和JpaSpecificationExcutor這兩個接口就能夠保證正常的增刪改查功能,這是為什麼呢?

//@Repository("studentDao") //這裏不用加入IOC容器 Spring默認幫我們注入
public interface StudentDao extends JpaRepository<Student, Integer>, JpaSpecificationExecutor<Student> {}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {
    //注入數據
    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;   //這裏注入進去的數據又是誰呢?
    
    @Test
    public void testA(){
        //查詢單個
        Student one = sd.findOne(2);
        System.out.println(one);
    }
}

  其實不難想象,但凡是憑空就可以注入對象的,這內部肯定用到了JDK動態代理,那JDK代理類又是誰呢?經過debug發現是JdkDynamicAopProxy

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
  ....//這個類實現了InvocationHandler 那必然有invoke方法,這裏面是操作該生成哪個代理對象返回 }

  所有說在那個注入的時候,Spring AOP就是通過JdkDyynamicAopProxy類幫我們實現的,那這個代理類最終創建的代理返回的對象又是誰呢?

   那我們再去查看一下SimpleJpaRepository類,肯定是實現了我們自己接口繼承的2個類,而且也有許多的操作方法的實現

@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID extends Serializable> implements JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
  .....
}

總結:就是通過Jdk動態代理生成一個對象返回注入后,我們就可以調出各種操作方法

 五:SpringDataJpa動態查詢

  在日常編程中往往是查詢某個實體的時候,給定的條件是不固定的,那我們在不固定查詢條件的情況下就在Dao編寫查詢方法嗎?這顯然是不可取的,只要在確定及肯定會用到這個查詢條件后才會去Dao編寫此查詢方法;那麼在查詢不固定的情況下我們就會用到Specification動態查詢,要想使用動態查詢的話必須在當前使用的Dao接口下繼承JpaSpecificationExecutor接口;在使用動態查詢對比JPQL,其動態查詢是類型安全,更加面向對象。

/**
 * ①:JpaSpecificationExecutor:
 *  用來做動態查詢的接口
 * ②:Specification:
 *  是Spring Data JPA提供的一個查詢規範,要做複雜的查詢,只需圍繞這個規範來設置查詢條件。
 * JpaSpecificationExecutor接口下一共就5個接口方法
 * 有查詢單個、查詢全部、查詢全部【分頁】、查詢全部【排序】、統計總數
 */
public interface JpaSpecificationExecutor<T> {
    T findOne(Specification<T> spec);
    List<T> findAll(Specification<T> spec);
    Page<T> findAll(Specification<T> spec, Pageable pageable);
    List<T> findAll(Specification<T> spec, Sort sort);
    long count(Specification<T> spec);
}

1:常用查詢(簡單)

①:Root<X> 接口
    此接口是代表查詢根對象,可以獲取實體類中的屬性
    如:root.get("name"); 獲取了實體類的name屬性

②:CriteriaBuilder 接口
    此接口用來構建查詢,此對象裏面有許多查詢條件方法
    方法名稱 Sql對應關係
    equle filed = value gt(greaterThan ) filed > value lt(lessThan ) filed < value ge(greaterThanOrEqualTo ) filed >= value le( lessThanOrEqualTo) filed <= value notEqule filed != value like filed like value notLike filed not like value 注:其實這裏面還有許多的查詢條件方法如求平均值啦查詢指定範圍等等

①:根據某個字段查詢單個數據

//查詢單個
@Test
public void testB() {
    // Specification:查詢條件設置
    Specification<Student> spe = new Specification<Student>() {
        //實現接口的方法   關於Root、CriteriaBuilder上面有介紹
        public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            //獲取實體屬性name
            Path<Object> name = root.get("name");
            //根據指定的姓名查詢
            Predicate select = cb.equal(name, "螞蟻小哥");
            //返回一定是Predicate類型
            return select;
        }
    };
    //查詢單個
    Student student = sd.findOne(spe);
    System.out.println(student);
}

②:多條件查詢、模糊查詢等一些操作

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {
    //注入數據
    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;

    @Test   //多條件查詢
    public void testC() {
        //根據年齡和地址查詢
        Specification<Student> spe=new Specification<Student>() {
            public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Path<Object> age = root.get("age");
                Path<Object> address = root.get("address");
                Predicate p1 = cb.equal(age, "23");
                Predicate p2 = cb.equal(address, "安徽大別山");
                Predicate select = cb.and(p1, p2);
                return select;
            }
        };
        List<Student> students = sd.findAll();
        //打印略
    }

    @Test   //查詢多個條件
    public void testD(){
        //根據精準地址和模糊姓名查詢
        List<Student> students = sd.findAll(new Specification<Student>() {
            public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Path<Object> address = root.get("address");
                Path<Object> name = root.get("name");
                Predicate p1 = cb.equal(address, "安徽大別山");
                Predicate p2 = cb.like(name.as(String.class), "李%");
                return cb.and(p1,p2);
            }
        });
        //注!!!:在使用gt、lt、ge、le、like等條件方法查詢的時候需要告知傳入的屬性的class屬性
        //如:cb.like(name.as(String.class), "李%");
        //打印略
    }

    @Test //查詢全部為指定住址的學生帶年齡排序
    public void testE(){
        List<Student> students = sd.findAll(new Specification<Student>() {
            public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.equal(root.get("address"), "安徽大別山");
            }
        }, new Sort(Sort.Direction.DESC, "age"));
    }
    
    @Test //查詢全部帶分頁
    public void testF(){
        Page<Student> students = sd.findAll(new Specification<Student>() {
            public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return null;    //return null 代表沒條件查詢全部
            }
        }, new PageRequest(2, 2));
    }
}

分頁、排序、多條件

2:複雜查詢(了解)

  我們在之前使用過了Root和CriteriaBuilder的2個接口,可是裏面還有一個參數,是CriteriaQuery接口,它代表一個頂層查詢條件,用來自定義查詢,操作頂層查詢可以使我們更靈活,但是在靈活的基礎上又多了一絲絲複雜

    @Test   //多條件查詢
    public void testC() {
        //根據年齡和地址查詢
        Specification<Student> spe=new Specification<Student>() {
            public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Path<Object> age = root.get("age");
                Path<Object> address = root.get("address");
                Predicate p1 = cb.equal(age, "23");
                Predicate p2 = cb.equal(address, "安徽大別山");
                //query內部提供了一些連接用的如where 、having、groupBy....
                query.where(cb.and(p1,p2));
                return query.getRestriction();
            }
        };
        List<Student> students = sd.findAll();
        System.out.println(students);
        //打印略
    }

六:多表操作(一對一了解

補充:多表操作的 一對一、多對一(一對多)、多對多

1:確定多表之間的關係:
    一對一和一對多:
        一的一方稱為主表,而多的一方稱為從表,外鍵就要建立在從表上,它們的取值的來源主要來自主鍵
    多對多:
        這個時候需要建立一个中間表,中間表中最少由2個字段組成,這2個字段作為外鍵指向2張表的主鍵又組成了聯合主鍵

2:配置多表聯繫註解介紹
@OneToOne(一對一)
@ManyToOne(多對一)
@OneToMany(一對多)
@ManyToMany(多對多)
    內部參數:
        cascade:配置級聯操作
            CascadeType.ALL:所有
            CascadeType.MERGE:更新
            CascadeType.PERSIST:保存
            CascadeType.REMOVE:刪除
        fetch:配置延遲加載和立即加載
            FetchType.EAGER 立即加載
            FetchType.LAZY  延遲加載
        mappedBy:放棄外鍵維護

3:配置外鍵關係的註解
@JoinColumn(定義主鍵字段和外鍵字段的對應關係)
    內部參數:
        name:外鍵字段的名稱
        referencedColumnName:指定引用主表的主鍵字段名稱
        unique:是否唯一。默認值不唯一
        nullable:是否允許為空。默認值允許。
        insertable:是否允許插入。默認值允許。
        updatable:是否允許更新。默認值允許。
        columnDefinition:列的定義信息。
@JoinTable(針對中間表的設置)
    內部參數:
        name:配置中間表的名稱
        joinColumns:中間表的外鍵字段關聯當前實體類所對應表的主鍵字段
        inverseJoinColumn:中間表的外鍵字段關聯對方表的主鍵字段

  在多表操作中、一對一併不是我們掌握的重點,因為在開發中最多的還是使用一對多(多對一)和多對多,廢話不多說,我先來和大家說說一對一的具體操作步驟吧!

  我們在開始使用多表操作的時候,要對原來的部分代碼進行改造,建議大家搭建一個全新的項目,具體的代碼我已經在最上面的入門案例中給出了,只要在新項目中導入相應的坐標和配置文件即可,然後修改配置文件裏面的具體數據庫和數據庫密碼即可,還有就是現在的建表語句和上面的全部操作不太一樣,為了更好的演示一對一的操作我這邊準備了一個數據庫建表語句,下面的表含義是,每個學生表(student)都有一條家庭(family)的登記的記錄表,為一對一關係(理想化表,雙胞胎除外)

-- 一對一關係
-- 刪除庫
drop database demo_jpa_one_one;
-- 創建庫
create database if not exists demo_jpa_one_one charset gbk collate gbk_chinese_ci;
-- 使用庫
use demo_jpa_one_one;

# 家庭表創建
create table if not exists family(
    fid int(11) primary key auto_increment, -- 家庭主鍵
    fmember int(11) not null,               -- 成員個數
    fguardian varchar(10) not null,         -- 監護人
    ftel char(11) not null,                 -- 監護人號碼          
    fdad varchar(10),                       -- 爸爸姓名
    fmom varchar(10),                       -- 媽媽姓名
    faddress varchar(20)                    -- 家庭詳細地址
)charset gbk; 
# 創建學生表
create table if not exists student (
    sid int(11) primary key auto_increment,             -- 編號
    sname varchar(5) not null,                          -- 姓名
    ssex enum('','') default '',                  -- 性別
    sage tinyint(11) unsigned not null default 20,      -- 年齡
    smoney decimal(4,1) default 0,                      -- 零花錢 最高999.9
    saddress varchar(10),                               -- 住址
    senrol date default '0000-00-00',                   -- 入學時間
    f_id int(11),                                       -- 連接家庭id
    foreign  key(f_id) references family(fid)           -- 連接家庭id主鍵
)charset gbk;
# 添加家庭信息
insert into family(fmember,fguardian,ftel,fdad,fmom,faddress)values
(0,"余蒙飄","","戚曦維","余蒙飄","安徽省六安市裕安區"),
(0,"孫戀烈","","梁輪亭","孫戀烈","安徽省合肥市瑤海區"),
(0,"張頻時","","張頻時","","安徽省安慶市宜秀區"),
(0,"王京正","","王京正","梁晝庭","安徽省六安市金安區"),
(0,"劉資納","","王諄斌","劉資納","安徽省滁州市全椒縣"),
(0,"白飛解","","廖旺賜","白飛解","安徽省安慶市大觀區"),
(0,"梁昀輝","","鄔國耿","梁昀輝","安徽省蚌埠市蚌山區"),
(0,"古錄鋼","","","古錄鋼","安徽省滁州市定遠縣"),
(0,"姬橋毅","","姬橋毅","寧竹熊","安徽省合肥市蜀山區"),
(0,"劉始瑛","","劉始瑛","韋歡億","安徽省淮南市大通區");
# 添加學生數據
insert into student(sid,sname,saddress,f_id)values
(1 ,"王生安","安徽六安",1 ),(2 ,"李鑫灝","安徽合肥",2 ),
(3 ,"薛佛世","安徽蚌埠",3 ),(4 ,"蔡壯保","安徽安慶",4 ),
(5 ,"錢勤堃","安徽合肥",5 ),(6 ,"潘恩依","安徽合肥",6 ),
(7 ,"陳國柏","安徽六安",7 ),(8 ,"魏皚虎","安徽六安",8 ),
(9 ,"周卓浩","安徽六安",9 ),(10,"湯辟邦","安徽六安",10);
-- 更新數據
update student set ssex=ceil(rand()*2),sage=ceil(rand()*5+20),smoney=(rand()*999),
senrol=concat(ceil(rand()*3+2017),'-' , ceil(rand()*12) , '-',ceil(rand()*20));
update family set ftel=concat(if(ceil(rand()*3)=1,if(ceil(rand()*3)=2,if(ceil(rand()*3)=1,
    if(ceil(rand()*3)=2,"155","166"),if(ceil(rand()*3)=3,"163","170")),"164"),
if(ceil(rand()*3)=3,"188","176")),floor(rand()*90000000+9999999)),fmember=ceil(rand()*3+2);
-- 查詢數據
show create table student\G
show create table family\G
select * from student;
select * from family;

一對一 數據庫建表語句

  其實我們並不需要創建數據庫表(數據庫必須創建),因為我們使用的是ORM思想的框架,說白了就是程序自動幫我們創建表(設置create配置),我們只需要關心實體類就可以了,這個實體類我也為大家準備好了

/**
 * 創建了一個Student的實體-表關係類
 */
@Entity
@Table(name = "student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "sid")
    private Integer id;
    @Column(name = "sname")
    private String name;
    @Column(name = "ssex")
    private String sex;
    @Column(name = "sage")
    private Integer age;
    @Column(name = "smoney")
    private Double money;
    @Column(name = "saddress")
    private String address;
    @Column(name = "senrol")
    private String enrol;
    //get/set/toString/無參有參構造器 大家自己創建
}

Student的Entity實體對象   未指定外鍵關係

/**
 * 創建了一個Family的實體-表關係類
 */
@Entity
@Table(name = "family")
public class Family {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "fid")
    private Integer id;
    @Column(name = "fmember")
    private Integer member;
    @Column(name = "fguardian")
    private String guardian;
    @Column(name = "ftel")
    private String tel;
    @Column(name = "fdad")
    private String dad;
    @Column(name = "fmom")
    private String mom;
    @Column(name = "faddress")
    private String address;
    //get/set/toString/無參有參構造器 大家自己創建
}

Family的Entity實體對象    未指定外鍵關係

  在準備好了映射關係之後,我們要創建StudentDao、FamilyDao這2個接口,並且分別繼承JpaRepository和JpaSpecificationExecutor這2個接口,完成了這些操作后就可以開始編寫測試類了

<!--大家把配置文件的JPA配置方式改為create每次都創建表-->
<property name="jpaProperties">
     <props>
          <prop key="hibernate.hbm2ddl.auto">create</prop>
     </props>
</property>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {
    //注入數據
    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;
    @Autowired
    @Qualifier(value = "familyDao")
    private FamilyDao fd;

    @Test   //完成保存操作
    @Transactional
    @Rollback(value = false)
    public void testC() {
       Student stu=new Student("王二","男",26,666.3,"安徽六安","2020-6-5");
       Family fam=new Family(3,"王二牛","17688888888","王二牛","母老虎","安徽六安裕安區白嫖村");
       //開始保存
       sd.save(stu);
       fd.save(fam);
    }
}

如果不出意外,大家可能會遇到一個異常,這正是我想和大家說的,具體說明在文章最後的【關於多表操作異常】問題總結和大家說明一下,我們解決問題后回來再次運行后,控制台會打印2條建表語句和插入數據的語句,前面說過,這2張表是一個一對一的關聯關係,我們接下來就對這2張表建立關係。

建立關係 Student---Family (一對一) 為Student建立關聯到Family的關係(在Student中增加一個字段)
    @OneToOne(targetEntity = Family.class)
    @JoinColumn(name = "f_id", referencedColumnName = "fid")
    private Family family;
為Family建立關聯到Student的關係(在Family中增加一個字段)
    @OneToOne(targetEntity = Student.class)
    @JoinColumn(name = "f_id", referencedColumnName = "sid")
    private Student student;
這樣就完成了相互引用了,每個實體類都擁護一個對方實體,完成了一對一的關聯

完成了關聯后我們對測試方法進行改造

    @Test   //多條件查詢
    @Transactional
    @Rollback(value = false)
    public void testC() {
        Student stu = new Student("王二", "男", 26, 666.3, "安徽六安", "2020-6-5");
        Family fam = new Family(3, "王二牛", "17688888888", "王二牛", "母老虎", "安徽六安裕安區白嫖村");
        //通過set方法分別設置對應的字段,使其關聯
 stu.setFamily(fam); fam.setStudent(stu);
        sd.save(stu);
        fd.save(fam);
    }

運行後會自動幫我們創建2張表並且設置外鍵

mysql> select * from student;
+-----+----------+------+----------+--------+-------+------+------+
| sid | saddress | sage | senrol   | smoney | sname | ssex | f_id |
+-----+----------+------+----------+--------+-------+------+------+
|   1 | 安徽六安  |   26  | 2020-6-5 |  666.3 | 王二  | 男   |  1   |
+-----+----------+------+----------+--------+-------+------+------+
mysql> select * from family;
+-----+----------------------+--------+-----------+---------+--------+-------------+------+
| fid | faddress             | fdad   | fguardian | fmember | fmom   | ftel        | f_id |
+-----+----------------------+--------+-----------+---------+--------+-------------+------+
|   1 | 安徽六安裕安區白嫖村   | 王二牛  | 王二牛     |       3 | 母老虎 | 17688888888  |   1  |
+-----+----------------------+--------+-----------+---------+--------+-------------+------+

  發現自動幫我們創建的表會有2個外鍵,可是這2個外鍵是多餘的,我們這個時候需要放棄外鍵維護,按照之前的一對一建表語句說明,外鍵是建立在學生表上的,通過學生表上面的外鍵來尋找到家庭表的信息,這個時候我們在家庭表上面放棄外鍵維護

//@OneToOne(targetEntity = Student.class)
    //@JoinColumn(name = "f_id", referencedColumnName = "sid")
    //引用對方的字段
    @OneToOne(mappedBy = "family")
    private Student student;

1:一對一的對象導航查詢

<property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <!--設置數據庫方言-->
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
            </props>
        </property>
 @Test   //多條件查詢
    @Transactional
    @Rollback(value = false)
    public void testC() {
        System.out.println(sd.findOne(1));
        //我們要編寫toString方法后才可以打印全部
    }

七:多表操作(一對多/多對一重點

  其實一對多和多對一本質上是一樣的,只是順序發生了變化,我就詳細的把一對多給講一下,在這裏我已經有過一個數據庫建表語句,但是因為是ORM項目,我們建表語句是由程序幫我們自動創建的,之所以我編寫了SQL語句是為了方便後期查詢練習

#### 一對多  這裏我使用的關係是一個老師對應多個學生,一個學生對應一個老師
#### 老師表為主表、學生表為從表
#### 一對多
-- 刪除庫
drop database demo_jpa_one_many;
-- 創建庫
create database if not exists demo_jpa_one_many charset gbk collate gbk_chinese_ci;
-- 使用庫
use demo_jpa_one_many;
-- 創建主表 (輔導員表)
create table if not exists teacher(
    tid int primary key,               -- 編號
    tname varchar(5) not null,         -- 姓名
    tsex enum('','') default '', -- 性別
    tage tinyint unsigned,             -- 年齡
    tsalary decimal(6,1) default 0,    -- 工資 最高99999.9
    taddress varchar(10)               -- 住址
)charset gbk collate gbk_chinese_ci;

-- 創建從表 (學生表)
create table if not exists student (
    sid int(11) primary key auto_increment,             -- 編號
    sname varchar(5) not null,                          -- 姓名
    ssex enum('','') default '',                  -- 性別
    sage tinyint(11) unsigned not null default 20,      -- 年齡
    smoney decimal(4,1) default 0,                      -- 零花錢 最高999.9
    saddress varchar(10),                               -- 住址
    senrol date default '0000-00-00',                   -- 入學時間
    tid int ,                                           -- 連接老師id
    foreign  key(tid) references teacher(tid)  -- 連接老師主鍵
)charset gbk;

-- 添加老師數據
insert into teacher (tid,tname,taddress)values
(1,'張老師','江蘇南京'),
(2,'李老師','江蘇無錫'),
(3,'王老師','江蘇常熟');

-- 添加學生數據
insert into student(sid,sname,saddress)values
(1 ,"王生安","安徽六安"),(2 ,"李鑫灝","安徽合肥"),
(3 ,"薛佛世","安徽蚌埠"),(4 ,"蔡壯保","安徽安慶"),
(5 ,"錢勤堃","安徽合肥"),(6 ,"潘恩依","安徽合肥"),
(7 ,"陳國柏","安徽六安"),(8 ,"魏皚虎",null);

-- 數據更新
update teacher set tsex=ceil(rand()*2),tage=ceil(rand()*10+25),tsalary=ceil(rand()*3000+8000);
update student set ssex=ceil(rand()*2),sage=ceil(rand()*5+20),smoney=(rand()*999),
senrol=concat(ceil(rand()*3+2017),'-' , ceil(rand()*12) , '-',ceil(rand()*20)),tid=ceil(rand()*3);

一對多 關係建表建庫語句

  此時的一對多關係是【一個老師有多個學生】,反之,如果是多對一隻需要調換關係即可,2者差不多,我就針對這個一對多講解;在編寫程序之前我們必須要搞明白什麼是主表和從表的關係,這影響到我們後面編寫一對多的關係:

主表:
    由主鍵和其它字段組成,後期由其它表參照當前表主鍵,此時的表為主表,
    在其它表中如果引用主表的主鍵字段,那麼主表的主鍵被引用后將不能隨意更改,
    如果強制更改則必須設置級聯操作(cascade)
從表:
    由主鍵、外鍵和其它字段組成,後期由本表的外鍵字段去引用其它帶有主鍵的表,一旦其它的
    表主鍵被引用后,被引用的表則被稱為主表,反之引用其它表主鍵的表被稱之從表

一句話概括:有外鍵字段的表是從表(排除其它複雜表,因為有的表即引用其它別也被其它表引用)

  在我們了解了上面的關係后,我們就可以搭建操作一對多的環境了,首先我們就是要準備建造一個數據庫(不是數據表)名稱為demo_jpa_one_many,因為這個框架只要編寫好映射關係後會自動幫我們創建表的;現在建立一個項目,只需要在空項目導入pom.xml坐標和配置外鍵(配置文件一定要修改正確數據庫連接),我將帶大家一步一步完成一對多操作,如果這些都會直接跳過;完成了上面的操作后,我們首先就是要去編寫實體類及映射關係:

/**
 * 創建了一個Teacher的實體-表關係類
 * 在案例中 當前老師表為主表
 */
@Entity
@Table(name = "teacher")
public class Teacher {

    //創建主鍵字段和普通字段
    @Id
    @Column(name = "tid")
    private Integer id;
    @Column(name = "tname")
    private String name;
    @Column(name = "tsex")
    private String sex;
    @Column(name = "tage")
    private Integer age;
    @Column(name = "tsalary")
    private Double salary;
    @Column(name = "taddress")
    private String address;

    //省略 get/set/有參無參構造器
}

老師表映射關係 主表

/**
 * 創建了一個Student的實體-表關係類
 * 在案例中 當前學生表為從表
 */
@Entity
@Table(name = "student")
public class Student {

    //創建主鍵字段和普通字段
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "sid")
    private Integer id;
    @Column(name = "sname")
    private String name;
    @Column(name = "ssex")
    private String sex;
    @Column(name = "sage")
    private Integer age;
    @Column(name = "smoney")
    private Double money;
    @Column(name = "saddress")
    private String address;
    @Column(name = "senrol")
    private String enrol;
    //省略 get/set/有參無參構造器
}

學生表映射關係 從表

  在完成上面的一些操作后,大家已經完成了實體類及映射的創建,這裏我沒有為這2張表建立外鍵關係,因為在沒接觸過多表操作的話冒然把映射一起寫完有可能會出現異常(肯定有人說,這咋這麼啰嗦呀)那好,對有操作的,只是複習怎麼使用的直接可以當cv攻城師複製即可;現在我們來編寫2個dao類

public interface StudentDao extends JpaRepository<Student,Integer>, JpaSpecificationExecutor<Student> { }
public interface TeacherDao extends JpaRepository<Teacher,Integer> , JpaSpecificationExecutor<Teacher> { }

  寫好以後就正式測試了,測試完成后大家會發現是2個沒有任何關係的2張獨立的表(如果出現異常了去下面查看異常講解

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {
    //注入數據
    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;
    @Autowired
    @Qualifier(value="teacherDao")
    private TeacherDao td;

    @Test   //多條件查詢
    @Transactional
    @Rollback(value = false)
    public void testC() {
        Student student=new Student("張小三","男",25,222.3,"安徽六安","2018-8-8");
        Teacher teacher=new Teacher(10,"張老師","男",35,9999.6,"北京順義");
        //保存
        td.save(teacher);
        sd.save(student);
    }
}

測試代碼 創建2張表

mysql> select * from student;
+-----+----------+------+----------+--------+--------+------+
| sid | saddress | sage | senrol   | smoney | sname  | ssex |
+-----+----------+------+----------+--------+--------+------+
|   1 | 安徽六安   |   25 | 2018-8-8 |  222.3 | 張小三 | 男   |
+-----+----------+------+----------+--------+--------+------+
mysql> select * from teacher;
+-----+----------+------+--------+---------+------+
| tid | taddress | tage | tname  | tsalary | tsex |
+-----+----------+------+--------+---------+------+
|  10 | 北京順義 |   35 | 張老師   |  9999.6 | 男    |
+-----+----------+------+--------+---------+------+

注:創建2表的聯繫

  在創建2表的關係中,我們必須要分清主表和從表的關係,這樣才可以設計出一個完整的關係創建,具體的主表和從表上面以給出介紹

創建從表的關係連接(student)
在從表中添加一個引用teacher屬性,因為一個student中有一個teacher
//創建外鍵字段 因為從表上有明確外鍵引用其它表 所以必須要有外鍵字段
@ManyToOne(targetEntity = Teacher.class) @JoinColumn(name="t_id",referencedColumnName = "tid") private Teacher teacher;
注:
    targetEntity = Teacher.class:當前引用的主表類型
    name="t_id" 代表當前student表中的外鍵字段
    referencedColumnName = "tid"  代表參照主表的哪個字段
建表樣式:
mysql> select * from student;
+-----+----------+------+----------+--------+--------+------+------+
| sid | saddress | sage | senrol   | smoney | sname  | ssex | t_id |
+-----+----------+------+----------+--------+--------+------+------+
|   1 | 安徽六安 |   25 | 2020-6-6 |  666.3 | 李小二 | 男   |  100 |
+-----+----------+------+----------+--------+--------+------+------+

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 創建主表的關係連接(teacher)
在主表中添加一個引用student的屬性,因為一個teacher中有多個student
//創建外鍵字段 這裡是主表,所以主動放棄外鍵維護
//@OneToMany(targetEntity = Student.class)
//@JoinColumn(name = "s_id",referencedColumnName = "sid")
//引用從的外鍵字段
@OneToMany(mappedBy = "teacher") private Set<Student> students=new HashSet<Student>();

注:
    mappedBy = "teacher" :代表參照對方表

假設主表不放棄外鍵維護就會出現下面情況:
mysql> select * from teacher;
+-----+----------+------+--------+---------+------+------+
| tid | taddress | tage | tname  | tsalary | tsex | s_id |
+-----+----------+------+--------+---------+------+------+
| 100 | 北京順義 |   32 | 王老師 |  6666.6 | 男   |   1  |
+-----+----------+------+--------+---------+------+------+
問題所在:這時候主表引用從表,而從表也引用主表,這顯然不是一個合格設計
解決后:在主表上設置的放棄外鍵維護,並參照從表的關係
mysql> select * from teacher;
+-----+----------+------+--------+---------+------+
| tid | taddress | tage | tname  | tsalary | tsex |
+-----+----------+------+--------+---------+------+
| 100 | 北京順義 |   32 | 王老師 |  6666.6 | 男   |
+-----+----------+------+--------+---------+------+

  創建測試,在測試的時候大家把配置文件的配置改為create,代表每次執行都會創建表

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {
    //注入數據
    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;
    @Autowired
    @Qualifier(value="teacherDao")
    private TeacherDao td;

    @Test   //多條件查詢
    @Transactional
    @Rollback(value = false)
    public void testC() {
        //創建2個對象
        Student student=new Student("李小二","男",25,666.3,"安徽六安","2020-6-6");
        Teacher teacher=new Teacher(100,"王老師","男",32,6666.6,"北京順義");
        //把老師放入學生數據中  把學生數據放入老師表中
        student.setTeacher(teacher);
        teacher.getStudents().add(student);
        //保存 這裏注意一下,因為我的teacher主鍵不是自動生成 ,
        // 所以先保存teacher才可以保存student,因為teacher主鍵不是自動生成,直接先保存student會無法獲取teacher主鍵
        td.save(teacher);
        sd.save(student);
    }
}

測試方法

  然後會生成和我給出的創建sql語句生成的相同的字段,並且外鍵也是可以的,如果出現異常,大家檢查一下get/set/無參構造/有參構造,這裏的無參構造必須存在,否則真的會異常

1:級聯操作

  級聯操作分級聯刪除、級聯添加、級聯更新,如果設置了級聯操作就可以完成級聯操作,具體的在一對一上的補充介紹了

    @OneToMany(mappedBy = "teacher",cascade = CascadeType.ALL)
    private Set<Student> students=new HashSet<Student>();

  在主表上設置了級聯操作(ALL=全部都支持),只要保存teaccher就會級聯着保存student

    @Test   //多條件查詢
    @Transactional
    @Rollback(value = false)
    public void testC() {
        //創建2個對象
        Student student=new Student("李小二","男",25,666.3,"安徽六安","2020-6-6");
        Teacher teacher=new Teacher(100,"王老師","男",32,6666.6,"北京順義");
        //把老師放入學生數據中  把學生數據放入老師表中
        student.setTeacher(teacher);
        teacher.getStudents().add(student);
        //直接添加老師就可以保存雙方數據
        td.save(teacher);
    }

2:查詢數據

  這個時候我們導入之前的sql語句,然後把配置改為update,每次執行,有表則不創建表,

    @Test   //多條件查詢
    @Transactional //必須添加事務
    public void testC() {
        Teacher teacher = td.findOne(2);
        System.out.println(teacher);
    }

 八:多表操作(多對多)

  多對多是一個雙向關係,我首先來展示一下SQL語句

##### 多對多

-- 刪除庫
drop database demo_jpa_many_many;
-- 創建庫
create database if not exists demo_jpa_many_many charset gbk collate gbk_chinese_ci;
-- 使用庫
use demo_jpa_many_many;
-- 創建從表 (學生表)
create table if not exists student (
    sid int(11) primary key auto_increment,             -- 編號
    sname varchar(5) not null,                          -- 姓名
    ssex enum('','') default '',                  -- 性別
    sage tinyint(11) unsigned not null default 20,      -- 年齡
    smoney decimal(4,1) default 0,                      -- 零花錢 最高999.9
    saddress varchar(10),                               -- 住址
    senrol date default '0000-00-00'                    -- 入學時間
)charset gbk;

# 學生社團組織
create table if not exists organization(
    oid int(11) primary key auto_increment,  -- 社團主鍵id
    oname varchar(10) not null unique        -- 社團名稱
)charset gbk;
# 中間表 社團和學生對應多對多關係
create table if not exists student_organization(
    soid int(11) primary key auto_increment,         -- 中間表id
    s_id int(11),                                    -- 學生id
    o_id int(11),                                    -- 社團id
    foreign key(s_id) references student(sid),       -- 連接學生id
    foreign key(o_id) references organization(oid)   -- 連接社團id
)charset gbk;

# 添加學生社團組織學習
insert into organization (oname) values
("書法協會"),("法律協會"),("武術協會"),
("魔術社團"),("網球協會"),("啦啦隊團");
-- 添加學生數據
insert into student(sid,sname,saddress)values
(1 ,"王生安","安徽六安"),(2 ,"李鑫灝","安徽合肥"),
(3 ,"薛佛世","安徽蚌埠"),(4 ,"蔡壯保","安徽安慶"),
(5 ,"錢勤堃","安徽合肥"),(6 ,"潘恩依","安徽合肥"),
(7 ,"陳國柏","安徽六安"),(8 ,"魏皚虎",null);
# 添加學生和社團的中間表
insert into student_organization(s_id,o_id) values
(1,1),(1,3),(1,2),(1,6),(2,5),(2,1),(2,2),
(6,6),(6,2),(6,4),(6,3),(7,4),(7,2);

多對多建表語句

  上面多對多關係是一個學生可以加入多個社團(組織),一個組織可以有多名學生,間就構建成多對多關係,廢話不多說,直接上映射關係

在創建映射關係的時候我們得確定哪一方會放棄外鍵維護權,
在多對多的時候我們通常放棄的被動的一方



@Entity
@Table(name = "student")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "sid")
    private Integer id;
    @Column(name = "sname")
    private String name;
    @Column(name = "ssex")
    private String sex;
    @Column(name = "sage")
    private Integer age;
    @Column(name = "smoney")
    private Double money;
    @Column(name = "saddress")
    private String address;
    @Column(name = "ssenrol")
    private String senrol;

//    @ManyToMany(targetEntity = Organization.class)
//    @JoinTable(name = "student_organization",
//            joinColumns = {@JoinColumn(name = "s_id", referencedColumnName = "sid")} ,
//            inverseJoinColumns = {@JoinColumn(name = "o_id", referencedColumnName = "oid")}
//    )
    @ManyToMany(mappedBy = "students")
    private Set<Organization> organizations = new HashSet<Organization>();
}


@Entity
@Table(name = "organization")
public class Organization {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "oid")
    private Integer id;
    @Column(name = "oname")
    private String name;

    @ManyToMany(targetEntity = Student.class)
    @JoinTable(name = "student_organization",
            joinColumns ={@JoinColumn(name = "o_id",referencedColumnName = "oid")} ,
            inverseJoinColumns = {@JoinColumn(name = "s_id",referencedColumnName = "sid")}
    )
    private Set<Student> students=new HashSet<Student>();
}

多表映射關係

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {

    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;

    @Autowired
    @Qualifier(value = "organizationDao")
    private OrganizationDao od;

    @Test
    @Transactional
    @Rollback(value = false)
    public void fun() {
        Student student=new Student("張三","男",45,555.6,"安徽六安","2012-8-8");
        Organization o=new Organization("魔術社");
        student.getOrganizations().add(o);
        o.getStudents().add(student);
        od.save(o);
        sd.save(student);
    }

}

測試環境

九:對象導航查詢

  在一對多查詢的時候,多的一方數據會有延遲加載,但是對於一對一查詢的時候數據會有立即加載

十:關於多表操作異常

1:關於hibernate數據庫方言問題(dialect)

6月 03, 2020 4:57:00 下午 org.hibernate.dialect.Dialect <init> INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
省去部分...
Hibernate: create table family (...) type=MyISAM
//上面一局為我們創建的是一張表並設置MyISAM引擎 錯誤就在這 無法運行了
6月 03, 2020 4:57:01 下午 org.hibernate.tool.schema.internal.ExceptionHandlerLoggedImpl handleException WARN: GenerationTarget encountered exception accepting command : Error executing DDL "create table family (...) type=MyISAM" via JDBC Statement org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "create table family (...) type=MyISAM" via JDBC Statement //從上面錯誤可以看出 程序運行的時候默認的數據庫方言設置了 org.hibernate.dialect.MySQLDialect 而這個默認是MyISAM引擎

  問題所在:因為我導入的hibernate坐標是5.4.10.Final,在導入這類高版本的坐標往往要為數據庫方言設置MySQL5InnoDBDialect的配置,在我前面也測試了,關於坐標版本問題,發現5.0.x.Final左右的版本不用設置數據庫方言,默認即可。

<property name="jpaProperties">
   <props>
     <prop key="hibernate.hbm2ddl.auto">create</prop>
     <!--設置數據庫方言--> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
   </props>
</property>

  具體的版本在創建數據庫表的時候會拋各種異常,這裏我整理了一下數據庫方言,方便大家參考

<property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">create</prop>
                <!--設置數據庫方言-->
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
            </props>
        </property>

<!--數據庫方言-->
DB2                         org.hibernate.dialect.DB2Dialect
DB2 AS/400                  org.hibernate.dialect.DB2400Dialect
DB2 OS390                   org.hibernate.dialect.DB2390Dialect
PostgreSQL                  org.hibernate.dialect.PostgreSQLDialect
MySQL                       org.hibernate.dialect.MySQLDialect
MySQL with InnoDB           org.hibernate.dialect.MySQLInnoDBDialect
MySQL with MyISAM           org.hibernate.dialect.MySQLMyISAMDialect
Oracle (any version)        org.hibernate.dialect.OracleDialect
Oracle 9i/10g               org.hibernate.dialect.Oracle9Dialect
Sybase                      org.hibernate.dialect.SybaseDialect
Sybase Anywhere             org.hibernate.dialect.SybaseAnywhereDialect
Microsoft SQL Server        org.hibernate.dialect.SQLServerDialect
SAP DB                      org.hibernate.dialect.SAPDBDialect
Informix                    org.hibernate.dialect.InformixDialect
HypersonicSQL               org.hibernate.dialect.HSQLDialect
Ingres                      org.hibernate.dialect.IngresDialect
Progress                    org.hibernate.dialect.ProgressDialect
Mckoi SQL                   org.hibernate.dialect.MckoiDialect
Interbase                   org.hibernate.dialect.InterbaseDialect
Pointbase                   org.hibernate.dialect.PointbaseDialect
FrontBase                   org.hibernate.dialect.FrontbaseDialect
Firebird                    org.hibernate.dialect.FirebirdDialect

數據庫方言 hibernate.dialect

2:關於對象導航查詢異常

WARNING: Please consider reporting this to the maintainers of org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release
Hibernate: sql語句
java.lang.StackOverflowError at java.base/java.lang.AbstractStringBuilder.inflate(AbstractStringBuilder.java:202)
    at java.base/java.lang.AbstractStringBuilder.putStringAt(AbstractStringBuilder.java:1639)
    at java.base/java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:513)
   .......

  關以這種錯誤大多都是編寫的toString語句發生了錯誤,大家在寫toString的時候一定要分清主表和從表,在編寫主表的toString的時候一定要去除外鍵字段,而在編寫從表的時候一定要加上外鍵字段,因為平時我們都是通過從表查詢數據(因為從表有指向主表的外鍵),這樣可以把主表的數據通過外鍵查詢出來,但是主表上如果也有從表的字段的話就會一隻循環,數據沒完沒了,所有拋異常也是對的,

總結一句話,從表有主表的字段,主表有從表的字段,打印從表順帶打印主表,但是主表裡面還有從表字段,然後繼續打印從表字段…….

 3:關於延遲加載事務問題

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: cn.xw.domain.Teacher.students, could not initialize proxy - no Session at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606) at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218) at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585) at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149) at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:327) at java.base/java.lang.String.valueOf(String.java:2801) at java.base/java.lang.StringBuilder.append(StringBuilder.java:135)

  關於在使用延遲加載的時候,在當前的方法上必須設置@Transactional,因為在使用延遲加載底層已經使用了事務的相關方法

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

Skype for Business Online 將於 7 月底結束,微軟開始提示商務用戶盡早轉移_貨運

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

Skype for Business Online直到現在還是很多企業、行號愛用的通訊工具,在 2019 年時,微軟已經提早預告將會在 2021 年 7 月底時終止這項服務,距離結束還有 6 個月,現在微軟也開始提醒商務用戶儘早升級、轉移到 Microsoft Teams,以免到時候手忙腳亂影響工作。

Skype for Business Online將於 7 月底結束,微軟開始提示商務用戶盡早轉移

雖然疫情的關係讓全球許多公司、企業原本的規劃大亂、行程也因而有所延宕,但這回 Skype for Business Online 的結束之日並不會因此而延後。微軟在一篇官方部落格中寫到,無論目前轉移進度如何,現在就是一個重要的檢視時間,以確保公司在 Skype for Business Online 停用前能夠升級到 Microsoft Teams,以利整體業務的無縫接軌。

在官方部落格裡面,微軟還提供了各搬遷階段的資源連結來幫助用戶的轉移:
Microsoft Teams 管理:這份文件將指導用戶如何管理、準備來使用 Microsoft Teams
Teams 升級規劃 workshops:引導用戶完成一個經驗正的框架,主要用於規劃和實際進行轉移工作
微軟粉筆講座 workshops:圍繞 Teams 中一些最受歡迎的功能提供最佳實作指導

Skype for Business Online 的用戶將擁有自動升級到 Microsoft Teams 的資格,用以幫助用戶直接升級,而計劃進行自動升級的用戶將在預計的升級日期前至少 3 個月前就會在 Teams 管理中心河 Microsoft 365 訊息中心裡面收到通知,以便有更充裕的時間進行技術層面與使用的事前準備。想瞭解更多有關自動升級的資訊,可閱讀官方說明文件。

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式