ImprovedNamingStrategy不再适用于Hibernate 5

时间:2015-09-07 11:08:46

标签: java hibernate jpa spring-data-jpa hibernate-5.x

我有简单的spring-jpa配置,我配置了Hibernate' ImprovedNamingStrategy。这意味着如果我的实体类有一个变量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();
    }
}

这是我的实体类:

@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注释中明确命名我的数据库字段。我希望我的配置可以隐式地将驼峰转换为下划线。

请指导。

7 个答案:

答案 0 :(得分:58)

感谢您发布自己的解决方案。设置Hibernate 5命名策略对我有很大的帮助!

pre-Hibernate 5.0的hibernate.ejb.naming_strategy属性似乎分为两部分:

  • hibernate.physical_naming_strategy
  • hibernate.implicit_naming_strategy

这些属性的值不像NamingStrategy那样实现hibernate.ejb.naming_strategy接口。为此目的有两个新接口:

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

Hibernate 5只提供PhysicalNamingStrategyPhysicalNamingStrategyStandardImpl)的一个实现,它假定物理标识符名称与逻辑标识符相同。

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命名策略设置为以前的版本设置是一个陷阱。

答案 1 :(得分:3)

感谢SamuelAndrés提供的非常有用的答案,但是避免使用手写的蛇形逻辑可能是一个好主意。这是使用番石榴的相同解决方案。

它假设您的实体名称是用id ' productid ' stockin 'stockout 'VoucherType ----------------------------------------- 1 | 1 | 5 | 0 | 1 2 | 1 | 5 | 0 | 1 3 | 1 | 5 | 0 | 1 4 | 2 | 5 | 0 | 1 5 | 2 | 0 | 10 | 2 6 | 1 | 0 | 2 | 2 7 | 2 | 0 | 3 | 2

中的StandardJavaClassFormat和列名称编写的

希望这可以节省一些人来这里使用谷歌搜索: - )

standardJavaFieldFormat

答案 2 :(得分:1)

希望这会有所帮助:

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

}

答案 3 :(得分:1)

感谢那篇文章。有点恼人,升级打破了表和列名称策略。而不是从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);
    }
}

答案 4 :(得分:1)

每个答案都通过实施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);
    }
}

答案 5 :(得分:0)

没有Guava和Apache utils

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

答案 6 :(得分:-8)

刚刚发现问题,使用Hibernate版本时配置绝对正常&lt; 5.0但不适用于Hibernate&gt; = 5.0。

我在Spring 4.2.0.RELEASE中使用了Hibernate 5.0.0.Final。我猜Hibernate 5与Spring 4.2不完全兼容。我刚刚将Hibernate降级到4.2.1.Final,事情开始正常。

在Hibernate 5中不推荐使用Hibernate的<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>类。

相关问题