Hibernate ImprovedNamingStrategy doesn't work - java

I have a database with columns naming using snakecase and entities where columns named in camelcase. The project was written using spring boot and had this configuration:
#Primary
#Bean
public LocalContainerEntityManagerFactoryBean mainEntityManagerFactory(
#Qualifier("mainDataSource") DataSource mainDataSource,
#Qualifier("mainJpaProperties") JpaProperties mainJpaProperties) {
AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setShowSql(mainJpaProperties.isShowSql());
adapter.setDatabase(mainJpaProperties.getDatabase());
adapter.setDatabasePlatform(mainJpaProperties.getDatabasePlatform());
adapter.setGenerateDdl(mainJpaProperties.isGenerateDdl());
Map<String, Object> jpaProperties = new HashMap<>();
jpaProperties.putAll(mainJpaProperties.getHibernateProperties(mainDataSource));
jpaProperties.put("jpa.hibernate.naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy");
return new EntityManagerFactoryBuilder(adapter, mainJpaProperties, persistenceUnitManager)
.dataSource(mainDataSource)
.packages(Employee.class)
.properties(jpaProperties)
.persistenceUnit("main")
.jta(false)
.build();
}
this is application.properties content:
jpa.hibernate.hbm2ddl.auto=none
jpa.hibernate.ddlAuto=none
jpa.hibernate.naming_strategy = org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy
jpa.generateDdl=false
jpa.showSql=false
jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL9Dialect
Now I can't use spring boot and trying to convert configuration:
#Primary
#Bean
public LocalContainerEntityManagerFactoryBean mainEntityManagerFactory(
#Qualifier("mainDataSource") DataSource mainDataSource
) {
AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setShowSql(Boolean.valueOf(environment.getProperty("jpa.showSql")));
adapter.setDatabase(Database.POSTGRESQL);
adapter.setDatabasePlatform(environment.getProperty("jpa.hibernate.dialect"));
adapter.setGenerateDdl(Boolean.valueOf(environment.getProperty("jpa.generateDdl")));
Properties jpaProperties = new Properties();
jpaProperties.put("jpa.hibernate.dialect", environment.getProperty("jpa.hibernate.dialect"));
jpaProperties.put("hibernate.naming_strategy", "main.util.NamingStrategy");
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(mainDataSource);
factoryBean.setJpaVendorAdapter(adapter);
factoryBean.setPersistenceUnitName("main");
factoryBean.setPersistenceUnitManager(persistenceUnitManager);
factoryBean.setJpaProperties(jpaProperties);
factoryBean.setPackagesToScan(packageToScan);
factoryBean.afterPropertiesSet();
return factoryBean;
}
main.util.NamingStrategy is just a copypaste of org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy.
When I trying to run it it looks like that this naming strategy doesn't work because I get an error:
org.hibernate.AnnotationException:
Unable to create unique key constraint (emp_address_id, year, yearNumber)
on table employeedata:
database column 'emp_address_id' not found.
Make sure that you use the correct column name which depends
on the naming strategy in use
(it may not be the same as the property name in the entity,
especially for relational types)
How can I make this work without spring boot?

Just to put it explicitly(as i had to spend some time figuring it out from Maciej's answer), this works for me on Hibernate 5.x:
properties.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");

jpa.hibernate.naming_strategy property key is used only in Spring Boot's configuration.
When you configure hibernate by yourself you should use Hibernate properties instead. Proper name of Hibernate naming strategy property key is
hibernate.ejb.naming_strategy
Try to replace it in your jpaProperties object:
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.dialect", environment.getProperty("jpa.hibernate.dialect")); //jpa dialect property key is different too
jpaProperties.put("hibernate.ejb.naming_strategy", "main.util.NamingStrategy");
List of all miscellaneous properties:
https://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/#configuration-misc-properties
I am assuming that you only removed Spring Boot and you are still using Hibernate 4.x (in Hibernate 5.x NamingStrategy was replaced with ImplicitNamingStrategy and PhysicalNamingStrategy)
For hibernate 5.x you should use org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNam‌​ingStrategy and default Hibernate's ImplicitNamingStrategy:
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.4-Release-Notes#naming-strategy
Configuration key for this physical naming strategy is: hibernate.physical_naming_strategy

Related

Hibernate schema validation with multiple data sources in multi tenant(separate databases) application

I have a Spring Boot app with multi-tenancy enabled with separate database for each tenant. I also have a database which holds tenant datasource information and another database for user data. All tenant databases have the same schema.
Datasource information is loaded at app start and kept in a map for AbstractDataSourceBasedMultiTenantConnectionProviderImpl implementation to use.
I'm using Liquibase and I've set it up so that it updates the tenant databases with each run of the app, and now I'm trying to enable spring.jpa.hibernate.ddl-auto=validate for each of the tenant databases but not for the user or datasource databases.
The idea is for the workflow to look like this: change app and db->liquibase updates db->hibernate validates the changes.
Here's my hibernate configuration right now:
#Configuration
public class HibernateConfig {
#Autowired
private JpaProperties jpaProperties;
#Bean
JpaVendorAdapter jpaVendorAdapter(){
return new HibernateJpaVendorAdapter();
}
#Bean(name="entityManagerFactory")
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(
DataSource dataSource,
MultiTenantConnectionProvider multiTenantConnectionProviderImpl,
CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl
){
Map<String,Object> jpaPropertiesMap=new HashMap<>(jpaProperties.getProperties());
jpaPropertiesMap.put(Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
jpaPropertiesMap.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER,multiTenantConnectionProviderImpl);
jpaPropertiesMap.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER,currentTenantIdentifierResolverImpl);
jpaPropertiesMap.put(Environment.FORMAT_SQL,true);
jpaPropertiesMap.put(Environment.SHOW_SQL,true);
//jpaPropertiesMap.put(Environment.HBM2DDL_AUTO,"validate");
LocalContainerEntityManagerFactoryBean em=new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan("com.klikfin.*");
em.setJpaVendorAdapter(this.jpaVendorAdapter());
em.setJpaPropertyMap(jpaPropertiesMap);
return em;
}
}
When I enable jpaPropertiesMap.put(Environment.HBM2DDL_AUTO,"validate"); it only validates the default datasource database.
How would I enable it for specific databases only?

Which dependency is responsible for creation of tables from spring boot entities automatically

I have a spring-boot project but I want it to migrate entity classes to database tables when I specify #Table and #Entity annotations. For now, I am creating the tables manually.
Below is my HibernateJpaVendorAdapter
HibernateJpaVendorAdapter vendorAdapter
= new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("spring.jpa.generate-ddl",env.getProperty("spring.jpa.generate-ddl"));
properties.put("spring.jpa.hibernate.ddl-auto",
env.getProperty("spring.jpa.hibernate.ddl-auto"));
em.setJpaPropertyMap(properties);
return em;
Below is application.properties
You would need spring-boot-starter-data-jpa dependency in your class path and you have to set spring.jpa.hibernate.ddl-auto to update or create in your application.properties.
But it should not be used it production.

How to create SessionFactory bean when datasource is defined but not xml but by configuration bean

Hibernate 5.4
The dialect is known, I need to implement the following method :
#Bean
public org.hibernate.SessionFactory sessionFactory(DataSource dataSource) {
// hibernate.dialect = "org.hibernate.dialect.PostgreSQL95Dialect"
// ???
}
Maybe creating one via a Configuration would work for you, as descibed here:
Create Sessionfactory in Hibernate
Configuration cfg = new Configuration()...
.setProperty("hibernate.connection.username", "myuser");
.setProperty("hibernate.connection.password", "mypassword")
.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/hibernate_example")
SessionFactory sessionFactory = cfg.buildSessionFactory();
Or you choose to use the Spring LocalSessionFactory-Class like so:
lsfb = new LocalSessionFactoryBean() [from hib5-package]
lsfb.setDataSource( yourDS );
return lsfb.getObject();
Setting the Datasource this might help:
Luiggi Mendoza on SO (How can I set Datasource when I'm creating Hibernate SessionFactory?
)
But if you use a custom data source provider like Apache DBCP or BoneCP and you don't want to use a dependency injection framework like Spring, then you may inject it on the StandardServiceRegistryBuilder before creating the SessionFactory...

LocalContainerEntityManagerFactoryBean

I have gone through the spring data jpa reference documentation
to configure a datasource in spring boot,and with LocalcontainerEntityManagerFactoryBean and transactionManager..etc,but run it
with error
but I want configure a datasource of mysql,single datasource.
this is configuration class code:
#Configuration
#EnableJpaRepositories
#EnableTransactionManagement
public class DataSourceConfig {
#Bean
#ConfigurationProperties(prefix="oneslide.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendor=new HibernateJpaVendorAdapter();
vendor.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory=new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendor);
factory.setPackagesToScan("com.oneslide.multiDataSource.domain");
factory.setDataSource(dataSource());
return factory;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager=new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
I don't want user DatasourceBuilder.create.url().password().. something chain invocation like that,I just want to congiure my sql connection metadata in
application.properties with oneslide.datasource namespce.And try to use the LocalContainerEntityManagerFactory Bean,not with tutorial's way in which they
use spring.datasource.* property.
but when i run it datasource debug info is null,there it is digest of exception log:
Invocation of init method failed; nested exception is
org.hibernate.service.spi.ServiceException: Unable to create requested
service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
: Access to DialectResolutionInfo cannot be null when
'hibernate.dialect' not set
Help me thanks a lot.
I totally misunderstand spring boot,Maybe.....it autoconfigure all bean,like
LocalContainerEntityManagerfactoryBean!so to use multidatasource,I just need to configure a datasource only,Right???
Your annotation #ConfigurationProperties(prefix="oneslide.datasource") is asking Sprint to get the info from the external configuration and bind it with the bean you are annotating, i.e. the result produced by the method.
BUT the annotated bean MUST have the properties to receive the configuration values. I.e. it has to have fields and inner objects that replicate the structure of the configurations you are passing (and the setters too).
As example if your config contains something like below:
oneslide.datasource.url = some_url
oneslide.datasource.user = usr
oneslide.datasource.password = pw
oneslide.datasource.special.detail = whatever
The bean you build should have fields "url", "user" and "password" AND an object "special" with a field "detail", so that Spring can set the values. Simplifying something along Y = X.getSpecial(); Y.setDetail() (with null recognition and object creation too, I think to remember).
If you do nothing... Spring behind the scene will create a DataSourceProperties bean (that unsurprisingly contains the fields normally used to set up a datasource with the config info under "spring.datasource").
You can get hold of this bean by defining your own bean that gets it as a parameter, like below:
public <whatever> getTheD_S_Properties(DataSourceProperties myDataSourceValesFromConfig) {
...do something with the bean you got,
that contains the values from your config...
}
The most common operation, in this case is to just build the datasource yourself, with some logic beyond just assigning the values form the config.
If you need to do nothing special, then let Spring to build the datasource too.
Just sit back and enjoy ! :)

Calling a Native Query in the same request life cycle as a JPA managed query causes automatic persist

I'm sure this is a feature, but I would appreciate any pointers/help on the issue.
I'm using Spring (Boot) / JPA (Hibernate) stack.
If I query for an entity (called Homes) using JPA, and increment a Calendar field
homeInstance.getListedDate().add(Calendar.DATE, 1), for example
with no intention of saving this change back to the database (it's only for quick intermediary calculations as the several routines run on a list of these entities).
Then I call a nativequery using an injected EntityManager bean.
#Autowired
EntityManager em;
...
Query nvqry = em.createNativeQuery(...)
nvqry.getResultList()
Doing this automatically persists the changes made to the entity above (which were never supposed to be persisted.
Is there a way to disable this feature without losing the in-memory changes? I manually persist anything I want using the repository, and as such a whole session persistence is useless for me.
That's related to the hibernate FlushMode. If you haven't modified the default configuration, this will be likely set to FlushMode.AUTO.
To alter this behavior you have to provide an EntityManagerFactory bean.
In the configuration pojo you can declare something like this:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
final DataSource dataSource,
final JpaVendorAdapter jpaVendorAdapter) {
final LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setPersistenceUnitName("domainPC");
factory.setDataSource(dataSource);
factory.setJpaVendorAdapter(jpaVendorAdapter);
factory.setPackagesToScan(getClass().getPackage().getName());
Properties jpaProperties = new Properties();
//and here is your flushMode set.
jpaProperties.setProperty("org.hibernate.flushMode", "COMMIT");
factory.setJpaProperties(jpaProperties);
return factory;
}

Categories

Resources