백엔드 개발 Golang Gorm: 사용자 정의 데이터 유형 미리보기

Gorm: 사용자 정의 데이터 유형 미리보기

Sep 13, 2024 pm 08:15 PM

돌아온 것을 환영합니다 여러분?! 오늘은 데이터베이스에서 데이터를 앞뒤로 이동할 때 직면할 수 있는 특정 사용 사례에 대해 논의합니다. 먼저 오늘의 과제에 대한 경계를 설정하겠습니다. 실제 사례에 충실하기 위해 미군의 개념을 몇 가지 빌려보겠습니다. 우리가 할 일은 경찰관의 경력 성적을 저장하고 읽을 수 있는 작은 소프트웨어를 작성하는 것입니다.

Gorm의 사용자 정의 데이터 유형

우리 소프트웨어는 육군 장교를 해당 등급으로 처리해야 합니다. 처음에는 쉬워 보일 수 있으며 여기서는 사용자 정의 데이터 유형이 필요하지 않을 수도 있습니다. 하지만 이 기능을 보여주기 위해 기존과는 다른 방식으로 데이터를 표현해 보겠습니다. 덕분에 Go 구조체와 DB 관계 간의 사용자 지정 매핑을 정의하라는 요청을 받았습니다. 또한 데이터를 구문 분석하려면 특정 논리를 정의해야 합니다. 프로그램의 대상을 살펴보며 이를 확장해 보겠습니다.

처리할 사용 사례

쉽게 설명하기 위해 그림을 사용하여 코드와 SQL 개체 간의 관계를 묘사해 보겠습니다.

Gorm: Sneak Peek of Custom Data Types

각 컨테이너에 대해 하나씩 집중적으로 살펴보겠습니다.

Go 구조체?

여기서 두 개의 구조체를 정의했습니다. Grade 구조체는 군사 등급의 비완전한 목록을 보유하고 있습니다 ?️. 이 구조체는 데이터베이스의 테이블이 아닙니다. 반대로 Officer 구조체에는 ID, 이름 및 Grade 구조체에 대한 포인터가 포함되어 지금까지 Officer가 달성한 등급을 나타냅니다.

DB에 장교를 쓸 때마다 grades_achieved 열에는 달성한 성적(Grade 구조체에서 true인 성적)로 채워진 문자열 배열이 포함되어야 합니다.

DB 관계?

SQL 객체의 경우 Officer 테이블만 있습니다. id 및 name 열은 설명이 필요하지 않습니다. 그런 다음 문자열 컬렉션에 장교의 등급을 포함하는 grades_achieved 열이 있습니다.

데이터베이스에서 장교를 디코딩할 때마다 grades_achieved 열을 구문 분석하고 Grade 구조체의 일치하는 "인스턴스"를 생성합니다.

동작이 표준 동작이 아니라는 것을 눈치챘을 것입니다. 원하는 방식으로 이를 이행하려면 몇 가지 준비를 해야 합니다.

여기서 모델의 레이아웃은 의도적으로 지나치게 복잡해졌습니다. 가능하다면 더 간단한 해결책을 고수하십시오.

사용자 정의 데이터 유형

Gorm은 사용자 정의 데이터 유형을 제공합니다. 이는 검색을 정의하고 데이터베이스에/에서 저장하는 데 있어 뛰어난 유연성을 제공합니다. Scanner와 Valuer ?라는 두 가지 인터페이스를 구현해야 합니다. 전자는 DB에서 데이터를 가져올 때 적용할 사용자 지정 동작을 지정합니다. 후자는 데이터베이스에 값을 쓰는 방법을 나타냅니다. 둘 다 우리가 필요로 하는 비전통적인 매핑 논리를 달성하는 데 도움이 됩니다.

우리가 구현해야 하는 함수의 서명은 Scan(값 인터페이스{}) 오류와 Value()(driver.Value, 오류)입니다. 이제 코드를 살펴보겠습니다.

코드

이 예제의 코드는 domain/models.go와 main.go라는 두 파일에 있습니다. 첫 번째 것부터 모델(Go에서는 구조체로 번역됨)을 다루겠습니다.

domain/models.go 파일

먼저 이 파일의 코드를 제시하겠습니다.

package models

import (
 "database/sql/driver"
 "slices"
 "strings"
)

type Grade struct {
 Lieutenant bool
 Captain    bool
 Colonel    bool
 General    bool
}

type Officer struct {
 ID             uint64 `gorm:"primaryKey"`
 Name           string
 GradesAchieved *Grade `gorm:"type:varchar[]"`
}

func (g *Grade) Scan(value interface{}) error {
 // we should have utilized the "comma, ok" idiom
 valueRaw := value.(string)
 valueRaw = strings.Replace(strings.Replace(valueRaw, "{", "", -1), "}", "", -1)
 grades := strings.Split(valueRaw, ",")
 if slices.Contains(grades, "lieutenant") {
 g.Lieutenant = true
 }
 if slices.Contains(grades, "captain") {
 g.Captain = true
 }
 if slices.Contains(grades, "colonel") {
 g.Colonel = true
 }
 if slices.Contains(grades, "general") {
 g.General = true
 }
 return nil
}

func (g Grade) Value() (driver.Value, error) {
 grades := make([]string, 0, 4)
 if g.Lieutenant {
 grades = append(grades, "lieutenant")
 }
 if g.Captain {
 grades = append(grades, "captain")
 }
 if g.Colonel {
 grades = append(grades, "colonel")
 }
 if g.General {
 grades = append(grades, "general")
 }
 return grades, nil
}
로그인 후 복사

이제 관련 부분을 강조해 보겠습니다.:

  1. Grade 구조체에는 소프트웨어에서 예측한 등급만 나열됩니다.
  2. Officer 구조체는 엔터티의 특성을 정의합니다. 이 엔터티는 DB의 관계입니다. 우리는 두 가지 Gorm 표기법을 적용했습니다.
    1. ID 필드의 gorm:"primaryKey"를 관계의 기본 키로 정의합니다
    2. gorm:"type:varchar[]" GradesAchieved 필드를 DB의 varchar 배열로 매핑합니다. 그렇지 않으면 별도의 DB 테이블 또는 임원 테이블의 추가 열로 변환됩니다
  3. Grade 구조체는 Scan 기능을 구현합니다. 여기서는 원시 값을 가져와 조정하고 g 변수에 일부 필드를 설정한 다음 반환합니다
  4. Grade 구조체는 Value 함수를 값 수신자 유형으로 구현합니다(이번에는 수신자를 변경할 필요가 없으며 * 참조를 사용하지 않습니다). 임원 테이블의 grades_achieved 열에 쓸 값을 반환합니다

이 두 가지 방법 덕분에 DB 상호작용 중에 Grade 유형을 전송하고 검색하는 방법을 제어할 수 있습니다. 이제 main.go 파일을 살펴보겠습니다.

main.go 파일?

여기서 DB 연결을 준비하고 객체를 관계로 마이그레이션하고(ORM은 O객체 RelationMapping을 나타냄) 삽입 및 가져오기를 수행합니다. 논리를 테스트하기 위한 기록입니다. 아래는 코드입니다:

package main

import (
 "encoding/json"
 "fmt"
 "os"

 "gormcustomdatatype/models"

 "gorm.io/driver/postgres"
 "gorm.io/gorm"
)

func seedDB(db *gorm.DB, file string) error {
 data, err := os.ReadFile(file)
 if err != nil {
  return err
 }
 if err := db.Exec(string(data)).Error; err != nil {
  return err
 }
 return nil
}

// docker run -d -p 54322:5432 -e POSTGRES_PASSWORD=postgres postgres
func main() {
 dsn := "host=localhost port=54322 user=postgres password=postgres dbname=postgres sslmode=disable"
 db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
 if err != nil {
 fmt.Fprintf(os.Stderr, "could not connect to DB: %v", err)
  return
 }
 db.AutoMigrate(&models.Officer{})
 defer func() {
 db.Migrator().DropTable(&models.Officer{})
 }()
 if err := seedDB(db, "data.sql"); err != nil {
 fmt.Fprintf(os.Stderr, "failed to seed DB: %v", err)
  return
 }
 // print all the officers
 var officers []models.Officer
 if err := db.Find(&officers).Error; err != nil {
 fmt.Fprintf(os.Stderr, "could not get the officers from the DB: %v", err)
  return
 }
 data, _ := json.MarshalIndent(officers, "", "\t")
 fmt.Fprintln(os.Stdout, string(data))

 // add a new officer
 db.Create(&models.Officer{
 Name: "Monkey D. Garp",
 GradesAchieved: &models.Grade{
 Lieutenant: true,
 Captain:    true,
 Colonel:    true,
 General:    true,
  },
 })
 var garpTheHero models.Officer
 if err := db.First(&garpTheHero, 4).Error; err != nil {
 fmt.Fprintf(os.Stderr, "failed to get officer from the DB: %v", err)
  return
 }
 data, _ = json.MarshalIndent(&garpTheHero, "", "\t")
 fmt.Fprintln(os.Stdout, string(data))
}
로그인 후 복사

Now, let's see the relevant sections of this file. First, we define the seedDB function to add dummy data in the DB. The data lives in the data.sql file with the following content:

INSERT INTO public.officers
(id, "name", grades_achieved)
VALUES(nextval('officers_id_seq'::regclass), 'john doe', '{captain,lieutenant}'),
(nextval('officers_id_seq'::regclass), 'gerard butler', '{general}'),
(nextval('officers_id_seq'::regclass), 'chuck norris', '{lieutenant,captain,colonel}');
로그인 후 복사

The main() function starts by setting up a DB connection. For this demo, we used PostgreSQL. Then, we ensure the officers table exists in the database and is up-to-date with the newest version of the models.Officer struct. Since this program is a sample, we did two additional things:

  • Removal of the table at the end of the main() function (when the program terminates, we would like to remove the table as well)
  • Seeding of some dummy data

Lastly, to ensure that everything works as expected, we do a couple of things:

  1. Fetching all the records in the DB
  2. Adding (and fetching back) a new officer

That's it for this file. Now, let's test our work ?.

The Truth Moment

Before running the code, please ensure that a PostgreSQL instance is running on your machine. With Docker ?, you can run this command:

docker run -d -p 54322:5432 -e POSTGRES_PASSWORD=postgres postgres
로그인 후 복사

Now, we can safely run our application by issuing the command: go run . ?

The output is:

[
        {
                "ID": 1,
                "Name": "john doe",
                "GradesAchieved": {
                        "Lieutenant": true,
                        "Captain": true,
                        "Colonel": false,
                        "General": false
                }
        },
        {
                "ID": 2,
                "Name": "gerard butler",
                "GradesAchieved": {
                        "Lieutenant": false,
                        "Captain": false,
                        "Colonel": false,
                        "General": true
                }
        },
        {
                "ID": 3,
                "Name": "chuck norris",
                "GradesAchieved": {
                        "Lieutenant": true,
                        "Captain": true,
                        "Colonel": true,
                        "General": false
                }
        }
]
{
        "ID": 4,
        "Name": "Monkey D. Garp",
        "GradesAchieved": {
                "Lieutenant": true,
                "Captain": true,
                "Colonel": true,
                "General": true
        }
}
로그인 후 복사

Voilà! Everything works as expected. We can re-run the code several times and always have the same output.

That's a Wrap

I hope you enjoyed this blog post regarding Gorm and the Custom Data Types. I always recommend you stick to the most straightforward approach. Opt for this only if you eventually need it. This approach adds flexibility in exchange for making the code more complex and less robust (a tiny change in the structs' definitions might lead to errors and extra work needed).

Keep this in mind. If you stick to conventions, you can be less verbose throughout your codebase.

That's a great quote to end this blog post.
If you realize that Custom Data Types are needed, this blog post should be a good starting point to present you with a working solution.

Please let me know your feelings and thoughts. Any feedback is always appreciated! If you're interested in a specific topic, reach out, and I'll shortlist it. Until next time, stay safe, and see you soon!

위 내용은 Gorm: 사용자 정의 데이터 유형 미리보기의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌
Will R.E.P.O. 크로스 플레이가 있습니까?
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

Debian Openssl의 취약점은 무엇입니까? Debian Openssl의 취약점은 무엇입니까? Apr 02, 2025 am 07:30 AM

보안 통신에 널리 사용되는 오픈 소스 라이브러리로서 OpenSSL은 암호화 알고리즘, 키 및 인증서 관리 기능을 제공합니다. 그러나 역사적 버전에는 알려진 보안 취약점이 있으며 그 중 일부는 매우 유해합니다. 이 기사는 데비안 시스템의 OpenSSL에 대한 일반적인 취약점 및 응답 측정에 중점을 둘 것입니다. DebianopensSL 알려진 취약점 : OpenSSL은 다음과 같은 몇 가지 심각한 취약점을 경험했습니다. 심장 출혈 ​​취약성 (CVE-2014-0160) :이 취약점은 OpenSSL 1.0.1 ~ 1.0.1F 및 1.0.2 ~ 1.0.2 베타 버전에 영향을 미칩니다. 공격자는이 취약점을 사용하여 암호화 키 등을 포함하여 서버에서 무단 읽기 민감한 정보를 사용할 수 있습니다.

PPROF 도구를 사용하여 GO 성능을 분석하는 방법은 무엇입니까? PPROF 도구를 사용하여 GO 성능을 분석하는 방법은 무엇입니까? Mar 21, 2025 pm 06:37 PM

이 기사는 프로파일 링 활성화, 데이터 수집 및 CPU 및 메모리 문제와 같은 일반적인 병목 현상을 식별하는 등 GO 성능 분석을 위해 PPROF 도구를 사용하는 방법을 설명합니다.

GO에서 단위 테스트를 어떻게 작성합니까? GO에서 단위 테스트를 어떻게 작성합니까? Mar 21, 2025 pm 06:34 PM

이 기사는 GO에서 단위 테스트 작성, 모범 사례, 조롱 기술 및 효율적인 테스트 관리를위한 도구를 다루는 것에 대해 논의합니다.

GO에서 플로팅 포인트 번호 작업에 어떤 라이브러리가 사용됩니까? GO에서 플로팅 포인트 번호 작업에 어떤 라이브러리가 사용됩니까? Apr 02, 2025 pm 02:06 PM

Go Language의 부동 소수점 번호 작동에 사용되는 라이브러리는 정확도를 보장하는 방법을 소개합니다.

Go 's Crawler Colly의 큐 스레드의 문제는 무엇입니까? Go 's Crawler Colly의 큐 스레드의 문제는 무엇입니까? Apr 02, 2025 pm 02:09 PM

Go Crawler Colly의 대기열 스레딩 문제는 Colly Crawler 라이브러리를 GO 언어로 사용하는 문제를 탐구합니다. � ...

GO FMT 명령은 무엇이며 왜 중요한가요? GO FMT 명령은 무엇이며 왜 중요한가요? Mar 20, 2025 pm 04:21 PM

이 기사는 Go Programming의 Go FMT 명령에 대해 논의합니다. GO 프로그래밍은 공식 스타일 지침을 준수하도록 코드를 형식화합니다. 코드 일관성, 가독성 및 스타일 토론을 줄이기위한 GO FMT의 중요성을 강조합니다. 모범 사례 fo

프론트 엔드에서 백엔드 개발로 전환하면 Java 또는 Golang을 배우는 것이 더 유망합니까? 프론트 엔드에서 백엔드 개발로 전환하면 Java 또는 Golang을 배우는 것이 더 유망합니까? Apr 02, 2025 am 09:12 AM

백엔드 학습 경로 : 프론트 엔드에서 백엔드 초보자로서 프론트 엔드에서 백엔드까지의 탐사 여행은 프론트 엔드 개발에서 변화하는 백엔드 초보자로서 이미 Nodejs의 기초를 가지고 있습니다.

Debian의 PostgreSQL 모니터링 방법 Debian의 PostgreSQL 모니터링 방법 Apr 02, 2025 am 07:27 AM

이 기사는 데비안 시스템에서 PostgresQL 데이터베이스를 모니터링하는 다양한 방법과 도구를 소개하여 데이터베이스 성능 모니터링을 완전히 파악할 수 있도록 도와줍니다. 1. PostgreSQL을 사용하여 빌드 인 모니터링보기 PostgreSQL 자체는 데이터베이스 활동 모니터링 활동을위한 여러보기를 제공합니다. PG_STAT_REPLICATION : 특히 스트림 복제 클러스터에 적합한 복제 상태를 모니터링합니다. PG_STAT_DATABASE : 데이터베이스 크기, 트랜잭션 커밋/롤백 시간 및 기타 주요 지표와 같은 데이터베이스 통계를 제공합니다. 2. 로그 분석 도구 PGBADG를 사용하십시오

See all articles