web-dev-qa-db-de.com

ImprovedNamingStrategy funktioniert in Ruhezustand 5 nicht mehr

Ich habe eine einfache Spring-JPA-Konfiguration, in der ich Hibernates ImprovedNamingStrategy konfiguriert habe. Das bedeutet, wenn meine Entitätsklasse eine Variable userName enthält, sollte Hibernate diese zum Abfragen der Datenbank in user_name Konvertieren. Diese Namenskonvertierung funktionierte jedoch nicht mehr, nachdem ich auf Hibernate 5 aktualisiert hatte. Ich erhalte den folgenden Fehler:

FEHLER: Unbekannte Spalte 'user0_.userName' in 'Feldliste'

Dies ist meine Hibernate-Konfiguration:

@Configuration
@EnableJpaRepositories("com.springJpa.repository")
@EnableTransactionManagement
public class DataConfig {

    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/test");
        ds.setUsername("root");
        ds.setPassword("admin");
        return ds;
    }


    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(){ 

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setShowSql(Boolean.TRUE);
        vendorAdapter.setDatabase(Database.MYSQL);

        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setDataSource(dataSource());
        factory.setPackagesToScan("com.springJpa.entity");


        Properties jpaProperties = new Properties();

        jpaProperties.put("hibernate.ejb.naming_strategy","org.hibernate.cfg.ImprovedNamingStrategy");
        jpaProperties.put("hibernate.dialect","org.hibernate.dialect.MySQL5InnoDBDialect");

        factory.setJpaProperties(jpaProperties);
        factory.afterPropertiesSet();
        return factory;
    }

    @Bean
    public SharedEntityManagerBean entityManager() {
        SharedEntityManagerBean entityManager = new SharedEntityManagerBean();
        entityManager.setEntityManagerFactory(entityManagerFactory().getObject());
        return entityManager;
    }



    @Bean
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory().getObject());
        return txManager;
    }

    @Bean
    public ImprovedNamingStrategy namingStrategy(){
        return new ImprovedNamingStrategy();
    }
}

Dies ist meine Entitätsklasse:

@Getter
@Setter
@Entity
@Table(name="user")
public class User{

    @Id
    @GeneratedValue
    private Long id;

    private String userName;
    private String email;
    private String password;
    private String role;

}

Ich möchte meine Datenbankfelder nicht explizit innerhalb der @ Column-Annotationen benennen. Ich möchte eine Konfiguration, mit der Camel Case implizit in einen Unterstrich umgewandelt werden kann.

Bitte führen.

36
Anup

Vielen Dank für die Veröffentlichung Ihrer eigenen Lösung. Es hilft mir sehr, die Benennungsstrategie für Hibernate 5 festzulegen!

Die Eigenschaft hibernate.ejb.naming_strategy Von Pre-Hibernate 5.0 scheint in zwei Teile aufgeteilt zu sein:

  • hibernate.physical_naming_strategy
  • hibernate.implicit_naming_strategy

Die Werte dieser Eigenschaften implementieren die Schnittstelle NamingStrategy nicht wie hibernate.ejb.naming_strategy. Für diese Zwecke gibt es zwei neue Schnittstellen:

  • org.hibernate.boot.model.naming.PhysicalNamingStrategy
  • org.hibernate.boot.model.naming.ImplicitNamingStrategy

Ruhezustand 5 bietet nur eine Implementierung von PhysicalNamingStrategy (PhysicalNamingStrategyStandardImpl), bei der angenommen wird, dass die Namen der physischen Bezeichner mit den logischen identisch sind.

Es gibt verschiedene Implementierungen von ImplicitNamingStrategy, aber ich habe keine gefunden, die der alten ImprovedNamingStrategy entspricht. (Siehe: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl)

Also habe ich mein eigenes PhysicalNamingStrategy implementiert, was sehr einfach ist:

public class PhysicalNamingStrategyImpl extends PhysicalNamingStrategyStandardImpl implements Serializable {

 public static final PhysicalNamingStrategyImpl INSTANCE = new PhysicalNamingStrategyImpl();

 @Override
 public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
     return new Identifier(addUnderscores(name.getText()), name.isQuoted());
 }

 @Override
 public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
     return new Identifier(addUnderscores(name.getText()), name.isQuoted());
 }


 protected static String addUnderscores(String name) {
     final StringBuilder buf = new StringBuilder( name.replace('.', '_') );
     for (int i=1; i<buf.length()-1; i++) {
        if (
             Character.isLowerCase( buf.charAt(i-1) ) &&
             Character.isUpperCase( buf.charAt(i) ) &&
             Character.isLowerCase( buf.charAt(i+1) )
         ) {
             buf.insert(i++, '_');
         }
     }
     return buf.toString().toLowerCase(Locale.ROOT);
 }
}

Beachten Sie, dass die addUnderscores() -Methode aus dem Original org.hibernate.cfg.ImprovedNamingStrategy Stammt.

Dann setze ich diese physikalische Strategie in die Datei persistence.xml:

  <property name="hibernate.physical_naming_strategy" value="my.package.PhysicalNamingStrategyImpl" />

Es ist eine Falle, die Benennungsstrategie von Hibernate 5 als vorherige Versionseinstellungen festzulegen.

59
Samuel Andrés

Danke und +1 an Samuel Andrés für die sehr hilfreiche Antwort, es ist jedoch wahrscheinlich eine gute Idee, die handgeschriebene Logik der Schlangenumhüllung zu umgehen. Hier ist die gleiche Lösung mit Guava.

Es wird davon ausgegangen, dass Ihre Entitätsnamen in StandardJavaClassFormat und Spaltennamen in standardJavaFieldFormat geschrieben sind.

Hoffentlich erspart dies einigen Leuten, die in Zukunft hierher kommen, etwas googeln :-)

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import static com.google.common.base.CaseFormat.*;

public class SnakeCaseNamingStrategy extends PhysicalNamingStrategyStandardImpl {

  public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
    return new Identifier(
      UPPER_CAMEL.to(LOWER_UNDERSCORE, name.getText()),
      name.isQuoted()
    );
  }

  public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
    return new Identifier(
      LOWER_CAMEL.to(LOWER_UNDERSCORE, name.getText()),
      name.isQuoted()
    );
  }
}
4
davnicwil

danke für diesen Beitrag. Wenig ärgerlich, dass das Upgrade die Strategie für Tabellen- und Spaltennamen bricht. Anstatt die Logik aus ImprovedNamingStrategy zu kopieren, können Sie auch die Delegierung verwenden.

public class TableNamingStrategy extends PhysicalNamingStrategyStandardImpl {
    private static final String TABLE_PREFIX = "APP_";
    private static final long serialVersionUID = 1L;
    private static final ImprovedNamingStrategy STRATEGY_INSTANCE = new ImprovedNamingStrategy();

    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        return new Identifier(classToTableName(name.getText()), name.isQuoted());
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
        return new Identifier(STRATEGY_INSTANCE.classToTableName(name.getText()), name.isQuoted());
    }

    private String classToTableName(String className) {
        return STRATEGY_INSTANCE.classToTableName(TABLE_PREFIX + className);
    }
}
3
Michael Hegner

Bei jeder Antwort wird die Lösung durch die Implementierung von PhysicalNamingStrategy veröffentlicht. Sie müssen jedoch nur ImplicitNamingStrategy implementieren (und sollten dies auch tun).

Wenn eine Entität die Datenbanktabelle, der sie zugeordnet ist, nicht explizit benennt, müssen wir diesen Tabellennamen implizit bestimmen. Oder wenn ein bestimmtes Attribut die Datenbankspalte, der es zugeordnet ist, nicht explizit benennt, müssen wir diesen Spaltennamen implizit bestimmen. Es gibt Beispiele für die Rolle des Vertrags org.hibernate.boot.model.naming.ImplicitNamingStrategy, um einen logischen Namen zu ermitteln, wenn die Zuordnung keinen expliziten Namen enthielt.

Und der Code kann so einfach sein (unter Verwendung des ursprünglichen addUnderscores wie in anderen Antworten):

public class ImplicitNamingStrategyImpl extends ImplicitNamingStrategyJpaCompliantImpl {

    @Override
    protected Identifier toIdentifier(String stringForm, MetadataBuildingContext buildingContext) {
        return super.toIdentifier(addUnderscores(stringForm), buildingContext);
    }

    protected static String addUnderscores(String name) {
        final StringBuilder buf = new StringBuilder(name.replace('.', '_'));
        for (int i = 1; i < buf.length() - 1; i++) {
            if (Character.isLowerCase(buf.charAt(i - 1))
                    && Character.isUpperCase(buf.charAt(i))
                    && Character.isLowerCase(buf.charAt(i + 1))) {
                buf.insert(i++, '_');
            }
        }
        return buf.toString().toLowerCase(Locale.ROOT);
    }
}
2
ThreeDots

hoffe das hilft:

hibernate.implicit_naming_strategy = .... ImplicitNamingStrategy hibernate.physical_naming_strategy = .... PhysicalNamingStrategyImpl

und hier ist der Code (der nur aus dem vorhandenen Code neu zusammengestellt wurde):

import Java.io.Serializable;
import Java.util.Locale;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

public class PhysicalNamingStrategyImpl extends PhysicalNamingStrategyStandardImpl implements Serializable {

    public static final PhysicalNamingStrategyImpl INSTANCE = new PhysicalNamingStrategyImpl();

    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        return new Identifier(addUnderscores(name.getText()), name.isQuoted());
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
        return new Identifier(addUnderscores(name.getText()), name.isQuoted());
    }

    protected static String addUnderscores(String name) {
        final StringBuilder buf = new StringBuilder( name.replace('.', '_') );
        for (int i=1; i<buf.length()-1; i++) {
            if (
                Character.isLowerCase( buf.charAt(i-1) ) &&
                Character.isUpperCase( buf.charAt(i) ) &&
                Character.isLowerCase( buf.charAt(i+1) )
            ) {
                buf.insert(i++, '_');
            }
        }
        return buf.toString().toLowerCase(Locale.ROOT);
    }

}
1
Ignacio

Keine Guaven- und Apachen-Utensilien

public class PhysicalNamingStrategyImpl extends PhysicalNamingStrategyStandardImpl {

    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        return context.getIdentifierHelper().toIdentifier(
                name.getText().replaceAll("((?!^)[^_])([A-Z])", "$1_$2").toLowerCase(),
                name.isQuoted()
        );
    }

    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
        return context.getIdentifierHelper().toIdentifier(
                name.getText().replaceAll("((?!^)[^_])([A-Z])", "$1_$2").toLowerCase(),
                name.isQuoted()
        );
    }
}
0
SoBeRich