SpringBoot 集成Spring Data JPA时,解决@Column注解name属性不生效,并兼容原来存在表的方案

作者简介:
🌹 作者:暗夜91
🤟 个人主页:暗夜91的主页
📝 微信公众号: 程序员的知识站 ,关注本人公众号,学习更多前沿知识。


具体问题:

最近项目遇到了一个因为Spring Data JPA命名策略的原因导致的数据库表与实体类映射错误的问题,具体问题如下:

数据库中已经有一批业务表了,字段命名方式是用 “_” 连接,然后其他组的人员创建数据表的时候,使用的是驼峰方式直接在数据库中创建的表,没有使用统一的命名方式。数据表中已经跑入了大量的数据,如果修改表结构的话,需要联系其他组人员修改代码,重新跑入数据,成本太高,不现实,只能在java端通过其他技术解决。

在java代码中,创建该表对应的实体类时,在对应的驼峰字段上使用@Column注解进行标注,结果发现启动项目的时候,@Column的注解没有生效,在表中创建了大量使用 “_” 连接的冗余字段。

原因分析:

数据库中的业务表是使用JPA自动在数据库中创建,所以表中 “_” 连接的字段是JPA生成的,查询发现JPA默认的命名策略是默认的隐式策略,会把所有的字段名称转为小写,并且在大写字母前面加上横线,比如testId映射到数据库中是test_id。

解决方法:

元凶找到了,接下来就是解决方法了。

Spring Data JPA是在 Hibernate 的基础之上的封装实现,所以我们可以使用Hibernate的命名规则,在properties文件中加入一下配置:

spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

重新启动服务,然后对数据表与实体类的映射比对,发现这次映射成功,可以在代码中使用jpa操作数据表了。

解决了数据表的映射以后,本以为万事大吉,结果查看启动日志时发现了问题,日志中显示在原本存在的业务表中添加了好多驼峰形式的字段,瞬间就麻瓜了,发现这种处理方式虽然解决了数据表的映射问题,但是对原来已经创建的业务表并没有兼容,只好苦逼的一个个业务表处理,把多余的字段删除掉。

分析发现,如果在配置文件中加入命名策略配置后,会对整个项目全部生效,这明显不符合需求。


经过一番实验,最后找到了一种迂回的解决办法,利用多数据源的方式,具体实现如下:

1)增加Datasource配置文件

@Configuration
public class DataSourceConfig {

    @Bean(name = "primaryDataSource")
    @Qualifier("primaryDataSource")
    @Primary
    @ConfigurationProperties(prefix="spring.datasource.primary")
    public DataSource dvDataSource() {
        return DataSourceBuilder.create().build();
    }

}

2)增加主数据源JPA配置文件

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef="entityManagerFactoryPrimary",
        transactionManagerRef="transactionManagerPrimary",
        basePackages= { "com.test.business" })
public class PrimaryJPAConfig {

   @Autowired
    private HibernateProperties hibernateProperties;

    @Autowired
    private JpaProperties jpaProperties;

    @Autowired
    @Qualifier("primaryDataSource")
    private DataSource dvDataSource;

    @Primary
    @Bean(name="entityManagerPrimary")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
    }

    @Primary
    @Bean(name="entityManagerFactoryPrimary")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary(EntityManagerFactoryBuilder builder) {
        Map<String,Object> properties = hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(),new HibernateSettings());
        return builder.dataSource(dvDataSource)
                .properties(properties)
                .packages("com.test.business") //次数据源实体所在位置
                .persistenceUnit("primaryPersistenceUnit")
                .build();
    }

    @Primary
    @Bean(name="transactionManagerPrimary")
    public PlatformTransactionManager transactionManagerDv(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
    }
}

3)增加数据表使用的数据源

与主数据源配置相比,需要为数据源对应的数据源添加命名策略的配置如下

properties.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");

具体配置文件如下:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef="entityManagerFactoryData",
        transactionManagerRef="transactionManagerData",
        basePackages= { "com.test.data" })
public class DataJPAConfig {

   @Autowired
    private HibernateProperties hibernateProperties;

    @Autowired
    private JpaProperties jpaProperties;

    @Autowired
    @Qualifier("primaryDataSource")
    private DataSource dvDataSource;

    @Bean(name="entityManagerData")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactoryData(builder).getObject().createEntityManager();
    }

    @Bean(name="entityManagerFactoryData")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryData(EntityManagerFactoryBuilder builder) {
        Map<String,Object> properties = hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(),new HibernateSettings());
        properties.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
        return builder.dataSource(dvDataSource)
                .properties(properties)
                .packages("com.test.data") //次数据源实体所在位置
                .persistenceUnit("dataPersistenceUnit")
                .build();
    }

    @Bean(name="transactionManagerData")
    public PlatformTransactionManager transactionManagerData(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactoryData(builder).getObject());
    }
}

4)创建数据表实体类所在package

在和业务表package路径同级的位置创建数据表的package路径,在上面的代码中,业务表package为com.test.business,数据表package为com.test.data

之后所有数据表的操作都放在com.test.data路径下,这样就可以实现在不影响原有业务表的情况下,解决数据表的@Column注解不生效的问题。

<think>根据用户描述,问题是在使用Spring Boot JPA保存实体出现错误:Unknown column 'biz_category' in 'field list'。这个错误JPA在生成SQL语句,试图访问一个数据库中不存在的列。我们需要逐步排查原因。 ### 问题分析步骤 1. **检查实体类字段与数据库列名的映射** - JPA默认使用驼峰命名转下划线命名(如 `bizCategory` → `biz_category`) - 如果实体类中有 `bizCategory` 字段,JPA会尝试查找 `biz_category` 列 2. **检查数据库结构** - 确认数据库中是否存在 `biz_category` 列(注意大小写) - 如果不存在,可能是DDL自动生成失败或手动建遗漏 3. **检查JPA配置** - 是否开启自动DDL(如 `spring.jpa.hibernate.ddl-auto=update`) - 查看启动日志是否有创建错误(类似引用[2]的DDL执行错误) 4. **检查字段注解** - 是否使用了 `@Column` 指定了不存在的列名 - 是否因特殊关键字导致DDL失败(如引用[2]中 `desc` 是MySQL保留字) 5. **检查关联关系映射** - 如果 `bizCategory` 是关联对象(如 `@ManyToOne`),则可能生成外键列 ### 解决方案 #### 情况1:实体类字段名与数据库列名不匹配 假设实体类有字段: ```java private String bizCategory; // JPA默认映射为biz_category列 ``` 但数据库实际列名为 `biz_type`,解决方案: ```java @Column(name = "biz_type") // 显式指定列名 private String bizCategory; ``` #### 情况2:数据库缺少列(DDL未自动创建) - **原因**:可能因字段类型问题导致建失败(如引用[2]中 `desc` 是关键字) - **解决**: 1. 检查启动日志是否有 `Error executing DDL` 2. 为关键字字段添加转义: ```java @Column(name = "`desc`") // MySQL用反引号,H2用双引号 private String description; ``` 3. 如果使用自动DDL,确保配置正确: ```properties spring.jpa.hibernate.ddl-auto=update ``` #### 情况3:关联关系映射错误 如果 `bizCategory` 是另一个实体引用: ```java @ManyToOne private BizCategory bizCategory; // 默认生成biz_category_id列 ``` 但数据库实际列名为 `category_id`,解决方案: ```java @ManyToOne @JoinColumn(name = "category_id") // 显式指定外键列 private BizCategory bizCategory; ``` #### 情况4:字段命名不一致(大小写问题) - MySQL在Linux下默认区分大小写,确保实体类字段名与数据库列名大小写一致 - 可在实体类统一使用小写列名映射: ```java @Column(name = "biz_category") private String bizCategory; ``` ### 排查流程 1. **确认数据库结构** 执行 `SHOW CREATE TABLE your_table;` 查看是否存在 `biz_category` 列。 2. **检查JPA日志** 在 `application.properties` 开启SQL日志: ```properties spring.jpa.show-sql=true logging.level.org.hibernate.SQL=DEBUG logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE ``` 观察保存操作生成的SQL语句中引用的列名。 3. **验证DDL自动更新** - 如果存在,设置 `ddl-auto=create` 启动项目后检查结构 - 如果存在,设置 `ddl-auto=update` 后重启,观察日志是否有更新列的操作 4. **手动修复结构** 如果自动更新失败,手动添加缺失列: ```sql ALTER TABLE your_table ADD COLUMN biz_category VARCHAR(255); ``` ### 示例修正 假设实体类如下: ```java @Entity public class Product { @Id @GeneratedValue private Long id; private String bizCategory; // 应映射到biz_category列 } ``` 但数据库缺少 `biz_category` 列,解决方案: 1. **通过DDL自动添加** 确保配置 `ddl-auto=update` 重启,观察日志是否执行 `ALTER TABLE`。 2. **通过注解修正列名**(如果实际列名不同) ```java @Column(name = "existing_column") // 实际存在的列名 private String bizCategory; ``` ### 常见陷阱 1. **保留字冲突** 如 `order`、`desc`、`group` 等需转义: ```java @Column(name = "`order`") // MySQL private Integer order; ``` 2. **字段名包含大写字母** 如 `bizCategory` 映射为 `biz_category`,但数据库列名为 `bizcategory`(全小写),解决方案: ```java @Column(name = "bizcategory") private String bizCategory; ``` 3. **MyBatis-Plus注解问题**(参考引用[3]) 如果混用MyBatis-Plus,确保 `@TableField` 和 `@TableId` 的 `value` 一致: ```java @TableField("biz_category") private String bizCategory; ``` ### 总结步骤 1. 开启SQL日志定位具体SQL 2. 核对数据库实际列名 3. 检查实体类字段映射关系 4. 验证DDL配置和日志 5. 修正注解或数据库结构 > 📌 **关键点**:错误信息 `Unknown column 'xxx'` 几乎总是由 **对象字段与数据库列名映射不一致** 引起,通过显式 `@Column` 注解解决90%的问题。 --- ### 相关问题 1. 如何配置Spring Boot JPA的自动建(DDL)功能? 2. JPA实体类中如何正确映射数据库关键字列? 3. Spring Data JPA保存实体出现`DataIntegrityViolationException`如何解决? 4. 如何让JPA在实体类字段名与数据库列名不一致正常运作?</think>在 Java Spring Boot 中使用 JPA 保存实体遇到 `Unknown column 'biz_category' in 'field list'` 错误,通常是由 **实体类字段与数据库列名映射不一致** 引起的。以下是完整的排查和解决方案: --- ### 🔍 问题原因分析(基于错误信息) 1. **字段映射不一致** JPA 默认将驼峰命名的实体字段(如 `bizCategory`)转换为下划线列名(`biz_category`)。如果数据库中缺少该列,则报错。 2. **DDL 自动生成失败** 当使用 `spring.jpa.hibernate.ddl-auto=update` Hibernate 尝试自动同步结构,但可能因字段类型冲突或关键字保留失败(如引用[2])。 3. **实体类注解配置错误** - 缺少 `@Column` 注解显式指定列名 - 使用了错误的字段名(如大小写不一致) - 关联关系映射错误(如 `@ManyToOne` 生成额外列) --- ### ✅ 解决方案步骤 #### 步骤 1:检查数据库结构 ```sql SHOW CREATE TABLE your_table_name; ``` 检查输出中是否存在 `biz_category` 列。若不存在,说明 DDL 同步失败。 #### 步骤 2:修正实体类注解 ```java @Entity @Table(name = "your_table") // 明确指定名 public class YourEntity { // 显式指定列名(防止命名转换问题) @Column(name = "biz_category") // 与数据库列名完全一致 private String bizCategory; // 处理特殊字段(如保留字) @Column(name = "`desc`") // MySQL 保留字需反引号转义(引用[2]) private String description; } ``` #### 步骤 3:解决 DDL 同步问题 1. **检查配置**(`application.properties`): ```properties spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true # 开启SQL日志 ``` 2. **查看启动日志**: 若出现类似引用[2]的 `Error executing DDL`,说明字段类型或保留字冲突: ```java // 错误示例:desc 是 MySQL 保留字 private String desc; // 修正:重命名或转义 @Column(name = "`desc`") private String description; ``` #### 步骤 4:处理关联关系(如存在) 若 `bizCategory` 是关联实体: ```java @ManyToOne @JoinColumn(name = "biz_category_id") // 显式指定外键列名 private BizCategory bizCategory; ``` #### 步骤 5:强制重建(开发环境) 1. 临修改配置: ```properties spring.jpa.hibernate.ddl-auto=create-drop ``` 2. 重启应用使 Hibernate 重新建 3. **重要**:完成后恢复为 `update` 避免数据丢失 --- ### ⚠️ 常见陷阱排查 1. **字段名大小写问题** Linux 系统 MySQL 名/列名默认**区分大小写**,确保注解中的 `name` 值大小写与数据库完全一致。 2. **MyBatis-Plus 混用问题**(参考引用[3]) 若同使用 JPA 和 MyBatis-Plus,检查是否冲突: ```java // 错误:混用不同ORM框架注解 @TableField("biz_category") // MyBatis注解 private String bizCategory; // 正确:统一用JPA注解 @Column(name = "biz_category") private String bizCategory; ``` 3. **实体类未扫描到** 确保主类包含 `@SpringBootApplication` 且在实体类同级或更高包路径。 --- ### 📊 问题排查流程图 ```mermaid graph TD A[报错 Unknown column 'biz_category'] --> B{检查数据库是否存在该列} B -->|存在| C[检查实体类@Column注解映射] B -->|不存在| D[检查DDL自动同步日志] C --> E[确认注解name值与数据库列名一致] D --> F[查看是否有Error executing DDL错误] F -->|有| G[修正字段类型/保留字冲突] F -->|无| H[手动添加缺失列或重启ddl-auto=update] G --> I[重命名字段或添加转义符] H --> J[ALTER TABLE 添加列] ``` --- ### 📝 最终解决方案 1. **立即修复**: 在实体类添加显式列映射: ```java @Column(name = "biz_category") // 与数据库列名完全匹配 private String bizCategory; ``` 2. **根治措施**: - 开启 SQL 日志确认生成的 SQL - 统一命名规范(全小写下划线) - 避免使用数据库保留字作字段名 --- ### 🔍 相关问题 1. 如何解决 JPA 自动创建的保留字冲突问题? 2. Spring Boot JPA 实体类字段名如何正确映射到数据库列? 3. `ddl-auto=update` 不生效如何强制更新结构? 4. JPA 和 MyBatis-Plus 混用如何避免注解冲突? > 通过显式 `@Column` 注解 + DDL 日志分析,90% 的 "Unknown column" 错误可快速解决。若问题仍在,请提供实体类代码片段和结构信息进一步分析。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暗夜91

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值