簡體   English   中英

在 Spring JPA 中設置表名

[英]Set table name in Spring JPA

我想我正在嘗試做一些非常簡單的事情。 使用帶有 JPA 的 Spring Boot (1.3.3.RELEASE) 我想設置一個表名。

@Entity
@Table(name = "MyTable_name")
public class MyTableData {
  ...
}

我在我的數據庫中期望的是一個帶有“MyTable_name”的表。 對我來說似乎完全合理。 但這不會發生。 我得到一個名為“MY_TABLE_NAME”(H2 后端)或“my_table_name”(Postgre 后端)的表。 從這里開始,我將堅持使用 Postgre,因為我的目標是讀取我不控制表名的現有數據庫。

經過一些研究,我發現帖子說我應該使用 spring.jpa.hibernate.naming-strategy 屬性。 這沒有多大幫助。 設置為最常用的 org.hibernate.cfg.ImprovedNamingStrategy 會產生相同的行為:“my_table_name”。 設置為 org.hibernate.cfg.EJB3NamingStrategy 會生成“mytable_name”。 設置為 org.hibernate.cfg.DefaultNamingStrategy 會導致 Spring 內部的應用程序上下文錯誤。

我放棄了自己的編寫,開始查看 org.hibernate.cfg.ImprovedNamingStrategy。 我發現它使用了已棄用的 org.hibernate.cfg.NamingStrategy。 這建議改用 NamingStrategyDelegator。 我查看了它的Java 文檔,但不確定如何申請。 我找到了這個帖子 盡管我很欣賞這個解釋,但在那里嘗試做的事情比我需要的要復雜得多,而且我在應用它時遇到了麻煩。

我的問題是如何讓 Spring JPA 只使用我指定的名稱? NamingStrategyDelegator 是否有新的屬性? 我需要編寫自己的策略嗎?

============ 更新 ==========================

我想我正在收斂於一個答案。 我創建了一個簡單的 Spring 啟動應用程序(與我的生產項目分開)。 我使用 H2 作為后端數據庫。

這個關於Hiberate 5 Naming 的討論非常有幫助。 有了它,我想出了如何在 Hibernate 5 中設置命名策略,如下所示(在 application.properties 中)。

hibernate.implicit_naming_strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
hibernate.physical_naming_strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

我創建了一個通過名稱傳遞的物理命名策略(如 org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl 所做的那樣)並打印出值。 從中我看到表名是我想要的物理命名層。

然后我設置 hibernate.show_sql=true 來顯示生成 SQL。 在生成的 SQL 中,名稱也是正確的。

我正在使用 DatabaseMetaData 檢查表名。

private void showTables() throws SQLException {
    DatabaseMetaData dbMetadata = getConnection().getMetaData();
    ResultSet result = dbMetadata.getTables(null, null, null, new String[] { "TABLE" });
    if (result != null) {
        boolean haveTable = false;
        while (result.next()) {
            haveTable = true;
            getLogger().info("Found table {}", result.getString("TABLE_NAME"));
        }
        if (!haveTable) {
            getLogger().info("No tables found");
        }

    }
}

當我使用上面的代碼時,我仍然看到所有大寫的表名。 這讓我相信 DatabaseMetaData 出於某種原因顯示所有大寫,但其余代碼使用正確的名稱。 [編輯:這個結論是不正確的。 我只是對正在發生的一切感到困惑。 后來的測試顯示 DatabaseMetaData 顯示了大小寫正確的表名。]

這還不是一個完整的答案,因為我的生產代碼中仍有一些我需要調查的奇怪之處。 但它已經接近了,我想發布一個更新,這樣潛在的讀者就不會浪費時間。

這是我通過物理命名策略的過程,以防有人感興趣。 我知道看看其他人做了什么會有所幫助,尤其是在 Spring 迷宮中嘗試查找類和包時。

package my.domain.eric;

import java.io.Serializable;

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NamingStrategyPhysicalLeaveAlone implements PhysicalNamingStrategy, Serializable {
    private static final long serialVersionUID = -5937286882099274612L;

    private static final Logger LOGGER = LoggerFactory.getLogger(NamingStrategyPhysicalLeaveAlone.class);

    protected Logger getLogger() {
        return LOGGER;
    }

    @Override
    public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment context) {
        String nameText = name == null ? "" : name.getText();
        getLogger().info("toPhysicalCatalogName name: {}", nameText);
        return name;
    }

    @Override
    public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment context) {
        String nameText = name == null ? "" : name.getText();
        getLogger().info("toPhysicalSchemaName name: {}", nameText);
        return name;
    }

    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        String nameText = name == null ? "" : name.getText();
        getLogger().info("toPhysicalTableName name: {}", nameText);
        return name;
    }

    @Override
    public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment context) {
        String nameText = name == null ? "" : name.getText();
        getLogger().info("toPhysicalSequenceName name: {}", nameText);
        return name;
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
        String nameText = name == null ? "" : name.getText();
        getLogger().info("toPhysicalColumnName name: {}", nameText);
        return name;
    }
}

我的問題的答案涉及以下內容。

  1. SQL不區分大小寫,但它並不那么簡單。 引用的名字是字面意思。 不帶引號的名稱可以自由解釋。 例如,PostgreSQL將不帶引號的名稱轉換為小寫,而H2將它們轉換為大寫。 因此,PostgreSQL中的select * from MyTable_name查找表mytable_name。 在H2中,相同的查詢會查找MYTABLE_NAME。 在我的例子中,PostgreSQL表是使用引用名稱“MyTable_name”創建的,因此select * from MyTable_name失敗而select * from“MyTable_name”成功。
  2. Spring JPA / Hibernate將不帶引號的名稱傳遞給SQL。
  3. 在Spring JPA / Hibernate中,有三種方法可用於傳遞引用的名稱
    1. 明確引用名稱:@Table(name =“\\”MyTable_name \\“”)
    2. 實現引用名稱的物理命名策略(詳情如下)
    3. 設置一個未記錄的屬性以引用所有表和列名稱:spring.jpa.properties.hibernate.globally_quoted_identifiers = true(請參閱此注釋 )。 這是我的所作所為,因為我也有列名,我需要區分大小寫。

另一個令我感到困惑的原因是許多網站都引用了舊的命名變量hibernate.ejb.naming_strategy或它的彈簧等價物。 對於已經過時的Hibernate 5。 相反,正如我在我的問題更新中提到的,Hibernate 5具有隱式和物理命名策略。

此外,我很困惑,因為有hibernate屬性,然后有Spring屬性。 我正在使用這個非常有用的教程 然而,它顯示了不必要的直接使用hibernate屬性(我在我的更新中列出),然后顯式配置LocalContainerEntityManagerFactoryBean和JpaTransactionManager。 更容易使用Spring屬性並自動拾取它們。 與我相關的是命名策略。

  1. spring.jpa.hibernate.naming.implicit策略
  2. spring.jpa.hibernate.naming.physical策略

要實現物理命名策略,我需要創建一個實現org.hibernate.boot.model.naming.PhysicalNamingStrategy的類,如上所述。 引用名稱實際上非常簡單,因為傳遞給方法的Identifier類管理或不管理引用。 因此,以下方法將引用表名。

@Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
    if (name == null) {
        return null;
    }
    return Identifier.quote(name);
}

我學到的其他東西可能對來到這里尋找答案的人有所幫助。

  1. 使用spring.jpa屬性自動選擇SQL方言。 使用直接休眠時,當我切換到Postgre時,我遇到了SQL錯誤。
  2. 盡管Spring應用程序上下文失敗非常常見,但仔細閱讀錯誤通常會指向解決方案。
  3. DatabaseMetaData正確報告表名,我只是被其他一切搞糊塗了。
  4. 設置spring.jpa.show-sql = true以查看生成的SQL。 非常有用的調試。 允許我看到正在使用正確的表名
  5. spring.jpa.hibernate.ddl-auto至少支持以下值。 create-drop:在條目上創建表,在退出時刪除。 create:在輸入時創建表但在退出時離開。 none:不創建或刪除。 我看到人們使用“更新”作為一個值,但這對我來說失敗了。 (例如這里 。)以下是對選項討論
  6. 我在H2中使用引用的列名稱遇到了麻煩,但沒有進一步調查。
  7. Spring屬性頁面很有用,但描述非常稀疏。

該名稱在實體注釋中指定

@Entity(name = "MyTable_name")
public class MyTableData {
  ...
}

要將@Table(...)指定的確切名稱作為數據庫中的表名,提出了以下解決方案:

application.yaml 文件

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/<dbname>?currentSchema=<schema-name>
    username: <username>
    password: <password>

  jpa:
    show-sql: true

    hibernate:
      # comment out ddl-auto as needed
      ddl-auto: create-drop
      naming:
        # the following property is important for this topic:
        # note that you are going implement the following java class:
        physical-strategy: paul.tuhin.sbtutorial.NamingStrategy

    properties:
      hibernate:
        dialect: org.hibernate.dialect.PostgreSQLDialect
        default_schema: <schema-name>
        format_sql: true
        # this will quote your schema name as well:
        globally_quoted_identifiers: true

paul.tuhin.sbtutorial.NamingStrategy.java 文件:

package paul.tuhin.sbtutorial;

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

public class NamingStrategy extends PhysicalNamingStrategyStandardImpl {
    private static final long serialVersionUID = 1L;

    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        //The table name all converted to uppercase
        String tableName = name.getText();
        
        return Identifier.quote(Identifier.toIdentifier(tableName));
    }
    
    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
        String colnumName = name.getText();

        return Identifier.quote(Identifier.toIdentifier(colnumName));
    }
}

該解決方案改編自(感謝): https : //titanwolf.org/Network/Articles/Article?AID=b0f17470-3cfe-4ebc-9c45-25a462115be5

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM