2015-09-07 36 views
27

Ho una semplice configurazione spring-jpa in cui ho configurato Hibernate's ImprovedNamingStrategy. Questo significa che se la mia classe di entità ha una variabile userName, Hibernate dovrebbe convertirla in user_name per interrogare il database. Ma questa conversione di denominazione smesso di funzionare dopo aver aggiornato a Hibernate 5. sto ottenendo l'errore:ImprovedNamingStrategy non funziona più in Hibernate 5

ERROR: Unknown column 'user0_.userName' in 'field list'

Questo è il mio Hibernate config:

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

Questa è la mia classe 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; 

} 

Non voglio nominare esplicitamente i miei campi di database all'interno delle annotazioni @Column. Voglio la mia configurazione che può implicitamente convertire il caso cammello in underscore.

Guida.

+1

Non capisco il motivo per cui non si desidera utilizzare @Column –

+4

@Tyler il suo solo per la facilità di codifica, aggiungendo "@Column" per ciascuna delle variabili è solo fastidioso, invece posso configurare Naming- Strategia che mapperà il nome della variabile ai nomi delle colonne db e posso evitare di scrivere così tante annotazioni sulla colonna – Anup

risposta

-8

Appena capito il problema, la configurazione è assolutamente valida quando si utilizza una versione Hibernate < 5.0 ma non per Hibernate> = 5.0.

Stavo usando Hibernate 5.0.0.Final con Spring 4.2.0.RELEASE. Immagino che Hibernate 5 non sia completamente compatibile con Spring 4.2. Ho appena decollato Hibernate alla 4.2.1. Finale e le cose hanno iniziato a funzionare bene.

classe di Hibernate NamingStrategy è deprecato in Hibernate 5.

50

Grazie per aver postato la propria soluzione. Mi aiuta così tanto ad impostare la strategia di naming di Hibernate 5!

La hibernate.ejb.naming_strategy proprietà di pre-Hibernate 5.0 sembra diviso in due parti:

  • hibernate.physical_naming_strategy
  • hibernate.implicit_naming_strategy

I valori di queste proprietà non implementano l'interfaccia NamingStrategy come ha fatto hibernate.ejb.naming_strategy. Ci sono due nuove interfacce per questi scopi:

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

Hibernate 5 prevede solo un'implementazione di PhysicalNamingStrategy (PhysicalNamingStrategyStandardImpl) che assume nomi di identificatore fisiche sono le stesse di quelle logiche.

Esistono diverse implementazioni di ImplicitNamingStrategy ma non ho trovato nessuno equivalente al vecchio ImprovedNamingStrategy. (Vedi: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl)

Così, ho implementato il mio PhysicalNamingStrategy che è molto semplice:

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

Si noti che il metodo è addUnderscores() dall'originale org.hibernate.cfg.ImprovedNamingStrategy.

Poi, ho impostato questa strategia fisica nel file persistence.xml:

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

E 'una trappola per impostare Hibernate 5 strategia di denominazione come precedenti impostazioni di versione.

+1

Inoltre, insieme alla strategia di denominazione fisica personalizzata, la proprietà 'hibernate.implicit_naming_strategy' può essere impostata su' org.hibernate.boot.model .naming.ImplicitNamingStrategyLegacyHbmImpl' – Glenn

+0

Grazie a @ Samuel per spiegare questo e fornire un esempio per implementare la propria Naming_Strategy. È utile e anche io sto cercando di implementare Naming_Strategy simile – Anup

+0

Worked For me :) –

1

speranza che questo aiuta:

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

ed ecco il codice (appena ri-arrenged da codice esistente):

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

} 
2

Grazie e uno a Samuel Andrés per la risposta molto utile, tuttavia è probabilmente una buona idea per evitare lo snake- scritta a mano logica di involucro. Ecco la stessa soluzione, usando Guava.

Assume i vostri nomi di entità sono scritti nei nomi StandardJavaClassFormat e colonna nel standardJavaFieldFormat

Speriamo che questo vi farà risparmiare alcune persone che vengono qui in futuro qualche googling :-)

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

Impressionante, anche se ha alcuni dei suoi difetti.Ad esempio, il nome di colonna discriminatore predefinito di Hibernate è "DTYPE" che verrà convertito in "d_t_y_p_e". – xathien

+0

@xathien grazie, non ne ero a conoscenza. Immagino che una mappatura statica di casi speciali possa essere aggiunta per affrontare cose del genere in modo chiaro. – davnicwil

1

grazie per quel post . Poco entusiasmante che l'aggiornamento rompa la strategia del nome della tabella e della tabella. Invece di copiare la logica da ImprovedNamingStrategy potresti anche usare la delega.

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

Non penso che sia un'idea geniale. 'ImprovedNamingStrategy' implementa un'interfaccia deprecata. Mentre solo l'interfaccia 'NamingStrategy' è esplicitamente contrassegnata come deprecata, suppongo che una volta rimossa sarà rimossa anche tutte le classi di implementazione. –

+0

@ MarcelStör - Penso che sia una caratteristica di questo approccio, non un bug! Se e quando Hibernate andrà in giro a sostituire la vecchia "ImprovedNamingStrategy", si spera che rimuoveranno quella classe deprecata e questo codice si romperà in modo fasullo, dandoci la possibilità di passare alla nuova implementazione ufficiale e rimuovere questa soluzione alternativa. – Rich