💻

JPA / Hibernate が発行する SQL のカラム名がスネークケースで困った時にすること

2023/05/02に公開

この記事について

Spring Boot 3 でデータベースアクセス用の Java アプリを作成していた際、JPA / Hibernate が発行する SQL のカラム名およびテーブル名がキャメルケース (camelCase) ではなく、スネークケース (snake_case) になってしまい、解決までに無駄な時間を溶かしてしまったので、備忘録として残しているものです。

結論

はじめに結論を書いておくと、application.properties で以下の 1 行を追加していなかったのが原因です。

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

実際の Spring Boot アプリコード

コミュニティ内で使用する用に書いたチュートリアルコードがあるので、それを見てください。

https://github.com/ymasaoka/mssql-spring-boot-tutorial/tree/main/chapter01/FirstMssqlProject

どんな SQL が発行されていたのか

JPA / Hibernate で発行されていた SQL は、application.properties で指定をしない限り、既定動作でカラム名やテーブル名を自動でスネークケース (snake_case) に変換します。

実際に流れたSQL(ログより抜粋)
select
    c1_0.customerid,
    c1_0.company_name,
    c1_0.email_address,
    c1_0.first_name,
    c1_0.last_name,
    c1_0.middle_name,
    c1_0.modified_date,
    c1_0.name_style,
    c1_0.password_hash,
    c1_0.password_salt,
    c1_0.phone,
    c1_0.rowguid,
    c1_0.sales_person,
    c1_0.suffix,
    c1_0.title
from
    saleslt.customer c1_0

以下のように、Entity クラスでアノテーションを使用して CamelCase のカラム名やテーブル名を明示しても、application.propertiesで 1 行書き忘れると、裏で勝手に snake_case になるのは落とし穴でした。

Customer.java
@Data
@Entity
@Table(schema = "SalesLT", name = "Customer")
public class Customer {
    @Id
    @Column(name = "CustomerID", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int customerId;

    @Column(name = "NameStyle", length = 1, nullable = false)
    private String nameStyle;

    @Column(name = "Title", length = 8)
    private String title;

    @Column(name = "FirstName", length = 100, nullable = false)
    private String firstName;

    @Column(name = "MiddleName", length = 100)
    private String middleName;

    @Column(name = "LastName", length = 100, nullable = false)
    private String lastName;

    @Column(name = "Suffix", length = 10)
    private String suffix;

    @Column(name = "CompanyName", length = 128)
    private String companyName;

    @Column(name = "SalesPerson", length = 256)
    private String salesPerson;

    @Column(name = "EmailAddress", length = 50)
    private String emailAddress;

    @Column(name = "Phone", length = 50)
    private String phone;

    @Column(name = "PasswordHash", length = 128, nullable = false)
    private String passwordHash;

    @Column(name = "PasswordSalt", length = 10, nullable = false)
    private String passwordSalt;

    @Column(name = "rowguid", nullable = false)
    @GeneratedValue(strategy = GenerationType.UUID)
    private String rowguid;

    @Column(name = "ModifiedDate", nullable = false)
    private Calendar modifiedDate;
}

ちなみに、この部分に気が付くまでに、私は 1 時間時間を溶かしてしまいました。
最終的に、以下の海外ニキの記事を見つけることができ、あっ、と気が付くことができたので、大変助かりました。

https://www.baeldung.com/hibernate-field-naming-spring-boot

https://noahhsu.medium.com/spring-jpa-trap-for-camel-case-table-field-name-2fa818e4c3c0

application.properties 修正後の SQL

spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl を設定することで、きちんとアノテーションで明示した通りの SQL になりました。
なお、実際に自分の環境で確認したい方は、application.propertiesspring.jpa.show-sql=true を設定することを忘れずに。

実際に流れたSQL(ログより抜粋)
select
    c1_0.CustomerID,
    c1_0.CompanyName,
    c1_0.EmailAddress,
    c1_0.FirstName,
    c1_0.LastName,
    c1_0.MiddleName,
    c1_0.ModifiedDate,
    c1_0.NameStyle,
    c1_0.PasswordHash,
    c1_0.PasswordSalt,
    c1_0.Phone,
    c1_0.rowguid,
    c1_0.SalesPerson,
    c1_0.Suffix,
    c1_0.title
from
    SalesLT.Customer c1_0

Discussion