web-dev-qa-db-ja.com

改善されたNamingStrategyはHibernate 5で動作しなくなりました

HibernateのImprovedNamingStrategyを設定した簡単なspring-jpa設定があります。つまり、エンティティクラスに変数userNameがある場合、Hibernateはそれをuser_nameに変換してデータベースを照会する必要があります。しかし、Hibernate 5にアップグレードした後、このネーミング変換は機能しなくなりました。エラーが発生しています。

エラー:「フィールドリスト」の不明な列「user0_.userName」

これは私のHibernate設定です:

@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();
    }
}

これは私のEntityクラスです:

@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;

}

@Columnアノテーション内でデータベースフィールドに明示的に名前を付けたくありません。キャメルケースを暗黙的にアンダースコアに変換できる構成が必要です。

ガイドしてください。

36
Anup

独自のソリューションを投稿していただきありがとうございます。 Hibernate 5の命名戦略を設定するのに非常に役立ちます!

Hibernate 5.0より前の_hibernate.ejb.naming_strategy_プロパティは、2つの部分に分かれているようです。

  • _hibernate.physical_naming_strategy_
  • _hibernate.implicit_naming_strategy_

これらのプロパティの値は、_hibernate.ejb.naming_strategy_のようにNamingStrategyインターフェイスを実装しません。これらの目的のための2つの新しいインターフェイスがあります。

  • _org.hibernate.boot.model.naming.PhysicalNamingStrategy_
  • _org.hibernate.boot.model.naming.ImplicitNamingStrategy_

Hibernate 5は、物理識別子名が論理識別子名と同じであると想定するPhysicalNamingStrategyPhysicalNamingStrategyStandardImpl)の実装を1つだけ提供します。

ImplicitNamingStrategyの実装はいくつかありますが、古いImprovedNamingStrategyに相当するものは見つかりませんでした。 (参照:_org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl_)

そこで、非常に簡単なPhysicalNamingStrategyを実装しました。

_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);
 }
}
_

addUnderscores()メソッドは元の_org.hibernate.cfg.ImprovedNamingStrategy_からのものであることに注意してください。

次に、この物理戦略をpersistence.xmlファイルに設定します。

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

Hibernate 5の命名戦略を以前のバージョン設定として設定することはトラップです。

59
Samuel Andrés

非常に有用な回答をありがとう、サミュエル・アンドレスに+1、しかし、手書きの蛇ケースのロジックを避けることはおそらく良い考えです。 Guavaを使用した同じソリューションを次に示します。

エンティティ名はStandardJavaClassFormatに、列名はstandardJavaFieldFormatに書かれていると仮定しています

うまくいけば、将来ここに来る人の一部をグーグルで救うことを願っています:-)

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

その投稿をありがとう。このアップグレードが少しいらいらするだけでは、テーブルと列の名前の戦略が崩れます。 ImprovedNamingStrategyからロジックをコピーする代わりに、委任を使用することもできます。

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

すべての回答はPhysicalNamingStrategyを実装することでソリューションを投稿しますが、必要な(そして行うべき)ことはImplicitNamingStrategyを実装することだけです。

エンティティがマップ先のデータベーステーブルに明示的に名前を付けない場合、そのテーブル名を暗黙的に決定する必要があります。または、特定の属性がマップ先のデータベース列に明示的に名前を付けていない場合、その列名を暗黙的に決定する必要があります。マッピングで明示的な名前が提供されなかった場合に論理名を決定するためのorg.hibernate.boot.model.naming.ImplicitNamingStrategyコントラクトの役割の例があります。

そして、コードはこれと同じくらい簡単にすることができます(他の回答と同じように元のaddUnderscoresを使用します):

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

お役に立てれば:

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

コードは次のとおりです(既存のコードから再配置されたものです)。

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

グアバとApacheユーティリティはありません

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