下载工作台
Go 数据库实战

GORM 模型与 CRUD

试读上半部分 · 解锁后可读全文

第 10 章 · GORM 实体映射与 gorm.DB

本章目标:在 shop-db 中使用纯 GORM(不含 Spring)配置 gorm.Open;掌握 gorm.Model / @Table / @Column 映射 ch07 DDL;理解 gorm.DB持久化上下文(Persistence Context);完成 Category / Product 的 CRUD 与 slug + is_published + price(分) 映射;为 ch11 关系映射打基础。

学时建议:6~7 小时(含 3 小时跟练)

前置:完成 go-database ch07~ch09;了解 GORM 为 GORM 参考实现。


10.1 场景说明:ORM 替代手写 database/sql

ch08 的 ProductRepository 手写 SQL 与 mapRow。表结构稳定后,GORM 将行映射为 Go 对象,由提供者(GORM)生成 SQL。

层次技术本章
SQLch07 DDL基线
database/sqlch08 QueryContext/ExecContext对照
连接池ch09 sqlx 连接池数据源
GORMgorm.DB本章
Spring Datach13后续
纯 GORM:不引入 spring-boot-starter-data-jpa,仅用 jakarta.persistence API + GORM,理解底层再学 Spring 封装。

10.2 go mod 依赖

<properties>
    <hibernate.version>6.4.4.Final</hibernate.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.hibernate.orm</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>${hibernate.version}</version>
    </dependency>
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>sqlx 连接池</artifactId>
        <version>5.1.0</version>
    </dependency>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.3.0</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate.orm</groupId>
        <artifactId>hibernate-jpamodelgen</artifactId>
        <version>${hibernate.version}</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
依赖作用
hibernate-coreGORM 实现
sqlx 连接池非 JTA 环境供 GORM 使用
hibernate-jpamodelgen编译期生成 Metamodel(Criteria 用,ch11)

10.3 gorm.Open 配置

src/main/resources/META-INF/gorm.Open

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
             version="3.0">
    <persistence-unit name="shop-db" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.GORMPersistenceProvider</provider>

        <class>com.example.shopdb.jpa.entity.Category</class>
        <class>com.example.shopdb.jpa.entity.Product</class>

        <properties>
            <property name="jakarta.persistence.jdbc.driver"
                      value="com.mysql.cj.jdbc.Driver"/>
            <property name="jakarta.persistence.jdbc.url"
                      value="mysql DSNmysql://127.0.0.1:3306/shop_db?useSSL=false&amp;serverTimezone=Asia/Shanghai"/>
            <property name="jakarta.persistence.jdbc.user" value="shop_user"/>
            <property name="jakarta.persistence.jdbc.password" value="shop_pass"/>

            <property name="hibernate.dialect"
                      value="org.hibernate.dialect.MySQLDialect"/>
            <property name="hibernate.hbm2ddl.auto" value="validate"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>

            <!-- 使用 sqlx 连接池 -->
            <property name="hibernate.connection.provider_class"
                      value="org.hibernate.hikaricp.internal.sqlx 连接池ConnectionProvider"/>
            <property name="hibernate.hikari.maximumPoolSize" value="10"/>
            <property name="hibernate.hikari.minimumIdle" value="2"/>
        </properties>
    </persistence-unit>
</persistence>
属性说明
transaction-type=RESOURCE_LOCAL应用代码手动 em.getTransaction()
hbm2ddl.auto=validate只校验实体与表一致,不自动建表(配合 ch12 golang-migrate)
show_sql打印 SQL(开发)

10.4 gorm.DBFactory 与 gorm.DB

package com.example.shopdb.jpa;

import jakarta.persistence.gorm.DB;
import jakarta.persistence.gorm.DBFactory;
import jakarta.persistence.Persistence;

public final class JpaBootstrap {

    private static final gorm.DBFactory EMF =
        Persistence.creategorm.DBFactory("shop-db");

    private JpaBootstrap() {}

    public static gorm.DB opengorm.DB() {
        return EMF.creategorm.DB();
    }

    public static void shutdown() {
        EMF.close();
    }
}

生命周期

Persistence.creategorm.DBFactory("shop-db")  // 昂贵,应用级单例
    → emf.creategorm.DB()                    // 轻量,每用例/线程
    → em.close()                                     // 释放持久化上下文
emf.close()                                          // 关闭池与缓存
对象作用域线程安全
gorm.DBFactory应用
gorm.DB请求/用例❌ 勿跨线程

10.5 持久化上下文(Persistence Context)

持久化上下文是 GORM 管理的一级缓存 + 变更追踪区域:

gorm.DB.find(Product.class, 1L)
    → 先查 PC(持久化上下文)
    → 未命中则 SELECT,结果放入 PC,实体状态 = Managed

em.persist(newProduct)
    → 新实体进入 PC,状态 = Managed(Transient → Managed)

修改 managed 实体字段
    → 脏检查(dirty checking),flush 时自动 UPDATE

em.detach(product)
    → 移出 PC,状态 = Detached

em.close()
    → PC 销毁,Managed → Detached
状态说明
Transientnew Product(),GORM 不知晓
Managed在 PC 内,变更自动同步
Detached曾管理,已脱离 EM
Removedremove() 标记删除,flush 时 DELETE

示例:脏检查

gorm.DB em = JpaBootstrap.opengorm.DB();
em.getTransaction().begin();
Product p = em.find(Product.class, 1L);
p.setStock(p.getStock() - 1);   // 无显式 UPDATE
em.getTransaction().commit();   // flush → UPDATE products SET stock=...
em.close();

10.6 gorm.Model Category 映射

gorm.Model
@Table(name = "categories")
public class Category {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(nullable = false, length = 64) private String name;
    @Column(nullable = false, unique = true, length = 64) private String slug;
    @Column(name = "sort_order") private int sortOrder = 0;
    protected Category() {}
    public Category(String name, String slug) { this.name = name; this.slug = slug; }
}
注解作用
gorm.Model / @Table声明实体与表名
@GeneratedValue(IDENTITY)MySQL AUTO_INCREMENT
@Column列名、nullable、length

10.7 gorm.Model Product 映射(slug / is_published / price 分)

gorm.Model
@Table(name = "products")
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "category_id", nullable = false)
    private Category category;

    @Column(nullable = false, length = 128)
    private String title;

    @Column(nullable = false, unique = true, length = 128)
    private String slug;

    @Column(nullable = false)
    private long price;  // 分,5990 = ¥59.90

    @Column(name = "is_published", nullable = false)
    private boolean published = false;

    @Column(nullable = false)
    private int stock = 0;

    protected Product() {}

    public Product(String title, String slug, long priceCents, Category category) {
        this.title = title;
        this.slug = slug;
        this.price = priceCents;
        this.category = category;
    }
    // getters / setters 省略
}

以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。