Java的前生今世

Java作為一門編程語言,自誕生以來已經流行了20多年,在學習它之前,我們有必要先了解一下它的歷史,了解它是如何一步步發展到今天這個樣子。

孕育

上世紀90年代,硬件領域出現了單片式計算機系統,比如電視機機頂盒這些消費設備。這些設備的特點是處理能力和內存都有限。如果使用C++編寫程序,則對於這類設備來說過於龐大。

所以需求便來了,為了適應這類設備,需要一種小型的計算機語言,Java便應運而生了。

1991年,Sun公司的James Gosling, Mike Sheridan和Patrick Naughton發起了一個名為“Green”的項目小組,主要用於電視機頂盒這類消費設備。

由於設備的處理能力和內存有限,所以要求語言短小、代碼緊湊。而對於電視機頂盒這類消費設備來說,廠商會選擇各種不同的CPU,所以要求語言可移植、不與特定的平台捆綁在一起

出生

Green項目組以C++為基礎,對C++進行了一些改造,並刪除了一些不太實用、複雜繁瑣的東西,開發出了名為“Oak”的語言,(Oka來自James Gosling辦公室外的橡樹)。

成長

挫折

1992年夏天,Green項目組已經能夠演示Green操作系統、Oak的程序設計語言、類庫和其硬件。

1992年9月,Green項目組發布了它的第一個名為“Start 7”的產品,該產品是一個個人数字助理設備,具有圖形界面和非常智能的遠程控制。

1992年11月,Green項目被剝離,成為Sun Microsystems的全資子公司Firstperson。

但是當時的硬件生產廠商對其項目並不感興趣,Firstperson花了一年多也沒找到希望購買其技術的買家。

1994年,First Person公司解散,被重新納入Sun公司。Oak語言無法進入市場,也被暫時擱置下來。

機遇

在上面的過程中,Internet的萬維網也在不斷髮展。對於客戶機/服務器模型來說,萬維網需要做的是將超文本頁面轉換到屏幕的瀏覽器中。

1994年6月到7月,Sun公司的團隊看到了互聯網的發展,一些圖形Web瀏覽器的出現讓他們認為互聯網可以演變成他們為有線電視所設想的相同的高度交互性媒體。經過三天的頭腦風暴后,該團隊將目標平台重新定位為萬維網平台。

1994年中期,Oak語言的開發者意識到他們可以開發一個瀏覽器,WebRunner瀏覽器誕生。

1995年,互聯網的發展帶給了Oak語言一個機遇。當時的網絡頁面是單調、死板的靜態頁面,業界希望頁面能夠“靈活”起來,並且希望代碼能夠通過網絡傳播進行跨平台運行。Oak的語言短小、可移植的特點和這些需求不謀而合。

Sun公司首先推出了可以嵌入網頁並且可以隨同網頁在網絡上傳輸的Applet(Applet是一種將小程序嵌入到網頁中進行執行的技術)。

設計者在設計WebRunner瀏覽器具有在網頁中執行內嵌代碼的能力,這種能力能讓網頁變得“靈活”。

同年,Sun公司將Oak更名為Java(因為Oak已經被使用了,Java一詞來自咖啡),WebRunner瀏覽器也演變成了HotJava瀏覽器,HotJava瀏覽器完全使用Java開發。

同年5月23日,Sun公司在Sun World會議上展示並正式發布了Java語言和HotJava瀏覽器,各大公司都紛紛停止了自己公司相關項目的開發,競相購買Java的使用許可證,併為自己的產品開發了相應的Java平台。

發展

1996年初,Sun公司發布了Java的第1個開發工具包(JDK1.0),這是Java發展的里程碑,標志著Java成為了一種獨立的開發工具。

同年9月,約8.3萬個網頁應用了Java來製作。

同年10月,Sun公司發布了Java平台的第1個即時編譯器。

但是人們很快意識到Java1.0並不能用來進行真正的應用開發。

1997年2月,JDK1.1面世,它彌補了JDK1.0的大多明顯缺陷,改進了反射能力,但是仍有局限性。

1998年12月,Sun公司發布了第二代Java平台——JDK1.2。此次發布有幾個不同的版本,分別是標準版J2SE、用於手機等嵌入式設備的微型版J2ME、用於服務器端處理的企業版J2EE。

此次發布是Java發展過程中最重要的里程碑,標志著Java的應用開始普及。

2000年5月,JDK1.3、JDK1.4、J2SE1.3相繼發布。

2001年9月,J2EE1.3發布。

2002年2月,J2SE1.4發布。該版本提高了Java的計算能力,擴展了標準類庫,提高了系統性能。

在此期間,Java Applet採用低調姿態,淡化了客戶端的應用,成為服務器端應用的首選平台

2004年9月,J2SE 1.5版本發布,該版本為重大更新,是自1.1版本以來第一個對Java語言做出重大改進的版本。JavaOne會議后,版本数字從1.5改為5.0。

2005年6月,Java SE 6發布。版本名稱發生了改變,如J2SE改為Java SE、J2ME改為Java ME、J2EE改為Java EE。該版本沒有對語言方面再進行改進,但是改進了其他性能,增強了類庫。

2006年11月,Sun公司將Java技術作為免費軟件對外發布。

2007年3月起,全世界所有開發人員均可對Java源代碼進行修改。

隨着數據中心越來越依賴於商業硬件而不是專用服務器,Sun公司在2009年被Oracle收購。

2011年,Oracle發布Java SE 7。

2014年,Java SE 8發布,近20年以來,該版本有了最大改變,Java 8提供了一種“函數式”編程方式,可以很容易地表述併發執行的計算。

2017年,Java SE 9發布。

Java 9之後,Oracle宣布每6月更新一次版本。

2020年3月,Java SE 14發布。

Java Web的發展

Java一路走來,為了適應時代發展,做出了許多改變。

在早期,Java是為了解決傳統的單機程序設計問題,但由於種種原因,Java後來轉向Web。

Web的最初模樣

Web為了提供交互性內容採取了客戶端/服務器系統,但是交互性完全由服務器提供。服務器產生靜態頁面,這些靜態頁面是事先寫好的,將其提供給能夠解釋並显示的客戶端瀏覽器。

但是我們的需求是千變萬化的,事先寫好的靜態頁面肯定不能滿足我們日益增長的需求。

CGI

CGI(Common Gateway Interfa,通用網關接口),它是一段程序,運行在服務器上,提供同客戶端HTML頁面的接口,通俗的講CGI就像是一座橋,把網頁和WEB服務器中的執行程序連接起來,它把HTML接收的指令傳遞給服務器,再把服務器執行的結果返還給HTML頁。

CGI使網頁變得不是靜態的,而是交互式的。

但是CGI程序的響應時間依賴於發送的數據量的大小、服務器和網絡的負載,一旦網站變得複雜起來,響應就會變得非常慢且難以維護。

Applet

Applet是一種將小程序嵌入到網頁中進行執行的技術,含有Applet的網頁的HTML文件代碼中部帶有<applet></applet>這樣一對標記,當支持Java的網絡瀏覽器遇到這對標記時,就將下載相應的小應用程序代碼並在本地計算機上執行該Applet。

在當時,Java Applet 可以大大提高Web頁面的交互能力和動態執行能力。

Java剛出現時,Applet是最受大家歡欣鼓舞的。但是由於當時的網絡環境的問題,安裝Java運行時環境(JRE)所需要的10MB帶寬對當時的用戶來說太過於恐怖。並且微軟的IE瀏覽器並並沒有包含JRE,所以Applet並沒有得到大規模應用。

Servlet

Servlet(Server+Applet),即服務器端小程序。Servlet能夠按照用戶提交的內容處理並返回相應的資源。可以輸出HTML頁面,動態展示數據。Servlet實現正確的接口就能夠處理Http請求。

但是出現了一個很大的問題:HTML代碼需要在Servlet類中用Java代碼一行一行輸出,表現、邏輯、控制、業務等全部在Servlet類中,造成Servlet類代碼混亂(HTML代碼、Java代碼都寫在一起),重複性高、閱讀性差、開發困難。

下面是使用Servlet在瀏覽器中輸出有一個h1標題的Hello World!字符串的HTML頁面。

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    PrintWriter out = response.getWriter();
    response.setContentType("text/html;charset=utf-8");
    out.println("<html>");
    out.println("<head>");
    out.println("<title>$Title$</title>");
    out.println("</head>");
    out.println("<body>");
    out.println("<h1>Hello World!</h1>");
    out.println("</body>");
    out.println("</html>");
}

可想而知,更複雜的頁面得寫多少代碼!

JSP

為了解決上面的問題,Sun公司開發出了JSP。

JSP(JavaServer Page)是一種動態網頁技術標準。JSP部署於網絡服務器上,可以響應客戶端發送的請求,並根據請求內容動態地生成HTML、XML或其他格式文檔的Web網頁,然後返回給請求者。JSP技術以Java語言作為腳本語言,為用戶的HTTP請求提供服務,並能與服務器上的其它Java程序共同處理複雜的業務需求。

可以將JSP頁面看成是一個能內嵌Java代碼的HTML頁面。

靜態部分(HTML頁面)是模板,可以提前寫好,動態部分(數據)可以由Java代碼動態生成。這樣一來就避免了在單純使用Servlet時HTML代碼和Java代碼都出現在Servlet類中的混亂情況。

JSP在運行時被轉換成Servlet,因此JSP本質是Servlet。

下面是使用JSP做和上面同樣的事情:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
    <h1>Hello World!</h1>
  </body>
</html>

仍使用HTML模板,但不是用Java語句一行一行“堆”出來,這多方便!

使用了JSP后,雖然不用一行行地“堆”HTML頁面了,但是代碼混亂的問題仍沒有解決。表現、邏輯、控制、業務等代碼仍然寫在一塊了,只不過是從Servlet類中挪到JSP中了。

這樣混亂的代碼極不利於團隊開發,如果項目很小,那還能理順,但如果項目非常大的話,那就“剪不斷,理還亂了”。

所以這時候,我們急需將代碼分門別類的碼放整齊。

JSP + JavaBean(Model1模式)

JavaBean 是用Java語言寫成的可重用組件,JavaBean類要求:

  1. 類是公開的
  2. 具有私有成員變量
  3. 具有無參構造器
  4. 具有setter和getter方法

上面已經說了,單純使用JSP會有大量的HTML代碼和Java代碼耦合在一起,不利於開發、維護、分工協作。

我們舉一個登錄的例子:

登錄大致需要以下幾步:

  1. 在瀏覽器中輸入用戶名、密碼
  2. 獲取數據,交給服務器
  3. 把數據處理成合適的形式
  4. 查詢數據庫的用戶表,看是否有一條用戶名和密碼都符合輸入的記錄。如果有,則登錄成功。

單純使用JSP時,以上步驟全部在JSP登錄頁面中完成,該頁面既需要显示登錄表單、又需要獲取、處理數據、還需要查詢數據庫。太亂了!!

有了JavaBean,我們可以將JSP中大量重複的代碼抽取成可重用的組件,封裝成JavaBean,在一定程度上減輕了代碼的耦合度,也在一定意義上實現了分層

比如,用戶本身就是一個對象,我們可以將其抽取成JavaBean,數據的傳遞、查找都可使用JavaBean來完成。

JSP+Servlet+JavaBean(Model2模式——MVC模式)

上面介紹了Servlet、JSP、JSP+JavaBean,雖然在進步,但是都不盡如人意。那為什麼不讓它們結合起來取長補短?

我們之所以使用JavaBean,就是為了讓Java代碼和HTML代碼分離開了,而Servlet也是一個Java類,為什麼不使用它呢?

JSP+JavaBean已經實現了部分分層,使用MVC(Model-View-Controller)模式能進一步分層。

Model(模型):MVC模式的中心組件,處理數據和邏輯。(比如JavaBean,但不止只有JavaBean)

View(視圖):展示數據,渲染頁面。(前端頁面,比如HTML頁面、JSP頁面)

Controller(控制器):從View接收數據,向Model發送數據,即用於控制數據分發。(比如Servlet)

還用上面的登錄例子:

  1. 將JSP登錄頁面作為View

  2. Servlet類(Controller)用於接收用戶輸入的用戶名、密碼,將其封裝為用戶類(JavaBean),然後去數據庫中查詢。如果能查詢到則登錄成功。

  3. 將查詢結果返回給Controller,Controller根據查詢結果選擇一個合適的View,並將數據渲染到頁面,展示給用戶。(在本例中,如果查詢成功,則進入系統主頁;如果查詢不到,則用戶會看到一個登錄失敗頁面)

使用了MVC設計模式后,代碼有了明顯的分層,結構清晰,各司其職。

框架

為了減輕工作量,各種框架逐漸出現在人們的視野中。

所謂框架,可以把它看做一個半成品的項目/軟件,框架中有許多東西別人都已經幫我們寫好了,比如各種類、方法。

我們要做的就是去了解、學習框架,在寫好的框架的基礎上、遵循框架的制定好的規則、按照自己的思路、結合框架的思想、去開發我們自己的項目。

目前Java的Web開發中最出名的框架就是Spring家族了,比如SpringMVC、Spring、SpringBoot。

熟悉、利用這些框架能夠幫助我們快速、優雅地開發出符合需求的項目。

另,框架是別人寫好的輪子,有了它,我們能夠跑得更快,但是別忘了怎麼走。

總結

說了這麼多,相當於陪着Java從出生到現在走了一遍。我們可以看出Java的發展也不是一帆風順的,我認為Java之所以現在還這麼流行,原因之一就是它能夠一直保持更新,一直在根據時代的需求結合自身的特點不斷地做出改變。正如那句話「窮則變,變則通,通則達」。一門語言能做到這樣,身為人更應該要做到。

如有錯誤,還請指正。

參考資料:

  1. 百度百科
  2. 維基百科
  3. Java官方文檔
  4. Java核心卷一(第10版)
  5. Java編程思想(第4版)

文章首發於公眾號『行人觀學』

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

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

【Spring註解驅動開發】關於BeanPostProcessor後置處理器,你了解多少?

寫在前面

有些小夥伴問我,學習Spring是不是不用學習到這麼細節的程度啊?感覺這些細節的部分在實際工作中使用不到啊,我到底需不需要學習到這麼細節的程度呢?我的答案是:有必要學習到這麼細節的程度,而且是有機會、有條件一定要學!吃透Spring的原理和源碼!往往拉開人與人之間差距的就是這些細節的部分,當前只要是使用Java技術棧開發的Web項目,幾乎都會使用Spring框架。而且目前各招聘網站上對於Java開發的要求幾乎清一色的都是熟悉或者精通Spring。所以,你,很有必要學習Spring的細節知識點。

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

BeanPostProcessor後置處理器概述

首先,我們來看下BeanPostProcessor的源碼,看下它到底是個什麼鬼,如下所示。

package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;
public interface BeanPostProcessor {
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

從源碼可以看出:BeanPostProcessor是一個接口,其中有兩個方法,postProcessBeforeInitialization和postProcessAfterInitialization兩個方法,這兩個方法分別是在spring容器中的bean初始化前後執行,所以spring容器中的每一個bean對象初始化前後,都會執行BeanPostProcessor接口的實現類的這兩個方法。

也就是說,postProcessBeforeInitialization方法會在bean實例化和屬性設置之後,自定義初始化方法之前被調用,而postProcessAfterInitialization方法會在自定義初始化方法之後被調用。當容器中存在多個BeanPostProcessor的實現類時,會按照它們在容器中註冊的順序執行。對於自定義BeanPostProcessor實現類,還可以讓其實現Ordered接口自定義排序。

因此我們可以在每個bean對象初始化前後,加上自己的邏輯。實現方式:自定義一個BeanPostProcessor接口的實現類MyBeanPostProcessor,然後在類MyBeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization方法裏面寫上自己的邏輯。

BeanPostProcessor後置處理器實例

我們創建一個MyBeanPostProcessor類,實現BeanPostProcessor接口,如下所示。

package io.mykit.spring.plugins.register.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

/**
 * @author binghe
 * @version 1.0.0
 * @description 測試BeanPostProcessor
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("調用了postProcessBeforeInitialization方法,beanName = " + beanName + ", bean = " + bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("調用了postProcessAfterInitialization,beanName = " + beanName + ", bean = " + bean);
        return bean;
    }
}

接下來,我們運行BeanLifeCircleTest類的testBeanLifeCircle04()方法,輸出的結果信息如下所示。

調用了postProcessBeforeInitialization方法,beanName = animalConfig, bean = io.mykit.spring.plugins.register.config.AnimalConfig$$EnhancerBySpringCGLIB$$e8ab4f2e@56528192
調用了postProcessAfterInitialization,beanName = animalConfig, bean = io.mykit.spring.plugins.register.config.AnimalConfig$$EnhancerBySpringCGLIB$$e8ab4f2e@56528192
Cat類的構造方法...
調用了postProcessBeforeInitialization方法,beanName = cat, bean = io.mykit.spring.plugins.register.bean.Cat@1b1473ab
Cat的postConstruct()方法...
Cat的init()方法...
調用了postProcessAfterInitialization,beanName = cat, bean = io.mykit.spring.plugins.register.bean.Cat@1b1473ab
Cat的preDestroy()方法...
Cat的destroy()方法...

可以看到,postProcessBeforeInitialization方法會在bean實例化和屬性設置之後,自定義初始化方法之前被調用,而postProcessAfterInitialization方法會在自定義初始化方法之後被調用。

也可以讓實現Ordered接口自定義排序,如下所示。

package io.mykit.spring.plugins.register.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

/**
 * @author binghe
 * @version 1.0.0
 * @description 測試BeanPostProcessor
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("調用了postProcessBeforeInitialization方法,beanName = " + beanName + ", bean = " + bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("調用了postProcessAfterInitialization,beanName = " + beanName + ", bean = " + bean);
        return bean;
    }

    @Override
    public int getOrder() {
        return 3;
    }
}

再次運行BeanLifeCircleTest類的testBeanLifeCircle04()方法,輸出的結果信息如下所示。

調用了postProcessBeforeInitialization方法,beanName = animalConfig, bean = io.mykit.spring.plugins.register.config.AnimalConfig$$EnhancerBySpringCGLIB$$b045438a@1ed1993a
調用了postProcessAfterInitialization,beanName = animalConfig, bean = io.mykit.spring.plugins.register.config.AnimalConfig$$EnhancerBySpringCGLIB$$b045438a@1ed1993a
Cat類的構造方法...
調用了postProcessBeforeInitialization方法,beanName = cat, bean = io.mykit.spring.plugins.register.bean.Cat@36c88a32
Cat的postConstruct()方法...
Cat的init()方法...
調用了postProcessAfterInitialization,beanName = cat, bean = io.mykit.spring.plugins.register.bean.Cat@36c88a32
Cat的preDestroy()方法...
Cat的destroy()方法...

BeanPostProcessor後置處理器作用

後置處理器用於bean對象初始化前後進行邏輯增強。spring提供了BeanPostProcessor的很多實現類,例如AutowiredAnnotationBeanPostProcessor用於@Autowired註解的實現,AnnotationAwareAspectJAutoProxyCreator用於SpringAOP的動態代理等等。

除此之外,我們還可以自定義BeanPostProcessor的實現類,在其中寫入需要的邏輯。下面以AnnotationAwareAspectJAutoProxyCreator為例,說明後置處理器是怎樣工作的。我們都知道springAOP的實現原理是動態代理,最終放入容器的是代理類的對象,而不是bean本身的對象,那麼spring是什麼時候做到這一步的?就是在AnnotationAwareAspectJAutoProxyCreator後置處理器的postProcessAfterInitialization方法,即bean對象初始化完成之後,後置處理器會判斷該bean是否註冊了切面,如果是,則生成代理對象注入容器。Spring中的關鍵代碼如下所示。

/**
  * Create a proxy with the configured interceptors if the bean is
  * identified as one to proxy by the subclass.
  * @see #getAdvicesAndAdvisorsForBean
  */
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

好了,咱們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

寫在最後

如果覺得文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公眾號,跟冰河學習Spring註解驅動開發。公眾號回復“spring註解”關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發不再迷茫。

部分參考:https://www.cnblogs.com/dubhlinn/p/10668156.html

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

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

NFC芯片選型及基本電路框架

RFID作為一項專業度較高的技術,在一些公司,可能還會專門招聘專業的RFID工程師。本篇闡述的涉及到的只是基本選型設計、電路框架,關於RFID天線調試、低功耗檢卡調試等,後續再其他篇章會繼續更新!

 

NFC(Near Field Communication)芯片選型:

 

主要考量點:

芯片支持的協議、是否支持低功耗檢卡、是否能過金融認證、芯片價格

 

芯片支持協議:

  • ISO14443A/B、ISO15693、 ISO18092 和 ISO21481 等

  • ISO14443A 卡:Mifare 系列、 Ultralight 系列、 Plus 系列、 CPU 卡系列等。

  • ISO14443B 卡:身份證、 SR176、 SRI512 等。

  • ISO15693:NXP 的 ICODE 系列、 TI 的 Tag_it HF-I、 ST LRI 等。

  • ISO18092:包括讀卡模式、卡模式、點對點通信模式。

  • ISO21481:在 ISO18092 基礎上兼容 ISO15693 協議。

  • LPCD 功能:芯片低功耗檢測卡片功能。沒有卡片靠近時,芯片處於低功耗狀態, 僅需10uA 電流,就能完成卡片偵測, 當卡片靠近時,芯片偵測到卡片,喚醒單片機讀卡。

  • 金融認證:PBOC2.0/3.0 標準、 EMV 標準

 

電路架構:

 

NFC芯片外部電路通常由以下幾個部分組成:供電電路、通信接口電路、天線電路、振蕩電路;

供電電路:主要包括模擬電源AVDD、数字電源DVDD、發射器電源TVDD、引腳電源PVDD、測試引腳電源PVDD2;

a. 如果需要提高發射功率可提高TVDD的電壓,例如5V供電的TVDD形成的發射功率會比3V的要強;

b. 芯片的供電電流通常在幾十到幾百mA,主要的能量消耗在發射器的電路上。例如FM175xx的天線發射電流在100mA,RC663則可以達250mA,因此選擇供電芯片、電感器件時,需要注意留足余量;

c.讀卡芯片天線13.56MHz的正弦波信號會幹擾電源,為減少傳導干擾,可以在電源端加π型濾波器,但為減少電路設計冗餘度,一般情況下不添加。

 

通信接口:

通常都支持SPI/I2C/UART,一般通過外部引腳配置選擇,為方便升級,可做兼容設計;

 

天線設計:

天線電路主要由4部分組成:EMC濾波、匹配電路、天線、接收電路。以FM17550為例,如下:

濾波電路:

由L1、C1組成的低通濾波器用於濾除13.56MHz的衍生諧波,該濾波器截止頻率應設計在14MHz以上。L1電感不可靠近擺放,以免互相干擾(互感效應)。濾波電路元件匹配公式:f=1/(2π√LC)

 

匹配電路:

用於調節發射負載和諧振頻率。射頻電路功率受芯片內阻和外阻抗影響,當芯片內阻和外阻抗一致時,發射功率效率最高。C2是負載電容,天線感量越大,C2取值越小。C3是諧振電容,取值和天線電感量直接相關,使得諧振頻率在13.56MHz。

 

接收電路:

C4濾除直流信號,R2和R3組成分壓電路,使得RX接收端正弦波信號幅度在1.5-3V之間。

 

天線:

  • 由R1電阻(通常是1ohm或0ohm)和印製PCB組成。

  • 天線越大,讀卡距離越遠,當天線面積達到5cm x 5cm以後,再增大天線,讀卡距離沒有明顯提升。

  • 天線線寬建議選擇0.5mm – 1mm。天線大於5cm x 5cm不能多於3圈,小於3cm x 3cm不能小於4圈

  • 為減小EMC輻射干擾,需要將PCB走線轉角處畫成圓弧。

  • 天線區域內和天線邊緣禁止將信號、電源、地線畫成圈或者半圓,天線圈內不可有大面積金屬物體、金屬鍍膜,避免引起磁場渦流效應造成能力嚴重損耗。

  • 天線PCB繞線方式是相對的,不是同向。

  • 天線電路設計元件的精度應控制在2%以內,否則容易導致天線諧振頻點偏差,導致讀卡性能嚴重下降,產品一致性難以保證

天線大小和讀卡距離關係

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

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

測試同學動手搭個簡易web開發項目

技術棧

node.js, vue.js, axios, python, django, orm, restful api, djangorestframework, mysql, nginx, jenkins.

環境配置

操作系統

Windows 7 旗艦版,Service Pack 1。

前端

Node.js

>node -v
v12.18.0
>npm -v
6.14.4

Vue.js

>vue -V(大寫)
@vue/cli 4.4.1

後端

Python

>python --version
Python 3.7.2

Django

>python -m django --version
3.0.7

數據庫

MySQL

>mysqladmin --version
mysqladmin  Ver 8.0.19 for Win64 on x86_64 (MySQL Community Server - GPL)

命令行登錄mysql,

>mysql -u root -p
Enter password: ******

查詢數據庫,

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| new_schema         |
| performance_schema |
| sakila             |
| sys                |
| world              |
+--------------------+
7 rows in set (0.00 sec)

代理

Nginx

在nginx安裝目錄執行start nginx,瀏覽器訪問http://localhost:80,

持續集成

Jenkins

安裝后,會自動打開http://localhost:8080/,

軟件安裝過程就不贅述了,聰明的你一定知道怎麼安。

項目搭建

本文的目的是走通整個項目的鏈路,於是會“弱化”掉系統功能的實現。

創建後端工程

執行django-admin startproject djangotest創建項目。

cd djangotest,執行python manage.py startapp myapp創建應用。

python manage.py runserver,啟動服務,訪問http://localhost:8000/,

創建RESTful API

安裝mysqlclient和djangorestframework,

pip --default-timeout=6000 install -i https://pypi.tuna.tsinghua.edu.cn/simple mysqlclient
pip --default-timeout=6000 install -i https://pypi.tuna.tsinghua.edu.cn/simple djangorestframework

在settings.py中,添加’rest_framework’和’myapp’,

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'rest_framework',

    'myapp',
]

同時修改數據庫配置,

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'NAME': 'world',
        'USER': 'root',
        'PASSWORD': '123456'
    }
}

在myapp\models.py添加model,model叫做HellloDjango,有2個字段id和name,

from django.db import models

# Create your models here.


class HelloDjango(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(null=False, max_length=64, unique=True)

執行python manage.py makemigrations,提交,

>python manage.py makemigrations
Migrations for 'myapp':
  myapp\migrations\0001_initial.py
    - Create model HelloDjango

執行python manage.py migrate,創建,

>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, myapp, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying myapp.0001_initial... OK
  Applying sessions.0001_initial... OK

看看數據庫,新增了auth_和django_開頭的表,以及model映射的表myapp_hellodjango,

mysql> show tables;
+----------------------------+
| Tables_in_world            |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| city                       |
| country                    |
| countrylanguage            |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
| myapp_hellodjango          |
+----------------------------+
14 rows in set (0.00 sec)

插入2條測試數據,

mysql> insert into myapp_hellodjango(name) values('hello');
Query OK, 1 row affected (0.09 sec)

mysql> insert into myapp_hellodjango(name) values('django');
Query OK, 1 row affected (0.20 sec)

mysql> select * from myapp_hellodjango;
+----+--------+
| id | name   |
+----+--------+
|  2 | django |
|  1 | hello  |
+----+--------+
2 rows in set (0.00 sec)

照着官網的例子,在myapp目錄下新增urls.py,添加rest代碼,

from django.conf.urls import url, include
from rest_framework import routers, serializers, viewsets

from .models import HelloDjango


# Serializers define the API representation.
class HelloSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = HelloDjango
        fields = ['id', 'name']


# ViewSets define the view behavior.
class HelloViewSet(viewsets.ModelViewSet):
    queryset = HelloDjango.objects.all()
    serializer_class = HelloSerializer


# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r'hello', HelloViewSet)

urlpatterns = [
    url(r'demo/', include(router.urls)),
]

在djangotest下的urls.py中添加路由,

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('myapp.urls'))
]

通過這2個urls.py文件的指定,api接口的路徑為,/api/demo/hello。

執行python manage.py runserver啟動服務,使用postman來調用http://127.0.0.1:8000/api/demo/hello/。先發1個post請求,往數據庫新增1條數據,

再發1個get請求,會看到返回了3條數據,2條預先插入的數據,1條post請求新增的數據,

創建前端工程

在djangotest根目錄下,執行vue create vuetest,創建vue工程。

默認安裝,一路回車,啪啪啪。

開始創建,

Vue CLI v4.4.1
a  Creating project in D:\cicd\vuetest.
a  Initializing git repository...
aa Installing CLI plugins. This might take a while...

創建成功,

a  Successfully created project vuetest.
a  Get started with the following commands:

 $ cd vuetest
 $ npm run serve

執行cd vuetestnpm run serve,前端工程就啟動起來了,訪問http://localhost:8080/,Welcome to Your Vue.js App,

前端調後端接口

此時djangotest的目錄結構為,

├─djangotest
│  ├─djangotest
│  ├─myapp  # app
│  ├─vuetest  # 前端
│  ├─manage.py

修改vuetest\src\components\HelloWorld.vue,添加{{info}},用來展示後端api返回的數據,

<div class="hello">
  {{info}}
    <h1>{{ msg }}</h1>

同時在<script>中使用axios添加ajax請求,請求http://127.0.0.1:8000/api/demo/hello/,將response.data賦值給info,

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data() {
    return {
        info: 123
    }
  },
  mounted () {
    this.$axios
      .get('http://127.0.0.1:8000/api/demo/hello/')
      .then(response => (this.info = response.data))
      .catch(function (error) { // 請求失敗處理
        console.log(error);
      });
  }
}
</script>

為了運行起來,需要安裝axios,

npm install --save axios

並在vuetest\src\main.js中引入,

import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'

Vue.config.productionTip = false

Vue.prototype.$axios = axios;

new Vue({
  render: h => h(App)
}).$mount('#app')

分別啟動後端和前端服務,

python manage.py runserver
cd vuetest
npm run serve

嚯!ajax請求失敗了,F12可以看到報錯信息,

localhost/:1 Access to XMLHttpRequest at ‘http://127.0.0.1:8000/api/demo/hello/’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

django的端口是8000,vue的端口是8080,vue在請求django的時候,出現了跨域問題。瀏覽器有個同源策略,域名+端口+協議都相同才認為是同一來源。

通過配置django來解決,先安裝django-cors-headers,

pip install django-cors-headers

在settings.py中添加中間件和開關,

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',  # 添加
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

CORS_ORIGIN_ALLOW_ALL = True  # 添加

此時vue就可以請求到django提供的接口了,http://localhost:8080/

前後端結合

vuetest目錄下創建vue.config.js,這是因為django只能識別static目錄下的靜態文件,這裏指定vue生成靜態文件時套一層static目錄,

module.exports = {
    assetsDir: 'static'
};

在vuetest目錄下執行npm run build,生成靜態文件到vuetest/dist文件夾。

修改urls.py,指定django的模板視圖,

from django.conf.urls import url
from django.contrib import admin
from django.urls import path, include
from django.views.generic import TemplateView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('myapp.urls')),
    url(r'^$', TemplateView.as_view(template_name="index.html")),
]

在settings.py中配置模板目錄為dist文件夾,

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ['vuetest/dist'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

指定靜態文件目錄為vuetest/dist/static,

# Add for vuejs
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "vuetest/dist/static"),
]

瀏覽器訪問http://localhost:8000/,显示的不再是django的歡迎頁面,而是vue的頁面。

前後端結合完成。vue的8080可以停了。

Nginx轉發

nginx常用3個命令,啟動,重新加載,停止,

nginx start
nginx -s reload
nginx -s stop

修改\conf\nginx.conf,監聽端口改為8090,添加轉發proxy_pass http://localhost:8000;

   server {
        listen       8090;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
            proxy_pass http://localhost:8000;
        }

執行nginx start,瀏覽器訪問http://localhost:8090/,也能正常訪問djangotest。

通過nginx將8090轉發到了8000。

持續集成

本來想弄個pipline的,無奈家裡這台破機器安裝失敗,windows也沒有linux對jenkins支持好,只能將就做個雞肋版本。

New Item,命名為vuetest,添加vue的build腳本,

d:
cd D:\cicd\djangotest\vuetest
npm run build

New Item,命名為djangotest,添加django的build腳本,

d:
cd D:\cicd\djangotest
python manage.py runserver

直接執行會報錯python不是可運行命令。添加python環境變量,在首頁左下角,

把路徑D:\Python37添加為環境變量path並保存,

建好的這2個job就可以用來編譯vue和啟動django了,

專註測試,堅持原創,只做精品。歡迎關注公眾號『東方er』
版權申明:本文為博主原創文章,轉載請保留原文鏈接及作者。

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

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

Pytorch入門——手把手帶你配置雲服務器環境

本文始發於個人公眾號:TechFlow,原創不易,求個關注

今天這篇是Pytorch專題第一篇文章。

大家好,由於我最近自己在學習Pytorch框架的運用,並且也是為了響應許多讀者的需求,推出了這個Pytorch專題。由於這個專題是周末加更的,所以不能保證更新進度,我盡量和其他專題一樣,每周一更。

Pytorch簡介

Pytorch底層是Torch框架,Torch框架是一個科學計算框架,擁有一個與Numpy類似的張量操作庫。非常靈活,但是它的語言是Lua,比較小眾,因此沒有廣泛流行。

後來開發團隊在Torch的基礎上包裝了一層Python的Api,使得我們可以通過Python來進行調用。它是由Facebook的人工智能小組開發維護的,目前在業內也非常流行,尤其是學術界,幾乎清一色的Pytorch。它擁有兩個最大的優點,一個是動態網絡,像是TensorFlow等框架定義出來的神經網絡是靜態的,一旦寫死不能輕易改變。但是Pytorch我們可以零延遲地改變任何神經網絡。第二個有點是具有強大的GPU加速計算的工具,Pytorch的GPU加速非常好用。

另外Pytorch的語法更加簡潔規範,更加Pythonic,學習曲線也更平穩一些。寫出來的代碼更加容易理解,更適合初學者。

當然由於誕生的時間還短,並且在工業界的普及度還不如TensorFlow,所以它也有一些短板,比如一些底層的文檔不夠完善,一些功能欠缺等等。在我個人的學習和使用當中,我的體驗非常好,因此如果你沒有學過深度學習的框架的話,推薦使用它作為你的第一門框架。

雲服務器

既然是深度學習的框架,那麼最好是能夠擁有GPU環境。但是對於我們大多數人而言,GPU環境並不是一個容易的事情。比如我是Mac黨,本身的機器就沒有N卡,外接也不方便。當然沒有GPU用CPU硬肝也是可以的,不過發熱很嚴重,對電腦也有損傷。所以,最好的辦法就是租借網上的GPU雲服務器或者是雲服務。

推薦一下滴滴雲服務器,我個人使用下來體驗還不錯,最便宜的只要兩塊多一個小時,應該比網吧上網便宜。最近在搞巨大活動,包年雲GPU只要2200,簡直是白菜價中的白菜價。注意這個優惠只能第一筆下單的時候享受。如果感興趣的話可以訪問鏈接:https://i.didiyun.com/2cvmFVGpCjz

查看原文

因為我是特邀用戶,所以我拿到了內部優惠的大師碼,如果要購買其他GPU雲服務器的話,可以在付款的時候輸入我的大師碼2323,可以再享受9折優惠。

當然你也可以購買裝好環境的Notebook,或者是按照時常購買。Notebook好處是預裝了各種環境,上手可用,但是缺點是不支持定製化,一些操作不太方便。畢竟有了雲服務器可以自己搭建Notebook,但是Notebook並不支持服務器的功能。

如果你想要使用其他雲服務器平台,可以查看我之前的文章

想要學深度學習但是沒有GPU?我幫你找了一些不錯的平台

環境配置

為了防止一些小白租借了機器不會用,接下來提供一下配置環境的詳細教程(基於滴滴雲)。如果你用的別家的服務器,由於環境不一定完全相同,所以可能並不一定適用,只能當做參考。

總之我們整個流程是:安裝Python3,安裝jupyter,配置jupyter遠程訪問,安裝Pytorch

這些是明面上的流程,如果機器環境不健全,還會有一些隱藏流程。比如說普通的Linux環境需要配置apt-get,還需要下載git,wget等常用工具。如果沒有cuda驅動的話,還需要自己安裝cuda配置。如果其中的步驟出現了問題,還需要分析問題的原因解決問題。所以說配置環境說起來簡單,但是實際操作的時候問題並不少。

安裝Python3

yum install python36

安裝 jupyter notebook

sudo pip3 install ipython jupyter notebook lab

設置jupyter的密碼

jupyter notebook password

生成jupyter notebook的配置,這個配置默認不存在,需要我們通過這個命令來生成。

jupyter notebook --generate-config

運行之後,會返回配置文件所在的路徑:

我們用vim打開,修改其中的幾行:

c.NotebookApp.ip='0.0.0.0'
c.NotebookApp.open_browser = False
c.NotebookApp.port =8888 # 可以自己指定

我們需要將本地的端口和遠程進行綁定,這樣我們就可以在本地打開遠程的jupyter了。這一行代碼當中我們將本地的8899綁定了遠程的8888端口。這裏的8888端口就是上面配置當中的遠程的jupyter端口。

ssh dc2-user@116.85.10.225 -L 127.0.0.1:8899:127.0.0.1:8888

如果你喜歡的話,還可以安裝一下jupyter lab

sudo pip3 install jupyterlab

使用方法和notebook類似,在遠程執行命令開啟jupyter

jupyter lab --allow-root

啟動沒有問題的話,我們在本地訪問:http://localhost:8899就可以打開Jupyter,輸入密碼之後就可以使用了。

當我們用完了雲服務器之後,要記得刪除機器。但由於平台機器數量有限,根據當前平台的設置,關機之後依然會為用戶保留資源,但是前提是需要付費。如果你是按時租的服務器顯然不能接受自己沒在使用還需要付費。針對這個問題,我們可以通過快照來解決。我們在關機之前先創建快照

然後刪除雲服務器,下次重新創建服務器的時候選擇從快照創建。這樣下次創建的機器還可以保留當前的配置和數據,而且也不需要扣費了。如果你嫌麻煩的話,還可以加我的微信聯繫我,我幫你找到工作人員加入關機免扣費的白名單

我們用pip安裝一下Pytorch和一些其他需要用到的包。安裝好了,之後,我們通過torch.cuda.is_avaiable()查看一下cuda的情況,如果輸出是True,那說明已經安裝好了。

tensor

Pytorch當中很重要的一個概念就是tensor,它直譯過來是張量,TensorFlow當中的tensor也是這個意思。我們可以認為tensor是一個高維的數組。當它是0維的時候就是一個數,一個標量。當它是一維的時候就是一個向量,二維的時候是一個矩陣,多維的時候是高維的數組。它和Numpy當中的數組類似,不過Tensor可以使用GPU進行加速。

我們通過torch當中的接口來初始化tensor,和Numpy當中的數組類似,它支持好幾種初始化的方式。

empty函數創建一個指定大小的未初始化的tensor,當中的元素內容是不可保證的。

rand創建一個隨機初始化的數組:

ones和zeros創建全是0或者全是1的數組:

我們可以傳入類型指定元素的類型

我們也可以通過現成的數組創建tensor:

這些創建函數都有對應的like方法,可以傳入一個已有的tensor,創建出一個和它一樣大小的新的tensor來。

這裏只是列舉了常用的幾種,官方的api當中還有其他的幾種創建tensor的方式:

除此之外,tensor還支持從numpy的數組當中創建,我們可以利用from_numpy函數來實現:

同理,我們也可以通過numpy函數,從一個tensor得到numpy的數組:

還可以通過tolist方法將tensor轉化成Python的數組:

函數 功能
Tensor(*sizes) 基礎構造函數
tensor(data,) 類似np.array的構造函數
ones(*sizes) 全1Tensor
zeros(*sizes) 全0Tensor
eye(*sizes) 對角線為1,其他為0
arange(s,e,step) 從s到e,步長為step
linspace(s,e,steps) 從s到e,均勻切分成steps份
rand/randn(*sizes) 均勻/標準分佈
normal(mean,std)/uniform(from,to) 正態分佈/均勻分佈
randperm(m) 隨機排列

我們在創建tensor的時候不僅可以指定它們的類型,還可以指定它們存放的設備。比如是CPU還是GPU。當然前期我們暫時用不到這點,只需要記得即可。

總結

這是Pytorch的第一篇文章,我們簡單了解了一下這個框架,以及它當中tensor這個數據結構。簡單來說,我們可以用常用的Numpy去類比它。基本上Numpy當中有的功能它都有,它還有一些自己特性Numpy沒有的api。但不管怎麼說,萬變不離其宗,tensor的用處就是為了方便我們處理數據的

關於Pytorch中tensor的用法還有很多,實在是沒有辦法在一篇文章當中窮盡,所以這裏只是簡單介紹,具體的用法將會放在下一篇文章當中,讓我們下周再見吧。

如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

本文使用 mdnice 排版

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

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

MySQL按指定字符合併及拆分

按照指定字符進行合併或拆分是經常碰到的場景,MySQL在合併的寫法上比較簡單,但是按指定字符拆分相對比較麻煩一點(也就是要多寫一些字符)。本文將舉例演示如何進行按照指定字符合併及拆分。

1、 合併

MySQL數據庫中按照指定字符合併可以直接用group_concat來實現。

創建測試表

mysql> create table  tb_group(id int auto_increment primary key ,col1 varchar(20));
Query OK, 0 rows affected (0.01 sec)

插入測試數據

mysql> insert into  tb_group(col1) values('a'),('c'),('dddd'),('ewdw'),('vxgdh');;
Query OK, 5 rows affected (0.01 sec)
Records: 5  Duplicates: 0  Warnings: 0

合併col1字段的內容

默認是按照逗號進行合併的,例如:

mysql> select group_concat(col1) from tb_group; 
+---------------------+
| group_concat(col1)  |
+---------------------+
| a,c,dddd,ewdw,vxgdh |
+---------------------+
1 row in set (0.01 sec)

指定分隔符合併,例如指定使用 ||  符號進行合併

mysql> select group_concat(col1,'||') from tb_group; 
+-------------------------------+
| group_concat(col1,'||')       |
+-------------------------------+
| a||,c||,dddd||,ewdw||,vxgdh|| |
+-------------------------------+
1 row in set (0.00 sec)

注意

默認情況下,合併后的長度不能超過1024,否則結果會被截斷

例如,我再寫個腳本插入一些數據

#  使用shell腳本來實現
vim  test_insert.sh
#   添加如下內容 

#!/bin/bash
# gjc

for i in  {1..1025}
do
    mysql -uroot -p'123456' --socket=/data/mysql3306/tmp/mysql.sock  -e "insert into testdb.tb_group1(col1)values('a') "
done

#  運行腳本插入數據 
sh test_insert.sh
mysql> select  count(*)from tb_group;
+----------+
| count(*) |
+----------+
|     1030 |
+----------+
1 row in set (0.00 sec)

再進行合併

mysql> select group_concat(col1)cols, length(group_concat(col1)) col_len from tb_group\G
*************************** 1. row ***************************
   cols: a,c,dddd,ewdw,vxgdh,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,
col_len: 1024
1 row in set, 2 warnings (0.01 sec)

可以看出,結果中總長度字節只有1024

對於這種情況,實際使用時肯定是不滿足的,如何解決呢?其實此長度與MySQL數據庫的group_concat_max_len參數有直接關係(默認為1024)

mysql> show global variables like 'group_concat_max_len';
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| group_concat_max_len | 1024  |
+----------------------+-------+
1 row in set (0.08 sec)

那我們調整一下參數看看

/* 修改全局參數,這樣所有的新連接都會生效 */
mysql> set  global group_concat_max_len=102400;
Query OK, 0 rows affected (0.01 sec)

/* 修改本會話參數,這樣當前連接不用退出也可以生效 */
mysql> set  session  group_concat_max_len=102400;
Query OK, 0 rows affected (0.00 sec)

mysql> show global variables like 'group_concat_max_len';
+----------------------+--------+
| Variable_name        | Value  |
+----------------------+--------+
| group_concat_max_len | 102400 |
+----------------------+--------+
1 row in set (0.00 sec)

mysql> show  variables like 'group_concat_max_len';
+----------------------+--------+
| Variable_name        | Value  |
+----------------------+--------+
| group_concat_max_len | 102400 |
+----------------------+--------+
1 row in set (0.01 sec)

再合併一下看看

mysql> select group_concat(col1)cols, length(group_concat(col1)) col_len from tb_group\G
*************************** 1. row ***************************
   cols: a,c,dddd,ewdw,vxgdh,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a
col_len: 2069
1 row in set (0.01 sec)

這樣結果就對了。因此生產環境中 該參數建議調整為合適的大小。

(Tips:Oracle數據庫中可以使用listagg或wm_concat等多種方式實現,也比較簡單,可以自行測試)

2、 拆分

按指定字符拆分字符串,也是比較常見的場景。但是MySQL數據庫中字符串的拆分沒有其他數據庫那麼方便(其他數據庫直接有拆分函數),且需要藉助mysql庫中的mysql.help_topic表來輔助實現。例子如下:

創建測試表及數據

mysql> create table tb_split(id int primary key auto_increment,col1 varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> insert into  tb_split(col1) values('a,b,c,d'),('c,a,g,h');
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0

按照逗號拆分

mysql> SELECT a.id, substring_index(substring_index(a.col1, ',', b.help_topic_id + 1), ',',- 1) NAME  

FROM tb_split a JOIN mysql.help_topic b

ON b.help_topic_id < (length(a.col1) - length(REPLACE(a.col1, ',', '')) + 1); +----+------+ | id | NAME | +----+------+ | 1 | a | | 1 | b | | 1 | c | | 1 | d | | 2 | c | | 2 | a | | 2 | g | | 2 | h | +----+------+ 8 rows in set (0.00 sec)

這樣也就實現了拆分。

按指定字符拆分

如果是其他分隔符的,修改瑞陽的分隔符字段即可。

mysql> insert into  tb_split(col1) values('a|v|f');
Query OK, 1 row affected (0.00 sec)

mysql> select  * from tb_split;
+----+---------+
| id | col1    |
+----+---------+
|  1 | a,b,c,d |
|  2 | c,a,g,h |
|  3 | a|v|f   |
+----+---------+
3 rows in set (0.01 sec)

mysql> SELECT a.id, substring_index(substring_index(a.col1, '|', b.help_topic_id + 1), '|',- 1) col_split  FROM tb_split a JOIN mysql.help_topic b ON b.help_topic_id < (length(a.col1) - length(REPLACE(a.col1, '|', '')) + 1) where a.id=3;
+----+-----------+
| id | col_split |
+----+-----------+
|  3 | a         |
|  3 | v         |
|  3 | f         |
+----+-----------+
3 rows in set (0.00 sec)

這樣就完成按照指定字符的合併及拆分了。

3、 結語

本文介紹了MySQL常用的合併及拆分方法,對於擅長寫SQL的同學也可以使用其他方式實現,以便解決權限不足(例如拆分時需要使用mysql庫的help_topic表的權限)等情況下的需求。

想了解更多內容或參与技術交流可以關注微信公眾號【數據庫乾貨鋪】或進技術交流群溝通。

 

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

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

自來水源遭廢油污染 河內數十萬戶受影響

摘錄自2019年10月16日中央社報導

河內市若干區域民眾10日起發現自來水有異味,主管單位獲報後取樣送驗,相關單位隨後查出自來水水源上游小溪被歹徒偷倒廢油,使水質受污染。目擊者說,8日看到一輛貨車在小溪偷倒廢油,當地翌日下大雨,大量廢油流出大河,再往下游流去。

河內市政府15日公布自來水檢驗結果,水中苯乙烯(Styrene)含量超標1.3倍至3.65倍,是水源廢油污染所致,勸告民眾停止飲用遭污染自來水,指示相關單位安排水罐車免費供水給受影響社區民眾。

事件影響當地數十萬戶居民,由於自來水供應商和主管單位對此事處理進度緩慢,引起民眾激烈反應。

 

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

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

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

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

※教你寫出一流的銷售文案?

※超省錢租車方案

稻米進口米價暴跌 菲農民世界糧食日上街抗議

摘錄自2019年10月16日中央社報導

菲律賓總統杜特蒂(Rodrigo Duterte)2月簽署稻米關稅法,取消稻米進口數量限制,允許民間貿易商選擇國家進口稻米,盼藉此改善國內稻米供應情形,降低市場價格。

但在16日世界糧食日這一天,菲律賓農民運動(KMP)、全國務農婦女聯盟(Amihan)和守護稻米(Bantay Bigas)等農民組織率領10多名農民,帶著全空的鍋瓢到農業部前抗議稻米關稅法讓農民無米可下鍋。他們表示稻米關稅法(RTL)實施後,越南、泰國的廉價稻米進口使菲律賓米價暴跌,嚴重影響農民生計。

菲律賓農民運動主席、農業改革部(DAR)前部長馬利安諾(Rafael Mariano)說,稻米關稅法實施後,「世界糧食日」在菲律賓已變成「世界無糧食日」。

因應米價暴跌和農民訴求,馬尼拉當局正研議對進口稻米設定建議零售價。與此同時,「守護稻米」和婦女組織「嘉布瑞拉」(Gabriela)也發起連署,預計11月把連署書呈交參眾兩院議員,希望促成廢除稻米關稅法。

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

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

氣候變遷示威者中斷倫敦交通 與通勤民眾爆衝突

摘錄自2019年10月17日中央社報導

自「反抗滅絕」10月7日在倫敦發動新一波公民不服從運動以來,社運人士17日中斷了英國倫敦東區的鐵路交通,社群媒體影片顯示,一名爬上地鐵車廂的示威者與憤怒的通勤民眾爆發肢體衝突。

英國交通警察局(BTP)表示,警方正因應在靠近倫敦金絲雀碼頭(Canary Wharf)金融區的沙德韋爾(Shadwell)、斯特拉特福(Stratford)及坎寧鎮(Canning Town)發生的事件。警方在聲明中指出:「已經展開逮捕行動,警方正致力迅速恢復服務。」

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

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

舊金山煉油廠兩儲存槽爆炸起火 或跟地震有關

摘錄自2019年10月16日ET Today報導

美國舊金山灣區一座煉油廠的儲存設施15日下午爆炸釀成大火。火勢在約4小時後被控制,無人傷亡。由於當地附近在前一晚曾發生芮氏規模4.5的地震,當局正調查爆炸是否跟地震有關。

發生爆炸的煉油廠位於舊金山市西北面50公里的Crockett地區。消防部門指出,爆炸的是兩個共裝有約25萬加侖乙醇的儲存槽,呼籲附近居民留在家中,緊閉門窗以免吸入有毒氣體。

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

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?